=== modified file 'src/android.cpp'
--- src/android.cpp	2012-07-30 12:28:16 +0000
+++ src/android.cpp	2012-08-13 10:09:08 +0000
@@ -101,7 +101,7 @@
         std::string line;
         while (getline(ifs, line)) {
             if (!line.empty())
-                Util::split(line, ' ', arguments);
+                Util::split(line, ' ', arguments, Util::SplitModeQuoted);
         }
     }
 
@@ -118,7 +118,7 @@
 get_args_from_string(const std::string &args_str, int &argc, char **&argv)
 {
     std::vector<std::string> arguments;
-    Util::split(args_str, ' ', arguments);
+    Util::split(args_str, ' ', arguments, Util::SplitModeQuoted);
 
     arg_vector_to_argv(arguments, argc, argv);
 }

=== modified file 'src/benchmark.cpp'
--- src/benchmark.cpp	2012-07-17 09:29:41 +0000
+++ src/benchmark.cpp	2012-08-13 10:09:08 +0000
@@ -35,7 +35,7 @@
 {
     vector<string> elems;
 
-    Util::split(s, ':', elems);
+    Util::split(s, ':', elems, Util::SplitModeNormal);
 
     const string &name = !elems.empty() ? elems[0] : "";
 
@@ -48,7 +48,7 @@
     vector<Benchmark::OptionPair> options;
     vector<string> elems;
 
-    Util::split(s, ':', elems);
+    Util::split(s, ':', elems, Util::SplitModeNormal);
 
     for (vector<string>::const_iterator iter = elems.begin() + 1;
          iter != elems.end();
@@ -56,7 +56,7 @@
     {
         vector<string> opt;
 
-        Util::split(*iter, '=', opt);
+        Util::split(*iter, '=', opt, Util::SplitModeNormal);
         if (opt.size() == 2)
             options.push_back(Benchmark::OptionPair(opt[0], opt[1]));
         else

=== modified file 'src/gl-visual-config.cpp'
--- src/gl-visual-config.cpp	2012-05-11 13:49:53 +0000
+++ src/gl-visual-config.cpp	2012-08-13 10:09:08 +0000
@@ -30,7 +30,7 @@
 {
     std::vector<std::string> elems;
 
-    Util::split(s, ':', elems);
+    Util::split(s, ':', elems, Util::SplitModeNormal);
 
     for (std::vector<std::string>::const_iterator iter = elems.begin();
          iter != elems.end();
@@ -38,7 +38,7 @@
     {
         std::vector<std::string> opt;
 
-        Util::split(*iter, '=', opt);
+        Util::split(*iter, '=', opt, Util::SplitModeNormal);
         if (opt.size() == 2) {
             if (opt[0] == "r" || opt[0] == "red")
                 red = Util::fromString<int>(opt[1]);

=== modified file 'src/libmatrix/Makefile'
--- src/libmatrix/Makefile	2012-05-22 09:43:45 +0000
+++ src/libmatrix/Makefile	2012-08-13 10:09:08 +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/shader-source.cc'
--- src/libmatrix/shader-source.cc	2012-01-26 17:00:43 +0000
+++ src/libmatrix/shader-source.cc	2012-08-13 10:09:08 +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 17:00:43 +0000
+++ src/libmatrix/test/libmatrix_test.cc	2012-08-13 10:09:08 +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-13 10:09:08 +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-13 10:09:08 +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:33:07 +0000
+++ src/libmatrix/util.cc	2012-08-13 10:09:08 +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:33:07 +0000
+++ src/libmatrix/util.h	2012-08-13 10:09:08 +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
      */

=== modified file 'src/main-loop.cpp'
--- src/main-loop.cpp	2012-06-15 13:24:32 +0000
+++ src/main-loop.cpp	2012-08-13 10:09:08 +0000
@@ -252,7 +252,7 @@
 {
     LibMatrix::vec2 v(0.0, 0.0);
     std::vector<std::string> elems;
-    Util::split(s, ',', elems);
+    Util::split(s, ',', elems, Util::SplitModeNormal);
 
     if (elems.size() > 0)
         v.x(Util::fromString<float>(elems[0]));

=== modified file 'src/options.cpp'
--- src/options.cpp	2012-06-15 09:03:04 +0000
+++ src/options.cpp	2012-08-13 10:09:08 +0000
@@ -74,7 +74,7 @@
 parse_size(const std::string &str, std::pair<int,int> &size)
 {
     std::vector<std::string> d;
-    Util::split(str, 'x', d);
+    Util::split(str, 'x', d, Util::SplitModeNormal);
 
     size.first = Util::fromString<int>(d[0]);
 

=== modified file 'src/scene-effect-2d.cpp'
--- src/scene-effect-2d.cpp	2012-07-16 14:25:30 +0000
+++ src/scene-effect-2d.cpp	2012-08-13 10:09:08 +0000
@@ -194,7 +194,7 @@
     std::vector<std::string> rows;
     unsigned int w = UINT_MAX;
 
-    Util::split(str, ';', rows);
+    Util::split(str, ';', rows, Util::SplitModeNormal);
 
     Log::debug("Parsing kernel matrix:\n");
     static const std::string format("%f ");
@@ -206,7 +206,7 @@
          iter++)
     {
         std::vector<std::string> elems;
-        Util::split(*iter, ',', elems);
+        Util::split(*iter, ',', elems, Util::SplitModeNormal);
 
         if (w != UINT_MAX && elems.size() != w) {
             Log::error("Matrix row %u contains %u elements, whereas previous"

=== modified file 'src/scene.cpp'
--- src/scene.cpp	2012-07-17 09:12:39 +0000
+++ src/scene.cpp	2012-08-13 10:09:08 +0000
@@ -38,7 +38,7 @@
                       const std::string &values) :
 name(nam), value(val), default_value(val), description(desc), set(false)
 {
-    Util::split(values, ',', acceptable_values);
+    Util::split(values, ',', acceptable_values, Util::SplitModeNormal);
 }
 
 Scene::Scene(Canvas &pCanvas, const string &name) :

