Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1520 lines
35 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1994 - 1996.
  5. //
  6. // File: parse.cxx
  7. //
  8. // Contents: Functions that support parsing.
  9. //
  10. // History: 04-01-95 DavidMun Created
  11. //
  12. //----------------------------------------------------------------------------
  13. #include <headers.hxx>
  14. #pragma hdrstop
  15. #include "jt.hxx"
  16. //
  17. // Private globals
  18. //
  19. // s_awszTokens - the strings in this array must exactly match the order
  20. // of tokens in the TOKEN enum in parse.hxx.
  21. //
  22. WCHAR *s_awszTokens[] =
  23. {
  24. L"ABJ",
  25. L"ABQ",
  26. L"AJQ",
  27. L"CSAGE",
  28. L"CTJ",
  29. L"CTQ",
  30. L"DTJ",
  31. L"DTQ",
  32. L"EJ",
  33. L"EJQ",
  34. L"ENC",
  35. L"ENN",
  36. L"ENR",
  37. L"ENS",
  38. L"GC",
  39. L"GM",
  40. L"LJ",
  41. L"LQ",
  42. L"NSGA",
  43. L"NSSA",
  44. L"PJ",
  45. L"PQ",
  46. L"PRJ",
  47. L"PRQ",
  48. L"PSJ",
  49. L"PSQ",
  50. L"PTJ",
  51. L"PTQ",
  52. L"RJ",
  53. L"RQ",
  54. L"RMJQ",
  55. L"SAC",
  56. L"SAJ",
  57. L"SAQ",
  58. L"SC",
  59. L"SCE",
  60. L"SD",
  61. L"SE",
  62. L"ISJQ",
  63. L"SJ",
  64. L"SM",
  65. L"SNJ",
  66. L"SNQ",
  67. L"SQ",
  68. L"STJ",
  69. L"STQ",
  70. L"SVJ",
  71. L"SVQ",
  72. L"ApplicationName",
  73. L"Parameters",
  74. L"WorkingDirectory",
  75. L"Comment",
  76. L"Creator",
  77. L"Priority",
  78. L"MaxRunTime",
  79. L"TaskFlags",
  80. L"Interactive",
  81. L"DeleteWhenDone",
  82. L"Suspend",
  83. L"NetSchedule",
  84. L"DontStartIfOnBatteries",
  85. L"KillIfGoingOnBatteries",
  86. L"RunOnlyIfLoggedOn",
  87. L"Hidden",
  88. L"StartDate",
  89. L"EndDate",
  90. L"StartTime",
  91. L"MinutesDuration",
  92. L"HasEndDate",
  93. L"KillAtDuration",
  94. L"StartOnlyIfIdle",
  95. L"KillOnIdleEnd",
  96. L"RestartOnIdleResume",
  97. L"SystemRequired",
  98. L"Disabled",
  99. L"MinutesInterval",
  100. L"Type",
  101. L"TypeArguments",
  102. L"IDLE",
  103. L"NORMAL",
  104. L"HIGH",
  105. L"REALTIME",
  106. L"ONCE",
  107. L"DAILY",
  108. L"WEEKLY",
  109. L"MONTHLYDATE",
  110. L"MONTHLYDOW",
  111. L"YEARLYDATE",
  112. L"YEARLYDOW",
  113. L"ONIDLE",
  114. L"ATSTARTUP",
  115. L"ATLOGON",
  116. //
  117. // CAUTION: single-character nonalpha tokens need to be added to the
  118. // constant DELIMITERS.
  119. //
  120. L"TODAY",
  121. L"NOW",
  122. L"=",
  123. L"@",
  124. L"?",
  125. L":",
  126. L",",
  127. L"!"
  128. };
  129. const WCHAR DELIMITERS[] = L"=@?:,!;/- \t";
  130. #define NUM_TOKEN_STRINGS ARRAY_LEN(s_awszTokens)
  131. //
  132. // Forward references
  133. //
  134. WCHAR *SkipSpaces(WCHAR *pwsz);
  135. TOKEN _GetStringToken(WCHAR **ppwsz);
  136. TOKEN _GetNumberToken(WCHAR **ppwsz, WCHAR *pwszEnd);
  137. //+---------------------------------------------------------------------------
  138. //
  139. // Function: ProcessCommandLine
  140. //
  141. // Synopsis: Dispatch to the routine that completes parsing for and carries
  142. // out the next command specified on [pwszCommandLine].
  143. //
  144. // Arguments: [pwszCommandLine] - command line
  145. //
  146. // Returns: S_OK - command performed
  147. // E_* - error logged
  148. //
  149. // History: 04-21-95 DavidMun Created
  150. //
  151. //----------------------------------------------------------------------------
  152. HRESULT ProcessCommandLine(WCHAR *pwszCommandLine)
  153. {
  154. HRESULT hr;
  155. WCHAR *pwsz = pwszCommandLine;
  156. static CHAR s_szJob[] = "Job";
  157. static CHAR s_szQueue[] = "Queue";
  158. //
  159. // If a command starts with TKN_BANG instead of TKN_SWITCH, then its
  160. // return value will be ignored. Clear this flag in any case statement
  161. // that should cause a failure regardless of the use of TKN_BANG (see
  162. // TKN_INVALID, for example).
  163. //
  164. BOOL fIgnoreReturnValue = FALSE;
  165. while (*pwsz)
  166. {
  167. WCHAR *pwszLast = pwsz;
  168. TOKEN tkn;
  169. hr = S_OK;
  170. tkn = GetToken(&pwsz);
  171. if (tkn == TKN_EOL)
  172. {
  173. break;
  174. }
  175. if (tkn == TKN_SWITCH)
  176. {
  177. tkn = GetToken(&pwsz);
  178. }
  179. else if (tkn == TKN_BANG)
  180. {
  181. fIgnoreReturnValue = TRUE;
  182. tkn = GetToken(&pwsz);
  183. }
  184. else if (tkn != TKN_ATSIGN && tkn != TKN_QUESTION)
  185. {
  186. hr = E_FAIL;
  187. LogSyntaxError(tkn, L"'/', '-', '?', '!', or '@'");
  188. break;
  189. }
  190. switch (tkn)
  191. {
  192. #if 0
  193. case TKN_ABORTQUEUE:
  194. hr = Abort(&pwsz, FALSE);
  195. break;
  196. case TKN_LOADQUEUE:
  197. hr = Load(&pwsz, s_szQueue, FALSE);
  198. break;
  199. case TKN_CREATETRIGGERQUEUE:
  200. hr = CreateTrigger(&pwsz, FALSE);
  201. break;
  202. case TKN_DELETETRIGGERQUEUE:
  203. hr = DeleteTrigger(&pwsz, FALSE);
  204. break;
  205. case TKN_EDITJOBINQUEUE:
  206. hr = EditJob(&pwsz, FALSE);
  207. break;
  208. case TKN_PRINTQUEUE:
  209. hr = PrintAll(&pwsz, FALSE);
  210. break;
  211. case TKN_PRINTRUNTIMEQUEUE:
  212. hr = PrintRunTimes(&pwsz, FALSE);
  213. break;
  214. case TKN_PRINTTRIGGERQUEUE:
  215. hr = PrintTrigger(&pwsz, FALSE);
  216. break;
  217. case TKN_PRINTSTRINGQUEUE:
  218. hr = PrintTriggerStrings(&pwsz, s_szQueue, FALSE);
  219. break;
  220. case TKN_RUNQUEUE:
  221. hr = Run(FALSE);
  222. break;
  223. case TKN_SAVEQUEUE:
  224. hr = Save(&pwsz, s_szQueue, FALSE);
  225. break;
  226. case TKN_SETTRIGGERQUEUE:
  227. hr = SetTrigger(&pwsz, FALSE);
  228. break;
  229. #endif
  230. case TKN_ATSIGN:
  231. hr = DoAtSign(&pwsz);
  232. break;
  233. case TKN_ABORTJOB:
  234. hr = Abort(&pwsz, TRUE);
  235. break;
  236. #ifndef RES_KIT
  237. case TKN_CONVERTSAGETASKSTOJOBS:
  238. hr = ConvertSage();
  239. break;
  240. #endif // RES_KIT not defined
  241. case TKN_CREATETRIGGERJOB:
  242. hr = CreateTrigger(&pwsz, TRUE);
  243. break;
  244. case TKN_DELETETRIGGERJOB:
  245. hr = DeleteTrigger(&pwsz, TRUE);
  246. break;
  247. #ifndef RES_KIT
  248. case TKN_EDITJOB:
  249. hr = EditJob(&pwsz, TRUE);
  250. break;
  251. case TKN_ENUMCLONE:
  252. hr = EnumClone(&pwsz);
  253. break;
  254. case TKN_ENUMNEXT:
  255. hr = EnumNext(&pwsz);
  256. break;
  257. case TKN_ENUMRESET:
  258. hr = EnumReset(&pwsz);
  259. break;
  260. case TKN_ENUMSKIP:
  261. hr = EnumSkip(&pwsz);
  262. break;
  263. #endif // RES_KIT not defined
  264. case TKN_EOL:
  265. hr = E_FAIL;
  266. fIgnoreReturnValue = FALSE; // be sure we exit loop
  267. g_Log.Write(LOG_ERROR, "Unexpected end of line after switch character");
  268. break;
  269. case TKN_GETCREDENTIALS:
  270. hr = GetCredentials();
  271. break;
  272. case TKN_GETMACHINE:
  273. hr = SchedGetMachine();
  274. break;
  275. case TKN_INVALID:
  276. hr = E_FAIL;
  277. fIgnoreReturnValue = FALSE; // be sure we exit loop
  278. LogSyntaxError(tkn, L"valid token after switch");
  279. break;
  280. case TKN_LOADJOB:
  281. hr = Load(&pwsz, s_szJob, TRUE);
  282. break;
  283. case TKN_SETCREDENTIALS:
  284. hr = SetCredentials(&pwsz);
  285. break;
  286. case TKN_SETMACHINE:
  287. hr = SchedSetMachine(&pwsz);
  288. break;
  289. case TKN_PRINTJOB:
  290. hr = PrintAll(&pwsz, TRUE);
  291. break;
  292. case TKN_PRINTRUNTIMEJOB:
  293. hr = PrintRunTimes(&pwsz, TRUE);
  294. break;
  295. case TKN_PRINTTRIGGERJOB:
  296. hr = PrintTrigger(&pwsz, TRUE);
  297. break;
  298. case TKN_PRINTSTRINGJOB:
  299. hr = PrintTriggerStrings(&pwsz, s_szJob, TRUE);
  300. break;
  301. case TKN_NSGETACCOUNTINFO:
  302. hr = PrintNSAccountInfo();
  303. break;
  304. case TKN_NSSETACCOUNTINFO:
  305. hr = SetNSAccountInfo(&pwsz);
  306. break;
  307. case TKN_QUESTION:
  308. DoHelp(&pwsz);
  309. break;
  310. case TKN_RUNJOB:
  311. hr = Run(TRUE);
  312. break;
  313. case TKN_SAVEJOB:
  314. hr = Save(&pwsz, s_szJob, TRUE);
  315. break;
  316. case TKN_SCHEDADDJOB:
  317. hr = SchedAddJob(&pwsz);
  318. break;
  319. case TKN_SCHEDACTIVATE:
  320. hr = SchedActivate(&pwsz);
  321. break;
  322. #ifndef RES_KIT
  323. case TKN_SCHEDCREATEENUM:
  324. hr = SchedCreateEnum(&pwsz);
  325. break;
  326. #endif // RES_KIT not defined
  327. case TKN_SCHEDDELETE:
  328. hr = SchedDelete(&pwsz);
  329. break;
  330. case TKN_SCHEDENUM:
  331. hr = SchedEnum(&pwsz);
  332. break;
  333. #ifndef RES_KIT
  334. case TKN_SCHEDISJOBORQUEUE:
  335. hr = SchedIsJobOrQueue(&pwsz);
  336. break;
  337. #endif // RES_KIT not defined
  338. case TKN_SCHEDNEWJOB:
  339. hr = SchedNewJob(&pwsz);
  340. break;
  341. case TKN_SETJOB:
  342. hr = SetJob(&pwsz);
  343. break;
  344. case TKN_SETTRIGGERJOB:
  345. hr = SetTrigger(&pwsz, TRUE);
  346. break;
  347. default:
  348. hr = E_FAIL;
  349. fIgnoreReturnValue = FALSE; // be sure we exit loop
  350. LogSyntaxError(tkn, L"command");
  351. break;
  352. }
  353. if (fIgnoreReturnValue)
  354. {
  355. fIgnoreReturnValue = FALSE;
  356. }
  357. else if (FAILED(hr))
  358. {
  359. break;
  360. }
  361. }
  362. return hr;
  363. }
  364. //+---------------------------------------------------------------------------
  365. //
  366. // Function: LogSyntaxError
  367. //
  368. // Synopsis: Complain that we expected [pwszExpected] but found [tkn].
  369. //
  370. // Arguments: [tkn] - token that was found
  371. // [pwszExpected] - description of what was expected
  372. //
  373. // History: 04-21-95 DavidMun Created
  374. //
  375. //----------------------------------------------------------------------------
  376. VOID LogSyntaxError(TOKEN tkn, WCHAR *pwszExpected)
  377. {
  378. if (tkn == TKN_INVALID)
  379. {
  380. return;
  381. }
  382. if (tkn == TKN_EOL)
  383. {
  384. g_Log.Write(
  385. LOG_ERROR,
  386. "Expected %S but found end of line",
  387. pwszExpected);
  388. }
  389. else
  390. {
  391. g_Log.Write(
  392. LOG_ERROR,
  393. "Expected %S but found token '%S'",
  394. pwszExpected,
  395. GetTokenStringForLogging(tkn));
  396. }
  397. }
  398. //+---------------------------------------------------------------------------
  399. //
  400. // Function: GetToken
  401. //
  402. // Synopsis: Return a token representing the next characters in *[ppwsz],
  403. // and advance *[ppwsz] past the end of this token.
  404. //
  405. // Arguments: [ppwsz] - command line
  406. //
  407. // Returns: token describing characters found
  408. //
  409. // Modifies: *[ppwsz]
  410. //
  411. // History: 04-21-95 DavidMun Created
  412. //
  413. //----------------------------------------------------------------------------
  414. TOKEN GetToken(WCHAR **ppwsz)
  415. {
  416. ULONG i;
  417. *ppwsz = SkipSpaces(*ppwsz);
  418. if (!**ppwsz)
  419. {
  420. return TKN_EOL;
  421. }
  422. if (**ppwsz == L';')
  423. {
  424. *ppwsz += wcslen(*ppwsz);
  425. return TKN_EOL;
  426. }
  427. if (**ppwsz == L'/' || **ppwsz == L'-')
  428. {
  429. ++*ppwsz;
  430. return TKN_SWITCH;
  431. }
  432. if (**ppwsz == L'"')
  433. {
  434. return _GetStringToken(ppwsz);
  435. }
  436. if (iswdigit(**ppwsz))
  437. {
  438. WCHAR *pwszEnd;
  439. ULONG ulTemp;
  440. ulTemp = wcstoul(*ppwsz, &pwszEnd, 10);
  441. g_ulLastNumberToken = ulTemp;
  442. return _GetNumberToken(ppwsz, pwszEnd);
  443. }
  444. ULONG cchToken;
  445. //
  446. // We've already skipped leading whitespace, so length of token is number
  447. // of characters that are not whitespace or single-character tokens. If
  448. // wcscspn returns 0, then **ppwsz must be one of the single character
  449. // tokens.
  450. //
  451. cchToken = wcscspn(*ppwsz, DELIMITERS);
  452. if (!cchToken)
  453. {
  454. cchToken = 1;
  455. }
  456. //
  457. // Check the input against all the tokens
  458. //
  459. for (i = 0; i < NUM_TOKEN_STRINGS; i++)
  460. {
  461. if (!_wcsnicmp(*ppwsz, s_awszTokens[i], cchToken))
  462. {
  463. if (wcslen(s_awszTokens[i]) != cchToken)
  464. {
  465. continue;
  466. }
  467. *ppwsz += cchToken;
  468. return (TOKEN) i;
  469. }
  470. }
  471. //
  472. // Not a number or token. Return it as a string.
  473. //
  474. return _GetStringToken(ppwsz);
  475. }
  476. //+---------------------------------------------------------------------------
  477. //
  478. // Function: _GetStringToken
  479. //
  480. // Synopsis: Treat *[ppwsz] as the start of an optionally quote-enclosed
  481. // string (even if it's a digit or matches a predefined token).
  482. //
  483. // Arguments: [ppwsz] - command line
  484. //
  485. // Returns: TKN_STRING
  486. // TKN_INVALID - string too long
  487. //
  488. // Modifies: Moves *[ppwsz] past end of string; g_wszLastStringToken.
  489. //
  490. // History: 04-21-95 DavidMun Created
  491. // 01-08-96 DavidMun Allow empty strings
  492. //
  493. //----------------------------------------------------------------------------
  494. TOKEN _GetStringToken(WCHAR **ppwsz)
  495. {
  496. BOOL fFoundQuote = FALSE;
  497. *ppwsz = SkipSpaces(*ppwsz);
  498. if (!**ppwsz)
  499. {
  500. return TKN_EOL;
  501. }
  502. if (**ppwsz == L';')
  503. {
  504. *ppwsz += wcslen(*ppwsz);
  505. return TKN_EOL;
  506. }
  507. if (**ppwsz == L'"')
  508. {
  509. ++*ppwsz;
  510. fFoundQuote = TRUE;
  511. }
  512. //
  513. // It's not a recognized token, so consider it a string. If we found a
  514. // double-quote, copy everything till next double quote into
  515. // g_wszLastStringToken. If not, just copy till next whitespace char or
  516. // eol.
  517. //
  518. // Note that if !fFoundQuote *ppwsz != L'\0' or whitespace or else we
  519. // would've returned TKN_EOL already.
  520. //
  521. ULONG cchToCopy;
  522. if (fFoundQuote)
  523. {
  524. if (**ppwsz == L'"')
  525. {
  526. ++*ppwsz;
  527. g_wszLastStringToken[0] = L'\0';
  528. return TKN_STRING;
  529. }
  530. if (!**ppwsz)
  531. {
  532. g_Log.Write(LOG_ERROR, "Syntax: '\"' followed by end of line");
  533. return TKN_INVALID;
  534. }
  535. cchToCopy = wcscspn(*ppwsz, L"\"");
  536. if ((*ppwsz)[cchToCopy] != L'"')
  537. {
  538. *ppwsz += cchToCopy;
  539. g_Log.Write(LOG_ERROR, "Syntax: unterminated string");
  540. return TKN_INVALID;
  541. }
  542. }
  543. else
  544. {
  545. cchToCopy = wcscspn(*ppwsz, L", \t");
  546. }
  547. if (cchToCopy + 1 >= ARRAY_LEN(g_wszLastStringToken))
  548. {
  549. *ppwsz += cchToCopy + (fFoundQuote == TRUE);
  550. g_Log.Write(
  551. LOG_ERROR,
  552. "String token > %u characters",
  553. ARRAY_LEN(g_wszLastStringToken) - 1);
  554. return TKN_INVALID;
  555. }
  556. wcsncpy(g_wszLastStringToken, *ppwsz, cchToCopy);
  557. g_wszLastStringToken[cchToCopy] = L'\0';
  558. *ppwsz += cchToCopy + (fFoundQuote == TRUE);
  559. return TKN_STRING;
  560. }
  561. //+---------------------------------------------------------------------------
  562. //
  563. // Function: _GetNumberToken
  564. //
  565. // Synopsis: Copy the number starting at *[ppwsz] and ending at [pwszEnd]
  566. // into g_wszLastNumberToken.
  567. //
  568. // Arguments: [ppwsz] - command line containing number
  569. // [pwszEnd] - first non-numeric character in command line
  570. //
  571. // Returns: TKN_NUMBER - g_wszLastNumberToken modified
  572. // TKN_INVALID - string too long, g_wszLastNumberToken unchanged
  573. //
  574. // Modifies: *[ppwsz] is always moved to [pwszEnd].
  575. // g_wszLastNumberToken gets copy of number, but only if
  576. // return value is TKN_NUMBER.
  577. //
  578. // History: 04-21-95 DavidMun Created
  579. //
  580. //----------------------------------------------------------------------------
  581. TOKEN _GetNumberToken(WCHAR **ppwsz, WCHAR *pwszEnd)
  582. {
  583. ULONG cchToCopy;
  584. cchToCopy = pwszEnd - *ppwsz;
  585. if (cchToCopy >= ARRAY_LEN(g_wszLastNumberToken))
  586. {
  587. *ppwsz = pwszEnd;
  588. g_Log.Write(
  589. LOG_ERROR,
  590. "Number token > %u characters",
  591. ARRAY_LEN(g_wszLastNumberToken) - 1);
  592. return TKN_INVALID;
  593. }
  594. wcsncpy(g_wszLastNumberToken, *ppwsz, cchToCopy);
  595. g_wszLastNumberToken[cchToCopy] = L'\0';
  596. *ppwsz = pwszEnd;
  597. return TKN_NUMBER;
  598. }
  599. //+---------------------------------------------------------------------------
  600. //
  601. // Function: PeekToken
  602. //
  603. // Synopsis: Same as GetToken(), but *[ppwsz] is unmodified.
  604. //
  605. // Arguments: [ppwsz] - command line
  606. //
  607. // Returns: token describing characters at *[ppwsz]
  608. //
  609. // Modifies: May modify g_*LastNumberToken, g_wszLastStringToken
  610. //
  611. // History: 04-21-95 DavidMun Created
  612. //
  613. //----------------------------------------------------------------------------
  614. TOKEN PeekToken(WCHAR **ppwsz)
  615. {
  616. WCHAR *pwszSavedPosition = *ppwsz;
  617. TOKEN tkn;
  618. tkn = GetToken(ppwsz);
  619. *ppwsz = pwszSavedPosition;
  620. return tkn;
  621. }
  622. //+---------------------------------------------------------------------------
  623. //
  624. // Function: Expect
  625. //
  626. // Synopsis: Get a token and log a syntax error if it isn't [tknExpected]
  627. //
  628. // Arguments: [tknExpected] - token we should get
  629. // [ppwsz] - command line
  630. // [wszExpected] - description for logging if next token isn't
  631. // [tknExpected].
  632. //
  633. // Returns: S_OK - got expected token
  634. // E_FAIL - got different token
  635. //
  636. // Modifies: *[ppwsz]
  637. //
  638. // History: 04-21-95 DavidMun Created
  639. //
  640. //----------------------------------------------------------------------------
  641. HRESULT Expect(TOKEN tknExpected, WCHAR **ppwsz, WCHAR *wszExpected)
  642. {
  643. HRESULT hr = S_OK;
  644. TOKEN tkn;
  645. if (tknExpected == TKN_STRING)
  646. {
  647. tkn = _GetStringToken(ppwsz);
  648. }
  649. else
  650. {
  651. tkn = GetToken(ppwsz);
  652. }
  653. if (tkn != tknExpected)
  654. {
  655. hr = E_FAIL;
  656. LogSyntaxError(tkn, wszExpected);
  657. }
  658. return hr;
  659. }
  660. //+---------------------------------------------------------------------------
  661. //
  662. // Function: GetFilename
  663. //
  664. // Synopsis: Expect a string token at *[ppwsz] and convert it to a full
  665. // path in g_wszLastStringToken.
  666. //
  667. // Arguments: [ppwsz] - command line
  668. // [wszExpected] - for logging if next token isn't string
  669. //
  670. // Returns: S_OK - [wszExpected] valid
  671. //
  672. // Modifies: *[ppwsz], g_wszLastStringToken
  673. //
  674. // History: 04-21-95 DavidMun Created
  675. //
  676. //----------------------------------------------------------------------------
  677. HRESULT GetFilename(WCHAR **ppwsz, WCHAR *wszExpected)
  678. {
  679. HRESULT hr = S_OK;
  680. WCHAR wszFullPath[MAX_PATH+1];
  681. ULONG cchRequired;
  682. TOKEN tkn;
  683. do
  684. {
  685. tkn = _GetStringToken(ppwsz);
  686. if (tkn != TKN_STRING)
  687. {
  688. hr = E_FAIL;
  689. g_Log.Write(
  690. LOG_FAIL,
  691. "Expected %S but got invalid or missing string",
  692. wszExpected);
  693. break;
  694. }
  695. #ifdef UNICODE
  696. cchRequired = GetFullPathName(
  697. g_wszLastStringToken,
  698. MAX_PATH + 1,
  699. wszFullPath,
  700. NULL);
  701. #else
  702. CHAR szToken[MAX_PATH + 1];
  703. CHAR szFullPath[MAX_PATH + 1];
  704. wcstombs(szToken, g_wszLastStringToken, MAX_PATH + 1);
  705. cchRequired = GetFullPathName(
  706. szToken,
  707. MAX_PATH + 1,
  708. szFullPath,
  709. NULL);
  710. #endif
  711. if (!cchRequired)
  712. {
  713. hr = E_FAIL;
  714. g_Log.Write(
  715. LOG_ERROR,
  716. "GetFullPathName(%S) %u",
  717. g_wszLastStringToken,
  718. GetLastError());
  719. break;
  720. }
  721. if (cchRequired > MAX_PATH)
  722. {
  723. hr = E_FAIL;
  724. g_Log.Write(LOG_ERROR, "Full path > MAX_PATH chars");
  725. break;
  726. }
  727. #ifdef UNICODE
  728. wcscpy(g_wszLastStringToken, wszFullPath);
  729. #else
  730. mbstowcs(g_wszLastStringToken, szFullPath, MAX_PATH + 1);
  731. #endif
  732. } while (0);
  733. return hr;
  734. }
  735. //+---------------------------------------------------------------------------
  736. //
  737. // Function: ParseDate
  738. //
  739. // Synopsis: Fill [pwMonth], [pwDay], and [pwYear] with numeric values
  740. // taken from the command line in [ppwsz].
  741. //
  742. // Arguments: [ppwsz] - Command line
  743. // [pwMonth] - filled with first value
  744. // [pwDay] - filled with second value
  745. // [pwYear] - filled with third value
  746. //
  747. // Returns: S_OK - [pwMonth], [pwDay], and [pwYear] contain numbers
  748. // (but do not necessarily constitute a valid date)
  749. // E_* - error logged
  750. //
  751. // Modifies: All args.
  752. //
  753. // History: 01-04-96 DavidMun Created
  754. //
  755. // Notes: Dates can be of the form:
  756. //
  757. // n/n/n
  758. // n-n-n
  759. //
  760. // or any other single character nonalpha token may be used to
  761. // separate the numbers. If spaces appear on both sides of
  762. // the tokens separating the numbers, then the tokens can be
  763. // of any type at all.
  764. //
  765. //----------------------------------------------------------------------------
  766. HRESULT ParseDate(WCHAR **ppwsz, WORD *pwMonth, WORD *pwDay, WORD *pwYear)
  767. {
  768. HRESULT hr = S_OK;
  769. TOKEN tkn;
  770. SYSTEMTIME stNow;
  771. do
  772. {
  773. tkn = PeekToken(ppwsz);
  774. if (tkn == TKN_TODAY)
  775. {
  776. GetToken(ppwsz);
  777. GetLocalTime(&stNow);
  778. *pwMonth = stNow.wMonth;
  779. *pwDay = stNow.wDay;
  780. *pwYear = stNow.wYear;
  781. break;
  782. }
  783. hr = Expect(TKN_NUMBER, ppwsz, L"month value");
  784. BREAK_ON_FAILURE(hr);
  785. *pwMonth = (WORD) g_ulLastNumberToken;
  786. GetToken(ppwsz); // eat whatever separator there is
  787. hr = Expect(TKN_NUMBER, ppwsz, L"day value");
  788. BREAK_ON_FAILURE(hr);
  789. *pwDay = (WORD) g_ulLastNumberToken;
  790. GetToken(ppwsz); // eat whatever separator there is
  791. hr = Expect(TKN_NUMBER, ppwsz, L"year value");
  792. BREAK_ON_FAILURE(hr);
  793. *pwYear = (WORD) g_ulLastNumberToken;
  794. if (*pwYear < 100)
  795. {
  796. *pwYear += 1900;
  797. }
  798. }
  799. while (0);
  800. return hr;
  801. }
  802. //+---------------------------------------------------------------------------
  803. //
  804. // Function: ParseTime
  805. //
  806. // Synopsis: Fill [pwHour] and [pwMinute] with numeric values taken from
  807. // the command line in [ppwsz].
  808. //
  809. // Arguments: [ppwsz] - command line
  810. // [pwHour] - filled with first number
  811. // [pwMinute] - filled with second number
  812. //
  813. // Returns: S_OK - [pwHour] and [pwMinute] contain numbers (but do not
  814. // necessarily constitute a valid time).
  815. // E_* - error logged
  816. //
  817. // Modifies: All args.
  818. //
  819. // History: 01-04-96 DavidMun Created
  820. //
  821. // Notes: See ParseDate for rules about delimiters.
  822. //
  823. //----------------------------------------------------------------------------
  824. HRESULT ParseTime(WCHAR **ppwsz, WORD *pwHour, WORD *pwMinute)
  825. {
  826. HRESULT hr = S_OK;
  827. TOKEN tkn;
  828. SYSTEMTIME stNow;
  829. do
  830. {
  831. tkn = PeekToken(ppwsz);
  832. if (tkn == TKN_NOW)
  833. {
  834. GetToken(ppwsz);
  835. GetLocalTime(&stNow);
  836. //
  837. // Add some time to the current time so that a trigger with
  838. // NOW start time is far enough in the future to get run
  839. //
  840. AddSeconds(&stNow, TIME_NOW_INCREMENT);
  841. *pwHour = stNow.wHour;
  842. *pwMinute = stNow.wMinute;
  843. break;
  844. }
  845. hr = Expect(TKN_NUMBER, ppwsz, L"hour value");
  846. BREAK_ON_FAILURE(hr);
  847. *pwHour = (WORD) g_ulLastNumberToken;
  848. GetToken(ppwsz); // eat whatever separator there is
  849. hr = Expect(TKN_NUMBER, ppwsz, L"minute value");
  850. *pwMinute = (WORD) g_ulLastNumberToken;
  851. }
  852. while (0);
  853. return hr;
  854. }
  855. //+---------------------------------------------------------------------------
  856. //
  857. // Function: ParseDaysOfWeek
  858. //
  859. // Synopsis: Fill [pwDaysOfTheWeek] with the days of the week specified
  860. // by the next string token in the command line.
  861. //
  862. // Arguments: [ppwsz] - command line
  863. // [pwDaysOfTheWeek] - filled with JOB_*DAY bits
  864. //
  865. // Returns: S_OK - *[pwDaysOfTheWeek] valid
  866. // E_* - invalid string token
  867. //
  868. // Modifies: All args.
  869. //
  870. // History: 01-04-96 DavidMun Created
  871. //
  872. //----------------------------------------------------------------------------
  873. HRESULT ParseDaysOfWeek(WCHAR **ppwsz, WORD *pwDaysOfTheWeek)
  874. {
  875. HRESULT hr = S_OK;
  876. TOKEN tkn;
  877. WCHAR *pwszDay;
  878. tkn = _GetStringToken(ppwsz);
  879. if (tkn != TKN_STRING)
  880. {
  881. hr = E_FAIL;
  882. }
  883. *pwDaysOfTheWeek = 0;
  884. for (pwszDay = g_wszLastStringToken; SUCCEEDED(hr) && *pwszDay; pwszDay++)
  885. {
  886. switch (towupper(*pwszDay))
  887. {
  888. case L'U':
  889. *pwDaysOfTheWeek |= TASK_SUNDAY;
  890. break;
  891. case L'M':
  892. *pwDaysOfTheWeek |= TASK_MONDAY;
  893. break;
  894. case L'T':
  895. *pwDaysOfTheWeek |= TASK_TUESDAY;
  896. break;
  897. case L'W':
  898. *pwDaysOfTheWeek |= TASK_WEDNESDAY;
  899. break;
  900. case L'R':
  901. *pwDaysOfTheWeek |= TASK_THURSDAY;
  902. break;
  903. case L'F':
  904. *pwDaysOfTheWeek |= TASK_FRIDAY;
  905. break;
  906. case L'A':
  907. *pwDaysOfTheWeek |= TASK_SATURDAY;
  908. break;
  909. case L'.':
  910. // ignore this, since we display day as . when its bit is off
  911. break;
  912. default:
  913. hr = E_FAIL;
  914. g_Log.Write(
  915. LOG_FAIL,
  916. "Expected day of week character 'UMTWRFA' but got '%wc'",
  917. *pwszDay);
  918. break;
  919. }
  920. }
  921. return hr;
  922. }
  923. //+---------------------------------------------------------------------------
  924. //
  925. // Function: ParseDaysOfMonth
  926. //
  927. // Synopsis: Translate a comma separated list of day numbers into a bit
  928. // field in [pdwDays].
  929. //
  930. // Arguments: [ppwsz] - command line
  931. // [pdwDays] - least significant bit represents day 1
  932. //
  933. // Returns: S_OK
  934. // E_FAIL - syntax or value error
  935. //
  936. // History: 03-07-96 DavidMun Created
  937. //
  938. // Notes: Day list may contain dashes to indicate day ranges. For
  939. // example, "1,3-5,7,10-12" is equivalent to
  940. // "1,3,4,5,7,10,11,12". Expressions like "1-3-5" are allowed
  941. // (it's equivalent to "1-5"). Ranges with the first bit
  942. // less than the second are automatically swapped, for example
  943. // "4-2" is treated as "2-4". So even "4-2-1" would be
  944. // interpreted as "1-4".
  945. //
  946. // Day numbers up to 32 are allowed for the purposes of
  947. // exercising the error checking code in the job scheduler
  948. // interfaces.
  949. //
  950. // CAUTION: this function will eat a trailing comma OR SWITCH
  951. // character! It is therefore a requirement that a DaysOfMonth
  952. // list be followed by some other nonswitch string to avoid
  953. // having it eat the switch character that starts the next
  954. // command.
  955. //
  956. //----------------------------------------------------------------------------
  957. HRESULT ParseDaysOfMonth(WCHAR **ppwsz, DWORD *pdwDays)
  958. {
  959. HRESULT hr = S_OK;
  960. TOKEN tkn;
  961. ULONG ulLastDay = 0;
  962. BOOL fGotDash = FALSE;
  963. ULONG i;
  964. *pdwDays = 0;
  965. do
  966. {
  967. tkn = PeekToken(ppwsz);
  968. //
  969. // A string, EOL, or error token means we've gone past the end of the
  970. // list of days and can quit. (The latter means we're about to
  971. // abort!)
  972. //
  973. if (tkn == TKN_STRING || tkn == TKN_EOL || tkn == TKN_INVALID)
  974. {
  975. break;
  976. }
  977. //
  978. // Eat commas, but don't allow "-,". Also, getting a comma resets the
  979. // last day to zero, which allows TKN_SWITCH check to complain about
  980. // "1,-2"
  981. //
  982. if (tkn == TKN_COMMA)
  983. {
  984. ulLastDay = 0;
  985. if (fGotDash)
  986. {
  987. hr = E_FAIL;
  988. g_Log.Write(
  989. LOG_FAIL,
  990. "Expected a number following the dash but got ',' in day of month list");
  991. break;
  992. }
  993. GetToken(ppwsz);
  994. continue;
  995. }
  996. //
  997. // A dash is valid only if the preceding token was a number
  998. //
  999. if (tkn == TKN_SWITCH)
  1000. {
  1001. if (fGotDash)
  1002. {
  1003. hr = E_FAIL;
  1004. g_Log.Write(
  1005. LOG_FAIL,
  1006. "Didn't expect two switch characters in a row in day of month list");
  1007. break;
  1008. }
  1009. if (ulLastDay == 0)
  1010. {
  1011. hr = E_FAIL;
  1012. g_Log.Write(
  1013. LOG_FAIL,
  1014. "Expected a number preceding switch character in day of month list");
  1015. break;
  1016. }
  1017. //
  1018. // It's ok to have a dash. Note that we got one, consume the
  1019. // token, and look at the next one.
  1020. //
  1021. fGotDash = TRUE;
  1022. GetToken(ppwsz);
  1023. continue;
  1024. }
  1025. //
  1026. // At this point, anything other than a number means we're done. If
  1027. // there's a hanging switch, though, that's an error.
  1028. if (tkn != TKN_NUMBER)
  1029. {
  1030. if (fGotDash)
  1031. {
  1032. hr = E_FAIL;
  1033. LogSyntaxError(tkn, L"a number following the switch character");
  1034. }
  1035. break;
  1036. }
  1037. //
  1038. // The next token is TKN_NUMBER, so consume it. Also make sure it's
  1039. // >= 1 and <= 32. Yes, 32, because we want to allow specifying an
  1040. // invalid bit pattern to the Job Scheduler code.
  1041. //
  1042. // If fGotDash, this number is the end of a range that started with
  1043. // ulLastDay (which has already been verified).
  1044. //
  1045. // Otherwise it's just a single day bit to turn on.
  1046. //
  1047. GetToken(ppwsz);
  1048. if (g_ulLastNumberToken < 1 ||
  1049. #ifndef RES_KIT
  1050. g_ulLastNumberToken > 32)
  1051. #else
  1052. g_ulLastNumberToken > 31)
  1053. #endif
  1054. {
  1055. hr = E_FAIL;
  1056. g_Log.Write(
  1057. LOG_FAIL,
  1058. #ifndef RES_KIT
  1059. "Expected a day number from 1 to 32 (yes, thirty-two) but got %u",
  1060. #else
  1061. "Day numbers run from 1 to 31, but %u was passed in, instead.",
  1062. #endif
  1063. g_ulLastNumberToken);
  1064. break;
  1065. }
  1066. if (fGotDash)
  1067. {
  1068. fGotDash = FALSE;
  1069. // allow backwards ranges
  1070. if (ulLastDay > g_ulLastNumberToken)
  1071. {
  1072. ULONG ulTemp = ulLastDay;
  1073. ulLastDay = g_ulLastNumberToken;
  1074. g_ulLastNumberToken = ulTemp;
  1075. }
  1076. //
  1077. // Turn on all the bits in the range. Note that the previous
  1078. // iteration already saw ulLastDay and turned it on, so we can
  1079. // skip that bit.
  1080. //
  1081. for (i = ulLastDay + 1; i <= g_ulLastNumberToken; i++)
  1082. {
  1083. *pdwDays |= 1 << (i - 1);
  1084. }
  1085. }
  1086. else
  1087. {
  1088. *pdwDays |= 1 << (g_ulLastNumberToken - 1);
  1089. }
  1090. ulLastDay = g_ulLastNumberToken;
  1091. } while (TRUE);
  1092. return hr;
  1093. }
  1094. //+---------------------------------------------------------------------------
  1095. //
  1096. // Function: ParseMonths
  1097. //
  1098. // Synopsis: Translate MONTH_ABBREV_LEN character long month
  1099. // abbreviations in [ppwsz] into month bits in [pwMonths].
  1100. //
  1101. // Arguments: [ppwsz] - command line
  1102. // [pwMonths] - filled with month bits
  1103. //
  1104. // Returns: S_OK
  1105. // E_FAIL
  1106. //
  1107. // History: 03-07-96 DavidMun Created
  1108. //
  1109. //----------------------------------------------------------------------------
  1110. HRESULT ParseMonths(WCHAR **ppwsz, WORD *pwMonths)
  1111. {
  1112. HRESULT hr = S_OK;
  1113. TOKEN tkn;
  1114. WCHAR *pwszMonth;
  1115. ULONG i;
  1116. *pwMonths = 0;
  1117. tkn = _GetStringToken(ppwsz);
  1118. if (tkn != TKN_STRING)
  1119. {
  1120. hr = E_FAIL;
  1121. }
  1122. else if (wcslen(g_wszLastStringToken) % MONTH_ABBREV_LEN)
  1123. {
  1124. hr = E_FAIL;
  1125. g_Log.Write(
  1126. LOG_FAIL,
  1127. "Month string must consist of %u letter abbreviations",
  1128. MONTH_ABBREV_LEN);
  1129. }
  1130. for (pwszMonth = g_wszLastStringToken;
  1131. SUCCEEDED(hr) && *pwszMonth;
  1132. pwszMonth += 3)
  1133. {
  1134. for (i = 0; i < 12; i++)
  1135. {
  1136. if (!_wcsnicmp(pwszMonth, g_awszMonthAbbrev[i], MONTH_ABBREV_LEN))
  1137. {
  1138. switch (i)
  1139. {
  1140. case 0:
  1141. *pwMonths |= TASK_JANUARY;
  1142. break;
  1143. case 1:
  1144. *pwMonths |= TASK_FEBRUARY;
  1145. break;
  1146. case 2:
  1147. *pwMonths |= TASK_MARCH;
  1148. break;
  1149. case 3:
  1150. *pwMonths |= TASK_APRIL;
  1151. break;
  1152. case 4:
  1153. *pwMonths |= TASK_MAY;
  1154. break;
  1155. case 5:
  1156. *pwMonths |= TASK_JUNE;
  1157. break;
  1158. case 6:
  1159. *pwMonths |= TASK_JULY;
  1160. break;
  1161. case 7:
  1162. *pwMonths |= TASK_AUGUST;
  1163. break;
  1164. case 8:
  1165. *pwMonths |= TASK_SEPTEMBER;
  1166. break;
  1167. case 9:
  1168. *pwMonths |= TASK_OCTOBER;
  1169. break;
  1170. case 10:
  1171. *pwMonths |= TASK_NOVEMBER;
  1172. break;
  1173. case 11:
  1174. *pwMonths |= TASK_DECEMBER;
  1175. break;
  1176. }
  1177. //
  1178. // Since we've found the month abbreviation, break out of the
  1179. // inner loop that's comparing abbreviations against the
  1180. // user's string.
  1181. //
  1182. break;
  1183. }
  1184. }
  1185. //
  1186. // If the inner loop found the next MONTH_ABBREV_LEN chars of the
  1187. // user's string in the g_awszMonthAbbrev array, then it executed the
  1188. // break and i would be less than 12.
  1189. //
  1190. if (i >= 12)
  1191. {
  1192. hr = E_FAIL;
  1193. g_Log.Write(
  1194. LOG_FAIL,
  1195. "Expected %u character month abbreviation at '%S",
  1196. MONTH_ABBREV_LEN,
  1197. pwszMonth);
  1198. }
  1199. }
  1200. return hr;
  1201. }
  1202. //+---------------------------------------------------------------------------
  1203. //
  1204. // Function: GetTokenStringForLogging
  1205. //
  1206. // Synopsis: Return a human-readable string describing [tkn].
  1207. //
  1208. // Arguments: [tkn] - token to describe.
  1209. //
  1210. // History: 04-21-95 DavidMun Created
  1211. //
  1212. //----------------------------------------------------------------------------
  1213. WCHAR *GetTokenStringForLogging(TOKEN tkn)
  1214. {
  1215. switch (tkn)
  1216. {
  1217. case TKN_INVALID:
  1218. return L"an invalid token";
  1219. case TKN_EOL:
  1220. return L"end of line";
  1221. case TKN_SWITCH:
  1222. return L"switch character";
  1223. case TKN_STRING:
  1224. return g_wszLastStringToken;
  1225. case TKN_NUMBER:
  1226. return g_wszLastNumberToken;
  1227. default:
  1228. if (tkn < NUM_TOKEN_STRINGS)
  1229. {
  1230. return s_awszTokens[tkn];
  1231. }
  1232. return L"an unknown token value";
  1233. }
  1234. }
  1235. //+---------------------------------------------------------------------------
  1236. //
  1237. // Function: SkipSpaces
  1238. //
  1239. // Synopsis: Return [pwsz] advanced to end of string, end of line, or
  1240. // next non-space character.
  1241. //
  1242. // Arguments: [pwsz] - string to skip spaces in
  1243. //
  1244. // Returns: [pwsz] + n
  1245. //
  1246. // History: 04-21-95 DavidMun Created
  1247. //
  1248. //----------------------------------------------------------------------------
  1249. WCHAR *SkipSpaces(WCHAR *pwsz)
  1250. {
  1251. while (*pwsz && *pwsz == L' ' || *pwsz == L'\t')
  1252. {
  1253. pwsz++;
  1254. }
  1255. return pwsz;
  1256. }