=== modified file 'src/libmatrix/Makefile'
--- src/libmatrix/Makefile	2012-05-22 09:55:39 +0000
+++ src/libmatrix/Makefile	2012-08-22 18:35:18 +0000
@@ -9,6 +9,7 @@
            $(TESTDIR)/inverse_test.cc \
            $(TESTDIR)/transpose_test.cc \
            $(TESTDIR)/shader_source_test.cc \
+           $(TESTDIR)/util_split_test.cc \
            $(TESTDIR)/libmatrix_test.cc
 TESTOBJS = $(TESTSRCS:.cc=.o)
 
@@ -32,6 +33,7 @@
 $(TESTDIR)/inverse_test.o: $(TESTDIR)/inverse_test.cc $(TESTDIR)/inverse_test.h $(TESTDIR)/libmatrix_test.h mat.h
 $(TESTDIR)/transpose_test.o: $(TESTDIR)/transpose_test.cc $(TESTDIR)/transpose_test.h $(TESTDIR)/libmatrix_test.h mat.h
 $(TESTDIR)/shader_source_test.o: $(TESTDIR)/shader_source_test.cc $(TESTDIR)/shader_source_test.h $(TESTDIR)/libmatrix_test.h shader-source.h
+$(TESTDIR)/util_split_test.o: $(TESTDIR)/util_split_test.cc $(TESTDIR)/util_split_test.h $(TESTDIR)/libmatrix_test.h util.h
 $(TESTDIR)/libmatrix_test: $(TESTOBJS) libmatrix.a
 	$(CXX) -o $@ $^
 run_tests: $(LIBMATRIX_TESTS)

=== modified file 'src/libmatrix/log.cc'
--- src/libmatrix/log.cc	2012-01-27 22:21:17 +0000
+++ src/libmatrix/log.cc	2012-08-22 18:35:18 +0000
@@ -10,6 +10,7 @@
 //     Alexandros Frantzis <alexandros.frantzis@linaro.org>
 //     Jesse Barker <jesse.barker@linaro.org>
 //
+#include <unistd.h>
 #include <cstdio>
 #include <cstdarg>
 #include <string>
@@ -26,8 +27,7 @@
 const string Log::continuation_prefix("\x10");
 string Log::appname_;
 bool Log::do_debug_(false);
-
-#ifndef ANDROID
+std::ostream* Log::extra_out_(0);
 
 static const string terminal_color_normal("\033[0m");
 static const string terminal_color_red("\033[1;31m");
@@ -96,17 +96,26 @@
     delete[] buf;
 }
 
+
 void
 Log::info(const char *fmt, ...)
 {
     static const string infoprefix("Info");
+    const string& prefix(do_debug_ ? infoprefix : empty);
+    va_list ap;
+    va_start(ap, fmt);
+
+#ifndef ANDROID
     static const string& infocolor(isatty(fileno(stdout)) ? terminal_color_cyan : empty);
-    va_list ap;
-    va_start(ap, fmt);
-    if (do_debug_)
-        print_prefixed_message(std::cout, infocolor, infoprefix, fmt, ap);
-    else
-        print_prefixed_message(std::cout, empty, empty, fmt, ap);
+    const string& color(do_debug_ ? infocolor : empty);
+    print_prefixed_message(std::cout, color, prefix, fmt, ap);
+#else
+    __android_log_vprint(ANDROID_LOG_INFO, appname_.c_str(), fmt, ap);
+#endif
+
+    if (extra_out_)
+        print_prefixed_message(*extra_out_, empty, prefix, fmt, ap);
+
     va_end(ap);
 }
 
