diff options
Diffstat (limited to 'winpr/libwinpr/thread/argv.c')
-rw-r--r-- | winpr/libwinpr/thread/argv.c | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/winpr/libwinpr/thread/argv.c b/winpr/libwinpr/thread/argv.c new file mode 100644 index 0000000..3eedc41 --- /dev/null +++ b/winpr/libwinpr/thread/argv.c @@ -0,0 +1,279 @@ +/** + * WinPR: Windows Portable Runtime + * Process Argument Vector Functions + * + * Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <winpr/crt.h> +#include <winpr/handle.h> + +#include <winpr/thread.h> + +#ifdef WINPR_HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include "../log.h" +#define TAG WINPR_TAG("thread") + +/** + * CommandLineToArgvW function: + * http://msdn.microsoft.com/en-us/library/windows/desktop/bb776391/ + * + * CommandLineToArgvW has a special interpretation of backslash characters + * when they are followed by a quotation mark character ("), as follows: + * + * 2n backslashes followed by a quotation mark produce n backslashes followed by a quotation mark. + * (2n) + 1 backslashes followed by a quotation mark again produce n backslashes followed by a + * quotation mark. n backslashes not followed by a quotation mark simply produce n backslashes. + * + * The address returned by CommandLineToArgvW is the address of the first element in an array of + * LPWSTR values; the number of pointers in this array is indicated by pNumArgs. Each pointer to a + * null-terminated Unicode string represents an individual argument found on the command line. + * + * CommandLineToArgvW allocates a block of contiguous memory for pointers to the argument strings, + * and for the argument strings themselves; the calling application must free the memory used by the + * argument list when it is no longer needed. To free the memory, use a single call to the LocalFree + * function. + */ + +/** + * Parsing C++ Command-Line Arguments: + * http://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft + * + * Microsoft C/C++ startup code uses the following rules when + * interpreting arguments given on the operating system command line: + * + * Arguments are delimited by white space, which is either a space or a tab. + * + * The caret character (^) is not recognized as an escape character or delimiter. + * The character is handled completely by the command-line parser in the operating + * system before being passed to the argv array in the program. + * + * A string surrounded by double quotation marks ("string") is interpreted as a + * single argument, regardless of white space contained within. A quoted string + * can be embedded in an argument. + * + * A double quotation mark preceded by a backslash (\") is interpreted as a + * literal double quotation mark character ("). + * + * Backslashes are interpreted literally, unless they immediately + * precede a double quotation mark. + * + * If an even number of backslashes is followed by a double quotation mark, + * one backslash is placed in the argv array for every pair of backslashes, + * and the double quotation mark is interpreted as a string delimiter. + * + * If an odd number of backslashes is followed by a double quotation mark, + * one backslash is placed in the argv array for every pair of backslashes, + * and the double quotation mark is "escaped" by the remaining backslash, + * causing a literal double quotation mark (") to be placed in argv. + * + */ + +LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs) +{ + const char* p = NULL; + size_t length = 0; + const char* pBeg = NULL; + const char* pEnd = NULL; + char* buffer = NULL; + char* pOutput = NULL; + int numArgs = 0; + LPSTR* pArgs = NULL; + size_t maxNumArgs = 0; + size_t maxBufferSize = 0; + size_t cmdLineLength = 0; + BOOL* lpEscapedChars = NULL; + LPSTR lpEscapedCmdLine = NULL; + + if (!lpCmdLine) + return NULL; + + if (!pNumArgs) + return NULL; + + pArgs = NULL; + lpEscapedCmdLine = NULL; + cmdLineLength = strlen(lpCmdLine); + lpEscapedChars = (BOOL*)calloc(cmdLineLength + 1, sizeof(BOOL)); + + if (!lpEscapedChars) + return NULL; + + if (strstr(lpCmdLine, "\\\"")) + { + size_t n = 0; + const char* pLastEnd = NULL; + lpEscapedCmdLine = (char*)calloc(cmdLineLength + 1, sizeof(char)); + + if (!lpEscapedCmdLine) + { + free(lpEscapedChars); + return NULL; + } + + p = (const char*)lpCmdLine; + pLastEnd = (const char*)lpCmdLine; + pOutput = (char*)lpEscapedCmdLine; + + while (p < &lpCmdLine[cmdLineLength]) + { + pBeg = strstr(p, "\\\""); + + if (!pBeg) + { + length = strlen(p); + CopyMemory(pOutput, p, length); + pOutput += length; + break; + } + + pEnd = pBeg + 2; + + while (pBeg >= lpCmdLine) + { + if (*pBeg != '\\') + { + pBeg++; + break; + } + + pBeg--; + } + + n = ((pEnd - pBeg) - 1); + length = (pBeg - pLastEnd); + CopyMemory(pOutput, p, length); + pOutput += length; + p += length; + + for (size_t i = 0; i < (n / 2); i++) + *pOutput++ = '\\'; + + p += n + 1; + + if ((n % 2) != 0) + lpEscapedChars[pOutput - lpEscapedCmdLine] = TRUE; + + *pOutput++ = '"'; + pLastEnd = p; + } + + *pOutput++ = '\0'; + lpCmdLine = (LPCSTR)lpEscapedCmdLine; + cmdLineLength = strlen(lpCmdLine); + } + + maxNumArgs = 2; + p = (const char*)lpCmdLine; + + while (p < lpCmdLine + cmdLineLength) + { + p += strcspn(p, " \t"); + p += strspn(p, " \t"); + maxNumArgs++; + } + + maxBufferSize = (maxNumArgs * (sizeof(char*))) + (cmdLineLength + 1); + buffer = calloc(maxBufferSize, sizeof(char)); + + if (!buffer) + { + free(lpEscapedCmdLine); + free(lpEscapedChars); + return NULL; + } + + pArgs = (LPSTR*)buffer; + pOutput = (char*)&buffer[maxNumArgs * (sizeof(char*))]; + p = (const char*)lpCmdLine; + + while (p < lpCmdLine + cmdLineLength) + { + pBeg = p; + + while (1) + { + p += strcspn(p, " \t\"\0"); + + if ((*p != '"') || !lpEscapedChars[p - lpCmdLine]) + break; + + p++; + } + + if (*p != '"') + { + /* no whitespace escaped with double quotes */ + length = (p - pBeg); + CopyMemory(pOutput, pBeg, length); + pOutput[length] = '\0'; + pArgs[numArgs++] = pOutput; + pOutput += (length + 1); + } + else + { + p++; + + while (1) + { + p += strcspn(p, "\"\0"); + + if ((*p != '"') || !lpEscapedChars[p - lpCmdLine]) + break; + + p++; + } + + if (*p != '"') + WLog_ERR(TAG, "parsing error: uneven number of unescaped double quotes!"); + + if (*p && *(++p)) + p += strcspn(p, " \t\0"); + + pArgs[numArgs++] = pOutput; + + while (pBeg < p) + { + if (*pBeg != '"') + *pOutput++ = *pBeg; + + pBeg++; + } + + *pOutput++ = '\0'; + } + + p += strspn(p, " \t"); + } + + free(lpEscapedCmdLine); + free(lpEscapedChars); + *pNumArgs = numArgs; + return pArgs; +} + +#ifndef _WIN32 + +LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs) +{ + return NULL; +} + +#endif |