cmd: Reorder some functions to avoid forward declarations.
Francois Gouget
fgouget at free.fr
Tue Dec 30 17:56:53 CST 2008
---
This patch is independent from the previous one.
The next step is to make a bunch of these functions static...
programs/cmd/wcmdmain.c | 2676 +++++++++++++++++++++++------------------------
1 files changed, 1334 insertions(+), 1342 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 8d587e6..954f5a3 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -97,1126 +97,210 @@ static char *output_bufA = NULL;
#define MAX_WRITECONSOLE_SIZE 65535
BOOL unicodePipes = FALSE;
-static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR *forvar, WCHAR *forVal);
-static void WCMD_output_asis_len(const WCHAR *message, int len, HANDLE device);
-/*****************************************************************************
- * Main entry point. This is a console application so we have a main() not a
- * winmain().
+/*******************************************************************
+ * WCMD_output_asis_len - send output to current standard output
+ *
+ * Output a formatted unicode string. Ideally this will go to the console
+ * and hence required WriteConsoleW to output it, however if file i/o is
+ * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
*/
+static void WCMD_output_asis_len(const WCHAR *message, int len, HANDLE device) {
-int wmain (int argc, WCHAR *argvW[])
-{
- int args;
- WCHAR *cmd = NULL;
- WCHAR string[1024];
- WCHAR envvar[4];
- HANDLE h;
- int opt_q;
- int opt_t = 0;
- static const WCHAR autoexec[] = {'\\','a','u','t','o','e','x','e','c','.',
- 'b','a','t','\0'};
- char ansiVersion[100];
- CMD_LIST *toExecute = NULL; /* Commands left to be executed */
-
- srand(time(NULL));
-
- /* Pre initialize some messages */
- strcpy(ansiVersion, PACKAGE_VERSION);
- MultiByteToWideChar(CP_ACP, 0, ansiVersion, -1, string, 1024);
- wsprintf(version_string, WCMD_LoadMessage(WCMD_VERSION), string);
- strcpyW(anykey, WCMD_LoadMessage(WCMD_ANYKEY));
-
- args = argc;
- opt_c=opt_k=opt_q=opt_s=0;
- while (args > 0)
- {
- WCHAR c;
- WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(*argvW));
- if ((*argvW)[0]!='/' || (*argvW)[1]=='\0') {
- argvW++;
- args--;
- continue;
- }
-
- c=(*argvW)[1];
- if (tolowerW(c)=='c') {
- opt_c=1;
- } else if (tolowerW(c)=='q') {
- opt_q=1;
- } else if (tolowerW(c)=='k') {
- opt_k=1;
- } else if (tolowerW(c)=='s') {
- opt_s=1;
- } else if (tolowerW(c)=='a') {
- unicodePipes=FALSE;
- } else if (tolowerW(c)=='u') {
- unicodePipes=TRUE;
- } else if (tolowerW(c)=='t' && (*argvW)[2]==':') {
- opt_t=strtoulW(&(*argvW)[3], NULL, 16);
- } else if (tolowerW(c)=='x' || tolowerW(c)=='y') {
- /* Ignored for compatibility with Windows */
- }
-
- if ((*argvW)[2]==0) {
- argvW++;
- args--;
- }
- else /* handle `cmd /cnotepad.exe` and `cmd /x/c ...` */
- {
- *argvW+=2;
- }
-
- if (opt_c || opt_k) /* break out of parsing immediately after c or k */
- break;
- }
-
- if (opt_q) {
- const WCHAR eoff[] = {'O','F','F','\0'};
- WCMD_echo(eoff);
- }
-
- if (opt_c || opt_k) {
- int len,qcount;
- WCHAR** arg;
- int argsLeft;
- WCHAR* p;
-
- /* opt_s left unflagged if the command starts with and contains exactly
- * one quoted string (exactly two quote characters). The quoted string
- * must be an executable name that has whitespace and must not have the
- * following characters: &<>()@^| */
-
- /* Build the command to execute */
- len = 0;
- qcount = 0;
- argsLeft = args;
- for (arg = argvW; argsLeft>0; arg++,argsLeft--)
- {
- int has_space,bcount;
- WCHAR* a;
-
- has_space=0;
- bcount=0;
- a=*arg;
- if( !*a ) has_space=1;
- while (*a!='\0') {
- if (*a=='\\') {
- bcount++;
- } else {
- if (*a==' ' || *a=='\t') {
- has_space=1;
- } else if (*a=='"') {
- /* doubling of '\' preceding a '"',
- * plus escaping of said '"'
- */
- len+=2*bcount+1;
- qcount++;
- }
- bcount=0;
- }
- a++;
- }
- len+=(a-*arg) + 1; /* for the separating space */
- if (has_space)
- {
- len+=2; /* for the quotes */
- qcount+=2;
- }
- }
-
- if (qcount!=2)
- opt_s=1;
-
- /* check argvW[0] for a space and invalid characters */
- if (!opt_s) {
- opt_s=1;
- p=*argvW;
- while (*p!='\0') {
- if (*p=='&' || *p=='<' || *p=='>' || *p=='(' || *p==')'
- || *p=='@' || *p=='^' || *p=='|') {
- opt_s=1;
- break;
- }
- if (*p==' ')
- opt_s=0;
- p++;
- }
- }
-
- cmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
- if (!cmd)
- exit(1);
-
- p = cmd;
- argsLeft = args;
- for (arg = argvW; argsLeft>0; arg++,argsLeft--)
- {
- int has_space,has_quote;
- WCHAR* a;
-
- /* Check for quotes and spaces in this argument */
- has_space=has_quote=0;
- a=*arg;
- if( !*a ) has_space=1;
- while (*a!='\0') {
- if (*a==' ' || *a=='\t') {
- has_space=1;
- if (has_quote)
- break;
- } else if (*a=='"') {
- has_quote=1;
- if (has_space)
- break;
- }
- a++;
- }
-
- /* Now transfer it to the command line */
- if (has_space)
- *p++='"';
- if (has_quote) {
- int bcount;
- WCHAR* a;
-
- bcount=0;
- a=*arg;
- while (*a!='\0') {
- if (*a=='\\') {
- *p++=*a;
- bcount++;
- } else {
- if (*a=='"') {
- int i;
-
- /* Double all the '\\' preceding this '"', plus one */
- for (i=0;i<=bcount;i++)
- *p++='\\';
- *p++='"';
- } else {
- *p++=*a;
- }
- bcount=0;
- }
- a++;
- }
- } else {
- strcpyW(p,*arg);
- p+=strlenW(*arg);
- }
- if (has_space)
- *p++='"';
- *p++=' ';
- }
- if (p > cmd)
- p--; /* remove last space */
- *p = '\0';
-
- WINE_TRACE("/c command line: '%s'\n", wine_dbgstr_w(cmd));
-
- /* strip first and last quote characters if opt_s; check for invalid
- * executable is done later */
- if (opt_s && *cmd=='\"')
- WCMD_opt_s_strip_quotes(cmd);
- }
-
- if (opt_c) {
- /* If we do a "wcmd /c command", we don't want to allocate a new
- * console since the command returns immediately. Rather, we use
- * the currently allocated input and output handles. This allows
- * us to pipe to and read from the command interpreter.
- */
-
- /* Parse the command string, without reading any more input */
- WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
- WCMD_process_commands(toExecute, FALSE, NULL, NULL);
- WCMD_free_commands(toExecute);
- toExecute = NULL;
-
- HeapFree(GetProcessHeap(), 0, cmd);
- return errorlevel;
- }
-
- SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT |
- ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
- SetConsoleTitle(WCMD_LoadMessage(WCMD_CONSTITLE));
-
- /* Note: cmd.exe /c dir does not get a new color, /k dir does */
- if (opt_t) {
- if (!(((opt_t & 0xF0) >> 4) == (opt_t & 0x0F))) {
- defaultColor = opt_t & 0xFF;
- param1[0] = 0x00;
- WCMD_color();
- }
- } else {
- /* Check HKCU\Software\Microsoft\Command Processor
- Then HKLM\Software\Microsoft\Command Processor
- for defaultcolour value
- Note Can be supplied as DWORD or REG_SZ
- Note2 When supplied as REG_SZ it's in decimal!!! */
- HKEY key;
- DWORD type;
- DWORD value=0, size=4;
- static const WCHAR regKeyW[] = {'S','o','f','t','w','a','r','e','\\',
- 'M','i','c','r','o','s','o','f','t','\\',
- 'C','o','m','m','a','n','d',' ','P','r','o','c','e','s','s','o','r','\0'};
- static const WCHAR dfltColorW[] = {'D','e','f','a','u','l','t','C','o','l','o','r','\0'};
+ DWORD nOut= 0;
+ DWORD res = 0;
- if (RegOpenKeyEx(HKEY_CURRENT_USER, regKeyW,
- 0, KEY_READ, &key) == ERROR_SUCCESS) {
- WCHAR strvalue[4];
+ /* If nothing to write, return (MORE does this sometimes) */
+ if (!len) return;
- /* See if DWORD or REG_SZ */
- if (RegQueryValueEx(key, dfltColorW, NULL, &type,
- NULL, NULL) == ERROR_SUCCESS) {
- if (type == REG_DWORD) {
- size = sizeof(DWORD);
- RegQueryValueEx(key, dfltColorW, NULL, NULL,
- (LPBYTE)&value, &size);
- } else if (type == REG_SZ) {
- size = sizeof(strvalue)/sizeof(WCHAR);
- RegQueryValueEx(key, dfltColorW, NULL, NULL,
- (LPBYTE)strvalue, &size);
- value = strtoulW(strvalue, NULL, 10);
- }
- }
- RegCloseKey(key);
- }
+ /* Try to write as unicode assuming it is to a console */
+ res = WriteConsoleW(device, message, len, &nOut, NULL);
- if (value == 0 && RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKeyW,
- 0, KEY_READ, &key) == ERROR_SUCCESS) {
- WCHAR strvalue[4];
+ /* If writing to console fails, assume its file
+ i/o so convert to OEM codepage and output */
+ if (!res) {
+ BOOL usedDefaultChar = FALSE;
+ DWORD convertedChars;
- /* See if DWORD or REG_SZ */
- if (RegQueryValueEx(key, dfltColorW, NULL, &type,
- NULL, NULL) == ERROR_SUCCESS) {
- if (type == REG_DWORD) {
- size = sizeof(DWORD);
- RegQueryValueEx(key, dfltColorW, NULL, NULL,
- (LPBYTE)&value, &size);
- } else if (type == REG_SZ) {
- size = sizeof(strvalue)/sizeof(WCHAR);
- RegQueryValueEx(key, dfltColorW, NULL, NULL,
- (LPBYTE)strvalue, &size);
- value = strtoulW(strvalue, NULL, 10);
- }
- }
- RegCloseKey(key);
- }
+ if (!unicodePipes) {
+ /*
+ * Allocate buffer to use when writing to file. (Not freed, as one off)
+ */
+ if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
+ MAX_WRITECONSOLE_SIZE);
+ if (!output_bufA) {
+ WINE_FIXME("Out of memory - could not allocate ansi 64K buffer\n");
+ return;
+ }
- /* If one found, set the screen to that colour */
- if (!(((value & 0xF0) >> 4) == (value & 0x0F))) {
- defaultColor = value & 0xFF;
- param1[0] = 0x00;
- WCMD_color();
+ /* Convert to OEM, then output */
+ convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, message,
+ len, output_bufA, MAX_WRITECONSOLE_SIZE,
+ "?", &usedDefaultChar);
+ WriteFile(device, output_bufA, convertedChars,
+ &nOut, FALSE);
+ } else {
+ WriteFile(device, message, len*sizeof(WCHAR),
+ &nOut, FALSE);
}
+ }
+ return;
+}
- }
-
- /* Save cwd into appropriate env var */
- GetCurrentDirectory(1024, string);
- if (IsCharAlpha(string[0]) && string[1] == ':') {
- static const WCHAR fmt[] = {'=','%','c',':','\0'};
- wsprintf(envvar, fmt, string[0]);
- SetEnvironmentVariable(envvar, string);
- WINE_TRACE("Set %s to %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(string));
- }
-
- if (opt_k) {
- /* Parse the command string, without reading any more input */
- WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
- WCMD_process_commands(toExecute, FALSE, NULL, NULL);
- WCMD_free_commands(toExecute);
- toExecute = NULL;
- HeapFree(GetProcessHeap(), 0, cmd);
- }
-
-/*
- * If there is an AUTOEXEC.BAT file, try to execute it.
+/*******************************************************************
+ * WCMD_output - send output to current standard output device.
+ *
*/
- GetFullPathName (autoexec, sizeof(string)/sizeof(WCHAR), string, NULL);
- h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
- if (h != INVALID_HANDLE_VALUE) {
- CloseHandle (h);
-#if 0
- WCMD_batch (autoexec, autoexec, 0, NULL, INVALID_HANDLE_VALUE);
-#endif
- }
-
-/*
- * Loop forever getting commands and executing them.
- */
+void WCMD_output (const WCHAR *format, ...) {
- WCMD_version ();
- while (TRUE) {
+ va_list ap;
+ WCHAR string[1024];
+ int ret;
- /* Read until EOF (which for std input is never, but if redirect
- in place, may occur */
- WCMD_show_prompt ();
- if (WCMD_ReadAndParseLine(NULL, &toExecute,
- GetStdHandle(STD_INPUT_HANDLE)) == NULL)
- break;
- WCMD_process_commands(toExecute, FALSE, NULL, NULL);
- WCMD_free_commands(toExecute);
- toExecute = NULL;
+ va_start(ap,format);
+ ret = wvsprintf (string, format, ap);
+ if( ret >= (sizeof(string)/sizeof(WCHAR))) {
+ WINE_ERR("Output truncated in WCMD_output\n" );
+ ret = (sizeof(string)/sizeof(WCHAR)) - 1;
+ string[ret] = '\0';
}
- return 0;
+ va_end(ap);
+ WCMD_output_asis_len(string, ret, GetStdHandle(STD_OUTPUT_HANDLE));
}
-/*****************************************************************************
- * Expand the command. Native expands lines from batch programs as they are
- * read in and not again, except for 'for' variable substitution.
- * eg. As evidence, "echo %1 && shift && echo %1" or "echo %%path%%"
- */
-void handleExpansion(WCHAR *cmd, BOOL justFors, WCHAR *forVariable, WCHAR *forValue) {
-
- /* For commands in a context (batch program): */
- /* Expand environment variables in a batch file %{0-9} first */
- /* including support for any ~ modifiers */
- /* Additionally: */
- /* Expand the DATE, TIME, CD, RANDOM and ERRORLEVEL special */
- /* names allowing environment variable overrides */
- /* NOTE: To support the %PATH:xxx% syntax, also perform */
- /* manual expansion of environment variables here */
- WCHAR *p = cmd;
- WCHAR *s, *t;
- int i;
-
- while ((p = strchrW(p, '%'))) {
-
- WINE_TRACE("Translate command:%s %d (at: %s)\n",
- wine_dbgstr_w(cmd), justFors, wine_dbgstr_w(p));
- i = *(p+1) - '0';
-
- /* Don't touch %% unless its in Batch */
- if (!justFors && *(p+1) == '%') {
- if (context) {
- s = WCMD_strdupW(p+1);
- strcpyW (p, s);
- free (s);
- }
- p+=1;
-
- /* Replace %~ modifications if in batch program */
- } else if (*(p+1) == '~') {
- WCMD_HandleTildaModifiers(&p, forVariable, forValue, justFors);
- p++;
-
- /* Replace use of %0...%9 if in batch program*/
- } else if (!justFors && context && (i >= 0) && (i <= 9)) {
- s = WCMD_strdupW(p+2);
- t = WCMD_parameter (context -> command, i + context -> shift_count[i], NULL);
- strcpyW (p, t);
- strcatW (p, s);
- free (s);
-
- /* Replace use of %* if in batch program*/
- } else if (!justFors && context && *(p+1)=='*') {
- WCHAR *startOfParms = NULL;
- s = WCMD_strdupW(p+2);
- t = WCMD_parameter (context -> command, 1, &startOfParms);
- if (startOfParms != NULL) strcpyW (p, startOfParms);
- else *p = 0x00;
- strcatW (p, s);
- free (s);
-
- } else if (forVariable &&
- (CompareString (LOCALE_USER_DEFAULT,
- SORT_STRINGSORT,
- p,
- strlenW(forVariable),
- forVariable, -1) == 2)) {
- s = WCMD_strdupW(p + strlenW(forVariable));
- strcpyW(p, forValue);
- strcatW(p, s);
- free(s);
+static int line_count;
+static int max_height;
+static int max_width;
+static BOOL paged_mode;
+static int numChars;
- } else if (!justFors) {
- p = WCMD_expand_envvar(p, forVariable, forValue);
+void WCMD_enter_paged_mode(const WCHAR *msg)
+{
+ CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
- /* In a FOR loop, see if this is the variable to replace */
- } else { /* Ignore %'s on second pass of batch program */
- p++;
- }
+ if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo)) {
+ max_height = consoleInfo.dwSize.Y;
+ max_width = consoleInfo.dwSize.X;
+ } else {
+ max_height = 25;
+ max_width = 80;
}
-
- return;
+ paged_mode = TRUE;
+ line_count = 0;
+ numChars = 0;
+ pagedMessage = (msg==NULL)? anykey : msg;
}
-
-/*****************************************************************************
- * Process one command. If the command is EXIT this routine does not return.
- * We will recurse through here executing batch files.
- */
-
-
-void WCMD_execute (WCHAR *command, WCHAR *redirects,
- WCHAR *forVariable, WCHAR *forValue,
- CMD_LIST **cmdList)
+void WCMD_leave_paged_mode(void)
{
- WCHAR *cmd, *p, *redir;
- int status, i;
- DWORD count, creationDisposition;
- HANDLE h;
- WCHAR *whichcmd;
- SECURITY_ATTRIBUTES sa;
- WCHAR *new_cmd = NULL;
- WCHAR *new_redir = NULL;
- HANDLE old_stdhandles[3] = {INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE,
- INVALID_HANDLE_VALUE};
- DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE,
- STD_OUTPUT_HANDLE,
- STD_ERROR_HANDLE};
- BOOL piped = FALSE;
-
- WINE_TRACE("command on entry:%s (%p), with '%s'='%s'\n",
- wine_dbgstr_w(command), cmdList,
- wine_dbgstr_w(forVariable), wine_dbgstr_w(forValue));
-
- /* If the next command is a pipe then we implement pipes by redirecting
- the output from this command to a temp file and input into the
- next command from that temp file.
- FIXME: Use of named pipes would make more sense here as currently this
- process has to finish before the next one can start but this requires
- a change to not wait for the first app to finish but rather the pipe */
- if (cmdList && (*cmdList)->nextcommand &&
- (*cmdList)->nextcommand->prevDelim == CMD_PIPE) {
-
- WCHAR temp_path[MAX_PATH];
- static const WCHAR cmdW[] = {'C','M','D','\0'};
-
- /* Remember piping is in action */
- WINE_TRACE("Output needs to be piped\n");
- piped = TRUE;
-
- /* Generate a unique temporary filename */
- GetTempPath (sizeof(temp_path)/sizeof(WCHAR), temp_path);
- GetTempFileName (temp_path, cmdW, 0, (*cmdList)->nextcommand->pipeFile);
- WINE_TRACE("Using temporary file of %s\n",
- wine_dbgstr_w((*cmdList)->nextcommand->pipeFile));
- }
-
- /* Move copy of the command onto the heap so it can be expanded */
- new_cmd = HeapAlloc( GetProcessHeap(), 0, MAXSTRING * sizeof(WCHAR));
- if (!new_cmd)
- {
- WINE_ERR("Could not allocate memory for new_cmd\n");
- return;
- }
- strcpyW(new_cmd, command);
-
- /* Move copy of the redirects onto the heap so it can be expanded */
- new_redir = HeapAlloc( GetProcessHeap(), 0, MAXSTRING * sizeof(WCHAR));
- if (!new_redir)
- {
- WINE_ERR("Could not allocate memory for new_redir\n");
- HeapFree( GetProcessHeap(), 0, new_cmd );
- return;
- }
-
- /* If piped output, send stdout to the pipe by appending >filename to redirects */
- if (piped) {
- static const WCHAR redirOut[] = {'%','s',' ','>',' ','%','s','\0'};
- wsprintf (new_redir, redirOut, redirects, (*cmdList)->nextcommand->pipeFile);
- WINE_TRACE("Redirects now %s\n", wine_dbgstr_w(new_redir));
- } else {
- strcpyW(new_redir, redirects);
- }
-
- /* Expand variables in command line mode only (batch mode will
- be expanded as the line is read in, except for 'for' loops) */
- handleExpansion(new_cmd, (context != NULL), forVariable, forValue);
- handleExpansion(new_redir, (context != NULL), forVariable, forValue);
- cmd = new_cmd;
-
- /* Show prompt before batch line IF echo is on and in batch program */
- if (context && echo_mode && (cmd[0] != '@')) {
- WCMD_show_prompt();
- WCMD_output_asis ( cmd);
- WCMD_output_asis ( newline);
- }
-
-/*
- * Changing default drive has to be handled as a special case.
- */
-
- if ((cmd[1] == ':') && IsCharAlpha (cmd[0]) && (strlenW(cmd) == 2)) {
- WCHAR envvar[5];
- WCHAR dir[MAX_PATH];
-
- /* According to MSDN CreateProcess docs, special env vars record
- the current directory on each drive, in the form =C:
- so see if one specified, and if so go back to it */
- strcpyW(envvar, equalsW);
- strcatW(envvar, cmd);
- if (GetEnvironmentVariable(envvar, dir, MAX_PATH) == 0) {
- static const WCHAR fmt[] = {'%','s','\\','\0'};
- wsprintf(cmd, fmt, cmd);
- WINE_TRACE("No special directory settings, using dir of %s\n", wine_dbgstr_w(cmd));
- }
- WINE_TRACE("Got directory %s as %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(cmd));
- status = SetCurrentDirectory (cmd);
- if (!status) WCMD_print_error ();
- HeapFree( GetProcessHeap(), 0, cmd );
- HeapFree( GetProcessHeap(), 0, new_redir );
- return;
- }
-
- sa.nLength = sizeof(sa);
- sa.lpSecurityDescriptor = NULL;
- sa.bInheritHandle = TRUE;
+ paged_mode = FALSE;
+ pagedMessage = NULL;
+}
-/*
- * Redirect stdin, stdout and/or stderr if required.
+/***************************************************************************
+ * WCMD_Readfile
+ *
+ * Read characters in from a console/file, returning result in Unicode
+ * with signature identical to ReadFile
*/
+BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
+ LPDWORD charsRead, const LPOVERLAPPED unused) {
- /* STDIN could come from a preceding pipe, so delete on close if it does */
- if (cmdList && (*cmdList)->pipeFile[0] != 0x00) {
- WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->pipeFile));
- h = CreateFile ((*cmdList)->pipeFile, GENERIC_READ,
- FILE_SHARE_READ, &sa, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
- if (h == INVALID_HANDLE_VALUE) {
- WCMD_print_error ();
- HeapFree( GetProcessHeap(), 0, cmd );
- HeapFree( GetProcessHeap(), 0, new_redir );
- return;
- }
- old_stdhandles[0] = GetStdHandle (STD_INPUT_HANDLE);
- SetStdHandle (STD_INPUT_HANDLE, h);
-
- /* No need to remember the temporary name any longer once opened */
- (*cmdList)->pipeFile[0] = 0x00;
-
- /* Otherwise STDIN could come from a '<' redirect */
- } else if ((p = strchrW(new_redir,'<')) != NULL) {
- h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (h == INVALID_HANDLE_VALUE) {
- WCMD_print_error ();
- HeapFree( GetProcessHeap(), 0, cmd );
- HeapFree( GetProcessHeap(), 0, new_redir );
- return;
- }
- old_stdhandles[0] = GetStdHandle (STD_INPUT_HANDLE);
- SetStdHandle (STD_INPUT_HANDLE, h);
- }
-
- /* Scan the whole command looking for > and 2> */
- redir = new_redir;
- while (redir != NULL && ((p = strchrW(redir,'>')) != NULL)) {
- int handle = 0;
-
- if (*(p-1)!='2') {
- handle = 1;
- } else {
- handle = 2;
- }
-
- p++;
- if ('>' == *p) {
- creationDisposition = OPEN_ALWAYS;
- p++;
- }
- else {
- creationDisposition = CREATE_ALWAYS;
- }
+ BOOL res;
- /* Add support for 2>&1 */
- redir = p;
- if (*p == '&') {
- int idx = *(p+1) - '0';
+ /* Try to read from console as Unicode */
+ res = ReadConsoleW(hIn, intoBuf, maxChars, charsRead, NULL);
- if (DuplicateHandle(GetCurrentProcess(),
- GetStdHandle(idx_stdhandles[idx]),
- GetCurrentProcess(),
- &h,
- 0, TRUE, DUPLICATE_SAME_ACCESS) == 0) {
- WINE_FIXME("Duplicating handle failed with gle %d\n", GetLastError());
- }
- WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h);
+ /* If reading from console has failed we assume its file
+ i/o so read in and convert from OEM codepage */
+ if (!res) {
- } else {
- WCHAR *param = WCMD_parameter (p, 0, NULL);
- h = CreateFile (param, GENERIC_WRITE, 0, &sa, creationDisposition,
- FILE_ATTRIBUTE_NORMAL, NULL);
- if (h == INVALID_HANDLE_VALUE) {
- WCMD_print_error ();
- HeapFree( GetProcessHeap(), 0, cmd );
- HeapFree( GetProcessHeap(), 0, new_redir );
- return;
- }
- if (SetFilePointer (h, 0, NULL, FILE_END) ==
- INVALID_SET_FILE_POINTER) {
- WCMD_print_error ();
+ DWORD numRead;
+ /*
+ * Allocate buffer to use when reading from file. Not freed
+ */
+ if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
+ MAX_WRITECONSOLE_SIZE);
+ if (!output_bufA) {
+ WINE_FIXME("Out of memory - could not allocate ansi 64K buffer\n");
+ return 0;
}
- WINE_TRACE("Redirect %d to '%s' (%p)\n", handle, wine_dbgstr_w(param), h);
- }
-
- old_stdhandles[handle] = GetStdHandle (idx_stdhandles[handle]);
- SetStdHandle (idx_stdhandles[handle], h);
- }
-
-/*
- * Strip leading whitespaces, and a '@' if supplied
- */
- whichcmd = WCMD_strtrim_leading_spaces(cmd);
- WINE_TRACE("Command: '%s'\n", wine_dbgstr_w(cmd));
- if (whichcmd[0] == '@') whichcmd++;
-
-/*
- * Check if the command entered is internal. If it is, pass the rest of the
- * line down to the command. If not try to run a program.
- */
-
- count = 0;
- while (IsCharAlphaNumeric(whichcmd[count])) {
- count++;
- }
- for (i=0; i<=WCMD_EXIT; i++) {
- if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
- whichcmd, count, inbuilt[i], -1) == 2) break;
- }
- p = WCMD_strtrim_leading_spaces (&whichcmd[count]);
- WCMD_parse (p, quals, param1, param2);
- WINE_TRACE("param1: %s, param2: %s\n", wine_dbgstr_w(param1), wine_dbgstr_w(param2));
- switch (i) {
+ /* Read from file (assume OEM codepage) */
+ res = ReadFile(hIn, output_bufA, maxChars, &numRead, unused);
- case WCMD_ATTRIB:
- WCMD_setshow_attrib ();
- break;
- case WCMD_CALL:
- WCMD_call (p);
- break;
- case WCMD_CD:
- case WCMD_CHDIR:
- WCMD_setshow_default (p);
- break;
- case WCMD_CLS:
- WCMD_clear_screen ();
- break;
- case WCMD_COPY:
- WCMD_copy ();
- break;
- case WCMD_CTTY:
- WCMD_change_tty ();
- break;
- case WCMD_DATE:
- WCMD_setshow_date ();
- break;
- case WCMD_DEL:
- case WCMD_ERASE:
- WCMD_delete (p, TRUE);
- break;
- case WCMD_DIR:
- WCMD_directory (p);
- break;
- case WCMD_ECHO:
- WCMD_echo(&whichcmd[count]);
- break;
- case WCMD_FOR:
- WCMD_for (p, cmdList);
- break;
- case WCMD_GOTO:
- WCMD_goto (cmdList);
- break;
- case WCMD_HELP:
- WCMD_give_help (p);
- break;
- case WCMD_IF:
- WCMD_if (p, cmdList);
- break;
- case WCMD_LABEL:
- WCMD_volume (1, p);
- break;
- case WCMD_MD:
- case WCMD_MKDIR:
- WCMD_create_dir ();
- break;
- case WCMD_MOVE:
- WCMD_move ();
- break;
- case WCMD_PATH:
- WCMD_setshow_path (p);
- break;
- case WCMD_PAUSE:
- WCMD_pause ();
- break;
- case WCMD_PROMPT:
- WCMD_setshow_prompt ();
- break;
- case WCMD_REM:
- break;
- case WCMD_REN:
- case WCMD_RENAME:
- WCMD_rename ();
- break;
- case WCMD_RD:
- case WCMD_RMDIR:
- WCMD_remove_dir (p);
- break;
- case WCMD_SETLOCAL:
- WCMD_setlocal(p);
- break;
- case WCMD_ENDLOCAL:
- WCMD_endlocal();
- break;
- case WCMD_SET:
- WCMD_setshow_env (p);
- break;
- case WCMD_SHIFT:
- WCMD_shift (p);
- break;
- case WCMD_TIME:
- WCMD_setshow_time ();
- break;
- case WCMD_TITLE:
- if (strlenW(&whichcmd[count]) > 0)
- WCMD_title(&whichcmd[count+1]);
- break;
- case WCMD_TYPE:
- WCMD_type (p);
- break;
- case WCMD_VER:
- WCMD_version ();
- break;
- case WCMD_VERIFY:
- WCMD_verify (p);
- break;
- case WCMD_VOL:
- WCMD_volume (0, p);
- break;
- case WCMD_PUSHD:
- WCMD_pushd(p);
- break;
- case WCMD_POPD:
- WCMD_popd();
- break;
- case WCMD_ASSOC:
- WCMD_assoc(p, TRUE);
- break;
- case WCMD_COLOR:
- WCMD_color();
- break;
- case WCMD_FTYPE:
- WCMD_assoc(p, FALSE);
- break;
- case WCMD_MORE:
- WCMD_more(p);
- break;
- case WCMD_EXIT:
- WCMD_exit (cmdList);
- break;
- default:
- WCMD_run_program (whichcmd, 0);
- }
- HeapFree( GetProcessHeap(), 0, cmd );
- HeapFree( GetProcessHeap(), 0, new_redir );
+ /* Convert from OEM */
+ *charsRead = MultiByteToWideChar(GetConsoleCP(), 0, output_bufA, numRead,
+ intoBuf, maxChars);
- /* Restore old handles */
- for (i=0; i<3; i++) {
- if (old_stdhandles[i] != INVALID_HANDLE_VALUE) {
- CloseHandle (GetStdHandle (idx_stdhandles[i]));
- SetStdHandle (idx_stdhandles[i], old_stdhandles[i]);
- }
}
+ return res;
}
-static void init_msvcrt_io_block(STARTUPINFO* st)
-{
- STARTUPINFO st_p;
- /* fetch the parent MSVCRT info block if any, so that the child can use the
- * same handles as its grand-father
- */
- st_p.cb = sizeof(STARTUPINFO);
- GetStartupInfo(&st_p);
- st->cbReserved2 = st_p.cbReserved2;
- st->lpReserved2 = st_p.lpReserved2;
- if (st_p.cbReserved2 && st_p.lpReserved2)
- {
- /* Override the entries for fd 0,1,2 if we happened
- * to change those std handles (this depends on the way wcmd sets
- * it's new input & output handles)
- */
- size_t sz = max(sizeof(unsigned) + (sizeof(char) + sizeof(HANDLE)) * 3, st_p.cbReserved2);
- BYTE* ptr = HeapAlloc(GetProcessHeap(), 0, sz);
- if (ptr)
- {
- unsigned num = *(unsigned*)st_p.lpReserved2;
- char* flags = (char*)(ptr + sizeof(unsigned));
- HANDLE* handles = (HANDLE*)(flags + num * sizeof(char));
-
- memcpy(ptr, st_p.lpReserved2, st_p.cbReserved2);
- st->cbReserved2 = sz;
- st->lpReserved2 = ptr;
+/*******************************************************************
+ * WCMD_output_asis - send output to current standard output device.
+ * without formatting eg. when message contains '%'
+ */
+void WCMD_output_asis (const WCHAR *message) {
+ DWORD count;
+ const WCHAR* ptr;
+ WCHAR string[1024];
-#define WX_OPEN 0x01 /* see dlls/msvcrt/file.c */
- if (num <= 0 || (flags[0] & WX_OPEN))
- {
- handles[0] = GetStdHandle(STD_INPUT_HANDLE);
- flags[0] |= WX_OPEN;
- }
- if (num <= 1 || (flags[1] & WX_OPEN))
- {
- handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
- flags[1] |= WX_OPEN;
- }
- if (num <= 2 || (flags[2] & WX_OPEN))
- {
- handles[2] = GetStdHandle(STD_ERROR_HANDLE);
- flags[2] |= WX_OPEN;
- }
-#undef WX_OPEN
+ if (paged_mode) {
+ do {
+ ptr = message;
+ while (*ptr && *ptr!='\n' && (numChars < max_width)) {
+ numChars++;
+ ptr++;
+ };
+ if (*ptr == '\n') ptr++;
+ WCMD_output_asis_len(message, (ptr) ? ptr - message : strlenW(message),
+ GetStdHandle(STD_OUTPUT_HANDLE));
+ if (ptr) {
+ numChars = 0;
+ if (++line_count >= max_height - 1) {
+ line_count = 0;
+ WCMD_output_asis_len(pagedMessage, strlenW(pagedMessage),
+ GetStdHandle(STD_OUTPUT_HANDLE));
+ WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
+ sizeof(string)/sizeof(WCHAR), &count, NULL);
}
- }
+ }
+ } while (((message = ptr) != NULL) && (*ptr));
+ } else {
+ WCMD_output_asis_len(message, lstrlen(message),
+ GetStdHandle(STD_OUTPUT_HANDLE));
+ }
}
-/******************************************************************************
- * WCMD_run_program
- *
- * Execute a command line as an external program. Must allow recursion.
- *
- * Precedence:
- * Manual testing under windows shows PATHEXT plays a key part in this,
- * and the search algorithm and precedence appears to be as follows.
+/****************************************************************************
+ * WCMD_print_error
*
- * Search locations:
- * If directory supplied on command, just use that directory
- * If extension supplied on command, look for that explicit name first
- * Otherwise, search in each directory on the path
- * Precedence:
- * If extension supplied on command, look for that explicit name first
- * Then look for supplied name .* (even if extension supplied, so
- * 'garbage.exe' will match 'garbage.exe.cmd')
- * If any found, cycle through PATHEXT looking for name.exe one by one
- * Launching
- * Once a match has been found, it is launched - Code currently uses
- * findexecutable to achieve this which is left untouched.
+ * Print the message for GetLastError
*/
-void WCMD_run_program (WCHAR *command, int called) {
-
- WCHAR temp[MAX_PATH];
- WCHAR pathtosearch[MAXSTRING];
- WCHAR *pathposn;
- WCHAR stemofsearch[MAX_PATH];
- WCHAR *lastSlash;
- WCHAR pathext[MAXSTRING];
- BOOL extensionsupplied = FALSE;
- BOOL launched = FALSE;
- BOOL status;
- BOOL assumeInternal = FALSE;
- DWORD len;
- static const WCHAR envPath[] = {'P','A','T','H','\0'};
- static const WCHAR envPathExt[] = {'P','A','T','H','E','X','T','\0'};
- static const WCHAR delims[] = {'/','\\',':','\0'};
+void WCMD_print_error (void) {
+ LPVOID lpMsgBuf;
+ DWORD error_code;
+ int status;
- WCMD_parse (command, quals, param1, param2); /* Quick way to get the filename */
- if (!(*param1) && !(*param2))
+ error_code = GetLastError ();
+ status = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
+ if (!status) {
+ WINE_FIXME ("Cannot display message for error %d, status %d\n",
+ error_code, GetLastError());
return;
-
- /* Calculate the search path and stem to search for */
- if (strpbrkW (param1, delims) == NULL) { /* No explicit path given, search path */
- static const WCHAR curDir[] = {'.',';','\0'};
- strcpyW(pathtosearch, curDir);
- len = GetEnvironmentVariable (envPath, &pathtosearch[2], (sizeof(pathtosearch)/sizeof(WCHAR))-2);
- if ((len == 0) || (len >= (sizeof(pathtosearch)/sizeof(WCHAR)) - 2)) {
- static const WCHAR curDir[] = {'.','\0'};
- strcpyW (pathtosearch, curDir);
- }
- if (strchrW(param1, '.') != NULL) extensionsupplied = TRUE;
- strcpyW(stemofsearch, param1);
-
- } else {
-
- /* Convert eg. ..\fred to include a directory by removing file part */
- GetFullPathName(param1, sizeof(pathtosearch)/sizeof(WCHAR), pathtosearch, NULL);
- lastSlash = strrchrW(pathtosearch, '\\');
- if (lastSlash && strchrW(lastSlash, '.') != NULL) extensionsupplied = TRUE;
- strcpyW(stemofsearch, lastSlash+1);
-
- /* Reduce pathtosearch to a path with trailing '\' to support c:\a.bat and
- c:\windows\a.bat syntax */
- if (lastSlash) *(lastSlash + 1) = 0x00;
}
- /* Now extract PATHEXT */
- len = GetEnvironmentVariable (envPathExt, pathext, sizeof(pathext)/sizeof(WCHAR));
- if ((len == 0) || (len >= (sizeof(pathext)/sizeof(WCHAR)))) {
- static const WCHAR dfltPathExt[] = {'.','b','a','t',';',
- '.','c','o','m',';',
- '.','c','m','d',';',
- '.','e','x','e','\0'};
- strcpyW (pathext, dfltPathExt);
- }
-
- /* Loop through the search path, dir by dir */
- pathposn = pathtosearch;
- WINE_TRACE("Searching in '%s' for '%s'\n", wine_dbgstr_w(pathtosearch),
- wine_dbgstr_w(stemofsearch));
- while (!launched && pathposn) {
-
- WCHAR thisDir[MAX_PATH] = {'\0'};
- WCHAR *pos = NULL;
- BOOL found = FALSE;
- const WCHAR slashW[] = {'\\','\0'};
-
- /* Work on the first directory on the search path */
- pos = strchrW(pathposn, ';');
- if (pos) {
- memcpy(thisDir, pathposn, (pos-pathposn) * sizeof(WCHAR));
- thisDir[(pos-pathposn)] = 0x00;
- pathposn = pos+1;
-
- } else {
- strcpyW(thisDir, pathposn);
- pathposn = NULL;
- }
-
- /* Since you can have eg. ..\.. on the path, need to expand
- to full information */
- strcpyW(temp, thisDir);
- GetFullPathName(temp, MAX_PATH, thisDir, NULL);
-
- /* 1. If extension supplied, see if that file exists */
- strcatW(thisDir, slashW);
- strcatW(thisDir, stemofsearch);
- pos = &thisDir[strlenW(thisDir)]; /* Pos = end of name */
-
- /* 1. If extension supplied, see if that file exists */
- if (extensionsupplied) {
- if (GetFileAttributes(thisDir) != INVALID_FILE_ATTRIBUTES) {
- found = TRUE;
- }
- }
-
- /* 2. Any .* matches? */
- if (!found) {
- HANDLE h;
- WIN32_FIND_DATA finddata;
- static const WCHAR allFiles[] = {'.','*','\0'};
-
- strcatW(thisDir,allFiles);
- h = FindFirstFile(thisDir, &finddata);
- FindClose(h);
- if (h != INVALID_HANDLE_VALUE) {
-
- WCHAR *thisExt = pathext;
-
- /* 3. Yes - Try each path ext */
- while (thisExt) {
- WCHAR *nextExt = strchrW(thisExt, ';');
-
- if (nextExt) {
- memcpy(pos, thisExt, (nextExt-thisExt) * sizeof(WCHAR));
- pos[(nextExt-thisExt)] = 0x00;
- thisExt = nextExt+1;
- } else {
- strcpyW(pos, thisExt);
- thisExt = NULL;
- }
-
- if (GetFileAttributes(thisDir) != INVALID_FILE_ATTRIBUTES) {
- found = TRUE;
- thisExt = NULL;
- }
- }
- }
- }
-
- /* Internal programs won't be picked up by this search, so even
- though not found, try one last createprocess and wait for it
- to complete.
- Note: Ideally we could tell between a console app (wait) and a
- windows app, but the API's for it fail in this case */
- if (!found && pathposn == NULL) {
- WINE_TRACE("ASSUMING INTERNAL\n");
- assumeInternal = TRUE;
- } else {
- WINE_TRACE("Found as %s\n", wine_dbgstr_w(thisDir));
- }
-
- /* Once found, launch it */
- if (found || assumeInternal) {
- STARTUPINFO st;
- PROCESS_INFORMATION pe;
- SHFILEINFO psfi;
- DWORD console;
- HINSTANCE hinst;
- WCHAR *ext = strrchrW( thisDir, '.' );
- static const WCHAR batExt[] = {'.','b','a','t','\0'};
- static const WCHAR cmdExt[] = {'.','c','m','d','\0'};
-
- launched = TRUE;
-
- /* Special case BAT and CMD */
- if (ext && !strcmpiW(ext, batExt)) {
- WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE);
- return;
- } else if (ext && !strcmpiW(ext, cmdExt)) {
- WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE);
- return;
- } else {
-
- /* thisDir contains the file to be launched, but with what?
- eg. a.exe will require a.exe to be launched, a.html may be iexplore */
- hinst = FindExecutable (thisDir, NULL, temp);
- if ((INT_PTR)hinst < 32)
- console = 0;
- else
- console = SHGetFileInfo (temp, 0, &psfi, sizeof(psfi), SHGFI_EXETYPE);
-
- ZeroMemory (&st, sizeof(STARTUPINFO));
- st.cb = sizeof(STARTUPINFO);
- init_msvcrt_io_block(&st);
-
- /* Launch the process and if a CUI wait on it to complete
- Note: Launching internal wine processes cannot specify a full path to exe */
- status = CreateProcess (assumeInternal?NULL : thisDir,
- command, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pe);
- if ((opt_c || opt_k) && !opt_s && !status
- && GetLastError()==ERROR_FILE_NOT_FOUND && command[0]=='\"') {
- /* strip first and last quote WCHARacters and try again */
- WCMD_opt_s_strip_quotes(command);
- opt_s=1;
- WCMD_run_program(command, called);
- return;
- }
- if (!status) {
- WCMD_print_error ();
- /* If a command fails to launch, it sets errorlevel 9009 - which
- does not seem to have any associated constant definition */
- errorlevel = 9009;
- return;
- }
- if (!assumeInternal && !console) errorlevel = 0;
- else
- {
- /* Always wait when called in a batch program context */
- if (assumeInternal || context || !HIWORD(console)) WaitForSingleObject (pe.hProcess, INFINITE);
- GetExitCodeProcess (pe.hProcess, &errorlevel);
- if (errorlevel == STILL_ACTIVE) errorlevel = 0;
- }
- CloseHandle(pe.hProcess);
- CloseHandle(pe.hThread);
- return;
- }
- }
- }
-
- /* Not found anywhere - give up */
- SetLastError(ERROR_FILE_NOT_FOUND);
- WCMD_print_error ();
-
- /* If a command fails to launch, it sets errorlevel 9009 - which
- does not seem to have any associated constant definition */
- errorlevel = 9009;
+ WCMD_output_asis_len(lpMsgBuf, lstrlen(lpMsgBuf),
+ GetStdHandle(STD_ERROR_HANDLE));
+ LocalFree (lpMsgBuf);
+ WCMD_output_asis_len (newline, lstrlen(newline),
+ GetStdHandle(STD_ERROR_HANDLE));
return;
-
}
/******************************************************************************
@@ -1326,238 +410,25 @@ void WCMD_show_prompt (void) {
WCMD_output_asis (out_string);
}
-/****************************************************************************
- * WCMD_print_error
- *
- * Print the message for GetLastError
- */
-
-void WCMD_print_error (void) {
- LPVOID lpMsgBuf;
- DWORD error_code;
- int status;
-
- error_code = GetLastError ();
- status = FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
- NULL, error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
- if (!status) {
- WINE_FIXME ("Cannot display message for error %d, status %d\n",
- error_code, GetLastError());
- return;
- }
-
- WCMD_output_asis_len(lpMsgBuf, lstrlen(lpMsgBuf),
- GetStdHandle(STD_ERROR_HANDLE));
- LocalFree (lpMsgBuf);
- WCMD_output_asis_len (newline, lstrlen(newline),
- GetStdHandle(STD_ERROR_HANDLE));
- return;
-}
-
-/*******************************************************************
- * WCMD_parse - parse a command into parameters and qualifiers.
- *
- * On exit, all qualifiers are concatenated into q, the first string
- * not beginning with "/" is in p1 and the
- * second in p2. Any subsequent non-qualifier strings are lost.
- * Parameters in quotes are handled.
- */
-
-void WCMD_parse (WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2) {
-
-int p = 0;
-
- *q = *p1 = *p2 = '\0';
- while (TRUE) {
- switch (*s) {
- case '/':
- *q++ = *s++;
- while ((*s != '\0') && (*s != ' ') && *s != '/') {
- *q++ = toupperW (*s++);
- }
- *q = '\0';
- break;
- case ' ':
- case '\t':
- s++;
- break;
- case '"':
- s++;
- while ((*s != '\0') && (*s != '"')) {
- if (p == 0) *p1++ = *s++;
- else if (p == 1) *p2++ = *s++;
- else s++;
- }
- if (p == 0) *p1 = '\0';
- if (p == 1) *p2 = '\0';
- p++;
- if (*s == '"') s++;
- break;
- case '\0':
- return;
- default:
- while ((*s != '\0') && (*s != ' ') && (*s != '\t')
- && (*s != '=') && (*s != ',') ) {
- if (p == 0) *p1++ = *s++;
- else if (p == 1) *p2++ = *s++;
- else s++;
- }
- /* Skip concurrent parms */
- while ((*s == ' ') || (*s == '\t') || (*s == '=') || (*s == ',') ) s++;
-
- if (p == 0) *p1 = '\0';
- if (p == 1) *p2 = '\0';
- p++;
- }
- }
-}
-
-/*******************************************************************
- * WCMD_output_asis_len - send output to current standard output
- *
- * Output a formatted unicode string. Ideally this will go to the console
- * and hence required WriteConsoleW to output it, however if file i/o is
- * redirected, it needs to be WriteFile'd using OEM (not ANSI) format
- */
-static void WCMD_output_asis_len(const WCHAR *message, int len, HANDLE device) {
-
- DWORD nOut= 0;
- DWORD res = 0;
-
- /* If nothing to write, return (MORE does this sometimes) */
- if (!len) return;
-
- /* Try to write as unicode assuming it is to a console */
- res = WriteConsoleW(device, message, len, &nOut, NULL);
-
- /* If writing to console fails, assume its file
- i/o so convert to OEM codepage and output */
- if (!res) {
- BOOL usedDefaultChar = FALSE;
- DWORD convertedChars;
-
- if (!unicodePipes) {
- /*
- * Allocate buffer to use when writing to file. (Not freed, as one off)
- */
- if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
- MAX_WRITECONSOLE_SIZE);
- if (!output_bufA) {
- WINE_FIXME("Out of memory - could not allocate ansi 64K buffer\n");
- return;
- }
-
- /* Convert to OEM, then output */
- convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, message,
- len, output_bufA, MAX_WRITECONSOLE_SIZE,
- "?", &usedDefaultChar);
- WriteFile(device, output_bufA, convertedChars,
- &nOut, FALSE);
- } else {
- WriteFile(device, message, len*sizeof(WCHAR),
- &nOut, FALSE);
- }
- }
- return;
-}
-
-/*******************************************************************
- * WCMD_output - send output to current standard output device.
- *
- */
-
-void WCMD_output (const WCHAR *format, ...) {
-
- va_list ap;
- WCHAR string[1024];
- int ret;
-
- va_start(ap,format);
- ret = wvsprintf (string, format, ap);
- if( ret >= (sizeof(string)/sizeof(WCHAR))) {
- WINE_ERR("Output truncated in WCMD_output\n" );
- ret = (sizeof(string)/sizeof(WCHAR)) - 1;
- string[ret] = '\0';
- }
- va_end(ap);
- WCMD_output_asis_len(string, ret, GetStdHandle(STD_OUTPUT_HANDLE));
-}
-
-
-static int line_count;
-static int max_height;
-static int max_width;
-static BOOL paged_mode;
-static int numChars;
-
-void WCMD_enter_paged_mode(const WCHAR *msg)
-{
- CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
-
- if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &consoleInfo)) {
- max_height = consoleInfo.dwSize.Y;
- max_width = consoleInfo.dwSize.X;
- } else {
- max_height = 25;
- max_width = 80;
- }
- paged_mode = TRUE;
- line_count = 0;
- numChars = 0;
- pagedMessage = (msg==NULL)? anykey : msg;
-}
-
-void WCMD_leave_paged_mode(void)
-{
- paged_mode = FALSE;
- pagedMessage = NULL;
-}
-/*******************************************************************
- * WCMD_output_asis - send output to current standard output device.
- * without formatting eg. when message contains '%'
+/*************************************************************************
+ * WCMD_strdupW
+ * A wide version of strdup as its missing from unicode.h
*/
-
-void WCMD_output_asis (const WCHAR *message) {
- DWORD count;
- const WCHAR* ptr;
- WCHAR string[1024];
-
- if (paged_mode) {
- do {
- ptr = message;
- while (*ptr && *ptr!='\n' && (numChars < max_width)) {
- numChars++;
- ptr++;
- };
- if (*ptr == '\n') ptr++;
- WCMD_output_asis_len(message, (ptr) ? ptr - message : strlenW(message),
- GetStdHandle(STD_OUTPUT_HANDLE));
- if (ptr) {
- numChars = 0;
- if (++line_count >= max_height - 1) {
- line_count = 0;
- WCMD_output_asis_len(pagedMessage, strlenW(pagedMessage),
- GetStdHandle(STD_OUTPUT_HANDLE));
- WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
- sizeof(string)/sizeof(WCHAR), &count, NULL);
- }
- }
- } while (((message = ptr) != NULL) && (*ptr));
- } else {
- WCMD_output_asis_len(message, lstrlen(message),
- GetStdHandle(STD_OUTPUT_HANDLE));
- }
+WCHAR *WCMD_strdupW(WCHAR *input) {
+ int len=strlenW(input)+1;
+ /* Note: Use malloc not HeapAlloc to emulate strdup */
+ WCHAR *result = malloc(len * sizeof(WCHAR));
+ memcpy(result, input, len * sizeof(WCHAR));
+ return result;
}
-
/***************************************************************************
* WCMD_strtrim_leading_spaces
*
* Remove leading spaces from a string. Return a pointer to the first
* non-space character. Does not modify the input string
*/
-
WCHAR *WCMD_strtrim_leading_spaces (WCHAR *string) {
WCHAR *ptr;
@@ -1573,7 +444,6 @@ WCHAR *WCMD_strtrim_leading_spaces (WCHAR *string) {
* Remove trailing spaces from a string. This routine modifies the input
* string by placing a null after the last non-space WCHARacter
*/
-
void WCMD_strtrim_trailing_spaces (WCHAR *string) {
WCHAR *ptr;
@@ -1590,7 +460,6 @@ void WCMD_strtrim_trailing_spaces (WCHAR *string) {
*
* Remove first and last quote WCHARacters, preserving all other text
*/
-
void WCMD_opt_s_strip_quotes(WCHAR *cmd) {
WCHAR *src = cmd + 1, *dest = cmd, *lastq = NULL;
while((*dest=*src) != '\0') {
@@ -1605,6 +474,7 @@ void WCMD_opt_s_strip_quotes(WCHAR *cmd) {
}
}
+
/*************************************************************************
* WCMD_expand_envvar
*
@@ -1904,72 +774,829 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR *forVar, WCHAR *forVal) {
return start+1;
}
-/*************************************************************************
- * WCMD_LoadMessage
- * Load a string from the resource file, handling any error
- * Returns string retrieved from resource file
+/*****************************************************************************
+ * Expand the command. Native expands lines from batch programs as they are
+ * read in and not again, except for 'for' variable substitution.
+ * eg. As evidence, "echo %1 && shift && echo %1" or "echo %%path%%"
*/
-WCHAR *WCMD_LoadMessage(UINT id) {
- static WCHAR msg[2048];
- static const WCHAR failedMsg[] = {'F','a','i','l','e','d','!','\0'};
+void handleExpansion(WCHAR *cmd, BOOL justFors, WCHAR *forVariable, WCHAR *forValue) {
- if (!LoadString(GetModuleHandle(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
- WINE_FIXME("LoadString failed with %d\n", GetLastError());
- strcpyW(msg, failedMsg);
+ /* For commands in a context (batch program): */
+ /* Expand environment variables in a batch file %{0-9} first */
+ /* including support for any ~ modifiers */
+ /* Additionally: */
+ /* Expand the DATE, TIME, CD, RANDOM and ERRORLEVEL special */
+ /* names allowing environment variable overrides */
+ /* NOTE: To support the %PATH:xxx% syntax, also perform */
+ /* manual expansion of environment variables here */
+
+ WCHAR *p = cmd;
+ WCHAR *s, *t;
+ int i;
+
+ while ((p = strchrW(p, '%'))) {
+
+ WINE_TRACE("Translate command:%s %d (at: %s)\n",
+ wine_dbgstr_w(cmd), justFors, wine_dbgstr_w(p));
+ i = *(p+1) - '0';
+
+ /* Don't touch %% unless its in Batch */
+ if (!justFors && *(p+1) == '%') {
+ if (context) {
+ s = WCMD_strdupW(p+1);
+ strcpyW (p, s);
+ free (s);
+ }
+ p+=1;
+
+ /* Replace %~ modifications if in batch program */
+ } else if (*(p+1) == '~') {
+ WCMD_HandleTildaModifiers(&p, forVariable, forValue, justFors);
+ p++;
+
+ /* Replace use of %0...%9 if in batch program*/
+ } else if (!justFors && context && (i >= 0) && (i <= 9)) {
+ s = WCMD_strdupW(p+2);
+ t = WCMD_parameter (context -> command, i + context -> shift_count[i], NULL);
+ strcpyW (p, t);
+ strcatW (p, s);
+ free (s);
+
+ /* Replace use of %* if in batch program*/
+ } else if (!justFors && context && *(p+1)=='*') {
+ WCHAR *startOfParms = NULL;
+ s = WCMD_strdupW(p+2);
+ t = WCMD_parameter (context -> command, 1, &startOfParms);
+ if (startOfParms != NULL) strcpyW (p, startOfParms);
+ else *p = 0x00;
+ strcatW (p, s);
+ free (s);
+
+ } else if (forVariable &&
+ (CompareString (LOCALE_USER_DEFAULT,
+ SORT_STRINGSORT,
+ p,
+ strlenW(forVariable),
+ forVariable, -1) == 2)) {
+ s = WCMD_strdupW(p + strlenW(forVariable));
+ strcpyW(p, forValue);
+ strcatW(p, s);
+ free(s);
+
+ } else if (!justFors) {
+ p = WCMD_expand_envvar(p, forVariable, forValue);
+
+ /* In a FOR loop, see if this is the variable to replace */
+ } else { /* Ignore %'s on second pass of batch program */
+ p++;
}
- return msg;
+ }
+
+ return;
}
-/*************************************************************************
- * WCMD_strdupW
- * A wide version of strdup as its missing from unicode.h
+
+/*******************************************************************
+ * WCMD_parse - parse a command into parameters and qualifiers.
+ *
+ * On exit, all qualifiers are concatenated into q, the first string
+ * not beginning with "/" is in p1 and the
+ * second in p2. Any subsequent non-qualifier strings are lost.
+ * Parameters in quotes are handled.
*/
-WCHAR *WCMD_strdupW(WCHAR *input) {
- int len=strlenW(input)+1;
- /* Note: Use malloc not HeapAlloc to emulate strdup */
- WCHAR *result = malloc(len * sizeof(WCHAR));
- memcpy(result, input, len * sizeof(WCHAR));
- return result;
+void WCMD_parse (WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2)
+{
+ int p = 0;
+
+ *q = *p1 = *p2 = '\0';
+ while (TRUE) {
+ switch (*s) {
+ case '/':
+ *q++ = *s++;
+ while ((*s != '\0') && (*s != ' ') && *s != '/') {
+ *q++ = toupperW (*s++);
+ }
+ *q = '\0';
+ break;
+ case ' ':
+ case '\t':
+ s++;
+ break;
+ case '"':
+ s++;
+ while ((*s != '\0') && (*s != '"')) {
+ if (p == 0) *p1++ = *s++;
+ else if (p == 1) *p2++ = *s++;
+ else s++;
+ }
+ if (p == 0) *p1 = '\0';
+ if (p == 1) *p2 = '\0';
+ p++;
+ if (*s == '"') s++;
+ break;
+ case '\0':
+ return;
+ default:
+ while ((*s != '\0') && (*s != ' ') && (*s != '\t')
+ && (*s != '=') && (*s != ',') ) {
+ if (p == 0) *p1++ = *s++;
+ else if (p == 1) *p2++ = *s++;
+ else s++;
+ }
+ /* Skip concurrent parms */
+ while ((*s == ' ') || (*s == '\t') || (*s == '=') || (*s == ',') ) s++;
+
+ if (p == 0) *p1 = '\0';
+ if (p == 1) *p2 = '\0';
+ p++;
+ }
+ }
}
-/***************************************************************************
- * WCMD_Readfile
+static void init_msvcrt_io_block(STARTUPINFO* st)
+{
+ STARTUPINFO st_p;
+ /* fetch the parent MSVCRT info block if any, so that the child can use the
+ * same handles as its grand-father
+ */
+ st_p.cb = sizeof(STARTUPINFO);
+ GetStartupInfo(&st_p);
+ st->cbReserved2 = st_p.cbReserved2;
+ st->lpReserved2 = st_p.lpReserved2;
+ if (st_p.cbReserved2 && st_p.lpReserved2)
+ {
+ /* Override the entries for fd 0,1,2 if we happened
+ * to change those std handles (this depends on the way wcmd sets
+ * it's new input & output handles)
+ */
+ size_t sz = max(sizeof(unsigned) + (sizeof(char) + sizeof(HANDLE)) * 3, st_p.cbReserved2);
+ BYTE* ptr = HeapAlloc(GetProcessHeap(), 0, sz);
+ if (ptr)
+ {
+ unsigned num = *(unsigned*)st_p.lpReserved2;
+ char* flags = (char*)(ptr + sizeof(unsigned));
+ HANDLE* handles = (HANDLE*)(flags + num * sizeof(char));
+
+ memcpy(ptr, st_p.lpReserved2, st_p.cbReserved2);
+ st->cbReserved2 = sz;
+ st->lpReserved2 = ptr;
+
+#define WX_OPEN 0x01 /* see dlls/msvcrt/file.c */
+ if (num <= 0 || (flags[0] & WX_OPEN))
+ {
+ handles[0] = GetStdHandle(STD_INPUT_HANDLE);
+ flags[0] |= WX_OPEN;
+ }
+ if (num <= 1 || (flags[1] & WX_OPEN))
+ {
+ handles[1] = GetStdHandle(STD_OUTPUT_HANDLE);
+ flags[1] |= WX_OPEN;
+ }
+ if (num <= 2 || (flags[2] & WX_OPEN))
+ {
+ handles[2] = GetStdHandle(STD_ERROR_HANDLE);
+ flags[2] |= WX_OPEN;
+ }
+#undef WX_OPEN
+ }
+ }
+}
+
+/******************************************************************************
+ * WCMD_run_program
*
- * Read characters in from a console/file, returning result in Unicode
- * with signature identical to ReadFile
+ * Execute a command line as an external program. Must allow recursion.
+ *
+ * Precedence:
+ * Manual testing under windows shows PATHEXT plays a key part in this,
+ * and the search algorithm and precedence appears to be as follows.
+ *
+ * Search locations:
+ * If directory supplied on command, just use that directory
+ * If extension supplied on command, look for that explicit name first
+ * Otherwise, search in each directory on the path
+ * Precedence:
+ * If extension supplied on command, look for that explicit name first
+ * Then look for supplied name .* (even if extension supplied, so
+ * 'garbage.exe' will match 'garbage.exe.cmd')
+ * If any found, cycle through PATHEXT looking for name.exe one by one
+ * Launching
+ * Once a match has been found, it is launched - Code currently uses
+ * findexecutable to achieve this which is left untouched.
*/
-BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
- LPDWORD charsRead, const LPOVERLAPPED unused) {
- BOOL res;
+void WCMD_run_program (WCHAR *command, int called) {
- /* Try to read from console as Unicode */
- res = ReadConsoleW(hIn, intoBuf, maxChars, charsRead, NULL);
+ WCHAR temp[MAX_PATH];
+ WCHAR pathtosearch[MAXSTRING];
+ WCHAR *pathposn;
+ WCHAR stemofsearch[MAX_PATH];
+ WCHAR *lastSlash;
+ WCHAR pathext[MAXSTRING];
+ BOOL extensionsupplied = FALSE;
+ BOOL launched = FALSE;
+ BOOL status;
+ BOOL assumeInternal = FALSE;
+ DWORD len;
+ static const WCHAR envPath[] = {'P','A','T','H','\0'};
+ static const WCHAR envPathExt[] = {'P','A','T','H','E','X','T','\0'};
+ static const WCHAR delims[] = {'/','\\',':','\0'};
- /* If reading from console has failed we assume its file
- i/o so read in and convert from OEM codepage */
- if (!res) {
+ WCMD_parse (command, quals, param1, param2); /* Quick way to get the filename */
+ if (!(*param1) && !(*param2))
+ return;
- DWORD numRead;
- /*
- * Allocate buffer to use when reading from file. Not freed
- */
- if (!output_bufA) output_bufA = HeapAlloc(GetProcessHeap(), 0,
- MAX_WRITECONSOLE_SIZE);
- if (!output_bufA) {
- WINE_FIXME("Out of memory - could not allocate ansi 64K buffer\n");
- return 0;
+ /* Calculate the search path and stem to search for */
+ if (strpbrkW (param1, delims) == NULL) { /* No explicit path given, search path */
+ static const WCHAR curDir[] = {'.',';','\0'};
+ strcpyW(pathtosearch, curDir);
+ len = GetEnvironmentVariable (envPath, &pathtosearch[2], (sizeof(pathtosearch)/sizeof(WCHAR))-2);
+ if ((len == 0) || (len >= (sizeof(pathtosearch)/sizeof(WCHAR)) - 2)) {
+ static const WCHAR curDir[] = {'.','\0'};
+ strcpyW (pathtosearch, curDir);
+ }
+ if (strchrW(param1, '.') != NULL) extensionsupplied = TRUE;
+ strcpyW(stemofsearch, param1);
+
+ } else {
+
+ /* Convert eg. ..\fred to include a directory by removing file part */
+ GetFullPathName(param1, sizeof(pathtosearch)/sizeof(WCHAR), pathtosearch, NULL);
+ lastSlash = strrchrW(pathtosearch, '\\');
+ if (lastSlash && strchrW(lastSlash, '.') != NULL) extensionsupplied = TRUE;
+ strcpyW(stemofsearch, lastSlash+1);
+
+ /* Reduce pathtosearch to a path with trailing '\' to support c:\a.bat and
+ c:\windows\a.bat syntax */
+ if (lastSlash) *(lastSlash + 1) = 0x00;
+ }
+
+ /* Now extract PATHEXT */
+ len = GetEnvironmentVariable (envPathExt, pathext, sizeof(pathext)/sizeof(WCHAR));
+ if ((len == 0) || (len >= (sizeof(pathext)/sizeof(WCHAR)))) {
+ static const WCHAR dfltPathExt[] = {'.','b','a','t',';',
+ '.','c','o','m',';',
+ '.','c','m','d',';',
+ '.','e','x','e','\0'};
+ strcpyW (pathext, dfltPathExt);
+ }
+
+ /* Loop through the search path, dir by dir */
+ pathposn = pathtosearch;
+ WINE_TRACE("Searching in '%s' for '%s'\n", wine_dbgstr_w(pathtosearch),
+ wine_dbgstr_w(stemofsearch));
+ while (!launched && pathposn) {
+
+ WCHAR thisDir[MAX_PATH] = {'\0'};
+ WCHAR *pos = NULL;
+ BOOL found = FALSE;
+ const WCHAR slashW[] = {'\\','\0'};
+
+ /* Work on the first directory on the search path */
+ pos = strchrW(pathposn, ';');
+ if (pos) {
+ memcpy(thisDir, pathposn, (pos-pathposn) * sizeof(WCHAR));
+ thisDir[(pos-pathposn)] = 0x00;
+ pathposn = pos+1;
+
+ } else {
+ strcpyW(thisDir, pathposn);
+ pathposn = NULL;
+ }
+
+ /* Since you can have eg. ..\.. on the path, need to expand
+ to full information */
+ strcpyW(temp, thisDir);
+ GetFullPathName(temp, MAX_PATH, thisDir, NULL);
+
+ /* 1. If extension supplied, see if that file exists */
+ strcatW(thisDir, slashW);
+ strcatW(thisDir, stemofsearch);
+ pos = &thisDir[strlenW(thisDir)]; /* Pos = end of name */
+
+ /* 1. If extension supplied, see if that file exists */
+ if (extensionsupplied) {
+ if (GetFileAttributes(thisDir) != INVALID_FILE_ATTRIBUTES) {
+ found = TRUE;
+ }
+ }
+
+ /* 2. Any .* matches? */
+ if (!found) {
+ HANDLE h;
+ WIN32_FIND_DATA finddata;
+ static const WCHAR allFiles[] = {'.','*','\0'};
+
+ strcatW(thisDir,allFiles);
+ h = FindFirstFile(thisDir, &finddata);
+ FindClose(h);
+ if (h != INVALID_HANDLE_VALUE) {
+
+ WCHAR *thisExt = pathext;
+
+ /* 3. Yes - Try each path ext */
+ while (thisExt) {
+ WCHAR *nextExt = strchrW(thisExt, ';');
+
+ if (nextExt) {
+ memcpy(pos, thisExt, (nextExt-thisExt) * sizeof(WCHAR));
+ pos[(nextExt-thisExt)] = 0x00;
+ thisExt = nextExt+1;
+ } else {
+ strcpyW(pos, thisExt);
+ thisExt = NULL;
+ }
+
+ if (GetFileAttributes(thisDir) != INVALID_FILE_ATTRIBUTES) {
+ found = TRUE;
+ thisExt = NULL;
+ }
}
+ }
+ }
- /* Read from file (assume OEM codepage) */
- res = ReadFile(hIn, output_bufA, maxChars, &numRead, unused);
+ /* Internal programs won't be picked up by this search, so even
+ though not found, try one last createprocess and wait for it
+ to complete.
+ Note: Ideally we could tell between a console app (wait) and a
+ windows app, but the API's for it fail in this case */
+ if (!found && pathposn == NULL) {
+ WINE_TRACE("ASSUMING INTERNAL\n");
+ assumeInternal = TRUE;
+ } else {
+ WINE_TRACE("Found as %s\n", wine_dbgstr_w(thisDir));
+ }
- /* Convert from OEM */
- *charsRead = MultiByteToWideChar(GetConsoleCP(), 0, output_bufA, numRead,
- intoBuf, maxChars);
+ /* Once found, launch it */
+ if (found || assumeInternal) {
+ STARTUPINFO st;
+ PROCESS_INFORMATION pe;
+ SHFILEINFO psfi;
+ DWORD console;
+ HINSTANCE hinst;
+ WCHAR *ext = strrchrW( thisDir, '.' );
+ static const WCHAR batExt[] = {'.','b','a','t','\0'};
+ static const WCHAR cmdExt[] = {'.','c','m','d','\0'};
+
+ launched = TRUE;
+
+ /* Special case BAT and CMD */
+ if (ext && !strcmpiW(ext, batExt)) {
+ WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE);
+ return;
+ } else if (ext && !strcmpiW(ext, cmdExt)) {
+ WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE);
+ return;
+ } else {
+
+ /* thisDir contains the file to be launched, but with what?
+ eg. a.exe will require a.exe to be launched, a.html may be iexplore */
+ hinst = FindExecutable (thisDir, NULL, temp);
+ if ((INT_PTR)hinst < 32)
+ console = 0;
+ else
+ console = SHGetFileInfo (temp, 0, &psfi, sizeof(psfi), SHGFI_EXETYPE);
+
+ ZeroMemory (&st, sizeof(STARTUPINFO));
+ st.cb = sizeof(STARTUPINFO);
+ init_msvcrt_io_block(&st);
+ /* Launch the process and if a CUI wait on it to complete
+ Note: Launching internal wine processes cannot specify a full path to exe */
+ status = CreateProcess (assumeInternal?NULL : thisDir,
+ command, NULL, NULL, TRUE, 0, NULL, NULL, &st, &pe);
+ if ((opt_c || opt_k) && !opt_s && !status
+ && GetLastError()==ERROR_FILE_NOT_FOUND && command[0]=='\"') {
+ /* strip first and last quote WCHARacters and try again */
+ WCMD_opt_s_strip_quotes(command);
+ opt_s=1;
+ WCMD_run_program(command, called);
+ return;
+ }
+ if (!status) {
+ WCMD_print_error ();
+ /* If a command fails to launch, it sets errorlevel 9009 - which
+ does not seem to have any associated constant definition */
+ errorlevel = 9009;
+ return;
+ }
+ if (!assumeInternal && !console) errorlevel = 0;
+ else
+ {
+ /* Always wait when called in a batch program context */
+ if (assumeInternal || context || !HIWORD(console)) WaitForSingleObject (pe.hProcess, INFINITE);
+ GetExitCodeProcess (pe.hProcess, &errorlevel);
+ if (errorlevel == STILL_ACTIVE) errorlevel = 0;
+ }
+ CloseHandle(pe.hProcess);
+ CloseHandle(pe.hThread);
+ return;
+ }
}
- return res;
+ }
+
+ /* Not found anywhere - give up */
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ WCMD_print_error ();
+
+ /* If a command fails to launch, it sets errorlevel 9009 - which
+ does not seem to have any associated constant definition */
+ errorlevel = 9009;
+ return;
+
+}
+
+/*****************************************************************************
+ * Process one command. If the command is EXIT this routine does not return.
+ * We will recurse through here executing batch files.
+ */
+void WCMD_execute (WCHAR *command, WCHAR *redirects,
+ WCHAR *forVariable, WCHAR *forValue,
+ CMD_LIST **cmdList)
+{
+ WCHAR *cmd, *p, *redir;
+ int status, i;
+ DWORD count, creationDisposition;
+ HANDLE h;
+ WCHAR *whichcmd;
+ SECURITY_ATTRIBUTES sa;
+ WCHAR *new_cmd = NULL;
+ WCHAR *new_redir = NULL;
+ HANDLE old_stdhandles[3] = {INVALID_HANDLE_VALUE,
+ INVALID_HANDLE_VALUE,
+ INVALID_HANDLE_VALUE};
+ DWORD idx_stdhandles[3] = {STD_INPUT_HANDLE,
+ STD_OUTPUT_HANDLE,
+ STD_ERROR_HANDLE};
+ BOOL piped = FALSE;
+
+ WINE_TRACE("command on entry:%s (%p), with '%s'='%s'\n",
+ wine_dbgstr_w(command), cmdList,
+ wine_dbgstr_w(forVariable), wine_dbgstr_w(forValue));
+
+ /* If the next command is a pipe then we implement pipes by redirecting
+ the output from this command to a temp file and input into the
+ next command from that temp file.
+ FIXME: Use of named pipes would make more sense here as currently this
+ process has to finish before the next one can start but this requires
+ a change to not wait for the first app to finish but rather the pipe */
+ if (cmdList && (*cmdList)->nextcommand &&
+ (*cmdList)->nextcommand->prevDelim == CMD_PIPE) {
+
+ WCHAR temp_path[MAX_PATH];
+ static const WCHAR cmdW[] = {'C','M','D','\0'};
+
+ /* Remember piping is in action */
+ WINE_TRACE("Output needs to be piped\n");
+ piped = TRUE;
+
+ /* Generate a unique temporary filename */
+ GetTempPath (sizeof(temp_path)/sizeof(WCHAR), temp_path);
+ GetTempFileName (temp_path, cmdW, 0, (*cmdList)->nextcommand->pipeFile);
+ WINE_TRACE("Using temporary file of %s\n",
+ wine_dbgstr_w((*cmdList)->nextcommand->pipeFile));
+ }
+
+ /* Move copy of the command onto the heap so it can be expanded */
+ new_cmd = HeapAlloc( GetProcessHeap(), 0, MAXSTRING * sizeof(WCHAR));
+ if (!new_cmd)
+ {
+ WINE_ERR("Could not allocate memory for new_cmd\n");
+ return;
+ }
+ strcpyW(new_cmd, command);
+
+ /* Move copy of the redirects onto the heap so it can be expanded */
+ new_redir = HeapAlloc( GetProcessHeap(), 0, MAXSTRING * sizeof(WCHAR));
+ if (!new_redir)
+ {
+ WINE_ERR("Could not allocate memory for new_redir\n");
+ HeapFree( GetProcessHeap(), 0, new_cmd );
+ return;
+ }
+
+ /* If piped output, send stdout to the pipe by appending >filename to redirects */
+ if (piped) {
+ static const WCHAR redirOut[] = {'%','s',' ','>',' ','%','s','\0'};
+ wsprintf (new_redir, redirOut, redirects, (*cmdList)->nextcommand->pipeFile);
+ WINE_TRACE("Redirects now %s\n", wine_dbgstr_w(new_redir));
+ } else {
+ strcpyW(new_redir, redirects);
+ }
+
+ /* Expand variables in command line mode only (batch mode will
+ be expanded as the line is read in, except for 'for' loops) */
+ handleExpansion(new_cmd, (context != NULL), forVariable, forValue);
+ handleExpansion(new_redir, (context != NULL), forVariable, forValue);
+ cmd = new_cmd;
+
+ /* Show prompt before batch line IF echo is on and in batch program */
+ if (context && echo_mode && (cmd[0] != '@')) {
+ WCMD_show_prompt();
+ WCMD_output_asis ( cmd);
+ WCMD_output_asis ( newline);
+ }
+
+/*
+ * Changing default drive has to be handled as a special case.
+ */
+
+ if ((cmd[1] == ':') && IsCharAlpha (cmd[0]) && (strlenW(cmd) == 2)) {
+ WCHAR envvar[5];
+ WCHAR dir[MAX_PATH];
+
+ /* According to MSDN CreateProcess docs, special env vars record
+ the current directory on each drive, in the form =C:
+ so see if one specified, and if so go back to it */
+ strcpyW(envvar, equalsW);
+ strcatW(envvar, cmd);
+ if (GetEnvironmentVariable(envvar, dir, MAX_PATH) == 0) {
+ static const WCHAR fmt[] = {'%','s','\\','\0'};
+ wsprintf(cmd, fmt, cmd);
+ WINE_TRACE("No special directory settings, using dir of %s\n", wine_dbgstr_w(cmd));
+ }
+ WINE_TRACE("Got directory %s as %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(cmd));
+ status = SetCurrentDirectory (cmd);
+ if (!status) WCMD_print_error ();
+ HeapFree( GetProcessHeap(), 0, cmd );
+ HeapFree( GetProcessHeap(), 0, new_redir );
+ return;
+ }
+
+ sa.nLength = sizeof(sa);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+/*
+ * Redirect stdin, stdout and/or stderr if required.
+ */
+
+ /* STDIN could come from a preceding pipe, so delete on close if it does */
+ if (cmdList && (*cmdList)->pipeFile[0] != 0x00) {
+ WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->pipeFile));
+ h = CreateFile ((*cmdList)->pipeFile, GENERIC_READ,
+ FILE_SHARE_READ, &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ WCMD_print_error ();
+ HeapFree( GetProcessHeap(), 0, cmd );
+ HeapFree( GetProcessHeap(), 0, new_redir );
+ return;
+ }
+ old_stdhandles[0] = GetStdHandle (STD_INPUT_HANDLE);
+ SetStdHandle (STD_INPUT_HANDLE, h);
+
+ /* No need to remember the temporary name any longer once opened */
+ (*cmdList)->pipeFile[0] = 0x00;
+
+ /* Otherwise STDIN could come from a '<' redirect */
+ } else if ((p = strchrW(new_redir,'<')) != NULL) {
+ h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ WCMD_print_error ();
+ HeapFree( GetProcessHeap(), 0, cmd );
+ HeapFree( GetProcessHeap(), 0, new_redir );
+ return;
+ }
+ old_stdhandles[0] = GetStdHandle (STD_INPUT_HANDLE);
+ SetStdHandle (STD_INPUT_HANDLE, h);
+ }
+
+ /* Scan the whole command looking for > and 2> */
+ redir = new_redir;
+ while (redir != NULL && ((p = strchrW(redir,'>')) != NULL)) {
+ int handle = 0;
+
+ if (*(p-1)!='2') {
+ handle = 1;
+ } else {
+ handle = 2;
+ }
+
+ p++;
+ if ('>' == *p) {
+ creationDisposition = OPEN_ALWAYS;
+ p++;
+ }
+ else {
+ creationDisposition = CREATE_ALWAYS;
+ }
+
+ /* Add support for 2>&1 */
+ redir = p;
+ if (*p == '&') {
+ int idx = *(p+1) - '0';
+
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(idx_stdhandles[idx]),
+ GetCurrentProcess(),
+ &h,
+ 0, TRUE, DUPLICATE_SAME_ACCESS) == 0) {
+ WINE_FIXME("Duplicating handle failed with gle %d\n", GetLastError());
+ }
+ WINE_TRACE("Redirect %d (%p) to %d (%p)\n", handle, GetStdHandle(idx_stdhandles[idx]), idx, h);
+
+ } else {
+ WCHAR *param = WCMD_parameter (p, 0, NULL);
+ h = CreateFile (param, GENERIC_WRITE, 0, &sa, creationDisposition,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h == INVALID_HANDLE_VALUE) {
+ WCMD_print_error ();
+ HeapFree( GetProcessHeap(), 0, cmd );
+ HeapFree( GetProcessHeap(), 0, new_redir );
+ return;
+ }
+ if (SetFilePointer (h, 0, NULL, FILE_END) ==
+ INVALID_SET_FILE_POINTER) {
+ WCMD_print_error ();
+ }
+ WINE_TRACE("Redirect %d to '%s' (%p)\n", handle, wine_dbgstr_w(param), h);
+ }
+
+ old_stdhandles[handle] = GetStdHandle (idx_stdhandles[handle]);
+ SetStdHandle (idx_stdhandles[handle], h);
+ }
+
+/*
+ * Strip leading whitespaces, and a '@' if supplied
+ */
+ whichcmd = WCMD_strtrim_leading_spaces(cmd);
+ WINE_TRACE("Command: '%s'\n", wine_dbgstr_w(cmd));
+ if (whichcmd[0] == '@') whichcmd++;
+
+/*
+ * Check if the command entered is internal. If it is, pass the rest of the
+ * line down to the command. If not try to run a program.
+ */
+
+ count = 0;
+ while (IsCharAlphaNumeric(whichcmd[count])) {
+ count++;
+ }
+ for (i=0; i<=WCMD_EXIT; i++) {
+ if (CompareString (LOCALE_USER_DEFAULT, NORM_IGNORECASE | SORT_STRINGSORT,
+ whichcmd, count, inbuilt[i], -1) == 2) break;
+ }
+ p = WCMD_strtrim_leading_spaces (&whichcmd[count]);
+ WCMD_parse (p, quals, param1, param2);
+ WINE_TRACE("param1: %s, param2: %s\n", wine_dbgstr_w(param1), wine_dbgstr_w(param2));
+
+ switch (i) {
+
+ case WCMD_ATTRIB:
+ WCMD_setshow_attrib ();
+ break;
+ case WCMD_CALL:
+ WCMD_call (p);
+ break;
+ case WCMD_CD:
+ case WCMD_CHDIR:
+ WCMD_setshow_default (p);
+ break;
+ case WCMD_CLS:
+ WCMD_clear_screen ();
+ break;
+ case WCMD_COPY:
+ WCMD_copy ();
+ break;
+ case WCMD_CTTY:
+ WCMD_change_tty ();
+ break;
+ case WCMD_DATE:
+ WCMD_setshow_date ();
+ break;
+ case WCMD_DEL:
+ case WCMD_ERASE:
+ WCMD_delete (p, TRUE);
+ break;
+ case WCMD_DIR:
+ WCMD_directory (p);
+ break;
+ case WCMD_ECHO:
+ WCMD_echo(&whichcmd[count]);
+ break;
+ case WCMD_FOR:
+ WCMD_for (p, cmdList);
+ break;
+ case WCMD_GOTO:
+ WCMD_goto (cmdList);
+ break;
+ case WCMD_HELP:
+ WCMD_give_help (p);
+ break;
+ case WCMD_IF:
+ WCMD_if (p, cmdList);
+ break;
+ case WCMD_LABEL:
+ WCMD_volume (1, p);
+ break;
+ case WCMD_MD:
+ case WCMD_MKDIR:
+ WCMD_create_dir ();
+ break;
+ case WCMD_MOVE:
+ WCMD_move ();
+ break;
+ case WCMD_PATH:
+ WCMD_setshow_path (p);
+ break;
+ case WCMD_PAUSE:
+ WCMD_pause ();
+ break;
+ case WCMD_PROMPT:
+ WCMD_setshow_prompt ();
+ break;
+ case WCMD_REM:
+ break;
+ case WCMD_REN:
+ case WCMD_RENAME:
+ WCMD_rename ();
+ break;
+ case WCMD_RD:
+ case WCMD_RMDIR:
+ WCMD_remove_dir (p);
+ break;
+ case WCMD_SETLOCAL:
+ WCMD_setlocal(p);
+ break;
+ case WCMD_ENDLOCAL:
+ WCMD_endlocal();
+ break;
+ case WCMD_SET:
+ WCMD_setshow_env (p);
+ break;
+ case WCMD_SHIFT:
+ WCMD_shift (p);
+ break;
+ case WCMD_TIME:
+ WCMD_setshow_time ();
+ break;
+ case WCMD_TITLE:
+ if (strlenW(&whichcmd[count]) > 0)
+ WCMD_title(&whichcmd[count+1]);
+ break;
+ case WCMD_TYPE:
+ WCMD_type (p);
+ break;
+ case WCMD_VER:
+ WCMD_version ();
+ break;
+ case WCMD_VERIFY:
+ WCMD_verify (p);
+ break;
+ case WCMD_VOL:
+ WCMD_volume (0, p);
+ break;
+ case WCMD_PUSHD:
+ WCMD_pushd(p);
+ break;
+ case WCMD_POPD:
+ WCMD_popd();
+ break;
+ case WCMD_ASSOC:
+ WCMD_assoc(p, TRUE);
+ break;
+ case WCMD_COLOR:
+ WCMD_color();
+ break;
+ case WCMD_FTYPE:
+ WCMD_assoc(p, FALSE);
+ break;
+ case WCMD_MORE:
+ WCMD_more(p);
+ break;
+ case WCMD_EXIT:
+ WCMD_exit (cmdList);
+ break;
+ default:
+ WCMD_run_program (whichcmd, 0);
+ }
+ HeapFree( GetProcessHeap(), 0, cmd );
+ HeapFree( GetProcessHeap(), 0, new_redir );
+
+ /* Restore old handles */
+ for (i=0; i<3; i++) {
+ if (old_stdhandles[i] != INVALID_HANDLE_VALUE) {
+ CloseHandle (GetStdHandle (idx_stdhandles[i]));
+ SetStdHandle (idx_stdhandles[i], old_stdhandles[i]);
+ }
+ }
+}
+/*************************************************************************
+ * WCMD_LoadMessage
+ * Load a string from the resource file, handling any error
+ * Returns string retrieved from resource file
+ */
+WCHAR *WCMD_LoadMessage(UINT id) {
+ static WCHAR msg[2048];
+ static const WCHAR failedMsg[] = {'F','a','i','l','e','d','!','\0'};
+
+ if (!LoadString(GetModuleHandle(NULL), id, msg, sizeof(msg)/sizeof(WCHAR))) {
+ WINE_FIXME("LoadString failed with %d\n", GetLastError());
+ strcpyW(msg, failedMsg);
+ }
+ return msg;
}
/***************************************************************************
@@ -2510,3 +2137,368 @@ void WCMD_free_commands(CMD_LIST *cmds) {
HeapFree(GetProcessHeap(), 0, thisCmd);
}
}
+
+
+/*****************************************************************************
+ * Main entry point. This is a console application so we have a main() not a
+ * winmain().
+ */
+
+int wmain (int argc, WCHAR *argvW[])
+{
+ int args;
+ WCHAR *cmd = NULL;
+ WCHAR string[1024];
+ WCHAR envvar[4];
+ HANDLE h;
+ int opt_q;
+ int opt_t = 0;
+ static const WCHAR autoexec[] = {'\\','a','u','t','o','e','x','e','c','.',
+ 'b','a','t','\0'};
+ char ansiVersion[100];
+ CMD_LIST *toExecute = NULL; /* Commands left to be executed */
+
+ srand(time(NULL));
+
+ /* Pre initialize some messages */
+ strcpy(ansiVersion, PACKAGE_VERSION);
+ MultiByteToWideChar(CP_ACP, 0, ansiVersion, -1, string, 1024);
+ wsprintf(version_string, WCMD_LoadMessage(WCMD_VERSION), string);
+ strcpyW(anykey, WCMD_LoadMessage(WCMD_ANYKEY));
+
+ args = argc;
+ opt_c=opt_k=opt_q=opt_s=0;
+ while (args > 0)
+ {
+ WCHAR c;
+ WINE_TRACE("Command line parm: '%s'\n", wine_dbgstr_w(*argvW));
+ if ((*argvW)[0]!='/' || (*argvW)[1]=='\0') {
+ argvW++;
+ args--;
+ continue;
+ }
+
+ c=(*argvW)[1];
+ if (tolowerW(c)=='c') {
+ opt_c=1;
+ } else if (tolowerW(c)=='q') {
+ opt_q=1;
+ } else if (tolowerW(c)=='k') {
+ opt_k=1;
+ } else if (tolowerW(c)=='s') {
+ opt_s=1;
+ } else if (tolowerW(c)=='a') {
+ unicodePipes=FALSE;
+ } else if (tolowerW(c)=='u') {
+ unicodePipes=TRUE;
+ } else if (tolowerW(c)=='t' && (*argvW)[2]==':') {
+ opt_t=strtoulW(&(*argvW)[3], NULL, 16);
+ } else if (tolowerW(c)=='x' || tolowerW(c)=='y') {
+ /* Ignored for compatibility with Windows */
+ }
+
+ if ((*argvW)[2]==0) {
+ argvW++;
+ args--;
+ }
+ else /* handle `cmd /cnotepad.exe` and `cmd /x/c ...` */
+ {
+ *argvW+=2;
+ }
+
+ if (opt_c || opt_k) /* break out of parsing immediately after c or k */
+ break;
+ }
+
+ if (opt_q) {
+ const WCHAR eoff[] = {'O','F','F','\0'};
+ WCMD_echo(eoff);
+ }
+
+ if (opt_c || opt_k) {
+ int len,qcount;
+ WCHAR** arg;
+ int argsLeft;
+ WCHAR* p;
+
+ /* opt_s left unflagged if the command starts with and contains exactly
+ * one quoted string (exactly two quote characters). The quoted string
+ * must be an executable name that has whitespace and must not have the
+ * following characters: &<>()@^| */
+
+ /* Build the command to execute */
+ len = 0;
+ qcount = 0;
+ argsLeft = args;
+ for (arg = argvW; argsLeft>0; arg++,argsLeft--)
+ {
+ int has_space,bcount;
+ WCHAR* a;
+
+ has_space=0;
+ bcount=0;
+ a=*arg;
+ if( !*a ) has_space=1;
+ while (*a!='\0') {
+ if (*a=='\\') {
+ bcount++;
+ } else {
+ if (*a==' ' || *a=='\t') {
+ has_space=1;
+ } else if (*a=='"') {
+ /* doubling of '\' preceding a '"',
+ * plus escaping of said '"'
+ */
+ len+=2*bcount+1;
+ qcount++;
+ }
+ bcount=0;
+ }
+ a++;
+ }
+ len+=(a-*arg) + 1; /* for the separating space */
+ if (has_space)
+ {
+ len+=2; /* for the quotes */
+ qcount+=2;
+ }
+ }
+
+ if (qcount!=2)
+ opt_s=1;
+
+ /* check argvW[0] for a space and invalid characters */
+ if (!opt_s) {
+ opt_s=1;
+ p=*argvW;
+ while (*p!='\0') {
+ if (*p=='&' || *p=='<' || *p=='>' || *p=='(' || *p==')'
+ || *p=='@' || *p=='^' || *p=='|') {
+ opt_s=1;
+ break;
+ }
+ if (*p==' ')
+ opt_s=0;
+ p++;
+ }
+ }
+
+ cmd = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+ if (!cmd)
+ exit(1);
+
+ p = cmd;
+ argsLeft = args;
+ for (arg = argvW; argsLeft>0; arg++,argsLeft--)
+ {
+ int has_space,has_quote;
+ WCHAR* a;
+
+ /* Check for quotes and spaces in this argument */
+ has_space=has_quote=0;
+ a=*arg;
+ if( !*a ) has_space=1;
+ while (*a!='\0') {
+ if (*a==' ' || *a=='\t') {
+ has_space=1;
+ if (has_quote)
+ break;
+ } else if (*a=='"') {
+ has_quote=1;
+ if (has_space)
+ break;
+ }
+ a++;
+ }
+
+ /* Now transfer it to the command line */
+ if (has_space)
+ *p++='"';
+ if (has_quote) {
+ int bcount;
+ WCHAR* a;
+
+ bcount=0;
+ a=*arg;
+ while (*a!='\0') {
+ if (*a=='\\') {
+ *p++=*a;
+ bcount++;
+ } else {
+ if (*a=='"') {
+ int i;
+
+ /* Double all the '\\' preceding this '"', plus one */
+ for (i=0;i<=bcount;i++)
+ *p++='\\';
+ *p++='"';
+ } else {
+ *p++=*a;
+ }
+ bcount=0;
+ }
+ a++;
+ }
+ } else {
+ strcpyW(p,*arg);
+ p+=strlenW(*arg);
+ }
+ if (has_space)
+ *p++='"';
+ *p++=' ';
+ }
+ if (p > cmd)
+ p--; /* remove last space */
+ *p = '\0';
+
+ WINE_TRACE("/c command line: '%s'\n", wine_dbgstr_w(cmd));
+
+ /* strip first and last quote characters if opt_s; check for invalid
+ * executable is done later */
+ if (opt_s && *cmd=='\"')
+ WCMD_opt_s_strip_quotes(cmd);
+ }
+
+ if (opt_c) {
+ /* If we do a "wcmd /c command", we don't want to allocate a new
+ * console since the command returns immediately. Rather, we use
+ * the currently allocated input and output handles. This allows
+ * us to pipe to and read from the command interpreter.
+ */
+
+ /* Parse the command string, without reading any more input */
+ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
+ WCMD_process_commands(toExecute, FALSE, NULL, NULL);
+ WCMD_free_commands(toExecute);
+ toExecute = NULL;
+
+ HeapFree(GetProcessHeap(), 0, cmd);
+ return errorlevel;
+ }
+
+ SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), ENABLE_LINE_INPUT |
+ ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
+ SetConsoleTitle(WCMD_LoadMessage(WCMD_CONSTITLE));
+
+ /* Note: cmd.exe /c dir does not get a new color, /k dir does */
+ if (opt_t) {
+ if (!(((opt_t & 0xF0) >> 4) == (opt_t & 0x0F))) {
+ defaultColor = opt_t & 0xFF;
+ param1[0] = 0x00;
+ WCMD_color();
+ }
+ } else {
+ /* Check HKCU\Software\Microsoft\Command Processor
+ Then HKLM\Software\Microsoft\Command Processor
+ for defaultcolour value
+ Note Can be supplied as DWORD or REG_SZ
+ Note2 When supplied as REG_SZ it's in decimal!!! */
+ HKEY key;
+ DWORD type;
+ DWORD value=0, size=4;
+ static const WCHAR regKeyW[] = {'S','o','f','t','w','a','r','e','\\',
+ 'M','i','c','r','o','s','o','f','t','\\',
+ 'C','o','m','m','a','n','d',' ','P','r','o','c','e','s','s','o','r','\0'};
+ static const WCHAR dfltColorW[] = {'D','e','f','a','u','l','t','C','o','l','o','r','\0'};
+
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, regKeyW,
+ 0, KEY_READ, &key) == ERROR_SUCCESS) {
+ WCHAR strvalue[4];
+
+ /* See if DWORD or REG_SZ */
+ if (RegQueryValueEx(key, dfltColorW, NULL, &type,
+ NULL, NULL) == ERROR_SUCCESS) {
+ if (type == REG_DWORD) {
+ size = sizeof(DWORD);
+ RegQueryValueEx(key, dfltColorW, NULL, NULL,
+ (LPBYTE)&value, &size);
+ } else if (type == REG_SZ) {
+ size = sizeof(strvalue)/sizeof(WCHAR);
+ RegQueryValueEx(key, dfltColorW, NULL, NULL,
+ (LPBYTE)strvalue, &size);
+ value = strtoulW(strvalue, NULL, 10);
+ }
+ }
+ RegCloseKey(key);
+ }
+
+ if (value == 0 && RegOpenKeyEx(HKEY_LOCAL_MACHINE, regKeyW,
+ 0, KEY_READ, &key) == ERROR_SUCCESS) {
+ WCHAR strvalue[4];
+
+ /* See if DWORD or REG_SZ */
+ if (RegQueryValueEx(key, dfltColorW, NULL, &type,
+ NULL, NULL) == ERROR_SUCCESS) {
+ if (type == REG_DWORD) {
+ size = sizeof(DWORD);
+ RegQueryValueEx(key, dfltColorW, NULL, NULL,
+ (LPBYTE)&value, &size);
+ } else if (type == REG_SZ) {
+ size = sizeof(strvalue)/sizeof(WCHAR);
+ RegQueryValueEx(key, dfltColorW, NULL, NULL,
+ (LPBYTE)strvalue, &size);
+ value = strtoulW(strvalue, NULL, 10);
+ }
+ }
+ RegCloseKey(key);
+ }
+
+ /* If one found, set the screen to that colour */
+ if (!(((value & 0xF0) >> 4) == (value & 0x0F))) {
+ defaultColor = value & 0xFF;
+ param1[0] = 0x00;
+ WCMD_color();
+ }
+
+ }
+
+ /* Save cwd into appropriate env var */
+ GetCurrentDirectory(1024, string);
+ if (IsCharAlpha(string[0]) && string[1] == ':') {
+ static const WCHAR fmt[] = {'=','%','c',':','\0'};
+ wsprintf(envvar, fmt, string[0]);
+ SetEnvironmentVariable(envvar, string);
+ WINE_TRACE("Set %s to %s\n", wine_dbgstr_w(envvar), wine_dbgstr_w(string));
+ }
+
+ if (opt_k) {
+ /* Parse the command string, without reading any more input */
+ WCMD_ReadAndParseLine(cmd, &toExecute, INVALID_HANDLE_VALUE);
+ WCMD_process_commands(toExecute, FALSE, NULL, NULL);
+ WCMD_free_commands(toExecute);
+ toExecute = NULL;
+ HeapFree(GetProcessHeap(), 0, cmd);
+ }
+
+/*
+ * If there is an AUTOEXEC.BAT file, try to execute it.
+ */
+
+ GetFullPathName (autoexec, sizeof(string)/sizeof(WCHAR), string, NULL);
+ h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (h != INVALID_HANDLE_VALUE) {
+ CloseHandle (h);
+#if 0
+ WCMD_batch (autoexec, autoexec, 0, NULL, INVALID_HANDLE_VALUE);
+#endif
+ }
+
+/*
+ * Loop forever getting commands and executing them.
+ */
+
+ WCMD_version ();
+ while (TRUE) {
+
+ /* Read until EOF (which for std input is never, but if redirect
+ in place, may occur */
+ WCMD_show_prompt ();
+ if (WCMD_ReadAndParseLine(NULL, &toExecute,
+ GetStdHandle(STD_INPUT_HANDLE)) == NULL)
+ break;
+ WCMD_process_commands(toExecute, FALSE, NULL, NULL);
+ WCMD_free_commands(toExecute);
+ toExecute = NULL;
+ }
+ return 0;
+}
--
1.5.6.5
More information about the wine-patches
mailing list