libwinpr-thread: start implementing CommandLineToArgv
This commit is contained in:
parent
e841e6068b
commit
56672023db
@ -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
|
||||
|
@ -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()
|
||||
|
233
winpr/libwinpr/thread/argv.c
Normal file
233
winpr/libwinpr/thread/argv.c
Normal 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
|
@ -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
3
winpr/libwinpr/thread/test/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
TestThread
|
||||
TestThread.c
|
||||
|
30
winpr/libwinpr/thread/test/CMakeLists.txt
Normal file
30
winpr/libwinpr/thread/test/CMakeLists.txt
Normal 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")
|
88
winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c
Normal file
88
winpr/libwinpr/thread/test/TestThreadCommandLineToArgv.c
Normal 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user