#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 <ctype.h>
#ifdef DBCS // 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 // DBCS
};
ERRSTACK* perrstk;
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 _CRTAPI1
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;
// Initialize the error stack and the stream message object.
if (!(perrstk = NEW ERRSTACK) ||
!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);
}
// Suck 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;
}