libwinpr-thread: start implementing CommandLineToArgv

This commit is contained in:
Marc-André Moreau 2013-09-21 17:16:49 -04:00
parent e841e6068b
commit 56672023db
7 changed files with 377 additions and 0 deletions

View File

@ -117,6 +117,17 @@ WINPR_API DWORD GetCurrentProcessId(void);
WINPR_API BOOL TerminateProcess(HANDLE hProcess, UINT uExitCode);
/* Process Argument Vector Parsing */
WINPR_API LPSTR* CommandLineToArgvA(LPCSTR lpCmdLine, int* pNumArgs);
WINPR_API LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs);
#ifdef UNICODE
#define CommandLineToArgv CommandLineToArgvW
#else
#define CommandLineToArgv CommandLineToArgvA
#endif
/* Thread */
#define CREATE_SUSPENDED 0x00000004

View File

@ -19,6 +19,7 @@ set(MODULE_NAME "winpr-thread")
set(MODULE_PREFIX "WINPR_THREAD")
set(${MODULE_PREFIX}_SRCS
argv.c
process.c
processor.c
thread.c
@ -53,3 +54,7 @@ else()
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR")
if(BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@ -0,0 +1,233 @@
/**
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/handle.h>
#include <winpr/thread.h>
#ifndef _WIN32
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/**
* 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)
{
char* p;
int index;
int length;
char* pBeg;
char* pEnd;
char* buffer;
char* pOutput;
int numArgs;
LPSTR* pArgs;
int maxNumArgs;
int maxBufferSize;
int currentIndex;
int cmdLineLength;
if (!lpCmdLine)
return NULL;
if (!pNumArgs)
return NULL;
pArgs = NULL;
numArgs = 0;
cmdLineLength = strlen(lpCmdLine);
if (!strstr(lpCmdLine, "\\\""))
{
/* Simplified case: only literal backslashes */
maxNumArgs = 1;
currentIndex = 0;
p = (char*) lpCmdLine;
while (currentIndex < cmdLineLength - 1)
{
index = strcspn(p, " \t");
currentIndex += (index + 1);
p = (char*) &lpCmdLine[currentIndex];
maxNumArgs++;
}
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;
index = strcspn(p, " \t\"\0");
if (p[index] != '"')
{
/* no whitespace escaped with double quotes */
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];
index = strcspn(p, "\"\0");
if (p[index] != '"')
{
printf("CommandLineToArgvA parsing error: uneven number of unescaped double quotes!\n");
}
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 != '"')
{
*pOutput = *pBeg;
pOutput++;
length++;
}
pBeg++;
}
*pOutput = '\0';
pOutput++;
}
while ((*p == ' ') || (*p == '\t'))
p++;
currentIndex = (p - lpCmdLine);
}
}
else
{
/* TODO: handle escaping of double quotes */
}
*pNumArgs = numArgs;
return pArgs;
}
LPWSTR* CommandLineToArgvW(LPCWSTR lpCmdLine, int* pNumArgs)
{
return NULL;
}
#endif

View File

@ -63,6 +63,13 @@ BOOL CreateProcessA(LPCSTR lpApplicationName, LPSTR lpCommandLine, LPSECURITY_AT
LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment,
LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation)
{
int status = 0;
if (!lpApplicationName)
return FALSE;
status = execve(lpApplicationName, NULL, NULL);
return TRUE;
}

3
winpr/libwinpr/thread/test/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
TestThread
TestThread.c

View File

@ -0,0 +1,30 @@
set(MODULE_NAME "TestThread")
set(MODULE_PREFIX "TEST_THREAD")
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestThreadCommandLineToArgv.c)
create_test_sourcelist(${MODULE_PREFIX}_SRCS
${${MODULE_PREFIX}_DRIVER}
${${MODULE_PREFIX}_TESTS})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
MONOLITHIC ${MONOLITHIC_BUILD}
MODULE winpr
MODULES winpr-thread)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@ -0,0 +1,88 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/thread.h>
const char* test_args_line_1 = "abc d e";
const char* test_args_list_1[] =
{
"abc",
"d",
"e",
NULL
};
const char* test_args_line_2 = "abc \t def";
const char* test_args_list_2[] =
{
"abc",
"def",
NULL
};
const char* test_args_line_3 = "\"abc\" d e";
const char* test_args_list_3[] =
{
"abc",
"d",
"e",
NULL
};
const char* test_args_line_4 = "a\\\\b d\"e f\"g h";
const char* test_args_list_4[] =
{
"a\\\\b",
"de fg",
"h",
NULL
};
const char* test_args_line_5 = "a\\\\\\\"b c d";
const char* test_args_list_5[] =
{
"a\\\"b",
"c",
"d",
NULL
};
static int test_command_line_parsing_case(const char* line, const char** list)
{
int i;
LPSTR* pArgs;
int pNumArgs;
pArgs = NULL;
pNumArgs = 0;
printf("Parsing: %s\n", line);
pArgs = CommandLineToArgvA(line, &pNumArgs);
printf("pNumArgs: %d\n", pNumArgs);
for (i = 0; i < pNumArgs; i++)
{
printf("argv[%d] = %s\n", i, pArgs[i]);
}
return 0;
}
int TestThreadCommandLineToArgv(int argc, char* argv[])
{
test_command_line_parsing_case(test_args_line_1, test_args_list_1);
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);
return 0;
}