void config_parse_commandline( const char* const* cmdline, unsigned int num ) { //TODO: Implement, format --section:key=value unsigned int arg; for( arg = 0; arg < num; ++arg ) { if( string_match_pattern( cmdline[arg], "--*:*=*" ) ) { unsigned int first_sep = string_find( cmdline[arg], ':', 0 ); unsigned int second_sep = string_find( cmdline[arg], '=', 0 ); if( ( first_sep != STRING_NPOS ) && ( second_sep != STRING_NPOS ) && ( first_sep < second_sep ) ) { unsigned int section_length = first_sep - 2; unsigned int end_pos = first_sep + 1; unsigned int key_length = second_sep - end_pos; const char* section_str = cmdline[arg] + 2; const char* key_str = pointer_offset_const( cmdline[arg], end_pos ); hash_t section = hash( section_str, section_length ); hash_t key = hash( key_str, key_length ); char* value = string_substr( cmdline[arg], second_sep + 1, STRING_NPOS ); char* set_value = value; unsigned int value_length = string_length( value ); if( !value_length ) config_set_string( section, key, "" ); else if( string_equal( value, "false" ) ) config_set_bool( section, key, false ); else if( string_equal( value, "true" ) ) config_set_bool( section, key, true ); else if( ( string_find( value, '.', 0 ) != STRING_NPOS ) && ( string_find_first_not_of( value, "0123456789.", 0 ) == STRING_NPOS ) && ( string_find( value, '.', string_find( value, '.', 0 ) + 1 ) == STRING_NPOS ) ) //Exactly one "." config_set_real( section, key, string_to_real( value ) ); else if( string_find_first_not_of( value, "0123456789", 0 ) == STRING_NPOS ) config_set_int( section, key, string_to_int64( value ) ); else { if( ( value_length > 1 ) && ( value[0] == '"' ) && ( value[ value_length - 1 ] == '"' ) ) { value[ value_length - 1 ] = 0; set_value = value + 1; config_set_string( section, key, set_value ); } else { config_set_string( section, key, value ); } } log_infof( HASH_CONFIG, "Config value from command line: %.*s:%.*s = %s", section_length, section_str, key_length, key_str, set_value ); string_deallocate( value ); } } } }
static real _config_string_to_real( const char* str ) { unsigned int length = string_length( str ); unsigned int first_nonnumeric; unsigned int dot_position; if( length < 2 ) return string_to_real( str ); first_nonnumeric = string_find_first_not_of( str, "0123456789.", 0 ); if( ( first_nonnumeric == ( length - 1 ) ) && ( ( str[ first_nonnumeric ] == 'm' ) || ( str[ first_nonnumeric ] == 'M' ) ) ) { dot_position = string_find( str, '.', 0 ); if( dot_position != STRING_NPOS ) { if( string_find( str, '.', dot_position + 1 ) != STRING_NPOS ) return string_to_real( str ); //More than one dot } return string_to_real( str ) * ( REAL_C( 1024.0 ) * REAL_C( 1024.0 ) ); } if( ( first_nonnumeric == ( length - 1 ) ) && ( ( str[ first_nonnumeric ] == 'k' ) || ( str[ first_nonnumeric ] == 'K' ) ) ) { dot_position = string_find( str, '.', 0 ); if( dot_position != STRING_NPOS ) { if( string_find( str, '.', dot_position + 1 ) != STRING_NPOS ) return string_to_real( str ); //More than one dot } return string_to_real( str ) * REAL_C( 1024.0 ); } return string_to_real( str ); }
static int render_import_glsl_shader(stream_t* stream, const uuid_t uuid, const char* type, size_t type_length) { resource_source_t source; void* blob = 0; size_t size; size_t read; size_t begin; size_t next; hash_t checksum; tick_t timestamp; uint64_t platform; size_t maxtokens; size_t parameter; string_const_t* token = 0; string_const_t valstr; char buffer[128]; int ret = 0; resource_platform_t platformdecl = { -1, -1, RENDERAPIGROUP_OPENGL, -1, -1, -1}; resource_source_initialize(&source); resource_source_read(&source, uuid); read = 0; size = stream_size(stream); blob = memory_allocate(HASH_RESOURCE, size, 0, MEMORY_PERSISTENT); size = stream_read(stream, blob, size); platform = resource_platform(platformdecl); timestamp = time_system(); checksum = hash(blob, size); if (resource_source_write_blob(uuid, timestamp, HASH_SOURCE, platform, checksum, blob, size)) { resource_source_set_blob(&source, timestamp, HASH_SOURCE, platform, checksum, size); } else { ret = -1; goto finalize; } //Parse source and set parameters maxtokens = 256; token = memory_allocate(HASH_RESOURCE, sizeof(string_const_t) * maxtokens, 0, MEMORY_PERSISTENT); begin = 0; parameter = 0; do { string_const_t tokens[64], line; size_t itok, ntokens; next = string_find_first_of(blob, size, STRING_CONST("\n\r"), begin); line = string_substr(blob, size, begin, next - begin); ntokens = string_explode(STRING_ARGS(line), STRING_CONST(GLSL_TOKEN_DELIM), tokens, maxtokens, false); for (itok = 0; itok < ntokens; ++itok) { if ((string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("attribute")) || string_equal(STRING_ARGS(tokens[itok]), STRING_CONST("uniform"))) && (itok + 2 < ntokens)) { char typebuf[16], dimbuf[16]; string_const_t typestr = tokens[itok + 1]; string_const_t namestr = tokens[itok + 2]; string_const_t dimstr; int parameter_type = glsl_type_to_parameter_type(typestr); if (parameter_type < 0) continue; int parameter_dim = glsl_dim_from_token(typestr); if (parameter_dim == 1) parameter_dim = glsl_dim_from_token(namestr); namestr = glsl_name_from_token(namestr); typestr = string_to_const(string_from_uint(typebuf, sizeof(typebuf), (unsigned int)parameter_type, false, 0, 0)); dimstr = string_to_const(string_from_uint(dimbuf, sizeof(dimbuf), (unsigned int)parameter_dim, false, 0, 0)); log_debugf(HASH_RESOURCE, STRING_CONST("parameter: %.*s type %.*s dim %.*s"), STRING_FORMAT(namestr), STRING_FORMAT(typestr), STRING_FORMAT(dimstr)); string_t param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_type_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(typestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_name_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(namestr)); param = string_format(buffer, sizeof(buffer), STRING_CONST("parameter_dim_%" PRIsize), parameter); resource_source_set(&source, timestamp, hash(STRING_ARGS(param)), platform, STRING_ARGS(dimstr)); ++parameter; } } begin = string_find_first_not_of(blob, size, STRING_CONST(STRING_WHITESPACE), next); } while (next != STRING_NPOS); valstr = string_from_uint_static(parameter, false, 0, 0); resource_source_set(&source, timestamp, HASH_PARAMETER_COUNT, platform, STRING_ARGS(valstr)); resource_source_set(&source, timestamp, HASH_RESOURCE_TYPE, 0, type, type_length); if (!resource_source_write(&source, uuid, false)) { string_const_t uuidstr = string_from_uuid_static(uuid); log_warnf(HASH_RESOURCE, WARNING_SUSPICIOUS, STRING_CONST("Failed writing imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); ret = -1; goto finalize; } else { string_const_t uuidstr = string_from_uuid_static(uuid); log_infof(HASH_RESOURCE, STRING_CONST("Wrote imported GLSL shader: %.*s"), STRING_FORMAT(uuidstr)); } finalize: memory_deallocate(blob); memory_deallocate(token); resource_source_finalize(&source); return ret; }
int process_spawn( process_t* proc ) { static char unescaped[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:/\\"; int i, num_args; int size; #if FOUNDATION_PLATFORM_WINDOWS wchar_t* wcmdline; wchar_t* wwd; char* cmdline = 0; #endif if( !proc ) return PROCESS_INVALID_ARGS; proc->code = PROCESS_INVALID_ARGS; if( !string_length( proc->path ) ) return proc->code; //Always escape path on Windows platforms #if FOUNDATION_PLATFORM_POSIX if( string_find_first_not_of( proc->path, unescaped, 0 ) != STRING_NPOS ) #endif { if( proc->path[0] != '"' ) proc->path = string_prepend( proc->path, "\"" ); if( proc->path[ string_length( proc->path ) - 1 ] != '"' ) proc->path = string_append( proc->path, "\"" ); } size = array_size( proc->args ); for( i = 0, num_args = 0; i < size; ++i ) { char* arg = proc->args[i]; if( !string_length( arg ) ) continue; ++num_args; if( string_find_first_not_of( arg, unescaped, 0 ) != STRING_NPOS ) { if( arg[0] != '"' ) { //Check if we need to escape " characters unsigned int pos = string_find( arg, '"', 0 ); while( pos != STRING_NPOS ) { if( arg[ pos - 1 ] != '\\' ) { char* escarg = string_substr( arg, 0, pos ); char* left = string_substr( arg, pos, STRING_NPOS ); escarg = string_append( escarg, "\\" ); escarg = string_append( escarg, left ); string_deallocate( left ); string_deallocate( arg ); arg = escarg; } pos = string_find( arg, '"', pos + 2 ); } arg = string_prepend( arg, "\"" ); arg = string_append( arg, "\"" ); proc->args[i] = arg; } } } #if FOUNDATION_PLATFORM_WINDOWS # ifndef SEE_MASK_NOASYNC # define SEE_MASK_NOASYNC 0x00000100 # endif if( !( proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE ) ) //Don't prepend exe path to parameters if using ShellExecute cmdline = string_clone( proc->path ); //Build command line string for( i = 0; i < size; ++i ) { char* arg = proc->args[i]; if( !string_length( arg ) ) continue; if( cmdline ) cmdline = string_append( cmdline, " " ); cmdline = string_append( cmdline, arg ); } if( !string_length( proc->wd ) ) proc->wd = string_clone( environment_current_working_directory() ); wcmdline = wstring_allocate_from_string( cmdline, 0 ); wwd = wstring_allocate_from_string( proc->wd, 0 ); if( proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE ) { SHELLEXECUTEINFOW sei; wchar_t* wverb; wchar_t* wpath; wverb = ( proc->verb && string_length( proc->verb ) ) ? wstring_allocate_from_string( proc->verb, 0 ) : 0; wpath = wstring_allocate_from_string( proc->path, 0 ); ZeroMemory( &sei, sizeof( sei ) ); sei.cbSize = sizeof(SHELLEXECUTEINFOW); sei.hwnd = 0; sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; sei.lpVerb = wverb; sei.lpFile = wpath; sei.lpParameters = wcmdline; sei.lpDirectory = wwd; sei.nShow = SW_SHOWNORMAL; if( !( proc->flags & PROCESS_CONSOLE ) ) sei.fMask |= SEE_MASK_NO_CONSOLE; if( proc->flags & PROCESS_STDSTREAMS ) log_warnf( 0, WARNING_UNSUPPORTED, "Unable to redirect standard in/out through pipes when using ShellExecute for process spawning" ); log_debugf( 0, "Spawn process (ShellExecute): %s %s", proc->path, cmdline ); if( !ShellExecuteExW( &sei ) ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to spawn process (ShellExecute) for executable '%s': %s", proc->path, system_error_message( GetLastError() ) ); } else { proc->hp = sei.hProcess; proc->ht = 0; proc->code = 0; } wstring_deallocate( wverb ); wstring_deallocate( wpath ); } else { STARTUPINFOW si; PROCESS_INFORMATION pi; BOOL inherit_handles = FALSE; memset( &si, 0, sizeof( si ) ); memset( &pi, 0, sizeof( pi ) ); si.cb = sizeof( si ); if( proc->flags & PROCESS_STDSTREAMS ) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = pipe_write_handle( proc->pipeout ); si.hStdError = pipe_write_handle( proc->pipeout ); si.hStdInput = pipe_read_handle( proc->pipein ); //Don't inherit wrong ends of pipes SetHandleInformation( pipe_read_handle( proc->pipeout ), HANDLE_FLAG_INHERIT, 0 ); SetHandleInformation( pipe_write_handle( proc->pipein ), HANDLE_FLAG_INHERIT, 0 ); inherit_handles = TRUE; } log_debugf( 0, "Spawn process (CreateProcess): %s %s", proc->path, cmdline ); if( !CreateProcessW( 0/*wpath*/, wcmdline, 0, 0, inherit_handles, ( proc->flags & PROCESS_CONSOLE ) ? CREATE_NEW_CONSOLE : 0, 0, wwd, &si, &pi ) ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to spawn process (CreateProcess) for executable '%s': %s", proc->path, system_error_message( GetLastError() ) ); stream_deallocate( proc->pipeout ); stream_deallocate( proc->pipein ); proc->pipeout = 0; proc->pipein = 0; } else { proc->hp = pi.hProcess; proc->ht = pi.hThread; proc->code = 0; } if( proc->pipeout ) pipe_close_write( proc->pipeout ); if( proc->pipein ) pipe_close_read( proc->pipein ); } wstring_deallocate( wcmdline ); wstring_deallocate( wwd ); string_deallocate( cmdline ); if( proc->code < 0 ) return proc->code; //Error #endif #if FOUNDATION_PLATFORM_MACOSX if( proc->flags & PROCESS_OSX_USE_OPENAPPLICATION ) { proc->pid = 0; LSApplicationParameters params; ProcessSerialNumber psn; FSRef* fsref = memory_allocate( 0, sizeof( FSRef ), 0, MEMORY_TEMPORARY | MEMORY_ZERO_INITIALIZED ); memset( ¶ms, 0, sizeof( LSApplicationParameters ) ); memset( &psn, 0, sizeof( ProcessSerialNumber ) ); char* pathstripped = string_strip( string_clone( proc->path ), "\"" ); OSStatus status = 0; status = FSPathMakeRef( (uint8_t*)pathstripped, fsref, 0 ); if( status < 0 ) { pathstripped = string_append( pathstripped, ".app" ); status = FSPathMakeRef( (uint8_t*)pathstripped, fsref, 0 ); } CFStringRef* args = 0; for( i = 0, size = array_size( proc->args ); i < size; ++i ) //App gets executable path automatically, don't include array_push( args, CFStringCreateWithCString( 0, proc->args[i], kCFStringEncodingUTF8 ) ); CFArrayRef argvref = CFArrayCreate( 0, (const void**)args, (CFIndex)array_size( args ), 0 ); params.flags = kLSLaunchDefaults; params.application = fsref; params.argv = argvref; log_debugf( 0, "Spawn process (LSOpenApplication): %s", pathstripped ); status = LSOpenApplication( ¶ms, &psn ); if( status != 0 ) { proc->code = status; log_warnf( 0, WARNING_BAD_DATA, "Unable to spawn process for executable '%s': %s", proc->path, system_error_message( status ) ); } CFRelease( argvref ); for( i = 0, size = array_size( args ); i < size; ++i ) CFRelease( args[i] ); memory_deallocate( fsref ); string_deallocate( pathstripped ); if( status == 0 ) { pid_t pid = 0; GetProcessPID( &psn, &pid ); proc->pid = pid; //Always "detached" with LSOpenApplication, not a child process at all //Setup a kqueue to watch when process terminates so we can emulate a wait proc->kq = kqueue(); if( proc->kq < 0 ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to create kqueue for process watch: %s (%d)", proc->kq, system_error_message( proc->kq ) ); proc->kq = 0; } else { struct kevent changes; EV_SET( &changes, (pid_t)pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, 0 ); int ret = kevent( proc->kq, &changes, 1, &changes, 1, 0 ); if( ret != 1 ) { log_warnf( 0, WARNING_SYSTEM_CALL_FAIL, "Unable to setup kqueue for process watch, failed to add event to kqueue (%d)", ret ); close( proc->kq ); proc->kq = 0; } } } goto exit; } #endif #if FOUNDATION_PLATFORM_POSIX //Insert executable arg at start and null ptr at end int argc = array_size( proc->args ) + 1; array_grow( proc->args, 2 ); for( int arg = argc - 1; arg > 0; --arg ) proc->args[arg] = proc->args[arg-1]; proc->args[0] = string_clone( proc->path ); proc->args[argc] = 0; if( proc->flags & PROCESS_STDSTREAMS ) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); } proc->pid = 0; pid_t pid = fork(); if( pid == 0 ) { //Child if( string_length( proc->wd ) ) { log_debugf( 0, "Spawned child process, setting working directory to %s", proc->wd ); environment_set_current_working_directory( proc->wd ); } log_debugf( 0, "Child process executing: %s", proc->path ); if( proc->flags & PROCESS_STDSTREAMS ) { pipe_close_read( proc->pipeout ); dup2( pipe_write_fd( proc->pipeout ), STDOUT_FILENO ); pipe_close_write( proc->pipein ); dup2( pipe_read_fd( proc->pipein ), STDIN_FILENO ); } int code = execv( proc->path, proc->args ); if( code < 0 ) //Will always be true since this point will never be reached if execve() is successful log_warnf( 0, WARNING_BAD_DATA, "Child process failed execve() : %s : %s", proc->path, system_error_message( errno ) ); //Error process_exit( -1 ); } if( pid > 0 ) { log_debugf( 0, "Child process forked, pid %d", pid ); proc->pid = pid; if( proc->pipeout ) pipe_close_write( proc->pipeout ); if( proc->pipein ) pipe_close_read( proc->pipein ); if( proc->flags & PROCESS_DETACHED ) { int cstatus = 0; pid_t err = waitpid( pid, &cstatus, WNOHANG ); if( err == 0 ) { //TODO: Ugly wait to make sure process spawned correctly thread_sleep( 500 ); err = waitpid( pid, &cstatus, WNOHANG ); } if( err > 0 ) { //Process exited, check code proc->code = (int)((char)WEXITSTATUS( cstatus )); log_debugf( 0, "Child process returned: %d", proc->code ); return proc->code; } } } else { //Error proc->code = errno; log_warnf( 0, WARNING_BAD_DATA, "Unable to spawn process: %s : %s", proc->path, system_error_message( proc->code ) ); if( proc->pipeout ) stream_deallocate( proc->pipeout ); if( proc->pipein ) stream_deallocate( proc->pipein ); proc->pipeout = 0; proc->pipein = 0; return proc->code; } #endif #if !FOUNDATION_PLATFORM_WINDOWS && !FOUNDATION_PLATFORM_POSIX FOUNDATION_ASSERT_FAIL( "Process spawning not supported on platform" ); #endif #if FOUNDATION_PLATFORM_MACOSX exit: #endif if( proc->flags & PROCESS_DETACHED ) return PROCESS_STILL_ACTIVE; return process_wait( proc ); }
DECLARE_TEST( string, queries ) { { char teststr[] = ""; char teststr2[] = "test"; char teststr3[] = "testing long string with more than 16 characters"; char teststr4[] = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; char* str1 = string_substr( teststr, 0, 0 ); char* str2 = string_substr( teststr2, 0, 4 ); char* str3 = string_substr( teststr2, 0, 20 ); char* str4 = string_substr( teststr3, 0, 0 ); char* str5 = string_substr( teststr3, 0, 10 ); char* str6 = string_substr( teststr3, 0, STRING_NPOS ); char* str7 = string_substr( teststr4, 0, 0 ); char* str8 = string_substr( teststr4, 0, 20 ); char* str9 = string_substr( teststr4, 0, STRING_NPOS ); EXPECT_EQ( 0, str1[0] ); EXPECT_EQ( 0, str2[ string_length( str2 ) ] ); EXPECT_EQ( 0, str3[ string_length( str3 ) ] ); EXPECT_EQ( 0, str4[ string_length( str4 ) ] ); EXPECT_EQ( 0, str5[ string_length( str5 ) ] ); EXPECT_EQ( 0, str6[ string_length( str6 ) ] ); EXPECT_EQ( 0, str7[ string_length( str7 ) ] ); EXPECT_EQ( 0, str8[ string_length( str8 ) ] ); EXPECT_EQ( 0, str9[ string_length( str9 ) ] ); EXPECT_EQ( strlen( teststr ), string_length( str1 ) ); EXPECT_EQ( strlen( teststr2 ), string_length( str2 ) ); EXPECT_EQ( strlen( teststr2 ), string_length( str3 ) ); EXPECT_EQ( 0U, string_length( str4 ) ); EXPECT_EQ( 10U, string_length( str5 ) ); EXPECT_EQ( strlen( teststr3 ), string_length( str6 ) ); EXPECT_EQ( 0U, string_length( str7 ) ); EXPECT_EQ( 20U, string_length( str8 ) ); EXPECT_EQ( strlen( teststr4 ), string_length( str9 ) ); string_deallocate( str1 ); string_deallocate( str2 ); string_deallocate( str3 ); string_deallocate( str4 ); string_deallocate( str5 ); string_deallocate( str6 ); string_deallocate( str7 ); string_deallocate( str8 ); string_deallocate( str9 ); } { //Only ASCII characters, so wstring->string conversion should not introduce any extra UTF-8 sequences wchar_t teststr[] = L""; wchar_t teststr2[] = L"test"; wchar_t teststr3[] = L"testing long string with more than 16 characters"; wchar_t teststr4[] = L"01234567890123456789012345678901234567890123456789012345678901234567890123456789"; char* str1 = string_allocate_from_wstring( teststr, 0 ); char* str2 = string_allocate_from_wstring( teststr2, 0 ); char* str3 = string_allocate_from_wstring( teststr2, 20 ); char* str4 = string_allocate_from_wstring( teststr3, 0 ); char* str5 = string_allocate_from_wstring( teststr3, 10 ); char* str6 = string_allocate_from_wstring( teststr3, STRING_NPOS ); char* str7 = string_allocate_from_wstring( teststr4, 0 ); char* str8 = string_allocate_from_wstring( teststr4, 20 ); char* str9 = string_allocate_from_wstring( teststr4, STRING_NPOS ); EXPECT_EQ( 0, str1[0] ); EXPECT_EQ( 0, str2[ string_length( str2 ) ] ); EXPECT_EQ( 0, str3[ string_length( str3 ) ] ); EXPECT_EQ( 0, str4[ string_length( str4 ) ] ); EXPECT_EQ( 0, str5[ string_length( str5 ) ] ); EXPECT_EQ( 0, str6[ string_length( str6 ) ] ); EXPECT_EQ( 0, str7[ string_length( str7 ) ] ); EXPECT_EQ( 0, str8[ string_length( str8 ) ] ); EXPECT_EQ( 0, str9[ string_length( str9 ) ] ); EXPECT_EQ( wcslen( teststr ), string_length( str1 ) ); EXPECT_EQ( wcslen( teststr2 ), string_length( str2 ) ); EXPECT_EQ( wcslen( teststr2 ), string_length( str3 ) ); EXPECT_EQ( wcslen( teststr3 ), string_length( str4 ) ); EXPECT_EQ( 10U, string_length( str5 ) ); EXPECT_EQ( wcslen( teststr3 ), string_length( str6 ) ); EXPECT_EQ( wcslen( teststr4 ), string_length( str7 ) ); EXPECT_EQ( 20U, string_length( str8 ) ); EXPECT_EQ( wcslen( teststr4 ), string_length( str9 ) ); string_deallocate( str1 ); string_deallocate( str2 ); string_deallocate( str3 ); string_deallocate( str4 ); string_deallocate( str5 ); string_deallocate( str6 ); string_deallocate( str7 ); string_deallocate( str8 ); string_deallocate( str9 ); } { char* emptystr = string_allocate( 0 ); char* shortstr = string_clone( "short string" ); char* longstr = string_clone( "testing utility string methods like finds, split, merge, explode and similar." ); { unsigned int find = string_find( longstr, ' ', 0 ); unsigned int find2 = string_find( longstr, 12, 0 ); unsigned int find3 = string_find( emptystr, ' ', 0 ); unsigned int find4 = string_find( shortstr, ' ', 0 ); unsigned int find5 = string_find( shortstr, 'z', 0 ); unsigned int findofs = string_find( longstr, ' ', find ); unsigned int findofs2 = string_find( longstr, ' ', find + 1 ); unsigned int findofs3 = string_find( longstr, 'z', 10 ); unsigned int findofs4 = string_find( emptystr, 'z', STRING_NPOS ); unsigned int findofs5 = string_find( shortstr, 's', 10 ); unsigned int findofs6 = string_find( shortstr, 's', findofs5 ); unsigned int findofs7 = string_find( shortstr, 't', 0 ); unsigned int findofs8 = string_find( shortstr, ' ', 5 ); unsigned int findofs9 = string_find( longstr, ' ', STRING_NPOS ); EXPECT_EQ( find, 7U ); EXPECT_EQ( find2, STRING_NPOS ); EXPECT_EQ( find3, STRING_NPOS ); EXPECT_EQ( find4, 5U ); EXPECT_EQ( find5, STRING_NPOS ); EXPECT_EQ( findofs, find ); EXPECT_EQ( findofs2, 15U ); EXPECT_EQ( findofs3, STRING_NPOS ); EXPECT_EQ( findofs4, STRING_NPOS ); EXPECT_EQ( findofs5, STRING_NPOS ); EXPECT_EQ( findofs6, STRING_NPOS ); EXPECT_EQ( findofs7, 4U ); EXPECT_EQ( findofs8, 5U ); EXPECT_EQ( findofs9, STRING_NPOS ); } { unsigned int rfind = string_rfind( longstr, ' ', STRING_NPOS ); unsigned int rfind2 = string_rfind( longstr, ';', STRING_NPOS ); unsigned int rfind3 = string_rfind( emptystr, ';', STRING_NPOS ); unsigned int rfind4 = string_rfind( shortstr, 's', STRING_NPOS ); unsigned int rfind5 = string_rfind( shortstr, 'z', STRING_NPOS ); unsigned int rfindofs = string_rfind( longstr, ' ', rfind ); unsigned int rfindofs2 = string_rfind( longstr, ' ', rfind - 1 ); unsigned int rfindofs3 = string_rfind( longstr, ' ', string_length( longstr ) - 1 ); unsigned int rfindofs4 = string_rfind( emptystr, ' ', 0 ); unsigned int rfindofs5 = string_rfind( longstr, ' ', 0 ); unsigned int rfindofs6 = string_rfind( shortstr, 's', 5 ); unsigned int rfindofs7 = string_rfind( shortstr, 's', 0 ); EXPECT_EQ( rfind, 68U ); EXPECT_EQ( rfind2, STRING_NPOS ); EXPECT_EQ( rfind3, STRING_NPOS ); EXPECT_EQ( rfind4, 6U ); EXPECT_EQ( rfind5, STRING_NPOS ); EXPECT_EQ( rfindofs, rfind ); EXPECT_EQ( rfindofs2, 64U ); EXPECT_EQ( rfindofs3, rfind ); EXPECT_EQ( rfindofs4, STRING_NPOS ); EXPECT_EQ( rfindofs5, STRING_NPOS ); EXPECT_EQ( rfindofs6, 0U ); EXPECT_EQ( rfindofs7, 0U ); } { unsigned int findstr = string_find_string( longstr, "st", 0 ); unsigned int findstr2 = string_find_string( longstr, "xwqz", 0 ); unsigned int findstr3 = string_find_string( emptystr, "xwqz", 0 ); unsigned int findstr4 = string_find_string( longstr, "", 0 ); unsigned int findstr5 = string_find_string( longstr, "dslike", 0 ); unsigned int findstr6 = string_find_string( shortstr, "rt", 0 ); unsigned int findstr7 = string_find_string( shortstr, "long key that does not exist", 0 ); unsigned int findstr8 = string_find_string( shortstr, "so", 0 ); unsigned int findstrofs = string_find_string( longstr, "st", findstr ); unsigned int findstrofs2 = string_find_string( longstr, "st", findstr + 1 ); unsigned int findstrofs3 = string_find_string( longstr, "xwqz", string_length( longstr ) ); unsigned int findstrofs4 = string_find_string( emptystr, "xwqz", string_length( emptystr ) ); unsigned int findstrofs5 = string_find_string( shortstr, "", 5 ); unsigned int findstrofs6 = string_find_string( shortstr, "string", 0 ); unsigned int findstrofs7 = string_find_string( shortstr, "string", 7 ); unsigned int findstrofs8 = string_find_string( longstr, "utility", 14 ); unsigned int findstrofs9 = string_find_string( longstr, "", string_length( longstr ) ); unsigned int findstrofs10 = string_find_string( longstr, "", STRING_NPOS ); unsigned int findstrofs11 = string_find_string( longstr, "string", STRING_NPOS ); EXPECT_EQ( findstr, 2U ); EXPECT_EQ( findstr2, STRING_NPOS ); EXPECT_EQ( findstr3, STRING_NPOS ); EXPECT_EQ( findstr4, 0U ); EXPECT_EQ( findstr5, STRING_NPOS ); EXPECT_EQ( findstr6, 3U ); EXPECT_EQ( findstr7, STRING_NPOS ); EXPECT_EQ( findstr8, STRING_NPOS ); EXPECT_EQ( findstrofs, findstr ); EXPECT_EQ( findstrofs2, 16U ); EXPECT_EQ( findstrofs3, STRING_NPOS ); EXPECT_EQ( findstrofs4, STRING_NPOS ); EXPECT_EQ( findstrofs5, 5U ); EXPECT_EQ( findstrofs6, 6U ); EXPECT_EQ( findstrofs7, STRING_NPOS ); EXPECT_EQ( findstrofs8, STRING_NPOS ); EXPECT_EQ( findstrofs9, strlen( longstr ) ); EXPECT_EQ( findstrofs10, STRING_NPOS ); EXPECT_EQ( findstrofs11, STRING_NPOS ); } { unsigned int rfindstr = string_rfind_string( longstr, ", ", STRING_NPOS ); unsigned int rfindstr2 = string_rfind_string( longstr, ":;", STRING_NPOS ); unsigned int rfindstr3 = string_rfind_string( emptystr, ":;", STRING_NPOS ); unsigned int rfindstr4 = string_rfind_string( longstr, "", STRING_NPOS ); unsigned int rfindstr5 = string_rfind_string( shortstr, "string", STRING_NPOS ); unsigned int rfindstr6 = string_rfind_string( shortstr, " tring", STRING_NPOS ); unsigned int rfindstrofs = string_rfind_string( longstr, ", ", rfindstr ); unsigned int rfindstrofs2 = string_rfind_string( longstr, ", ", rfindstr - 1 ); unsigned int rfindstrofs3 = string_rfind_string( longstr, ":;", 0 ); unsigned int rfindstrofs4 = string_rfind_string( emptystr, ":;", 0 ); unsigned int rfindstrofs5 = string_rfind_string( longstr, "", 5 ); unsigned int rfindstrofs6 = string_rfind_string( shortstr, "ort str", 6 ); unsigned int rfindstrofs7 = string_rfind_string( shortstr, "ort str", 1 ); EXPECT_EQ( rfindstr, 55U ); EXPECT_EQ( rfindstr2, STRING_NPOS ); EXPECT_EQ( rfindstr3, STRING_NPOS ); EXPECT_EQ( rfindstr4, strlen( longstr ) ); EXPECT_EQ( rfindstr5, 6U ); EXPECT_EQ( rfindstr6, STRING_NPOS ); EXPECT_EQ( rfindstrofs, rfindstr ); EXPECT_EQ( rfindstrofs2, 48U ); EXPECT_EQ( rfindstrofs3, STRING_NPOS ); EXPECT_EQ( rfindstrofs4, STRING_NPOS ); EXPECT_EQ( rfindstrofs5, 5U ); EXPECT_EQ( rfindstrofs6, 2U ); EXPECT_EQ( rfindstrofs7, STRING_NPOS ); } { unsigned int findof = string_find_first_of( longstr, "ui", 0 ); unsigned int findof2 = string_find_first_of( longstr, ";:", 0 ); unsigned int findof3 = string_find_first_of( emptystr, "", 0 ); unsigned int findof4 = string_find_first_of( emptystr, " ", 0 ); unsigned int findof5 = string_find_first_of( shortstr, "", 0 ); unsigned int findofofs = string_find_first_of( longstr, "ui", findof ); unsigned int findofofs2 = string_find_first_of( longstr, "ui", findof - 1 ); unsigned int findofofs3 = string_find_first_of( longstr, "ui", findof + 1 ); unsigned int findofofs4 = string_find_first_of( longstr, "ui", string_length( longstr ) ); unsigned int findofofs5 = string_find_first_of( emptystr, "", string_length( emptystr ) ); unsigned int findofofs6 = string_find_first_of( shortstr, "string", 6 ); unsigned int findofofs7 = string_find_first_of( shortstr, "", 6 ); unsigned int findofofs8 = string_find_first_of( longstr, "", 10 ); unsigned int findofofs9 = string_find_first_of( longstr, "", string_length( longstr ) ); EXPECT_EQ( findof, 4U ); EXPECT_EQ( findof2, STRING_NPOS ); EXPECT_EQ( findof3, STRING_NPOS ); EXPECT_EQ( findof4, STRING_NPOS ); EXPECT_EQ( findof5, STRING_NPOS ); EXPECT_EQ( findofofs, 4U ); EXPECT_EQ( findofofs2, 4U ); EXPECT_EQ( findofofs3, 8U ); EXPECT_EQ( findofofs4, STRING_NPOS ); EXPECT_EQ( findofofs5, STRING_NPOS ); EXPECT_EQ( findofofs6, 6U ); EXPECT_EQ( findofofs7, STRING_NPOS ); EXPECT_EQ( findofofs8, STRING_NPOS ); EXPECT_EQ( findofofs9, STRING_NPOS ); } { unsigned int findnotof = string_find_first_not_of( longstr, "testing ", 0 ); unsigned int findnotof2 = string_find_first_not_of( longstr, longstr, 0 ); unsigned int findnotof3 = string_find_first_not_of( shortstr, "", 0 ); unsigned int findnotofofs = string_find_first_not_of( longstr, "testing ", findnotof ); unsigned int findnotofofs2 = string_find_first_not_of( longstr, "testing ", findnotof + 1 ); unsigned int findnotofofs3 = string_find_first_not_of( longstr, "testing ", string_length( longstr ) ); unsigned int findnotofofs4 = string_find_first_not_of( shortstr, "", string_length( shortstr ) ); EXPECT_EQ( findnotof, 8U ); EXPECT_EQ( findnotof2, STRING_NPOS ); EXPECT_EQ( findnotof3, 0U ); EXPECT_EQ( findnotofofs, 8U ); EXPECT_EQ( findnotofofs2, 11U ); EXPECT_EQ( findnotofofs3, STRING_NPOS ); EXPECT_EQ( findnotofofs4, STRING_NPOS ); } { unsigned int findlastof = string_find_last_of( longstr, "xp", STRING_NPOS ); unsigned int findlastof2 = string_find_last_of( longstr, ";:", STRING_NPOS ); unsigned int findlastof3 = string_find_last_of( emptystr, "", STRING_NPOS ); unsigned int findlastof4 = string_find_last_of( shortstr, "", STRING_NPOS ); unsigned int findlastofofs = string_find_last_of( longstr, "xp", findlastof ); unsigned int findlastofofs2 = string_find_last_of( longstr, "xp", findlastof - 2 ); unsigned int findlastofofs3 = string_find_last_of( longstr, "xp", 0 ); unsigned int findlastofofs4 = string_find_last_of( emptystr, "", 0 ); unsigned int findlastofofs5 = string_find_last_of( shortstr, "", 5 ); unsigned int findlastofofs6 = string_find_last_of( shortstr, "", string_length( shortstr ) ); unsigned int findlastofofs7 = string_find_last_of( shortstr, "short", 5 ); EXPECT_EQ( findlastof, 59U ); EXPECT_EQ( findlastof2, STRING_NPOS ); EXPECT_EQ( findlastof3, STRING_NPOS ); EXPECT_EQ( findlastof4, STRING_NPOS ); EXPECT_EQ( findlastofofs, 59U ); EXPECT_EQ( findlastofofs2, 44U ); EXPECT_EQ( findlastofofs3, STRING_NPOS ); EXPECT_EQ( findlastofofs4, STRING_NPOS ); EXPECT_EQ( findlastofofs5, STRING_NPOS ); EXPECT_EQ( findlastofofs6, STRING_NPOS ); EXPECT_EQ( findlastofofs7, 4U ); } { unsigned int findlastnotof = string_find_last_not_of( longstr, " similar.", STRING_NPOS ); unsigned int findlastnotof2 = string_find_last_not_of( longstr, longstr, STRING_NPOS ); unsigned int findlastnotof3 = string_find_last_not_of( emptystr, "", STRING_NPOS ); unsigned int findlastnotof4 = string_find_last_not_of( shortstr, "", STRING_NPOS ); unsigned int findlastnotof5 = string_find_last_not_of( longstr, " similar", STRING_NPOS ); unsigned int findlastnotofofs = string_find_last_not_of( longstr, " similar.", findlastnotof ); unsigned int findlastnotofofs2 = string_find_last_not_of( longstr, " and similar.", findlastnotof - 1 ); unsigned int findlastnotofofs3 = string_find_last_not_of( longstr, longstr, 0 ); unsigned int findlastnotofofs4 = string_find_last_not_of( emptystr, "", 0 ); unsigned int findlastnotofofs5 = string_find_last_not_of( shortstr, "string", 5 ); unsigned int findlastnotofofs6 = string_find_last_not_of( shortstr, "string ", 5 ); unsigned int findlastnotofofs7 = string_find_last_not_of( shortstr, "", 5 ); unsigned int findlastnotofofs8 = string_find_last_not_of( longstr, "", string_length( longstr ) ); EXPECT_EQ( findlastnotof, 67U ); EXPECT_EQ( findlastnotof2, STRING_NPOS ); EXPECT_EQ( findlastnotof3, STRING_NPOS ); EXPECT_EQ( findlastnotof4, 11U ); EXPECT_EQ( findlastnotof5, 76U ); EXPECT_EQ( findlastnotofofs, 67U ); EXPECT_EQ( findlastnotofofs2, 63U ); EXPECT_EQ( findlastnotofofs3, STRING_NPOS ); EXPECT_EQ( findlastnotofofs4, STRING_NPOS ); EXPECT_EQ( findlastnotofofs5, 5U ); EXPECT_EQ( findlastnotofofs6, 2U ); EXPECT_EQ( findlastnotofofs7, 5U ); EXPECT_EQ( findlastnotofofs8, strlen( longstr ) - 1 ); } string_deallocate( emptystr ); string_deallocate( shortstr ); string_deallocate( longstr ); } return 0; }
int process_spawn(process_t* proc) { static const string_const_t unescaped = { STRING_CONST("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.:/\\") }; size_t i, num_args; size_t size; #if FOUNDATION_PLATFORM_WINDOWS wchar_t* wcmdline; wchar_t* wwd; string_t cmdline; #endif #if !FOUNDATION_PLATFORM_POSIX size_t capacity; #endif proc->code = PROCESS_INVALID_ARGS; if (!proc->path.length) return proc->code; //Always escape path on Windows platforms #if FOUNDATION_PLATFORM_POSIX if (string_find_first_not_of(STRING_ARGS(proc->path), STRING_ARGS(unescaped), 0) != STRING_NPOS) #endif { bool preesc = (proc->path.str[0] != '"'); bool postesc = (proc->path.str[ proc->path.length - 1 ] != '"'); if (preesc || postesc) { char* buffer = memory_allocate(HASH_STRING, proc->path.length + 4, 0, MEMORY_PERSISTENT); string_t pathesc = string_concat_varg(buffer, proc->path.length + 4, "\"", (size_t)(preesc ? 1 : 0), STRING_ARGS(proc->path), "\"", (size_t)(postesc ? 1 : 0), nullptr); string_deallocate(proc->path.str); proc->path = pathesc; } } size = array_size(proc->args); for (i = 0, num_args = 0; i < size; ++i) { string_t arg = proc->args[i]; if (!arg.length) continue; ++num_args; #if !FOUNDATION_PLATFORM_POSIX if (string_find_first_not_of(arg.str, arg.length, unescaped.str, unescaped.length, 0) != STRING_NPOS) { if (arg.str[0] != '"') { //Check if we need to escape " characters string_t escarg; size_t pos = string_find(arg.str, arg.length, '"', 0); while (pos != STRING_NPOS) { if (arg.str[ pos - 1 ] != '\\') { string_const_t right = string_substr(STRING_ARGS(arg), 0, pos); string_const_t left = string_substr(STRING_ARGS(arg), pos, STRING_NPOS); capacity = arg.length + 2; escarg = string_allocate(0, capacity); escarg = string_concat(escarg.str, capacity, STRING_ARGS(right), STRING_CONST("\\")); escarg = string_append(STRING_ARGS(escarg), capacity, STRING_ARGS(left)); string_deallocate(arg.str); arg = escarg; } pos = string_find(STRING_ARGS(arg), '"', pos + 2); } escarg = string_allocate_concat_varg(STRING_CONST("\""), STRING_ARGS(arg), STRING_CONST("\""), nullptr); string_deallocate(arg.str); proc->args[i] = escarg; } } #endif } #if FOUNDATION_PLATFORM_WINDOWS # ifndef SEE_MASK_NOASYNC # define SEE_MASK_NOASYNC 0x00000100 # endif capacity = BUILD_MAX_PATHLEN; cmdline = string_allocate(0, capacity); //Don't prepend exe path to parameters if using ShellExecute if (!(proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE)) cmdline = string_copy(cmdline.str, capacity, STRING_ARGS(proc->path)); //Build command line string for (i = 0; i < size; ++i) { string_t arg = proc->args[i]; if (!arg.length) continue; if (cmdline.length + arg.length + 2 >= capacity) { string_t newline; capacity *= 2; newline = string_allocate(0, capacity); newline = string_copy(newline.str, capacity, STRING_ARGS(cmdline)); } if (cmdline.length) cmdline = string_append(STRING_ARGS(cmdline), capacity, STRING_CONST(" ")); cmdline = string_append(STRING_ARGS(cmdline), capacity, STRING_ARGS(arg)); } if (!proc->wd.length) proc->wd = string_clone(STRING_ARGS(environment_current_working_directory())); wcmdline = wstring_allocate_from_string(STRING_ARGS(cmdline)); wwd = wstring_allocate_from_string(STRING_ARGS(proc->wd)); if (proc->flags & PROCESS_WINDOWS_USE_SHELLEXECUTE) { SHELLEXECUTEINFOW sei; wchar_t* wverb; wchar_t* wpath; wverb = (proc->verb.length ? wstring_allocate_from_string(STRING_ARGS(proc->verb)) : 0); wpath = wstring_allocate_from_string(STRING_ARGS(proc->path)); ZeroMemory(&sei, sizeof(sei)); sei.cbSize = sizeof(SHELLEXECUTEINFOW); sei.hwnd = 0; sei.fMask = SEE_MASK_NOASYNC | SEE_MASK_FLAG_NO_UI | SEE_MASK_NOCLOSEPROCESS; sei.lpVerb = wverb; sei.lpFile = wpath; sei.lpParameters = wcmdline; sei.lpDirectory = wwd; sei.nShow = SW_SHOWNORMAL; if (!(proc->flags & PROCESS_CONSOLE)) sei.fMask |= SEE_MASK_NO_CONSOLE; if (proc->flags & PROCESS_STDSTREAMS) log_warn(0, WARNING_UNSUPPORTED, STRING_CONST("Unable to redirect standard in/out" " through pipes when using ShellExecute for process spawning")); log_debugf(0, STRING_CONST("Spawn process (ShellExecute): %.*s %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(cmdline)); if (!ShellExecuteExW(&sei)) { string_const_t errstr = system_error_message(0); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process (ShellExecute) for executable '%.*s': %s"), STRING_FORMAT(proc->path), STRING_FORMAT(errstr)); } else { proc->hp = sei.hProcess; proc->ht = 0; proc->code = 0; } wstring_deallocate(wverb); wstring_deallocate(wpath); } else { STARTUPINFOW si; PROCESS_INFORMATION pi; BOOL inherit_handles = FALSE; memset(&si, 0, sizeof(si)); memset(&pi, 0, sizeof(pi)); si.cb = sizeof(si); if (proc->flags & PROCESS_STDSTREAMS) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); si.dwFlags |= STARTF_USESTDHANDLES; si.hStdOutput = pipe_write_handle(proc->pipeout); si.hStdInput = pipe_read_handle(proc->pipein); si.hStdError = GetStdHandle(STD_ERROR_HANDLE); //Don't inherit wrong ends of pipes SetHandleInformation(pipe_read_handle(proc->pipeout), HANDLE_FLAG_INHERIT, 0); SetHandleInformation(pipe_write_handle(proc->pipein), HANDLE_FLAG_INHERIT, 0); inherit_handles = TRUE; } log_debugf(0, STRING_CONST("Spawn process (CreateProcess): %.*s %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(cmdline)); if (!CreateProcessW(0, wcmdline, 0, 0, inherit_handles, (proc->flags & PROCESS_CONSOLE) ? CREATE_NEW_CONSOLE : 0, 0, wwd, &si, &pi)) { string_const_t errstr = system_error_message(0); log_warnf(0, WARNING_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process (CreateProcess) for executable '%.*s': %.*s"), STRING_FORMAT(proc->path), STRING_FORMAT(errstr)); stream_deallocate(proc->pipeout); stream_deallocate(proc->pipein); proc->pipeout = 0; proc->pipein = 0; } else { proc->hp = pi.hProcess; proc->ht = pi.hThread; proc->code = 0; } if (proc->pipeout) pipe_close_write(proc->pipeout); if (proc->pipein) pipe_close_read(proc->pipein); } wstring_deallocate(wcmdline); wstring_deallocate(wwd); string_deallocate(cmdline.str); if (proc->code < 0) return proc->code; //Error #endif #if FOUNDATION_PLATFORM_MACOSX if (proc->flags & PROCESS_MACOSX_USE_OPENAPPLICATION) { proc->pid = 0; LSApplicationParameters params; ProcessSerialNumber psn; FSRef* fsref = memory_allocate(0, sizeof(FSRef), 0, MEMORY_TEMPORARY | MEMORY_ZERO_INITIALIZED); memset(¶ms, 0, sizeof(LSApplicationParameters)); memset(&psn, 0, sizeof(ProcessSerialNumber)); string_const_t pathstripped = string_strip(proc->path.str, proc->path.length, STRING_CONST("\"")); size_t localcap = pathstripped.length + 5; string_t localpath = string_allocate(0, localcap - 1); localpath = string_copy(localpath.str, localcap, STRING_ARGS(pathstripped)); //Need it zero terminated OSStatus status = 0; status = FSPathMakeRef((uint8_t*)localpath.str, fsref, 0); if (status < 0) { localpath = string_append(localpath.str, localpath.length, localcap, STRING_CONST(".app")); status = FSPathMakeRef((uint8_t*)localpath.str, fsref, 0); } CFStringRef* args = 0; for (i = 0, size = array_size(proc->args); i < size; ++i) //App gets executable path automatically, don't include array_push(args, CFStringCreateWithCString(0, proc->args[i].str, kCFStringEncodingUTF8)); CFArrayRef argvref = CFArrayCreate(0, (const void**)args, (CFIndex)array_size(args), 0); params.flags = kLSLaunchDefaults; params.application = fsref; params.argv = argvref; log_debugf(0, STRING_CONST("Spawn process (LSOpenApplication): %.*s"), STRING_FORMAT(localpath)); status = LSOpenApplication(¶ms, &psn); if (status != 0) { int err = status; string_const_t errmsg = system_error_message(err); proc->code = status; log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process for executable '%.*s': %.*s (%d)"), STRING_FORMAT(localpath), STRING_FORMAT(errmsg), err); } CFRelease(argvref); for (i = 0, size = array_size(args); i < size; ++i) CFRelease(args[i]); array_deallocate(args); memory_deallocate(fsref); string_deallocate(localpath.str); if (status == 0) { pid_t pid = 0; GetProcessPID(&psn, &pid); proc->pid = pid; //Always "detached" with LSOpenApplication, not a child process at all //Setup a kqueue to watch when process terminates so we can emulate a wait proc->kq = kqueue(); if (proc->kq < 0) { string_const_t errmsg = system_error_message(proc->kq); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to create kqueue for process watch: %.*s (%d)"), STRING_FORMAT(errmsg), proc->kq); proc->kq = 0; } else { struct kevent changes; EV_SET(&changes, (pid_t)pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT, 0, 0); int ret = kevent(proc->kq, &changes, 1, &changes, 1, 0); if (ret != 1) { int err = errno; string_const_t errmsg = system_error_message(err); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to setup kqueue for process watch, failed to add event to kqueue: %.*s (%d)"), STRING_FORMAT(errmsg), err); close(proc->kq); proc->kq = 0; } } } goto exit; } #endif #if FOUNDATION_PLATFORM_POSIX //Insert executable arg at start and null ptr at end size_t arg; size_t argc = array_size(proc->args) + 1; array_grow(proc->args, 2); for (arg = argc - 1; arg > 0; --arg) proc->args[arg] = proc->args[arg - 1]; proc->args[0] = string_clone(STRING_ARGS(proc->path)); proc->args[argc] = (string_t) { 0, 0 }; char** argv = memory_allocate(0, sizeof(char*) * (argc + 1), 0, MEMORY_PERSISTENT); for (arg = 0; arg < argc; ++arg) argv[arg] = proc->args[arg].str; argv[argc] = 0; if (proc->flags & PROCESS_STDSTREAMS) { proc->pipeout = pipe_allocate(); proc->pipein = pipe_allocate(); } proc->pid = 0; pid_t pid = fork(); if (pid == 0) { //Child if (proc->wd.length) { log_debugf(0, STRING_CONST("Spawned child process, setting working directory to %.*s"), STRING_FORMAT(proc->wd)); environment_set_current_working_directory(STRING_ARGS(proc->wd)); } log_debugf(0, STRING_CONST("Child process executing: %.*s"), STRING_FORMAT(proc->path)); if (proc->flags & PROCESS_STDSTREAMS) { pipe_close_read(proc->pipeout); dup2(pipe_write_handle(proc->pipeout), STDOUT_FILENO); pipe_close_write(proc->pipein); dup2(pipe_read_handle(proc->pipein), STDIN_FILENO); } int code = execv(proc->path.str, (char* const*)argv); //Error int err = errno; string_const_t errmsg = system_error_message(err); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Child process failed execve() '%.*s': %.*s (%d) (%d)"), STRING_FORMAT(proc->path), STRING_FORMAT(errmsg), err, code); process_exit(PROCESS_EXIT_FAILURE); FOUNDATION_UNUSED(code); } memory_deallocate(argv); if (pid > 0) { log_debugf(0, STRING_CONST("Child process forked, pid %d"), pid); proc->pid = pid; if (proc->pipeout) pipe_close_write(proc->pipeout); if (proc->pipein) pipe_close_read(proc->pipein); /*if (proc->flags & PROCESS_DETACHED) { int cstatus = 0; pid_t err = waitpid(pid, &cstatus, WNOHANG); if (err == 0) { //TODO: Ugly wait to make sure process spawned correctly thread_sleep(500); err = waitpid(pid, &cstatus, WNOHANG); } if (err > 0) { //Process exited, check code proc->pid = 0; proc->code = (int)((char)WEXITSTATUS(cstatus)); log_debugf(0, STRING_CONST("Child process returned: %d"), proc->code); return proc->code; } }*/ } else { //Error string_const_t errmsg; errmsg = system_error_message(proc->code); log_errorf(0, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Unable to spawn process '%.*s': %.*s (%d)"), STRING_FORMAT(proc->path), STRING_FORMAT(errmsg), proc->code); if (proc->pipeout) stream_deallocate(proc->pipeout); if (proc->pipein) stream_deallocate(proc->pipein); proc->pipeout = 0; proc->pipein = 0; proc->code = PROCESS_INVALID_ARGS; return proc->code; } #endif #if !FOUNDATION_PLATFORM_WINDOWS && !FOUNDATION_PLATFORM_POSIX FOUNDATION_ASSERT_FAIL("Process spawning not supported on platform"); #endif #if FOUNDATION_PLATFORM_MACOSX exit: #endif if (proc->flags & PROCESS_DETACHED) return PROCESS_STILL_ACTIVE; return process_wait(proc); }
void config_parse( stream_t* stream, hash_t filter_section, bool overwrite ) { char* buffer; hash_t section = 0; hash_t key = 0; unsigned int line = 0; #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, "Parsing config stream: %s", stream_path( stream ) ); #endif buffer = memory_allocate_zero( 1024ULL, 0, MEMORY_TEMPORARY ); while( !stream_eos( stream ) ) { ++line; stream_read_line_buffer( stream, buffer, 1024, '\n' ); string_strip( buffer, " \t\n\r" ); if( !string_length( buffer ) || ( buffer[0] == ';' ) || ( buffer[0] == '#' ) ) continue; if( buffer[0] == '[' ) { //Section declaration unsigned int endpos = string_rfind( buffer, ']', string_length( buffer ) - 1 ); if( ( endpos == STRING_NPOS ) || ( endpos < 1 ) ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid section declaration on line %d in config stream '%s'", line, stream_path( stream ) ); continue; } buffer[endpos] = 0; section = hash( buffer + 1, endpos - 1 ); #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, " config: section set to '%s' (0x%llx)", buffer + 1, section ); #endif } else if( !filter_section || ( filter_section == section ) ) { //name=value declaration char* name; char* value; unsigned int separator = string_find( buffer, '=', 0 ); if( separator == STRING_NPOS ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid value declaration on line %d in config stream '%s', missing assignment operator '=': %s", line, stream_path( stream ), buffer ); continue; } name = string_strip_substr( buffer, " \t", separator ); value = string_strip( buffer + separator + 1, " \t" ); if( !string_length( name ) ) { log_warnf( HASH_CONFIG, WARNING_BAD_DATA, "Invalid value declaration on line %d in config stream '%s', empty name string", line, stream_path( stream ) ); continue; } key = hash( name, string_length( name ) ); if( overwrite || !config_key( section, key, false ) ) { #if BUILD_ENABLE_DEBUG_CONFIG log_debugf( HASH_CONFIG, " config: %s (0x%llx) = %s", name, key, value ); #endif if( !string_length( value ) ) config_set_string( section, key, "" ); else if( string_equal( value, "false" ) ) config_set_bool( section, key, false ); else if( string_equal( value, "true" ) ) config_set_bool( section, key, true ); else if( ( string_find( value, '.', 0 ) != STRING_NPOS ) && ( string_find_first_not_of( value, "0123456789.", 0 ) == STRING_NPOS ) && ( string_find( value, '.', string_find( value, '.', 0 ) + 1 ) == STRING_NPOS ) ) //Exactly one "." config_set_real( section, key, string_to_real( value ) ); else if( string_find_first_not_of( value, "0123456789", 0 ) == STRING_NPOS ) config_set_int( section, key, string_to_int64( value ) ); else config_set_string( section, key, value ); } } } memory_deallocate( buffer ); }