libwinpr-thread: partial support for character escaping in CommandLineToArgv
authorMarc-André Moreau <marcandre.moreau@gmail.com>
Sat, 21 Sep 2013 23:49:00 +0000 (19:49 -0400)
committerMarc-André Moreau <marcandre.moreau@gmail.com>
Sat, 21 Sep 2013 23:49:00 +0000 (19:49 -0400)
winpr/libwinpr/thread/argv.c
winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c

index 040aa81..01c887a 100644 (file)
@@ -101,6 +101,8 @@ LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
        int maxBufferSize;
        int currentIndex;
        int cmdLineLength;
+       LPSTR lpEscapedChars;
+       LPSTR lpEscapedCmdLine;
 
        if (!lpCmdLine)
                return NULL;
@@ -110,115 +112,196 @@ LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs)
 
        pArgs = NULL;
        numArgs = 0;
+
+       lpEscapedCmdLine = NULL;
        cmdLineLength = strlen(lpCmdLine);
 
-       if (!strstr(lpCmdLine, "\\\""))
+       lpEscapedChars = (char*) malloc(cmdLineLength + 1);
+       ZeroMemory(lpEscapedChars, cmdLineLength + 1);
+
+       if (strstr(lpCmdLine, "\\\""))
        {
-               /* Simplified case: only literal backslashes */
+               int i, n;
+
+               lpEscapedCmdLine = (char*) malloc(cmdLineLength + 1);
 
-               maxNumArgs = 1;
-               currentIndex = 0;
                p = (char*) lpCmdLine;
+               pOutput = (char*) lpEscapedCmdLine;
 
-               while (currentIndex < cmdLineLength - 1)
+               pBeg = strstr(lpCmdLine, "\\\"");
+               pEnd = pBeg + 2;
+
+               while (pBeg >= lpCmdLine)
                {
-                       index = strcspn(p, " \t");
+                       if (*pBeg != '\\')
+                       {
+                               pBeg++;
+                               break;
+                       }
+
+                       pBeg--;
+               }
 
-                       currentIndex += (index + 1);
-                       p = (char*) &lpCmdLine[currentIndex];
+               n = (pEnd - pBeg) - 1;
 
-                       maxNumArgs++;
+               length = (pBeg - lpCmdLine);
+               CopyMemory(pOutput, p, length);
+               pOutput += length;
+               p += length;
+
+               for (i = 0; i < (n / 2); i++)
+               {
+                       *pOutput = '\\';
+                       pOutput++;
                }
 
-               maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
+               p += n + 1;
 
-               buffer = (char*) malloc(maxBufferSize);
+               if ((n % 2) != 0)
+                       lpEscapedChars[pOutput - lpEscapedCmdLine] = '\\';
 
-               if (!buffer)
-                       return NULL;
+               *pOutput = '"';
+               pOutput++;
 
-               ZeroMemory(buffer, maxBufferSize);
+               length = cmdLineLength - (pEnd - lpCmdLine);
+               CopyMemory(pOutput, p, length);
+               pOutput += length;
+               p += length;
 
-               pArgs = (LPSTR*) buffer;
-               pOutput = (char*) &buffer[maxNumArgs * (sizeof(char*))];
+               *pOutput = '\0';
+               pOutput++;
 
-               numArgs = 0;
-               currentIndex = 0;
-               p = (char*) lpCmdLine;
+               lpCmdLine = (LPCSTR) lpEscapedCmdLine;
+               cmdLineLength = strlen(lpCmdLine);
 
-               while (currentIndex < cmdLineLength)
-               {
-                       pBeg = pEnd = p;
+               //printf("EscapedCmdLine: %s\n", lpEscapedCmdLine);
+       }
 
-                       index = strcspn(p, " \t\"\0");
+       maxNumArgs = 1;
+       currentIndex = 0;
+       p = (char*) lpCmdLine;
 
-                       if (p[index] != '"')
-                       {
-                               /* no whitespace escaped with double quotes */
+       while (currentIndex < cmdLineLength - 1)
+       {
+               index = strcspn(p, " \t");
 
-                               p = &p[index + 1];
-                               pEnd = p;
+               currentIndex += (index + 1);
+               p = (char*) &lpCmdLine[currentIndex];
 
-                               length = (pEnd - pBeg);
+               maxNumArgs++;
+       }
 
-                               CopyMemory(pOutput, pBeg, length);
-                               pOutput[length] = '\0';
-                               pArgs[numArgs++] = pOutput;
-                               pOutput += (length + 1);
-                       }
-                       else
+       maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1);
+
+       buffer = (char*) malloc(maxBufferSize);
+
+       if (!buffer)
+               return NULL;
+
+       ZeroMemory(buffer, maxBufferSize);
+
+       pArgs = (LPSTR*) buffer;
+       pOutput = (char*) &buffer[maxNumArgs * (sizeof(char*))];
+
+       numArgs = 0;
+       currentIndex = 0;
+       p = (char*) lpCmdLine;
+
+       while (currentIndex < cmdLineLength)
+       {
+               pBeg = pEnd = p;
+
+               while (1)
+               {
+                       index = strcspn(p, " \t\"\0");
+
+                       if ((p[index] == '"') && (lpEscapedChars[&p[index] - lpCmdLine]))
                        {
                                p = &p[index + 1];
+                               continue;
+                       }
 
-                               index = strcspn(p, "\"\0");
+                       break;
+               }
 
-                               if (p[index] != '"')
-                               {
-                                       printf("CommandLineToArgvA parsing error: uneven number of unescaped double quotes!\n");
-                               }
+               if (p[index] != '"')
+               {
+                       /* no whitespace escaped with double quotes */
 
-                               if (p[index] == '\0')
-                               {
-                                       p = &p[index + 1];
-                                       pEnd = p;
-                               }
-                               else
+                       p = &p[index + 1];
+                       pEnd = p;
+
+                       length = (pEnd - pBeg);
+
+                       CopyMemory(pOutput, pBeg, length);
+                       pOutput[length] = '\0';
+                       pArgs[numArgs++] = pOutput;
+                       pOutput += (length + 1);
+               }
+               else
+               {
+                       p = &p[index + 1];
+
+                       while (1)
+                       {
+                               index = strcspn(p, "\"\0");
+
+                               if ((p[index] == '"') && (lpEscapedChars[&p[index] - lpCmdLine]))
                                {
                                        p = &p[index + 1];
-                                       index = strcspn(p, " \t\0");
-                                       p = &p[index + 1];
-                                       pEnd = p;
+                                       continue;
                                }
 
-                               length = 0;
-                               pArgs[numArgs++] = pOutput;
+                               break;
+                       }
+
+                       if (p[index] != '"')
+                       {
+                               printf("CommandLineToArgvA parsing error: uneven number of unescaped double quotes!\n");
+                       }
 
-                               while (pBeg < pEnd)
+                       if (p[index] == '\0')
+                       {
+                               p = &p[index + 1];
+                               pEnd = p;
+                       }
+                       else
+                       {
+                               p = &p[index + 1];
+                               index = strcspn(p, " \t\0");
+                               p = &p[index + 1];
+                               pEnd = p;
+                       }
+
+                       length = 0;
+                       pArgs[numArgs++] = pOutput;
+
+                       while (pBeg < pEnd)
+                       {
+                               if (*pBeg != '"')
                                {
-                                       if (*pBeg != '"')
-                                       {
-                                               *pOutput = *pBeg;
-                                               pOutput++;
-                                               length++;
-                                       }
-
-                                       pBeg++;
+                                       *pOutput = *pBeg;
+                                       pOutput++;
+                                       length++;
                                }
 
-                               *pOutput = '\0';
-                               pOutput++;
+                               pBeg++;
                        }
 
-                       while ((*p == ' ') || (*p == '\t'))
-                               p++;
-
-                       currentIndex = (p - lpCmdLine);
+                       *pOutput = '\0';
+                       pOutput++;
                }
+
+               while ((*p == ' ') || (*p == '\t'))
+                       p++;
+
+               currentIndex = (p - lpCmdLine);
        }
