/*++ Copyright (c) 1992-2000 Microsoft Corporation Module Name: doskey.cxx Abstract: Edits command lines, recalls Windows 2000 commands, and creates macros. Author: Norbert P. Kusters (norbertk) 02-April-1992 Revision History: --*/ #include "ulib.hxx" #include "error.hxx" #include "arg.hxx" #include "smsg.hxx" #include "rtmsg.h" #include "wstring.hxx" #include "path.hxx" #include "filestrm.hxx" #include "system.hxx" #include "file.hxx" #include "ulibcl.hxx" extern "C" { #include "conapi.h" #include #ifdef FE_SB // isspace patch // isspace() causes access violation when x is Unicode char // that is not included in ASCII charset. #define isspace(x) ( (x) == ' ' ) ? TRUE : FALSE #endif }; VOID StripQuotesFromString( IN PWSTRING String ) /*++ Routine Description: This routine removes leading and trailing quote marks (if present) from a quoted string. If the string is not a quoted string, it is left unchanged. --*/ { if( String->QueryChCount() >= 2 && String->QueryChAt( 0 ) == '\"' && String->QueryChAt( String->QueryChCount() - 1 ) == '\"' ) { String->DeleteChAt( String->QueryChCount() - 1 ); String->DeleteChAt( 0 ); } } BOOLEAN DisplayPackedString( IN PCWSTR PackedStrings, IN ULONG PackedStringsLength, IN BOOLEAN IndentStrings, IN OUT PMESSAGE Message ) /*++ Routine Description: This routine outputs the given strings. One line per string. Arguments: PackedStrings - Supplies the null-separated strings to output. PackedStringsLength - Supplies the number of characters in the strings. NumIndentSpaces - Supplies whether or not to indent the strings. Message - Supplies the outlet for the strings. Return Value: FALSE - Failure. TRUE - Success. --*/ { DSTRING display_string; DSTRING raw_string; ULONG i; PCWSTR p; p = PackedStrings; for (i = 0; i < PackedStringsLength; i++) { if (i > 0 && p[i - 1]) { continue; } if (IndentStrings) { if (!display_string.Initialize(" ")) { return FALSE; } } else { if (!display_string.Initialize("")) { return FALSE; } } if (!raw_string.Initialize(&p[i]) || !display_string.Strcat(&raw_string)) { return FALSE; } Message->Set(MSG_ONE_STRING_NEWLINE); Message->Display("%W", &display_string); } return TRUE; } BOOLEAN QuerySourceAndTarget( IN PCWSTRING MacroLine, OUT PWSTR* Source, OUT PWSTR* Target ) /*++ Routine Description: This routine computes the sources and target string from the given macro line by isolating the '=' and then making sure that the source is a single token. Arguments: MacroLine - Supplies the macro. Source - Returns the source part of the macro. Target - Returns the target part of the macro. Return Value: FALSE - Failure. TRUE - Success. --*/ { LONG src_start, src_end, dst_start; ULONG i, n; WCHAR w; *Source = NULL; *Target = NULL; i = 0; n = MacroLine->QueryChCount(); for (; i < n; i++) { w = MacroLine->QueryChAt(i); if (!isspace(w)) { break; } } src_start = i; for (; i < n; i++) { w = MacroLine->QueryChAt(i); if (isspace(w) || w == '=') { break; } } src_end = i; if (src_start == src_end) { return FALSE; } for (; i < n; i++) { w = MacroLine->QueryChAt(i); if (!isspace(w)) { break; } } if (w != '=') { return FALSE; } i++; for (; i < n; i++) { w = MacroLine->QueryChAt(i); if (!isspace(w)) { break; } } dst_start = i; *Source = MacroLine->QueryWSTR(src_start, src_end - src_start); *Target = MacroLine->QueryWSTR(dst_start); if (!*Source || !*Target) { DELETE(*Source); DELETE(*Target); return FALSE; } return TRUE; } BOOLEAN DisplayMacros( IN PWSTR TargetExe, IN BOOLEAN IndentString, IN OUT PMESSAGE Message ) /*++ Routine Description: This routine displays the macros for the given exe. Arguments: TargetExe - Supplies the exe for which to display the macros. IndentStrings - Supplies whether or not to indent the macro strings. Message - Supplies an outlet for the macro strings. Return Value: FALSE - Failure. TRUE - Success. --*/ { ULONG buffer_length = 0; PWSTR buffer; buffer_length = GetConsoleAliasesLength(TargetExe); if (!(buffer = (PWCHAR) MALLOC(buffer_length + 1))) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } if (buffer_length) { buffer_length = GetConsoleAliases(buffer, buffer_length, TargetExe); } if (!DisplayPackedString(buffer, buffer_length/sizeof(WCHAR), IndentString, Message)) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } FREE(buffer); return TRUE; } BOOLEAN DisplayAllMacros( IN OUT PMESSAGE Message ) /*++ Routine Description: This routine displays all of the macros for all of the exes which have macros defined for them. Arguments: Message - Supplies an outlet for the macros. Return Value: FALSE - Failure. TRUE - Success. --*/ { PWSTR exes_buffer; ULONG exes_length; ULONG i; DSTRING exe_name; DSTRING display_name; exes_length = GetConsoleAliasExesLength(); if (!(exes_buffer = (PWSTR) MALLOC(exes_length + 1))) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } if (exes_length) { exes_length = GetConsoleAliasExes(exes_buffer, exes_length); } exes_length /= sizeof(WCHAR); for (i = 0; i < exes_length; i++) { if (i > 0 && exes_buffer[i - 1]) { continue; } if (!exe_name.Initialize(&exes_buffer[i]) || !display_name.Initialize("[") || !display_name.Strcat(&exe_name) || !exe_name.Initialize("]") || !display_name.Strcat(&exe_name)) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } Message->Set(MSG_ONE_STRING_NEWLINE); Message->Display("%W", &display_name); if (!DisplayMacros(&exes_buffer[i], TRUE, Message)) { return FALSE; } Message->Set(MSG_BLANK_LINE); Message->Display(); } return TRUE; } BOOLEAN ReadInMacrosFromFile( IN PPATH FilePath, IN PWSTR TargetExe, IN OUT PMESSAGE Message ) /*++ Routine Description: This routine reads in the macros in the given file. The file must have the same format as the output from DOSKEY /macros or DOSKEY /macros:all. Arguments: FilePath - Supplies the file with the macros. TargetExe - Supplies the exe for which the unclaimed macros are. Message - Supplies an outlet for messages. Return Value: FALSE - Failure. TRUE - Success. --*/ { PFSN_FILE file; PFILE_STREAM file_stream; DSTRING line; PWSTR target_exe; DSTRING target_string; PWSTR source, target; if (!(file = SYSTEM::QueryFile(FilePath)) || !(file_stream = file->QueryStream(READ_ACCESS))) { DELETE(file); Message->Set(MSG_ATTRIB_FILE_NOT_FOUND); Message->Display("%W", FilePath->GetPathString()); return FALSE; } // Set up the target exe. if (!line.Initialize("") || !target_string.Initialize(TargetExe) || !(target_exe = target_string.QueryWSTR())) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } while (!file_stream->IsAtEnd() && file_stream->ReadLine(&line)) { // First see if the current line will define a new exe name. if (!line.QueryChCount()) { continue; } if (line.QueryChAt(0) == '[' && line.Strchr(']') != INVALID_CHNUM && line.Strchr(']') > 1) { DELETE(target_exe); if (!target_string.Initialize(&line, 1, line.Strchr(']') - 1) || !(target_exe = target_string.QueryWSTR())) { Message->Set(MSG_CHK_NO_MEMORY); Message->Display(); return FALSE; } continue; } if (!QuerySourceAndTarget(&line, &source, &target) || !AddConsoleAlias(source, *target ? target : NULL, target_exe)) { Message->Set(MSG_DOSKEY_INVALID_MACRO_DEFINITION); Message->Display(); continue; } } DELETE(file_stream); DELETE(file); return TRUE; } int __cdecl main( ) /*++ Routine Description: This routine provides equivalent functionality to DOS 5's DOSKEY utility. Arguments: None. Return Value: 0 - Success. 1 - Failure. --*/ { STREAM_MESSAGE msg; ARGUMENT_LEXEMIZER arglex; ARRAY lex_array; ARRAY arg_array; STRING_ARGUMENT progname; FLAG_ARGUMENT help; FLAG_ARGUMENT reinstall; LONG_ARGUMENT bufsize; LONG_ARGUMENT listsize; STRING_ARGUMENT m_plus; FLAG_ARGUMENT m; STRING_ARGUMENT macros_plus; FLAG_ARGUMENT macros; FLAG_ARGUMENT history; FLAG_ARGUMENT h; FLAG_ARGUMENT insert; FLAG_ARGUMENT overstrike; STRING_ARGUMENT exename; PATH_ARGUMENT filename; REST_OF_LINE_ARGUMENT macro; PWSTRING pwstring; PWSTR target_exe; PWSTR source, target; PWSTR buffer; ULONG buffer_length; DSTRING all_string; // // DOSKEY /INSERT | /OVERSTRIKE changes the keyboard mode // and we do not want the keyboard mode to be restored on exit // Get_Standard_Input_Stream()->DoNotRestoreConsoleMode(); // Initialize the error stack and the stream message object. if (!msg.Initialize(Get_Standard_Output_Stream(), Get_Standard_Input_Stream())) { return 1; } // Initialize the parsing machinery. if (!lex_array.Initialize() || !arg_array.Initialize() || !arglex.Initialize(&lex_array)) { return 1; } arglex.SetCaseSensitive(FALSE); arglex.PutStartQuotes( "\"" ); arglex.PutEndQuotes( "\"" ); arglex.PutSeparators( " \t" ); // Tokenize the command line. if (!arglex.PrepareToParse()) { return 1; } // Initialize the argument patterns to be accepted. if (!progname.Initialize("*") || !help.Initialize("/?") || !reinstall.Initialize("/reinstall") || !bufsize.Initialize("/bufsize=*") || !listsize.Initialize("/listsize=*") || !macros_plus.Initialize("/macros:*") || !m_plus.Initialize("/m:*") || !macros.Initialize("/macros") || !m.Initialize("/m") || !history.Initialize("/history") || !h.Initialize("/h") || !insert.Initialize("/insert") || !overstrike.Initialize("/overstrike") || !exename.Initialize("/exename=*") || !filename.Initialize("/macrofile=*") || !macro.Initialize()) { return 1; } // Feed the arguments into the argument array. if (!arg_array.Put(&progname) || !arg_array.Put(&help) || !arg_array.Put(&reinstall) || !arg_array.Put(&bufsize) || !arg_array.Put(&listsize) || !arg_array.Put(¯os_plus) || !arg_array.Put(&m_plus) || !arg_array.Put(¯os) || !arg_array.Put(&m) || !arg_array.Put(&history) || !arg_array.Put(&h) || !arg_array.Put(&insert) || !arg_array.Put(&overstrike) || !arg_array.Put(&exename) || !arg_array.Put(&filename) || !arg_array.Put(¯o)) { return 1; } // Parse the command line. if (!arglex.DoParsing(&arg_array)) { msg.Set(MSG_INVALID_PARAMETER); msg.Display("%W", pwstring = arglex.QueryInvalidArgument()); DELETE(pwstring); return 1; } // Interpret the command line. if (bufsize.IsValueSet()) { msg.Set(MSG_DOSKEY_CANT_DO_BUFSIZE); msg.Display(); } if (help.QueryFlag()) { msg.Set(MSG_DOSKEY_HELP); msg.Display(); return 0; } if ((m.QueryFlag() || macros.QueryFlag()) && (macros_plus.IsValueSet() || m_plus.IsValueSet()) || (macros_plus.IsValueSet() && m_plus.IsValueSet())) { msg.Set(MSG_INCOMPATIBLE_PARAMETERS); msg.Display(); return 1; } // Compute the target exe name. if (exename.IsValueSet()) { StripQuotesFromString( (PWSTRING)exename.GetString() ); if (!(target_exe = exename.GetString()->QueryWSTR())) { return 1; } } else { target_exe = (PWSTR) L"cmd.exe"; } // Interpret reinstall switch. if (reinstall.QueryFlag()) { ExpungeConsoleCommandHistory(target_exe); } // Interpret list size switch. if (listsize.IsValueSet()) { if (!SetConsoleNumberOfCommands(listsize.QueryLong(), target_exe)) { msg.Set(MSG_DOSKEY_CANT_SIZE_LIST); msg.Display(); return 1; } } // Interpret insert and overstrike switches. if (insert.QueryFlag()) { SetConsoleCommandHistoryMode(0); } if (overstrike.QueryFlag()) { SetConsoleCommandHistoryMode(CONSOLE_OVERSTRIKE); } // Interpret the macro if any. if (macro.IsValueSet()) { if (!QuerySourceAndTarget(macro.GetRestOfLine(), &source, &target) || !AddConsoleAlias(source, *target ? target : NULL, target_exe)) { msg.Set(MSG_DOSKEY_INVALID_MACRO_DEFINITION); msg.Display(); } DELETE(source); DELETE(target); } // pull the stuff out from the file if provided. if (filename.IsValueSet()) { StripQuotesFromString((PWSTRING)filename.GetPath()->GetPathString()); if (!ReadInMacrosFromFile(filename.GetPath(), target_exe, &msg)) { return 1; } } // Print out history buffer. if (history.QueryFlag() || h.QueryFlag()) { buffer_length = GetConsoleCommandHistoryLength(target_exe); if (!(buffer = (PWSTR) MALLOC(buffer_length))) { msg.Set(MSG_CHK_NO_MEMORY); msg.Display(); return 1; } if (buffer_length) { buffer_length = GetConsoleCommandHistory(buffer, buffer_length, target_exe); } if (!DisplayPackedString(buffer, buffer_length/sizeof(WCHAR), FALSE, &msg)) { msg.Set(MSG_CHK_NO_MEMORY); msg.Display(); return 1; } FREE(buffer); } // Echo macros for target_exe. if (macros.QueryFlag() || m.QueryFlag()) { if (!DisplayMacros(target_exe, FALSE, &msg)) { return 1; } } // Echo macros for specific exe. if (macros_plus.IsValueSet()) { StripQuotesFromString(macros_plus.GetString()); if (!all_string.Initialize("all")) { return 1; } if (!macros_plus.GetString()->Stricmp(&all_string)) { if (!DisplayAllMacros(&msg)) { return 1; } } else { target_exe = macros_plus.GetString()->QueryWSTR(); if (!DisplayMacros(target_exe, FALSE, &msg)) { return 1; } DELETE(target_exe); } } // Echo macros for specific exe. if (m_plus.IsValueSet()) { StripQuotesFromString(m_plus.GetString()); if (!all_string.Initialize("all")) { return 1; } if (!m_plus.GetString()->Stricmp(&all_string)) { if (!DisplayAllMacros(&msg)) { return 1; } } else { target_exe = m_plus.GetString()->QueryWSTR(); if (!DisplayMacros(target_exe, FALSE, &msg)) { return 1; } DELETE(target_exe); } } return 0; }