@@ -114,12 +123,21 @@
 Log::debug(const char *fmt, ...)
 {
     static const string dbgprefix("Debug");
+    if (!do_debug_)
+        return;
+    va_list ap;
+    va_start(ap, fmt);
+
+#ifndef ANDROID
     static const string& dbgcolor(isatty(fileno(stdout)) ? terminal_color_yellow : empty);
-    if (!do_debug_)
-        return;
-    va_list ap;
-    va_start(ap, fmt);
     print_prefixed_message(std::cout, dbgcolor, dbgprefix, fmt, ap);
+#else
+    __android_log_vprint(ANDROID_LOG_DEBUG, appname_.c_str(), fmt, ap);
+#endif
+
+    if (extra_out_)
+        print_prefixed_message(*extra_out_, empty, dbgprefix, fmt, ap);
+
     va_end(ap);
 }
 
@@ -127,52 +145,29 @@
 Log::error(const char *fmt, ...)
 {
     static const string errprefix("Error");
+    va_list ap;
+    va_start(ap, fmt);
+
+#ifndef ANDROID
     static const string& errcolor(isatty(fileno(stderr)) ? terminal_color_red : empty);
-    va_list ap;
-    va_start(ap, fmt);
     print_prefixed_message(std::cerr, errcolor, errprefix, fmt, ap);
+#else
+    __android_log_vprint(ANDROID_LOG_ERROR, appname_.c_str(), fmt, ap);
+#endif
+
+    if (extra_out_)
+        print_prefixed_message(*extra_out_, empty, errprefix, fmt, ap);
+
     va_end(ap);
 }
 
 void
 Log::flush()
 {
+#ifndef ANDROID
     std::cout.flush();
     std::cerr.flush();
-}
-#else
-void
-Log::info(const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    __android_log_vprint(ANDROID_LOG_INFO, appname_.c_str(), fmt, ap);
-    va_end(ap);
-}
-
-void
-Log::debug(const char *fmt, ...)
-{
-    if (!do_debug_)
-        return;
-    va_list ap;
-    va_start(ap, fmt);
-    __android_log_vprint(ANDROID_LOG_DEBUG, appname_.c_str(), fmt, ap);
-    va_end(ap);
-}
-
-void
-Log::error(const char *fmt, ...)
-{
-    va_list ap;
-    va_start(ap, fmt);
-    __android_log_vprint(ANDROID_LOG_ERROR, appname_.c_str(), fmt, ap);
-    va_end(ap);
-}
-
-void
-Log::flush()
-{
-}
-
 #endif
+    if (extra_out_)
+        extra_out_->flush();
+}

=== modified file 'src/libmatrix/log.h'
--- src/libmatrix/log.h	2012-01-27 22:21:17 +0000
+++ src/libmatrix/log.h	2012-08-22 18:35:18 +0000
@@ -14,14 +14,17 @@
 #define LOG_H_
 
 #include <string>
+#include <iostream>
 
 class Log
 {
 public:
-    static void init(const std::string& appname, bool do_debug = false)
+    static void init(const std::string& appname, bool do_debug = false,
+                     std::ostream *extra_out = 0)
     {
         appname_ = appname;
         do_debug_ = do_debug;
+        extra_out_ = extra_out;
     }
     // Emit an informational message
     static void info(const char *fmt, ...);
@@ -41,6 +44,8 @@
     static std::string appname_;
     // Indicates whether debug level messages should generate any output
     static bool do_debug_;
+    // Extra stream to output log messages to
+    static std::ostream *extra_out_;
 };
 
 #endif /* LOG_H_ */

=== modified file 'src/libmatrix/program.cc'
--- src/libmatrix/program.cc	2012-01-26 19:36:28 +0000
+++ src/libmatrix/program.cc	2012-08-22 18:35:18 +0000
@@ -20,6 +20,7 @@
 
 using std::string;
 using LibMatrix::mat4;
+using LibMatrix::mat3;
 using LibMatrix::vec2;
 using LibMatrix::vec3;
 using LibMatrix::vec4;
@@ -276,6 +277,17 @@
 }
 
 Program::Symbol&