-       else
-       {
-               /* TODO: handle escaping of double quotes */
-       }
+
+       if (lpEscapedCmdLine)
+               free(lpEscapedCmdLine);
+
+       free(lpEscapedChars);
 
        *pNumArgs = numArgs;
 
index 827b535..2e17765 100644 (file)
@@ -52,6 +52,16 @@ const char* test_args_list_5[] =
        NULL
 };
 
+const char* test_args_line_6 = "a\\\\\\\\\"b c\" d e";
+
+const char* test_args_list_6[] =
+{
+       "a\\\\b c",
+       "d",
+       "e",
+       NULL
+};
+
 static int test_command_line_parsing_case(const char* line, const char** list)
 {
        int i;
@@ -81,7 +91,8 @@ int TestThreadCommandLineToArgv(int argc, char* argv[])
        test_command_line_parsing_case(test_args_line_2, test_args_list_2);
        test_command_line_parsing_case(test_args_line_3, test_args_list_3);
        test_command_line_parsing_case(test_args_line_4, test_args_list_4);
-       //test_command_line_parsing_case(test_args_line_5, test_args_list_5);
+       test_command_line_parsing_case(test_args_line_5, test_args_list_5);
+       test_command_line_parsing_case(test_args_line_6, test_args_list_6);
 
        return 0;
 }