diff --git a/programs/start/start.c b/programs/start/start.c index c1cc58df7c1..8eff595d44b 100644 --- a/programs/start/start.c +++ b/programs/start/start.c @@ -220,6 +220,18 @@ static BOOL is_option(const WCHAR* arg, const WCHAR* opt) arg, -1, opt, -1) == CSTR_EQUAL; } +static BOOL is_option_with_arg(WCHAR **argv, int *pos, const WCHAR* opt) +{ + if (!is_option( argv[*pos], opt )) return FALSE; + (*pos)++; + if (!argv[*pos]) + { + WINE_ERR("missing argument for %s\n", debugstr_w(opt)); + usage(); + } + return TRUE; +} + static WCHAR *parse_title(const WCHAR *arg) { /* See: @@ -385,229 +397,198 @@ static BOOL search_path(const WCHAR *firstParam, WCHAR **full_path) return FALSE; } +static struct +{ + SHELLEXECUTEINFOW sei; + WCHAR *title; + DWORD creation_flags; +} opts; + +static void parse_command_line( int argc, WCHAR *argv[] ) +{ + int i; + const WCHAR *file = NULL; + BOOL unix_mode = FALSE; + BOOL progid_open = FALSE; + + opts.sei.cbSize = sizeof(opts.sei); + opts.sei.lpVerb = L"open"; + opts.sei.nShow = SW_SHOWNORMAL; + /* Dunno what these mean, but it looks like winMe's start uses them */ + opts.sei.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI; + opts.creation_flags = CREATE_NEW_CONSOLE; + + /* Canonical Microsoft commandline flag processing: + * flags start with / and are case insensitive. + */ + for (i = 1; i < argc; i++) + { + /* parse first quoted argument as console title */ + if (!opts.title && argv[i][0] == '"') { + opts.title = parse_title(argv[i]); + continue; + } + if (argv[i][0] != '/') + break; + + if (argv[i][1] == 'd' || argv[i][1] == 'D') { + if (argv[i][2]) + /* The start directory was concatenated to the option */ + opts.sei.lpDirectory = argv[i]+2; + else if (i+1 == argc) { + WINE_ERR("you must specify a directory path for the /d option\n"); + usage(); + } else + opts.sei.lpDirectory = argv[++i]; + } + else if (is_option(argv[i], L"/b")) + opts.creation_flags &= ~CREATE_NEW_CONSOLE; + else if (argv[i][0] == '/' && (argv[i][1] == 'i' || argv[i][1] == 'I')) + TRACE("/i is ignored\n"); /* FIXME */ + else if (is_option(argv[i], L"/min")) + opts.sei.nShow = SW_SHOWMINIMIZED; + else if (is_option(argv[i], L"/max")) + opts.sei.nShow = SW_SHOWMAXIMIZED; + else if (is_option(argv[i], L"/low")) + opts.creation_flags |= IDLE_PRIORITY_CLASS; + else if (is_option(argv[i], L"/normal")) + opts.creation_flags |= NORMAL_PRIORITY_CLASS; + else if (is_option(argv[i], L"/high")) + opts.creation_flags |= HIGH_PRIORITY_CLASS; + else if (is_option(argv[i], L"/realtime")) + opts.creation_flags |= REALTIME_PRIORITY_CLASS; + else if (is_option(argv[i], L"/abovenormal")) + opts.creation_flags |= ABOVE_NORMAL_PRIORITY_CLASS; + else if (is_option(argv[i], L"/belownormal")) + opts.creation_flags |= BELOW_NORMAL_PRIORITY_CLASS; + else if (is_option(argv[i], L"/separate")) + opts.creation_flags |= CREATE_SEPARATE_WOW_VDM; + else if (is_option(argv[i], L"/shared")) + opts.creation_flags |= CREATE_SHARED_WOW_VDM; + else if (is_option(argv[i], L"/w") || is_option(argv[i], L"/wait")) + opts.sei.fMask |= SEE_MASK_NOCLOSEPROCESS; + else if (is_option(argv[i], L"/?")) + usage(); + else if (is_option_with_arg(argv, &i, L"/node")) + TRACE("/node is ignored\n"); /* FIXME */ + else if (is_option_with_arg(argv, &i, L"/affinity")) + TRACE("/affinity is ignored\n"); /* FIXME */ + + /* Wine extensions */ + + else if (is_option_with_arg(argv, &i, L"/unix")) { + unix_mode = TRUE; + break; + } + else if (is_option(argv[i], L"/exec")) { + opts.creation_flags = 0; + opts.sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_NO_UI; + i++; + break; + } + else if (is_option_with_arg(argv, &i, L"/progIDOpen")) { + progid_open = TRUE; + opts.sei.lpClass = argv[i++]; + opts.sei.fMask |= SEE_MASK_CLASSNAME; + break; + } + else + { + WINE_ERR("Unknown option '%s'\n", wine_dbgstr_w(argv[i])); + usage(); + } + } + + if (i == argc) + { + if (progid_open) usage(); + file = L"cmd.exe"; + } + else file = argv[i++]; + + opts.sei.lpParameters = build_command_line( &argv[i] ); + + if (unix_mode || progid_open) + { + LPWSTR (*CDECL wine_get_dos_file_name_ptr)(LPCSTR); + WCHAR *dos_filename; + char *unixpath; + int unixpath_len; + + wine_get_dos_file_name_ptr = (void*)GetProcAddress(GetModuleHandleA("KERNEL32"), "wine_get_dos_file_name"); + if (!wine_get_dos_file_name_ptr) fatal_string(STRING_UNIXFAIL); + + unixpath_len = WideCharToMultiByte(CP_UNIXCP, 0, file, -1, NULL, 0, NULL, NULL); + unixpath = malloc( unixpath_len ); + WideCharToMultiByte(CP_UNIXCP, 0, file, -1, unixpath, unixpath_len, NULL, NULL); + dos_filename = wine_get_dos_file_name_ptr(unixpath); + free( unixpath ); + + if (!dos_filename) + fatal_string(STRING_UNIXFAIL); + + opts.sei.lpFile = dos_filename; + if (!opts.sei.lpDirectory) opts.sei.lpDirectory = get_parent_dir(dos_filename); + opts.sei.fMask &= ~SEE_MASK_FLAG_NO_UI; + } + else + { + WCHAR *fullpath; + + if (search_path(file, &fullpath)) + { + if (!fullpath) fatal_string_error(STRING_EXECFAIL, ERROR_OUTOFMEMORY, file); + opts.sei.lpFile = fullpath; + } + else opts.sei.lpFile = file; + } +} + + int __cdecl wmain (int argc, WCHAR *argv[]) { - SHELLEXECUTEINFOW sei; - DWORD creation_flags; - int i; - BOOL unix_mode = FALSE; - BOOL progid_open = FALSE; - WCHAR *title = NULL; - const WCHAR *file; - WCHAR *dos_filename = NULL; - WCHAR *fullpath = NULL; - WCHAR *parent_directory = NULL; DWORD binary_type; - memset(&sei, 0, sizeof(sei)); - sei.cbSize = sizeof(sei); - sei.lpVerb = L"open"; - sei.nShow = SW_SHOWNORMAL; - /* Dunno what these mean, but it looks like winMe's start uses them */ - sei.fMask = SEE_MASK_FLAG_DDEWAIT| - SEE_MASK_FLAG_NO_UI; - sei.lpDirectory = NULL; - creation_flags = CREATE_NEW_CONSOLE; + parse_command_line( argc, argv ); - /* Canonical Microsoft commandline flag processing: - * flags start with / and are case insensitive. - */ - for (i=1; i