|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1996.
//
// File: parse.cxx
//
// Contents: Functions that support parsing.
//
// History: 04-01-95 DavidMun Created
//
//----------------------------------------------------------------------------
#include <headers.hxx>
#pragma hdrstop
#include "jt.hxx"
//
// Private globals
//
// s_awszTokens - the strings in this array must exactly match the order
// of tokens in the TOKEN enum in parse.hxx.
//
WCHAR *s_awszTokens[] = { L"ABJ", L"ABQ", L"AJQ", L"CSAGE", L"CTJ", L"CTQ", L"DTJ", L"DTQ", L"EJ", L"EJQ", L"ENC", L"ENN", L"ENR", L"ENS", L"GC", L"GM", L"LJ", L"LQ", L"NSGA", L"NSSA", L"PJ", L"PQ", L"PRJ", L"PRQ", L"PSJ", L"PSQ", L"PTJ", L"PTQ", L"RJ", L"RQ", L"RMJQ", L"SAC", L"SAJ", L"SAQ", L"SC", L"SCE", L"SD", L"SE", L"ISJQ", L"SJ", L"SM", L"SNJ", L"SNQ", L"SQ", L"STJ", L"STQ", L"SVJ", L"SVQ", L"ApplicationName", L"Parameters", L"WorkingDirectory", L"Comment", L"Creator", L"Priority", L"MaxRunTime", L"TaskFlags", L"Interactive", L"DeleteWhenDone", L"Suspend", L"NetSchedule", L"DontStartIfOnBatteries", L"KillIfGoingOnBatteries", L"RunOnlyIfLoggedOn", L"Hidden", L"StartDate", L"EndDate", L"StartTime", L"MinutesDuration", L"HasEndDate", L"KillAtDuration", L"StartOnlyIfIdle", L"KillOnIdleEnd", L"RestartOnIdleResume", L"SystemRequired", L"Disabled", L"MinutesInterval", L"Type", L"TypeArguments", L"IDLE", L"NORMAL", L"HIGH", L"REALTIME", L"ONCE", L"DAILY", L"WEEKLY", L"MONTHLYDATE", L"MONTHLYDOW", L"YEARLYDATE", L"YEARLYDOW", L"ONIDLE", L"ATSTARTUP", L"ATLOGON",
//
// CAUTION: single-character nonalpha tokens need to be added to the
// constant DELIMITERS.
//
L"TODAY", L"NOW", L"=", L"@", L"?", L":", L",", L"!" };
const WCHAR DELIMITERS[] = L"=@?:,!;/- \t";
#define NUM_TOKEN_STRINGS ARRAY_LEN(s_awszTokens)
//
// Forward references
//
WCHAR *SkipSpaces(WCHAR *pwsz); TOKEN _GetStringToken(WCHAR **ppwsz); TOKEN _GetNumberToken(WCHAR **ppwsz, WCHAR *pwszEnd);
//+---------------------------------------------------------------------------
//
// Function: ProcessCommandLine
//
// Synopsis: Dispatch to the routine that completes parsing for and carries
// out the next command specified on [pwszCommandLine].
//
// Arguments: [pwszCommandLine] - command line
//
// Returns: S_OK - command performed
// E_* - error logged
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
HRESULT ProcessCommandLine(WCHAR *pwszCommandLine) { HRESULT hr; WCHAR *pwsz = pwszCommandLine; static CHAR s_szJob[] = "Job"; static CHAR s_szQueue[] = "Queue";
//
// If a command starts with TKN_BANG instead of TKN_SWITCH, then its
// return value will be ignored. Clear this flag in any case statement
// that should cause a failure regardless of the use of TKN_BANG (see
// TKN_INVALID, for example).
//
BOOL fIgnoreReturnValue = FALSE;
while (*pwsz) { WCHAR *pwszLast = pwsz; TOKEN tkn;
hr = S_OK;
tkn = GetToken(&pwsz);
if (tkn == TKN_EOL) { break; }
if (tkn == TKN_SWITCH) { tkn = GetToken(&pwsz); } else if (tkn == TKN_BANG) { fIgnoreReturnValue = TRUE; tkn = GetToken(&pwsz); } else if (tkn != TKN_ATSIGN && tkn != TKN_QUESTION) { hr = E_FAIL; LogSyntaxError(tkn, L"'/', '-', '?', '!', or '@'"); break; }
switch (tkn) { #if 0
case TKN_ABORTQUEUE: hr = Abort(&pwsz, FALSE); break;
case TKN_LOADQUEUE: hr = Load(&pwsz, s_szQueue, FALSE); break;
case TKN_CREATETRIGGERQUEUE: hr = CreateTrigger(&pwsz, FALSE); break;
case TKN_DELETETRIGGERQUEUE: hr = DeleteTrigger(&pwsz, FALSE); break;
case TKN_EDITJOBINQUEUE: hr = EditJob(&pwsz, FALSE); break;
case TKN_PRINTQUEUE: hr = PrintAll(&pwsz, FALSE); break;
case TKN_PRINTRUNTIMEQUEUE: hr = PrintRunTimes(&pwsz, FALSE); break;
case TKN_PRINTTRIGGERQUEUE: hr = PrintTrigger(&pwsz, FALSE); break;
case TKN_PRINTSTRINGQUEUE: hr = PrintTriggerStrings(&pwsz, s_szQueue, FALSE); break;
case TKN_RUNQUEUE: hr = Run(FALSE); break;
case TKN_SAVEQUEUE: hr = Save(&pwsz, s_szQueue, FALSE); break;
case TKN_SETTRIGGERQUEUE: hr = SetTrigger(&pwsz, FALSE); break;
#endif
case TKN_ATSIGN: hr = DoAtSign(&pwsz); break;
case TKN_ABORTJOB: hr = Abort(&pwsz, TRUE); break;
#ifndef RES_KIT
case TKN_CONVERTSAGETASKSTOJOBS: hr = ConvertSage(); break; #endif // RES_KIT not defined
case TKN_CREATETRIGGERJOB: hr = CreateTrigger(&pwsz, TRUE); break;
case TKN_DELETETRIGGERJOB: hr = DeleteTrigger(&pwsz, TRUE); break;
#ifndef RES_KIT
case TKN_EDITJOB: hr = EditJob(&pwsz, TRUE); break;
case TKN_ENUMCLONE: hr = EnumClone(&pwsz); break;
case TKN_ENUMNEXT: hr = EnumNext(&pwsz); break;
case TKN_ENUMRESET: hr = EnumReset(&pwsz); break;
case TKN_ENUMSKIP: hr = EnumSkip(&pwsz); break; #endif // RES_KIT not defined
case TKN_EOL: hr = E_FAIL; fIgnoreReturnValue = FALSE; // be sure we exit loop
g_Log.Write(LOG_ERROR, "Unexpected end of line after switch character"); break;
case TKN_GETCREDENTIALS: hr = GetCredentials(); break;
case TKN_GETMACHINE: hr = SchedGetMachine(); break;
case TKN_INVALID: hr = E_FAIL; fIgnoreReturnValue = FALSE; // be sure we exit loop
LogSyntaxError(tkn, L"valid token after switch"); break;
case TKN_LOADJOB: hr = Load(&pwsz, s_szJob, TRUE); break;
case TKN_SETCREDENTIALS: hr = SetCredentials(&pwsz); break;
case TKN_SETMACHINE: hr = SchedSetMachine(&pwsz); break;
case TKN_PRINTJOB: hr = PrintAll(&pwsz, TRUE); break;
case TKN_PRINTRUNTIMEJOB: hr = PrintRunTimes(&pwsz, TRUE); break;
case TKN_PRINTTRIGGERJOB: hr = PrintTrigger(&pwsz, TRUE); break;
case TKN_PRINTSTRINGJOB: hr = PrintTriggerStrings(&pwsz, s_szJob, TRUE); break;
case TKN_NSGETACCOUNTINFO: hr = PrintNSAccountInfo(); break;
case TKN_NSSETACCOUNTINFO: hr = SetNSAccountInfo(&pwsz); break;
case TKN_QUESTION: DoHelp(&pwsz); break;
case TKN_RUNJOB: hr = Run(TRUE); break;
case TKN_SAVEJOB: hr = Save(&pwsz, s_szJob, TRUE); break;
case TKN_SCHEDADDJOB: hr = SchedAddJob(&pwsz); break;
case TKN_SCHEDACTIVATE: hr = SchedActivate(&pwsz); break;
#ifndef RES_KIT
case TKN_SCHEDCREATEENUM: hr = SchedCreateEnum(&pwsz); break; #endif // RES_KIT not defined
case TKN_SCHEDDELETE: hr = SchedDelete(&pwsz); break;
case TKN_SCHEDENUM: hr = SchedEnum(&pwsz); break;
#ifndef RES_KIT
case TKN_SCHEDISJOBORQUEUE: hr = SchedIsJobOrQueue(&pwsz); break; #endif // RES_KIT not defined
case TKN_SCHEDNEWJOB: hr = SchedNewJob(&pwsz); break;
case TKN_SETJOB: hr = SetJob(&pwsz); break;
case TKN_SETTRIGGERJOB: hr = SetTrigger(&pwsz, TRUE); break;
default: hr = E_FAIL; fIgnoreReturnValue = FALSE; // be sure we exit loop
LogSyntaxError(tkn, L"command"); break; }
if (fIgnoreReturnValue) { fIgnoreReturnValue = FALSE; } else if (FAILED(hr)) { break; } } return hr; }
//+---------------------------------------------------------------------------
//
// Function: LogSyntaxError
//
// Synopsis: Complain that we expected [pwszExpected] but found [tkn].
//
// Arguments: [tkn] - token that was found
// [pwszExpected] - description of what was expected
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
VOID LogSyntaxError(TOKEN tkn, WCHAR *pwszExpected) { if (tkn == TKN_INVALID) { return; }
if (tkn == TKN_EOL) { g_Log.Write( LOG_ERROR, "Expected %S but found end of line", pwszExpected); } else { g_Log.Write( LOG_ERROR, "Expected %S but found token '%S'", pwszExpected, GetTokenStringForLogging(tkn)); } }
//+---------------------------------------------------------------------------
//
// Function: GetToken
//
// Synopsis: Return a token representing the next characters in *[ppwsz],
// and advance *[ppwsz] past the end of this token.
//
// Arguments: [ppwsz] - command line
//
// Returns: token describing characters found
//
// Modifies: *[ppwsz]
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
TOKEN GetToken(WCHAR **ppwsz) { ULONG i;
*ppwsz = SkipSpaces(*ppwsz);
if (!**ppwsz) { return TKN_EOL; }
if (**ppwsz == L';') { *ppwsz += wcslen(*ppwsz); return TKN_EOL; }
if (**ppwsz == L'/' || **ppwsz == L'-') { ++*ppwsz; return TKN_SWITCH; }
if (**ppwsz == L'"') { return _GetStringToken(ppwsz); }
if (iswdigit(**ppwsz)) { WCHAR *pwszEnd; ULONG ulTemp;
ulTemp = wcstoul(*ppwsz, &pwszEnd, 10);
g_ulLastNumberToken = ulTemp; return _GetNumberToken(ppwsz, pwszEnd); }
ULONG cchToken;
//
// We've already skipped leading whitespace, so length of token is number
// of characters that are not whitespace or single-character tokens. If
// wcscspn returns 0, then **ppwsz must be one of the single character
// tokens.
//
cchToken = wcscspn(*ppwsz, DELIMITERS);
if (!cchToken) { cchToken = 1; }
//
// Check the input against all the tokens
//
for (i = 0; i < NUM_TOKEN_STRINGS; i++) { if (!_wcsnicmp(*ppwsz, s_awszTokens[i], cchToken)) { if (wcslen(s_awszTokens[i]) != cchToken) { continue; } *ppwsz += cchToken; return (TOKEN) i; } }
//
// Not a number or token. Return it as a string.
//
return _GetStringToken(ppwsz); }
//+---------------------------------------------------------------------------
//
// Function: _GetStringToken
//
// Synopsis: Treat *[ppwsz] as the start of an optionally quote-enclosed
// string (even if it's a digit or matches a predefined token).
//
// Arguments: [ppwsz] - command line
//
// Returns: TKN_STRING
// TKN_INVALID - string too long
//
// Modifies: Moves *[ppwsz] past end of string; g_wszLastStringToken.
//
// History: 04-21-95 DavidMun Created
// 01-08-96 DavidMun Allow empty strings
//
//----------------------------------------------------------------------------
TOKEN _GetStringToken(WCHAR **ppwsz) { BOOL fFoundQuote = FALSE;
*ppwsz = SkipSpaces(*ppwsz);
if (!**ppwsz) { return TKN_EOL; }
if (**ppwsz == L';') { *ppwsz += wcslen(*ppwsz); return TKN_EOL; }
if (**ppwsz == L'"') { ++*ppwsz; fFoundQuote = TRUE; }
//
// It's not a recognized token, so consider it a string. If we found a
// double-quote, copy everything till next double quote into
// g_wszLastStringToken. If not, just copy till next whitespace char or
// eol.
//
// Note that if !fFoundQuote *ppwsz != L'\0' or whitespace or else we
// would've returned TKN_EOL already.
//
ULONG cchToCopy;
if (fFoundQuote) { if (**ppwsz == L'"') { ++*ppwsz; g_wszLastStringToken[0] = L'\0'; return TKN_STRING; }
if (!**ppwsz) { g_Log.Write(LOG_ERROR, "Syntax: '\"' followed by end of line"); return TKN_INVALID; }
cchToCopy = wcscspn(*ppwsz, L"\"");
if ((*ppwsz)[cchToCopy] != L'"') { *ppwsz += cchToCopy; g_Log.Write(LOG_ERROR, "Syntax: unterminated string"); return TKN_INVALID; } } else { cchToCopy = wcscspn(*ppwsz, L", \t"); }
if (cchToCopy + 1 >= ARRAY_LEN(g_wszLastStringToken)) { *ppwsz += cchToCopy + (fFoundQuote == TRUE); g_Log.Write( LOG_ERROR, "String token > %u characters", ARRAY_LEN(g_wszLastStringToken) - 1); return TKN_INVALID; }
wcsncpy(g_wszLastStringToken, *ppwsz, cchToCopy); g_wszLastStringToken[cchToCopy] = L'\0'; *ppwsz += cchToCopy + (fFoundQuote == TRUE); return TKN_STRING; }
//+---------------------------------------------------------------------------
//
// Function: _GetNumberToken
//
// Synopsis: Copy the number starting at *[ppwsz] and ending at [pwszEnd]
// into g_wszLastNumberToken.
//
// Arguments: [ppwsz] - command line containing number
// [pwszEnd] - first non-numeric character in command line
//
// Returns: TKN_NUMBER - g_wszLastNumberToken modified
// TKN_INVALID - string too long, g_wszLastNumberToken unchanged
//
// Modifies: *[ppwsz] is always moved to [pwszEnd].
// g_wszLastNumberToken gets copy of number, but only if
// return value is TKN_NUMBER.
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
TOKEN _GetNumberToken(WCHAR **ppwsz, WCHAR *pwszEnd) { ULONG cchToCopy;
cchToCopy = pwszEnd - *ppwsz;
if (cchToCopy >= ARRAY_LEN(g_wszLastNumberToken)) { *ppwsz = pwszEnd;
g_Log.Write( LOG_ERROR, "Number token > %u characters", ARRAY_LEN(g_wszLastNumberToken) - 1); return TKN_INVALID; }
wcsncpy(g_wszLastNumberToken, *ppwsz, cchToCopy); g_wszLastNumberToken[cchToCopy] = L'\0'; *ppwsz = pwszEnd;
return TKN_NUMBER; }
//+---------------------------------------------------------------------------
//
// Function: PeekToken
//
// Synopsis: Same as GetToken(), but *[ppwsz] is unmodified.
//
// Arguments: [ppwsz] - command line
//
// Returns: token describing characters at *[ppwsz]
//
// Modifies: May modify g_*LastNumberToken, g_wszLastStringToken
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
TOKEN PeekToken(WCHAR **ppwsz) { WCHAR *pwszSavedPosition = *ppwsz; TOKEN tkn;
tkn = GetToken(ppwsz); *ppwsz = pwszSavedPosition; return tkn; }
//+---------------------------------------------------------------------------
//
// Function: Expect
//
// Synopsis: Get a token and log a syntax error if it isn't [tknExpected]
//
// Arguments: [tknExpected] - token we should get
// [ppwsz] - command line
// [wszExpected] - description for logging if next token isn't
// [tknExpected].
//
// Returns: S_OK - got expected token
// E_FAIL - got different token
//
// Modifies: *[ppwsz]
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
HRESULT Expect(TOKEN tknExpected, WCHAR **ppwsz, WCHAR *wszExpected) { HRESULT hr = S_OK; TOKEN tkn;
if (tknExpected == TKN_STRING) { tkn = _GetStringToken(ppwsz); } else { tkn = GetToken(ppwsz); }
if (tkn != tknExpected) { hr = E_FAIL; LogSyntaxError(tkn, wszExpected); } return hr; }
//+---------------------------------------------------------------------------
//
// Function: GetFilename
//
// Synopsis: Expect a string token at *[ppwsz] and convert it to a full
// path in g_wszLastStringToken.
//
// Arguments: [ppwsz] - command line
// [wszExpected] - for logging if next token isn't string
//
// Returns: S_OK - [wszExpected] valid
//
// Modifies: *[ppwsz], g_wszLastStringToken
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
HRESULT GetFilename(WCHAR **ppwsz, WCHAR *wszExpected) { HRESULT hr = S_OK; WCHAR wszFullPath[MAX_PATH+1]; ULONG cchRequired; TOKEN tkn;
do { tkn = _GetStringToken(ppwsz);
if (tkn != TKN_STRING) { hr = E_FAIL; g_Log.Write( LOG_FAIL, "Expected %S but got invalid or missing string", wszExpected); break; }
#ifdef UNICODE
cchRequired = GetFullPathName( g_wszLastStringToken, MAX_PATH + 1, wszFullPath, NULL); #else
CHAR szToken[MAX_PATH + 1]; CHAR szFullPath[MAX_PATH + 1];
wcstombs(szToken, g_wszLastStringToken, MAX_PATH + 1); cchRequired = GetFullPathName( szToken, MAX_PATH + 1, szFullPath, NULL); #endif
if (!cchRequired) { hr = E_FAIL; g_Log.Write( LOG_ERROR, "GetFullPathName(%S) %u", g_wszLastStringToken, GetLastError()); break; }
if (cchRequired > MAX_PATH) { hr = E_FAIL; g_Log.Write(LOG_ERROR, "Full path > MAX_PATH chars"); break; }
#ifdef UNICODE
wcscpy(g_wszLastStringToken, wszFullPath); #else
mbstowcs(g_wszLastStringToken, szFullPath, MAX_PATH + 1); #endif
} while (0); return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseDate
//
// Synopsis: Fill [pwMonth], [pwDay], and [pwYear] with numeric values
// taken from the command line in [ppwsz].
//
// Arguments: [ppwsz] - Command line
// [pwMonth] - filled with first value
// [pwDay] - filled with second value
// [pwYear] - filled with third value
//
// Returns: S_OK - [pwMonth], [pwDay], and [pwYear] contain numbers
// (but do not necessarily constitute a valid date)
// E_* - error logged
//
// Modifies: All args.
//
// History: 01-04-96 DavidMun Created
//
// Notes: Dates can be of the form:
//
// n/n/n
// n-n-n
//
// or any other single character nonalpha token may be used to
// separate the numbers. If spaces appear on both sides of
// the tokens separating the numbers, then the tokens can be
// of any type at all.
//
//----------------------------------------------------------------------------
HRESULT ParseDate(WCHAR **ppwsz, WORD *pwMonth, WORD *pwDay, WORD *pwYear) { HRESULT hr = S_OK; TOKEN tkn; SYSTEMTIME stNow;
do { tkn = PeekToken(ppwsz);
if (tkn == TKN_TODAY) { GetToken(ppwsz); GetLocalTime(&stNow); *pwMonth = stNow.wMonth; *pwDay = stNow.wDay; *pwYear = stNow.wYear; break; }
hr = Expect(TKN_NUMBER, ppwsz, L"month value"); BREAK_ON_FAILURE(hr);
*pwMonth = (WORD) g_ulLastNumberToken;
GetToken(ppwsz); // eat whatever separator there is
hr = Expect(TKN_NUMBER, ppwsz, L"day value"); BREAK_ON_FAILURE(hr);
*pwDay = (WORD) g_ulLastNumberToken;
GetToken(ppwsz); // eat whatever separator there is
hr = Expect(TKN_NUMBER, ppwsz, L"year value"); BREAK_ON_FAILURE(hr);
*pwYear = (WORD) g_ulLastNumberToken;
if (*pwYear < 100) { *pwYear += 1900; } } while (0); return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseTime
//
// Synopsis: Fill [pwHour] and [pwMinute] with numeric values taken from
// the command line in [ppwsz].
//
// Arguments: [ppwsz] - command line
// [pwHour] - filled with first number
// [pwMinute] - filled with second number
//
// Returns: S_OK - [pwHour] and [pwMinute] contain numbers (but do not
// necessarily constitute a valid time).
// E_* - error logged
//
// Modifies: All args.
//
// History: 01-04-96 DavidMun Created
//
// Notes: See ParseDate for rules about delimiters.
//
//----------------------------------------------------------------------------
HRESULT ParseTime(WCHAR **ppwsz, WORD *pwHour, WORD *pwMinute) { HRESULT hr = S_OK; TOKEN tkn; SYSTEMTIME stNow;
do { tkn = PeekToken(ppwsz);
if (tkn == TKN_NOW) { GetToken(ppwsz); GetLocalTime(&stNow);
//
// Add some time to the current time so that a trigger with
// NOW start time is far enough in the future to get run
//
AddSeconds(&stNow, TIME_NOW_INCREMENT);
*pwHour = stNow.wHour; *pwMinute = stNow.wMinute; break; } hr = Expect(TKN_NUMBER, ppwsz, L"hour value"); BREAK_ON_FAILURE(hr);
*pwHour = (WORD) g_ulLastNumberToken;
GetToken(ppwsz); // eat whatever separator there is
hr = Expect(TKN_NUMBER, ppwsz, L"minute value");
*pwMinute = (WORD) g_ulLastNumberToken; } while (0); return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseDaysOfWeek
//
// Synopsis: Fill [pwDaysOfTheWeek] with the days of the week specified
// by the next string token in the command line.
//
// Arguments: [ppwsz] - command line
// [pwDaysOfTheWeek] - filled with JOB_*DAY bits
//
// Returns: S_OK - *[pwDaysOfTheWeek] valid
// E_* - invalid string token
//
// Modifies: All args.
//
// History: 01-04-96 DavidMun Created
//
//----------------------------------------------------------------------------
HRESULT ParseDaysOfWeek(WCHAR **ppwsz, WORD *pwDaysOfTheWeek) { HRESULT hr = S_OK; TOKEN tkn; WCHAR *pwszDay;
tkn = _GetStringToken(ppwsz);
if (tkn != TKN_STRING) { hr = E_FAIL; }
*pwDaysOfTheWeek = 0;
for (pwszDay = g_wszLastStringToken; SUCCEEDED(hr) && *pwszDay; pwszDay++) { switch (towupper(*pwszDay)) { case L'U': *pwDaysOfTheWeek |= TASK_SUNDAY; break;
case L'M': *pwDaysOfTheWeek |= TASK_MONDAY; break;
case L'T': *pwDaysOfTheWeek |= TASK_TUESDAY; break;
case L'W': *pwDaysOfTheWeek |= TASK_WEDNESDAY; break;
case L'R': *pwDaysOfTheWeek |= TASK_THURSDAY; break;
case L'F': *pwDaysOfTheWeek |= TASK_FRIDAY; break;
case L'A': *pwDaysOfTheWeek |= TASK_SATURDAY; break;
case L'.': // ignore this, since we display day as . when its bit is off
break;
default: hr = E_FAIL; g_Log.Write( LOG_FAIL, "Expected day of week character 'UMTWRFA' but got '%wc'", *pwszDay); break; } } return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseDaysOfMonth
//
// Synopsis: Translate a comma separated list of day numbers into a bit
// field in [pdwDays].
//
// Arguments: [ppwsz] - command line
// [pdwDays] - least significant bit represents day 1
//
// Returns: S_OK
// E_FAIL - syntax or value error
//
// History: 03-07-96 DavidMun Created
//
// Notes: Day list may contain dashes to indicate day ranges. For
// example, "1,3-5,7,10-12" is equivalent to
// "1,3,4,5,7,10,11,12". Expressions like "1-3-5" are allowed
// (it's equivalent to "1-5"). Ranges with the first bit
// less than the second are automatically swapped, for example
// "4-2" is treated as "2-4". So even "4-2-1" would be
// interpreted as "1-4".
//
// Day numbers up to 32 are allowed for the purposes of
// exercising the error checking code in the job scheduler
// interfaces.
//
// CAUTION: this function will eat a trailing comma OR SWITCH
// character! It is therefore a requirement that a DaysOfMonth
// list be followed by some other nonswitch string to avoid
// having it eat the switch character that starts the next
// command.
//
//----------------------------------------------------------------------------
HRESULT ParseDaysOfMonth(WCHAR **ppwsz, DWORD *pdwDays) { HRESULT hr = S_OK; TOKEN tkn; ULONG ulLastDay = 0; BOOL fGotDash = FALSE; ULONG i;
*pdwDays = 0;
do { tkn = PeekToken(ppwsz);
//
// A string, EOL, or error token means we've gone past the end of the
// list of days and can quit. (The latter means we're about to
// abort!)
//
if (tkn == TKN_STRING || tkn == TKN_EOL || tkn == TKN_INVALID) { break; }
//
// Eat commas, but don't allow "-,". Also, getting a comma resets the
// last day to zero, which allows TKN_SWITCH check to complain about
// "1,-2"
//
if (tkn == TKN_COMMA) { ulLastDay = 0;
if (fGotDash) { hr = E_FAIL; g_Log.Write( LOG_FAIL, "Expected a number following the dash but got ',' in day of month list"); break; }
GetToken(ppwsz); continue; }
//
// A dash is valid only if the preceding token was a number
//
if (tkn == TKN_SWITCH) { if (fGotDash) { hr = E_FAIL;
g_Log.Write( LOG_FAIL, "Didn't expect two switch characters in a row in day of month list"); break; }
if (ulLastDay == 0) { hr = E_FAIL;
g_Log.Write( LOG_FAIL, "Expected a number preceding switch character in day of month list"); break; }
//
// It's ok to have a dash. Note that we got one, consume the
// token, and look at the next one.
//
fGotDash = TRUE; GetToken(ppwsz); continue; }
//
// At this point, anything other than a number means we're done. If
// there's a hanging switch, though, that's an error.
if (tkn != TKN_NUMBER) { if (fGotDash) { hr = E_FAIL; LogSyntaxError(tkn, L"a number following the switch character"); } break; }
//
// The next token is TKN_NUMBER, so consume it. Also make sure it's
// >= 1 and <= 32. Yes, 32, because we want to allow specifying an
// invalid bit pattern to the Job Scheduler code.
//
// If fGotDash, this number is the end of a range that started with
// ulLastDay (which has already been verified).
//
// Otherwise it's just a single day bit to turn on.
//
GetToken(ppwsz);
if (g_ulLastNumberToken < 1 || #ifndef RES_KIT
g_ulLastNumberToken > 32) #else
g_ulLastNumberToken > 31) #endif
{ hr = E_FAIL; g_Log.Write( LOG_FAIL, #ifndef RES_KIT
"Expected a day number from 1 to 32 (yes, thirty-two) but got %u", #else
"Day numbers run from 1 to 31, but %u was passed in, instead.", #endif
g_ulLastNumberToken); break; }
if (fGotDash) { fGotDash = FALSE;
// allow backwards ranges
if (ulLastDay > g_ulLastNumberToken) { ULONG ulTemp = ulLastDay; ulLastDay = g_ulLastNumberToken; g_ulLastNumberToken = ulTemp; }
//
// Turn on all the bits in the range. Note that the previous
// iteration already saw ulLastDay and turned it on, so we can
// skip that bit.
//
for (i = ulLastDay + 1; i <= g_ulLastNumberToken; i++) { *pdwDays |= 1 << (i - 1); } } else { *pdwDays |= 1 << (g_ulLastNumberToken - 1); }
ulLastDay = g_ulLastNumberToken; } while (TRUE); return hr; }
//+---------------------------------------------------------------------------
//
// Function: ParseMonths
//
// Synopsis: Translate MONTH_ABBREV_LEN character long month
// abbreviations in [ppwsz] into month bits in [pwMonths].
//
// Arguments: [ppwsz] - command line
// [pwMonths] - filled with month bits
//
// Returns: S_OK
// E_FAIL
//
// History: 03-07-96 DavidMun Created
//
//----------------------------------------------------------------------------
HRESULT ParseMonths(WCHAR **ppwsz, WORD *pwMonths) { HRESULT hr = S_OK; TOKEN tkn; WCHAR *pwszMonth; ULONG i;
*pwMonths = 0;
tkn = _GetStringToken(ppwsz);
if (tkn != TKN_STRING) { hr = E_FAIL; } else if (wcslen(g_wszLastStringToken) % MONTH_ABBREV_LEN) { hr = E_FAIL; g_Log.Write( LOG_FAIL, "Month string must consist of %u letter abbreviations", MONTH_ABBREV_LEN); }
for (pwszMonth = g_wszLastStringToken; SUCCEEDED(hr) && *pwszMonth; pwszMonth += 3) { for (i = 0; i < 12; i++) { if (!_wcsnicmp(pwszMonth, g_awszMonthAbbrev[i], MONTH_ABBREV_LEN)) { switch (i) { case 0: *pwMonths |= TASK_JANUARY; break;
case 1: *pwMonths |= TASK_FEBRUARY; break;
case 2: *pwMonths |= TASK_MARCH; break;
case 3: *pwMonths |= TASK_APRIL; break;
case 4: *pwMonths |= TASK_MAY; break;
case 5: *pwMonths |= TASK_JUNE; break;
case 6: *pwMonths |= TASK_JULY; break;
case 7: *pwMonths |= TASK_AUGUST; break;
case 8: *pwMonths |= TASK_SEPTEMBER; break;
case 9: *pwMonths |= TASK_OCTOBER; break;
case 10: *pwMonths |= TASK_NOVEMBER; break;
case 11: *pwMonths |= TASK_DECEMBER; break; }
//
// Since we've found the month abbreviation, break out of the
// inner loop that's comparing abbreviations against the
// user's string.
//
break; } }
//
// If the inner loop found the next MONTH_ABBREV_LEN chars of the
// user's string in the g_awszMonthAbbrev array, then it executed the
// break and i would be less than 12.
//
if (i >= 12) { hr = E_FAIL; g_Log.Write( LOG_FAIL, "Expected %u character month abbreviation at '%S", MONTH_ABBREV_LEN, pwszMonth); } } return hr; }
//+---------------------------------------------------------------------------
//
// Function: GetTokenStringForLogging
//
// Synopsis: Return a human-readable string describing [tkn].
//
// Arguments: [tkn] - token to describe.
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
WCHAR *GetTokenStringForLogging(TOKEN tkn) { switch (tkn) { case TKN_INVALID: return L"an invalid token";
case TKN_EOL: return L"end of line";
case TKN_SWITCH: return L"switch character";
case TKN_STRING: return g_wszLastStringToken;
case TKN_NUMBER: return g_wszLastNumberToken;
default: if (tkn < NUM_TOKEN_STRINGS) { return s_awszTokens[tkn]; } return L"an unknown token value"; } }
//+---------------------------------------------------------------------------
//
// Function: SkipSpaces
//
// Synopsis: Return [pwsz] advanced to end of string, end of line, or
// next non-space character.
//
// Arguments: [pwsz] - string to skip spaces in
//
// Returns: [pwsz] + n
//
// History: 04-21-95 DavidMun Created
//
//----------------------------------------------------------------------------
WCHAR *SkipSpaces(WCHAR *pwsz) { while (*pwsz && *pwsz == L' ' || *pwsz == L'\t') { pwsz++; } return pwsz; }
|