+Program::Symbol::operator=(const mat3& m)
+{
+    if (type_ == Uniform)
+    {
+        // Our matrix representation is column-major, so transpose is false here.
+        glUniformMatrix3fv(location_, 1, GL_FALSE, m);
+    }
+    return *this;
+}
+
+Program::Symbol&
 Program::Symbol::operator=(const vec2& v)
 {
     if (type_ == Uniform)

=== modified file 'src/libmatrix/program.h'
--- src/libmatrix/program.h	2012-01-26 19:36:28 +0000
+++ src/libmatrix/program.h	2012-08-22 18:35:18 +0000
@@ -127,6 +127,7 @@
         // These members cause data to be bound to program variables, so
         // the program must be bound for use for these to be effective.
         Symbol& operator=(const LibMatrix::mat4& m);
+        Symbol& operator=(const LibMatrix::mat3& m);
         Symbol& operator=(const LibMatrix::vec2& v);
         Symbol& operator=(const LibMatrix::vec3& v);
         Symbol& operator=(const LibMatrix::vec4& v);

=== modified file 'src/libmatrix/shader-source.cc'
--- src/libmatrix/shader-source.cc	2012-01-26 19:36:28 +0000
+++ src/libmatrix/shader-source.cc	2012-08-22 18:35:18 +0000
@@ -589,7 +589,7 @@
 {
     std::vector<std::string> elems;
 
-    Util::split(precision_values, ',', elems);
+    Util::split(precision_values, ',', elems, Util::SplitModeNormal);
 
     for (size_t i = 0; i < elems.size() && i < 4; i++) {
         const std::string& pstr(elems[i]);

=== modified file 'src/libmatrix/test/libmatrix_test.cc'
--- src/libmatrix/test/libmatrix_test.cc	2012-01-26 19:36:28 +0000
+++ src/libmatrix/test/libmatrix_test.cc	2012-08-22 18:35:18 +0000
@@ -8,6 +8,7 @@
 //
 // Contributors:
 //     Jesse Barker - original implementation.
+//     Alexandros Frantzis - Util::split tests
 //
 #include <iostream>
 #include <string>
@@ -17,6 +18,7 @@
 #include "transpose_test.h"
 #include "const_vec_test.h"
 #include "shader_source_test.h"
+#include "util_split_test.h"
 
 using std::cerr;
 using std::cout;
@@ -42,6 +44,8 @@
     testVec.push_back(new MatrixTest3x3Transpose());
     testVec.push_back(new MatrixTest4x4Transpose());
     testVec.push_back(new ShaderSourceBasic());
+    testVec.push_back(new UtilSplitTestNormal());
+    testVec.push_back(new UtilSplitTestQuoted());
 
     for (vector<MatrixTest*>::iterator testIt = testVec.begin();
          testIt != testVec.end();

=== added file 'src/libmatrix/test/util_split_test.cc'
--- src/libmatrix/test/util_split_test.cc	1970-01-01 00:00:00 +0000
+++ src/libmatrix/test/util_split_test.cc	2012-08-22 18:35:18 +0000
@@ -0,0 +1,180 @@
+//
+// Copyright (c) 2012 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis - original implementation.
+//
+#include <iostream>
+#include <string>
+#include <vector>
+#include "libmatrix_test.h"
+#include "util_split_test.h"
+#include "../util.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using std::vector;
+
+template <typename T> static bool
+areVectorsEqual(vector<T>& vec1, vector<T>& vec2)
+{
+    if (vec1.size() != vec2.size())
+        return false;
+
+    for (unsigned int i = 0; i < vec1.size(); i++)
+    {
+        if (vec1[i] != vec2[i])
+            return false;
+    }
+
+    return true;
+}
+
+template <typename T> static void
+printVector(vector<T>& vec)
+{
+    cout << "[";
+    for (unsigned int i = 0; i < vec.size(); i++)
+    {
+        cout << '"' << vec[i] << '"';
+        if (i < vec.size() - 1)
+            cout << ", ";
+    }
+    cout << "]";
+}
+
+void
+UtilSplitTestNormal::run(const Options& options)
+{
+    const string test1("abc def ghi");
+    const string test2(" abc: def :ghi ");
+    vector<string> expected1;
+    vector<string> expected2;
+    vector<string> results;
+
+    expected1.push_back("abc");
+    expected1.push_back("def");
+    expected1.push_back("ghi");
+
+    expected2.push_back(" abc");
+    expected2.push_back(" def ");
+    expected2.push_back("ghi ");
+
+    if (options.beVerbose())
+    {
+        cout << "Testing string \"" << test1 << "\"" << endl;
+    }
+
+    Util::split(test1, ' ', results, Util::SplitModeNormal);
+
+    if (options.beVerbose())
+    {
+        cout << "Split result: ";
+        printVector(results);
+        cout << endl << "Expected: ";
+        printVector(expected1);
+        cout << endl;
+    }
+
+    if (!areVectorsEqual(results, expected1))
+    {
+        return;
+    }
+
+    results.clear();
+
+    if (options.beVerbose())
+    {
+        cout << "Testing string \"" << test2 << "\"" << endl;
+    }
+
+    Util::split(test2, ':', results, Util::SplitModeNormal);
+
+    if (options.beVerbose())
+    {
+        cout << "Split result: ";
+        printVector(results);
+        cout << endl << "Expected: ";
+        printVector(expected2);
+        cout << endl;
+    }
+
+    if (!areVectorsEqual(results, expected2))
+    {
+        return;
+    }
+
+    pass_ = true;
+}
+
+void
+UtilSplitTestQuoted::run(const Options& options)
+{
+    const string test1("abc \"def' ghi\" klm\\ nop -b qr:title='123 \"456'");
+    const string test2("abc: def='1:2:3:'ghi : \":jk\"");
+    vector<string> expected1;
+    vector<string> expected2;
+    vector<string> results;
+
+    expected1.push_back("abc");
+    expected1.push_back("def' ghi");
+    expected1.push_back("klm nop");
+    expected1.push_back("-b");
+    expected1.push_back("qr:title=123 \"456");
+
+    expected2.push_back("abc");
+    expected2.push_back(" def=1:2:3:ghi ");
+    expected2.push_back(" :jk");
+
+    if (options.beVerbose())
+    {
+        cout << "Testing string \"" << test1 << "\"" << endl;
+    }
+
+    Util::split(test1, ' ', results, Util::SplitModeQuoted);
+
+    if (options.beVerbose())
+    {
+        cout << "Split result: ";
+        printVector(results);
+        cout << endl << "Expected: ";
+        printVector(expected1);
+        cout << endl;
+    }
+
+    if (!areVectorsEqual(results, expected1))
+    {
+        return;
+    }
+
+    results.clear();
+
+    if (options.beVerbose())
+    {
+        cout << "Testing string \"" << test2 << "\"" << endl;
+    }
+
+    Util::split(test2, ':', results, Util::SplitModeQuoted);
+
+    if (options.beVerbose())
+    {
+        cout << "Split result: ";
+        printVector(results);
+        cout << endl << "Expected: ";
+        printVector(expected2);
+        cout << endl;
+    }
+
+    if (!areVectorsEqual(results, expected2))
+    {
+        return;
+    }
+
+    pass_ = true;
+}

=== added file 'src/libmatrix/test/util_split_test.h'
--- src/libmatrix/test/util_split_test.h	1970-01-01 00:00:00 +0000
+++ src/libmatrix/test/util_split_test.h	2012-08-22 18:35:18 +0000
@@ -0,0 +1,31 @@
+//
+// Copyright (c) 2012 Linaro Limited
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License which accompanies
+// this distribution, and is available at
+// http://www.opensource.org/licenses/mit-license.php
+//
+// Contributors:
+//     Alexandros Frantzis - original implementation.
+//
+#ifndef UTIL_SPLIT_TEST_H_
+#define UTIL_SPLIT_TEST_H_
+
+class MatrixTest;
+class Options;
+
+class UtilSplitTestNormal : public MatrixTest
+{
+public:
+    UtilSplitTestNormal() : MatrixTest("Util::split::normal") {}
+    virtual void run(const Options& options);
+};
+
+class UtilSplitTestQuoted : public MatrixTest
+{
+public:
+    UtilSplitTestQuoted() : MatrixTest("Util::split::quoted") {}
+    virtual void run(const Options& options);
+};
+#endif // UTIL_SPLIT_TEST_H_

=== modified file 'src/libmatrix/util.cc'
--- src/libmatrix/util.cc	2012-05-22 09:55:39 +0000
+++ src/libmatrix/util.cc	2012-08-22 18:35:18 +0000
@@ -25,25 +25,102 @@
 using std::string;
 using std::vector;
 
-void
-Util::split(const string& src, char delim, vector<string>& elementVec, bool fuzzy)
-{
-    // Trivial rejection
-    if (src.empty())
-    {
-        return;
-    }
-
-    // Simple case: we want to enforce the value of 'delim' strictly 
-    if (!fuzzy)
-    {
-        std::stringstream ss(src);
-        string item;
-        while(std::getline(ss, item, delim))
-            elementVec.push_back(item);
-        return;
-    }
-
+/*
+ * State machine for bash-like quoted string escaping:
+ *
+ *         \
+ *    -----------> +---------+
+ *    | ---------- | Escaped |
+ *    | |  *,ESC   +---------+
+ *    | |
+ *    | v      '
+ * +--------+ ---> +--------------+ -----
+ * | Normal | <--- | SingleQuoted |     | *, ESC
+ * +--------+  '   +--------------+ <----
+ *    | ^
+ *    | |
+ *    | |  "       +--------------+ ----
+ *    | ---------- | DoubleQuoted |    | *, ESC
+ *    -----------> +--------------+ <---
+ *         "             | ^
+ *                     \ | | *, ESC
+ *                       v |
+ *             +---------------------+
+ *             | DoubleQuotedEscaped |
+ *             +---------------------+
+ *
+ * ESC: Mark character as Escaped
+ */
+static void
+fill_escape_vector(const string &str, vector<bool> &esc_vec)
+{
+    enum State {
+        StateNormal,
+        StateEscaped,
+        StateDoubleQuoted,
+        StateDoubleQuotedEscaped,
+        StateSingleQuoted
+    };
+
+    State state = StateNormal;
+
+    for (string::const_iterator iter = str.begin();
+         iter != str.end();
+         iter++)
+    {
+        const char c(*iter);
+        bool esc = false;
+
+        switch (state) {
+            case StateNormal:
+                if (c == '"')
+                    state = StateDoubleQuoted;
+                else if (c == '\\')
+                    state = StateEscaped;
+                else if (c == '\'')
+                    state = StateSingleQuoted;
+                break;
+            case StateEscaped:
+                esc = true;
+                state = StateNormal;
+                break;
+            case StateDoubleQuoted:
+                if (c == '"')
+                    state = StateNormal;
+                else if (c == '\\')
+                    state = StateDoubleQuotedEscaped;
+                else
+                    esc = true;
+                break;
+            case StateDoubleQuotedEscaped:
+                esc = true;
+                state = StateDoubleQuoted;
+                break;
+            case StateSingleQuoted:
+                if (c == '\'')
+                    state = StateNormal;
+                else
+                    esc = true;
+            default:
+                break;
+        }
+
+        esc_vec.push_back(esc);
+    }
+}
+
+static void
+split_normal(const string& src, char delim, vector<string>& elementVec)
+{
+    std::stringstream ss(src);
+    string item;
+    while(std::getline(ss, item, delim))
+        elementVec.push_back(item);
+}
+
+static void
+split_fuzzy(const string& src, char delim, vector<string>& elementVec)
+{
     // Fuzzy case: Initialize our delimiter string based upon the caller's plus
     // a space to allow for more flexibility.
     string delimiter(" ");
@@ -76,6 +153,70 @@
     elementVec.push_back(str);
 }
 
+static void
+split_quoted(const string& src, char delim, vector<string>& elementVec)
+{
+    std::stringstream ss;
+    vector<bool> escVec;
+
+    /* Mark characters in the string as escaped or not */
+    fill_escape_vector(src, escVec);
+
+    /* Sanity check... */
+    if (src.length() != escVec.size())
+        return;
+
+    for (vector<bool>::const_iterator iter = escVec.begin();
+         iter != escVec.end();
+         iter++)
+    {
+        bool escaped = static_cast<bool>(*iter);
+        char c = src[iter - escVec.begin()];
+
+        /* Output all characters, except unescaped ",\,' */
+        if ((c != '"' && c != '\\' && c != '\'') || escaped) {
+            /* If we reach an unescaped delimiter character, do a split */
+            if (c == delim && !escaped) {
+                elementVec.push_back(ss.str());
+                ss.str("");
+                ss.clear();
+            }
+            else {
+                ss << c;
+            }
+        }
+
+    }
+
+    /* Handle final element, delimited by end of string */
+    const string &finalElement(ss.str());
+    if (!finalElement.empty())
+        elementVec.push_back(finalElement);
+}
+
+void
+Util::split(const string& src, char delim, vector<string>& elementVec,
+            Util::SplitMode mode)
+{
+    // Trivial rejection
+    if (src.empty())
+    {
+        return;
+    }
+
+    switch (mode)
+    {
+        case Util::SplitModeNormal:
+            return split_normal(src, delim, elementVec);
+        case Util::SplitModeFuzzy:
+            return split_fuzzy(src, delim, elementVec);
+        case Util::SplitModeQuoted:
+            return split_quoted(src, delim, elementVec);
+        default:
+            break;
+    }
+}
+
 uint64_t
 Util::get_timestamp_us()
 {

=== modified file 'src/libmatrix/util.h'
--- src/libmatrix/util.h	2012-05-22 09:55:39 +0000
+++ src/libmatrix/util.h	2012-08-22 18:35:18 +0000
@@ -25,21 +25,33 @@
 
 struct Util {
     /**
+     * How to perform the split() operation
+     */
+    enum SplitMode {
+        /** Normal split operation */
+        SplitModeNormal,
+        /** Allow for spaces and multiple consecutive occurences of the delimiter */
+        SplitModeFuzzy,
+        /** Take into account bash-like quoting and escaping rules */
+        SplitModeQuoted
+    };
+
+    /**
      * split() - Splits a string into elements using a provided delimiter
      *
      * @s:          the string to split
      * @delim:      the delimiter to use
      * @elems:      the string vector to populate
-     * @fuzzy:      (optional) enable/disable strict handling of @delim
+     * @mode:       the SplitMode to use
      *
      * Using @delim to determine field boundaries, splits @s into separate
      * string elements.  These elements are returned in the string vector
-     * @elems.  If @fuzzy is true, then the handling of @delim allows for
-     * spaces and multiple consecutive occurences of @delim in determining
-     * field boundaries.  As long as @s is non-empty, there will be at least
-     * one element in @elems.
+     * @elems. As long as @s is non-empty, there will be at least one
+     * element in @elems.
      */
-    static void split(const std::string &s, char delim, std::vector<std::string> &elems, bool fuzzy = false);
+    static void split(const std::string& src, char delim,
+                      std::vector<std::string>& elems,
+                      Util::SplitMode mode);
     /**
      * get_timestamp_us() - Returns the current time in microseconds
      */

