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.

1279 lines
35 KiB

  1. //+------------------------------------------------------------------
  2. //
  3. // File: cmdline.cxx
  4. //
  5. // Contents: implementation of CCmdline class
  6. //
  7. // Synopsis: CCmdline encapsulates the actual command line, and
  8. // parses it looking for expected switches.
  9. //
  10. //
  11. // Classes: CCmdline
  12. //
  13. // Functions:
  14. //
  15. // History: 12/23/91 Lizch Created
  16. // 04/17/92 Lizch Converted to NLS_STR
  17. // 09/09/92 Lizch Changed SUCCESS to NO_ERROR
  18. // 09/18/92 Lizch Precompile headers
  19. // 11/14/92 DwightKr Updates for new version of NLS_STR
  20. //
  21. //-------------------------------------------------------------------
  22. #include <comtpch.hxx>
  23. #pragma hdrstop
  24. #include <cmdlinew.hxx> // public cmdlinew stuff
  25. #include "_clw.hxx" // private cmdlinew stuff
  26. #include <ctype.h> // is functions
  27. static void DelimitWithNulls(LPNSTR pnszArgline,
  28. LPNSTR pnszDest,
  29. UINT *puiArgc);
  30. //+------------------------------------------------------------------
  31. //
  32. // Member: CCmdline::CCmdline(int, char **, BOOL = FALSE)
  33. //
  34. // Synopsis: Copies argv and argc, and initialises defaults.
  35. //
  36. // Effects: Makes a copy of argv and argc. Argv (minus argv[0]) is
  37. // copied into an array of CCmdlineArg objects.
  38. // Argv[0] is copied into a program name data member.
  39. //
  40. // Arguments: [argc] - count of arguments.
  41. // [argv] - arguments.
  42. // [fInternalUsage] - Use internal Usage.
  43. //
  44. // Returns: None
  45. //
  46. // Modifies: aeLastError
  47. //
  48. // History: 12/23/91 Lizch Created.
  49. // 04/17/92 Lizch Converted to NLS_STR
  50. // 07/28/92 Davey Intialize _pfExtraUsage, _usIndent,
  51. // _usDisplayWidth.
  52. // Modified call to SetProgName,
  53. // Added fInternalUsage parameter.
  54. // 10/11/94 XimingZ Initialize _pnszProgName.
  55. // Call SetError before the first error return.
  56. // Set _apArgs to NULL as it is deleted in
  57. // case of memory allocatio failure.
  58. //
  59. //-------------------------------------------------------------------
  60. CCmdline::CCmdline(int argc, char *argv[], BOOL fInternalUsage) :
  61. _apArgs(NULL),
  62. _uiNumArgs(0),
  63. _pfExtraUsage(NULL),
  64. _usIndent(CMD_INDENT),
  65. _usDisplayWidth(CMD_DISPLAY_WIDTH),
  66. _usSwitchIndent(CMD_SWITCH_INDENT),
  67. _pbcInternalUsage(NULL),
  68. _pnszProgName(NULL)
  69. {
  70. INT iRC;
  71. PNSTR pnszBuf;
  72. SetError(CMDLINE_NO_ERROR);
  73. if (fInternalUsage)
  74. {
  75. iRC = SetInternalUsage();
  76. if (CMDLINE_NO_ERROR != iRC)
  77. return;
  78. }
  79. pnszBuf = new NCHAR[strlen(argv[0])+1];
  80. if (NULL == pnszBuf)
  81. {
  82. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  83. return;
  84. }
  85. else
  86. {
  87. #ifdef CTUNICODE
  88. mbstowcs(pnszBuf, argv[0], strlen(argv[0])+1);
  89. #else
  90. strcpy(pnszBuf, argv[0]);
  91. #endif
  92. }
  93. iRC = SetProgName(pnszBuf);
  94. delete pnszBuf;
  95. if (iRC != CMDLINE_NO_ERROR)
  96. {
  97. SetError(iRC);
  98. return;
  99. }
  100. // Don't include argv[0] in the argument count.
  101. _uiNumArgs = argc - 1;
  102. // Now set up an array of CCmdlineArg objects, each of which
  103. // encapsulates one argv element
  104. //
  105. _apArgs = new (CCmdlineArg *[_uiNumArgs]);
  106. if (_apArgs == NULL)
  107. {
  108. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  109. return;
  110. }
  111. for (INT i=0; i< (INT)_uiNumArgs; i++)
  112. {
  113. // Convert argv[i] to unicode
  114. pnszBuf = new NCHAR[strlen(argv[i+1])+1];
  115. if (NULL == pnszBuf)
  116. {
  117. _apArgs[i] = NULL;
  118. }
  119. else
  120. {
  121. // We check for errors in the next block
  122. #ifdef CTUNICODE
  123. mbstowcs(pnszBuf, argv[i+1], strlen(argv[i+1])+1);
  124. #else
  125. strcpy(pnszBuf, argv[i+1]);
  126. #endif
  127. _apArgs[i] = new CCmdlineArg(pnszBuf);
  128. delete pnszBuf;
  129. }
  130. // If an allocation failed, rewind through those we have
  131. // allocated
  132. //
  133. if ((_apArgs[i] == NULL) || (_apArgs[i]->QueryError() != CMDLINE_NO_ERROR))
  134. {
  135. for (; i>=0; i--)
  136. {
  137. delete _apArgs[i];
  138. }
  139. delete [] _apArgs;
  140. _apArgs = NULL;
  141. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  142. break;
  143. }
  144. }
  145. }
  146. //+------------------------------------------------------------------
  147. //
  148. // Member: CCmdline::CCmdline(BOOL fInternalUsage = FALSE)
  149. //
  150. // Synopsis: Takes arguments from GetCommandLine(). To be used
  151. // with Windows programs whose startpoint is WinMain()
  152. //
  153. // Effects: Takes the arguments out of GetCommandLine(), and
  154. // copies them into an array of CCmdlineArg objects.
  155. //
  156. // Arguments: [fInternalUsage] Use internal usage
  157. //
  158. // Returns: None
  159. //
  160. // Modifies: Not much really
  161. //
  162. // History: 2/6/1995 jesussp Created
  163. //
  164. // Note: For 16 bit, this will set the error to
  165. // CMDLINE_ERROR_USAGE_FOUND, as the GetCommandLine is
  166. // not support in WIN16.
  167. //
  168. //-------------------------------------------------------------------
  169. CCmdline::CCmdline(BOOL fInternalUsage) :
  170. _apArgs(NULL),
  171. _uiNumArgs(0),
  172. _pfExtraUsage(NULL),
  173. _usIndent(CMD_INDENT),
  174. _usDisplayWidth(CMD_DISPLAY_WIDTH),
  175. _usSwitchIndent(CMD_SWITCH_INDENT),
  176. _pbcInternalUsage(NULL),
  177. _pnszProgName(NULL)
  178. {
  179. #if !defined (_WIN32)
  180. SetError(CMDLINE_ERROR_USAGE_FOUND);
  181. return;
  182. #else
  183. INT iRC;
  184. NCHAR *pnszArgs,
  185. *pnszProg,
  186. *pnszDst;
  187. NCHAR *pncArg;
  188. INT iError;
  189. SetError(CMDLINE_NO_ERROR);
  190. if (fInternalUsage)
  191. {
  192. iRC = SetInternalUsage();
  193. if (CMDLINE_NO_ERROR != iRC)
  194. return;
  195. }
  196. // Obtain a copy of the command line
  197. #if defined(CTUNICODE)
  198. pnszProg = new NCHAR[1+wcslen(GetCommandLineW())];
  199. #else
  200. pnszProg = new NCHAR[1+strlen(GetCommandLineA())];
  201. #endif
  202. if (NULL == pnszProg)
  203. {
  204. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  205. return;
  206. }
  207. #if defined(CTUNICODE)
  208. wcscpy(pnszProg, GetCommandLineW());
  209. #else
  210. strcpy(pnszProg, GetCommandLineA());
  211. #endif
  212. // Skip through the command line looking for arguments
  213. pnszArgs = pnszProg;
  214. while (nchClSpace != *pnszArgs && nchClNull != *pnszArgs)
  215. {
  216. ++pnszArgs;
  217. }
  218. if (nchClSpace == *pnszArgs)
  219. {
  220. *pnszArgs++ = nchClNull;
  221. }
  222. // Now pnszProg points to a null-terminated string containing the
  223. // program name, and pnszArgs points to a null-terminated string
  224. // containing the program arguments...
  225. SetProgName(pnszProg);
  226. // Allocate memory for a buffer containing the different arguments
  227. // separated by nulls
  228. INT cBufSize = 1+_ncslen(pnszArgs);
  229. //
  230. // to accomodate for an extra null character
  231. //
  232. pnszDst = new NCHAR[1+cBufSize];
  233. if (NULL == pnszDst)
  234. {
  235. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  236. delete pnszProg;
  237. return;
  238. }
  239. // Parse the argument line and get a null-terminated string
  240. DelimitWithNulls(pnszArgs, pnszDst, &_uiNumArgs);
  241. // Set up an array of CCmdlineArg objects, each of which
  242. // encapsulates one argument
  243. //
  244. _apArgs = new (CCmdlineArg *[_uiNumArgs]);
  245. if (_apArgs == NULL)
  246. {
  247. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  248. return;
  249. }
  250. pncArg = pnszDst;
  251. for (INT i=0; i< (INT)_uiNumArgs; i++)
  252. {
  253. // Copy argument string
  254. NCHAR *pnszBuf = new NCHAR[_ncslen(pncArg)+1];
  255. if (NULL == pnszBuf)
  256. {
  257. _apArgs[i] = NULL;
  258. }
  259. else
  260. // We check for errors until the next block
  261. {
  262. _ncscpy(pnszBuf, pncArg);
  263. _apArgs[i] = new CCmdlineArg(pnszBuf);
  264. delete pnszBuf;
  265. }
  266. // If an allocation failed, rewind through those we have
  267. // allocated
  268. if ((_apArgs[i] == NULL) || (_apArgs[i]->QueryError() != CMDLINE_NO_ERROR))
  269. {
  270. for (; i>=0; i--)
  271. {
  272. delete _apArgs[i];
  273. }
  274. delete [] _apArgs;
  275. _apArgs = NULL;
  276. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  277. break;
  278. }
  279. // Skip to the next argument (past a null byte)
  280. while (*pncArg++)
  281. {
  282. ;
  283. }
  284. }
  285. // Release allocated memory
  286. delete pnszProg;
  287. delete pnszDst;
  288. #endif // if !defined (_WIN32)
  289. }
  290. //+------------------------------------------------------------------
  291. //
  292. // Member: CCmdline::~CCmdline()
  293. //
  294. // Synopsis: Destructor for CCmdline
  295. //
  296. // Effects: Deletes _apArgs
  297. //
  298. // History: 12/23/91 Lizch Created.
  299. // 04/17/92 Lizch Converted to NLS_STR
  300. // 08/10/92 Davey Added delete of _pnlsEquater/_pnlsSeparator
  301. // 10/11/94 XimingZ Added checking _apArgs != NULL
  302. //
  303. //-------------------------------------------------------------------
  304. CCmdline::~CCmdline()
  305. {
  306. if (_apArgs != NULL)
  307. {
  308. for (UINT i=0; i<_uiNumArgs; i++)
  309. {
  310. delete _apArgs[i];
  311. }
  312. delete [] _apArgs;
  313. }
  314. delete _pbcInternalUsage;
  315. delete _pnszProgName;
  316. }
  317. //+------------------------------------------------------------------
  318. //
  319. // Member: CCmdline::Parse, public
  320. //
  321. // Synoposis: Parses the stored command line, matching actual with
  322. // expected command line switches.
  323. //
  324. // Effects: Calls FindSwitch which stores the command line values
  325. // (eg. "lizch" in "/user:lizch") in the individual command
  326. // line objects, via the polymorphic SetValue call.
  327. //
  328. // Arguments: [apExpectedArgs] - an array of all possible command line
  329. // objects
  330. // [iMaxArgs] - the number of elements in that array.
  331. // [fExtras] - if TRUE, a warning is given if an
  332. // unexpected command line argument is given.
  333. //
  334. // Returns: CMDLINE_NO_ERROR,
  335. // CMDLINE_ERROR_INVALID_FLAG.
  336. // CMDLINE_ERROR_ARGUMENT_MISSING
  337. // CMDLINE_ERROR_UNRECOGNISED_ARG
  338. // CMDLINE_ERROR_USAGE_FOUND
  339. //
  340. // History: 12/23/91 Lizch Created.
  341. // 04/17/92 Lizch Converted to NLS_STR.
  342. // 08/10/92 Davey Added internal usage check.
  343. // 06/13/97 MariusB Reset found flag for each expected
  344. // arg. Reset the value of the arg if it
  345. // is not found, no default and not
  346. // required.
  347. //-------------------------------------------------------------------
  348. INT CCmdline::Parse(
  349. CBaseCmdlineObj *apExpectedArgs[],
  350. UINT uiMaxArgs,
  351. BOOL fCheckForExtras)
  352. {
  353. INT iRC = CMDLINE_NO_ERROR;
  354. UINT i;
  355. // Traverse the array of objects given, making sure none have errors
  356. for (i=0; i<uiMaxArgs; i++)
  357. {
  358. iRC = apExpectedArgs[i]->QueryError();
  359. if (CMDLINE_NO_ERROR != iRC)
  360. {
  361. return(iRC);
  362. }
  363. apExpectedArgs[i]->SetFoundFlag(FALSE);
  364. }
  365. // check for internal usage if defined.
  366. if (_pbcInternalUsage != NULL)
  367. {
  368. iRC = FindSwitch(_pbcInternalUsage, fCheckForExtras);
  369. if (iRC != CMDLINE_NO_ERROR)
  370. {
  371. DisplayUsage(apExpectedArgs, uiMaxArgs);
  372. return(iRC);
  373. }
  374. // if found, print out usage and return.
  375. if (_pbcInternalUsage->IsFound() == TRUE)
  376. {
  377. DisplayUsage(apExpectedArgs, uiMaxArgs);
  378. return(CMDLINE_ERROR_USAGE_FOUND);
  379. }
  380. }
  381. // look for each expected argument in turn.
  382. for (i=0; i<uiMaxArgs; i++)
  383. {
  384. CBaseCmdlineObj *pArg = apExpectedArgs[i];
  385. // Go along the actual command line, looking for this switch
  386. // CMDLINE_NO_ERROR doesn't necessarily indicate we found it,
  387. // just that nothing went wrong
  388. //
  389. iRC = FindSwitch(pArg, fCheckForExtras);
  390. if (iRC != CMDLINE_NO_ERROR)
  391. {
  392. break;
  393. }
  394. // We've gone through the entire command line. Check if any
  395. // switch was omitted. For those that had defaults specified,
  396. // set the default. For mandatory switches, generate an error.
  397. // Note that you should never have a default value for a
  398. // mandatory switch!
  399. //
  400. if ((pArg->IsFound()) == FALSE)
  401. {
  402. if (pArg->IsDefaultSpecified() == TRUE)
  403. {
  404. iRC = pArg->SetValueToDefault();
  405. if (iRC != CMDLINE_NO_ERROR)
  406. {
  407. break;
  408. }
  409. }
  410. else
  411. {
  412. if ((pArg->IsRequired()) == TRUE)
  413. {
  414. pArg->DisplayUsageDescr(
  415. _usSwitchIndent,
  416. _usDisplayWidth,
  417. _usIndent);
  418. iRC = CMDLINE_ERROR_ARGUMENT_MISSING;
  419. break;
  420. }
  421. else
  422. {
  423. // If the object is not found, it has no default
  424. // value and it is not required, reset it. This
  425. // action will not harm for normal use, but it
  426. // will allow you to reuse the same set of command
  427. // line object within the same app.
  428. pArg->ResetValue();
  429. }
  430. }
  431. }
  432. }
  433. if (iRC == CMDLINE_NO_ERROR)
  434. {
  435. // We've got all our expected switches. Now check to see if
  436. // any switches are left on the command line unparsed,
  437. // if that's what the user wanted.
  438. //
  439. if (fCheckForExtras == TRUE)
  440. {
  441. for (i=0; i<_uiNumArgs; i++)
  442. {
  443. if (_apArgs[i]->IsProcessed() != TRUE)
  444. {
  445. _sNprintf(_nszErrorBuf,
  446. _TEXTN("Unrecognised switch %s\n"),
  447. _apArgs[i]->QueryArg());
  448. (*_pfnDisplay)(_nszErrorBuf);
  449. iRC = CMDLINE_ERROR_UNRECOGNISED_ARG;
  450. }
  451. }
  452. }
  453. }
  454. else
  455. {
  456. DisplayUsage(apExpectedArgs, uiMaxArgs);
  457. }
  458. return(iRC);
  459. }
  460. //+------------------------------------------------------------------
  461. //
  462. // Member: CCmdline::FindSwitch, private
  463. //
  464. // Synoposis: Searches the command line for a given switch, checking
  465. // for separator and equator characters.
  466. //
  467. // Effects: Stores the value of the given switch, if any
  468. //
  469. // Arguments: [pArg] - pointer to the switch object to search
  470. // for
  471. // [fCheckForExtras] - flag to see if should check for extra
  472. // args.
  473. //
  474. // Returns: CMDLINE_NO_ERROR
  475. // CMDLINE_ERROR_INVALID_VALUE
  476. // CMDLINE_ERROR_UNRECOGNISED_ARG
  477. //
  478. // History: 12/23/91 Lizch Created.
  479. // 04/17/92 Lizch Converted to NLS_STR
  480. // 07/29/92 Davey Converted to use DisplayUsageDescr
  481. // Also fixed error output, was using
  482. // printf, changed to sprintf. Added
  483. // Check for extras flag.
  484. // 02/27/95 jesussp Corrected the case when the switch
  485. // name is non-existent
  486. // 06/25/97 mariusb Make /<switch>: and /<switch>
  487. // equivalent when the switch takes a
  488. // second argument (i.e. no equator
  489. // means the switch does not take a
  490. // second arg). Useful to specify NULL
  491. // values.
  492. // Notes: Terminology to help understand this:
  493. // Example: /username:lizch
  494. // The '/' is the separator
  495. // The ':' is the equator
  496. // 'username' is the switch
  497. // 'lizch' is the value
  498. //
  499. //-------------------------------------------------------------------
  500. INT CCmdline::FindSwitch(CBaseCmdlineObj * const pArg, BOOL fCheckForExtras)
  501. {
  502. INT iRC = CMDLINE_NO_ERROR;
  503. LPCNSTR nszArg;
  504. LPCNSTR nszSep;
  505. LPCNSTR nszSwitch;
  506. LPCNSTR nszThisSwitch;
  507. LPCNSTR nszEquater;
  508. USHORT cchSwitch;
  509. USHORT usSecondArg = 1;
  510. NCHAR nchSeparator = pArg->GetSeparator();
  511. NCHAR nchEquater = pArg->GetEquater();
  512. NCHAR nszSeparator[2];
  513. // If this switch does NOT take a second argument, we don't advance
  514. // past the equater when setting the value
  515. //
  516. if (!pArg->SecondArg())
  517. {
  518. usSecondArg = 0;
  519. }
  520. nszSeparator[0] = nchSeparator;
  521. nszSeparator[1] = nchClNull;
  522. for (UINT i=0; i<_uiNumArgs; i++)
  523. {
  524. nszArg = _apArgs[i]->QueryArg();
  525. // Find the separator character - it should be the first
  526. // character in the command line argument. If not, return
  527. // an error if fCheckForExtras == TRUE.
  528. //
  529. nszSep = nszArg;
  530. if (0 == _ncscspn(nszSep, nszSeparator))
  531. {
  532. // Get the switch value - first character after separator
  533. // up to but not including the equater
  534. //
  535. nszSwitch = nszSep+1;
  536. nszEquater = nszSwitch;
  537. // Look for equater
  538. while ((nchClNull != *nszEquater) && (nchEquater != *nszEquater))
  539. {
  540. nszEquater++;
  541. }
  542. cchSwitch = (USHORT) (nszEquater - nszSwitch);
  543. nszThisSwitch = pArg->QuerySwitchString();
  544. // See if this switch is for this argument
  545. if (0 != cchSwitch &&
  546. cchSwitch == _ncslen(nszThisSwitch) &&
  547. 0 == _ncsnicmp(nszSwitch, nszThisSwitch, cchSwitch))
  548. {
  549. // If we couldn't find the equater, decrease usSecondArg
  550. // which means we accept a NULL value for the switch. Thus
  551. // we make /<switch>: and /<switch> equivalent.
  552. if (nchEquater != *nszEquater && 1 == usSecondArg)
  553. {
  554. --usSecondArg;
  555. }
  556. // It is - get this switch's value
  557. _apArgs[i]->SetProcessedFlag(TRUE);
  558. // Now set the value of the switch (if any). How this is
  559. // done will vary from object to object: some won't take
  560. // values, some take lists of values etc. The polymorphic
  561. // SetValue call handles this. We don't want to pass
  562. // in the equater so add one to get to the beginning
  563. // of the value.
  564. //
  565. iRC = pArg->SetValue(nszSwitch+cchSwitch+usSecondArg);
  566. if (iRC != CMDLINE_NO_ERROR)
  567. {
  568. pArg->DisplayUsageDescr(
  569. _usSwitchIndent,
  570. _usDisplayWidth,
  571. _usIndent);
  572. }
  573. pArg->SetFoundFlag(TRUE);
  574. // We have found the value for the given switch.
  575. break;
  576. } //if
  577. }
  578. else
  579. {
  580. // Error out if didn't find separator, only if checking for
  581. // extras
  582. //
  583. if (fCheckForExtras == TRUE)
  584. {
  585. _sNprintf(
  586. _nszErrorBuf,
  587. _TEXTN("The initial separator %c was not found on switch %s\n"),
  588. nchSeparator,
  589. nszArg);
  590. (*_pfnDisplay)(_nszErrorBuf);
  591. return(CMDLINE_ERROR_UNRECOGNISED_ARG);
  592. }
  593. }
  594. }
  595. return(iRC);
  596. }
  597. //+------------------------------------------------------------------
  598. //
  599. // Member: CCmdline::SetProgName, public
  600. //
  601. // Synoposis: Sets the program name to the passed value
  602. // This value can later be used in usage statements
  603. //
  604. // Arguments: [nszProgName] - the program name
  605. //
  606. // Returns: Error code
  607. //
  608. // History: Created 05/23/91 Lizch
  609. //
  610. //-------------------------------------------------------------------
  611. INT CCmdline::SetProgName(PNSTR nszProgName)
  612. {
  613. INT nRet = CMDLINE_NO_ERROR;
  614. NCHAR *pnchStart = nszProgName;
  615. NCHAR *pnchDot;
  616. // check for last slash.
  617. if (NULL != (pnchStart = _ncsrchr(nszProgName, '\\')))
  618. {
  619. pnchStart++; // get past slash
  620. }
  621. else
  622. {
  623. // there was no slash so check for colon
  624. if (NULL != (pnchStart = _ncschr(nszProgName, ':')))
  625. {
  626. pnchStart++; // get past colon
  627. }
  628. else
  629. {
  630. // there was no colon or slash so set to beginning of name
  631. pnchStart = nszProgName;
  632. }
  633. }
  634. if (NULL != (pnchDot = _ncschr(pnchStart, _TEXTN('.'))))
  635. {
  636. *pnchDot = nchClNull;
  637. }
  638. _pnszProgName = new NCHAR[_ncslen(pnchStart)+1];
  639. if (NULL == _pnszProgName)
  640. {
  641. nRet = CMDLINE_ERROR_OUT_OF_MEMORY;
  642. }
  643. else
  644. {
  645. _ncscpy(_pnszProgName, pnchStart);
  646. }
  647. return(nRet);
  648. }
  649. //+------------------------------------------------------------------
  650. //
  651. // Member: CCmdline::GetProgName, public
  652. //
  653. // Synoposis: Returns a pointer to the program name
  654. //
  655. // Arguments: none
  656. //
  657. // Returns: a const NCHAR pointer to the program name
  658. //
  659. // History: Created 05/23/91 Lizch
  660. //
  661. //-------------------------------------------------------------------
  662. const NCHAR *CCmdline::GetProgName()
  663. {
  664. return(_pnszProgName);
  665. }
  666. //+------------------------------------------------------------------
  667. //
  668. // Member: CCmdline::SetExtraUsage, public
  669. //
  670. // Synoposis: Sets the extra usage file pointer.
  671. //
  672. // Arguments: [pfUsage] - function pointer to usage function.
  673. //
  674. // History: 07/28/92 Davey Created.
  675. //
  676. //-------------------------------------------------------------------
  677. void CCmdline::SetExtraUsage(PFVOID pfUsage)
  678. {
  679. _pfExtraUsage = pfUsage;
  680. }
  681. //+------------------------------------------------------------------
  682. //
  683. // Member: CCmdline::SetIndent, public
  684. //
  685. // Synoposis: Sets the indent parameter for usage display.
  686. //
  687. // Arguments: [usIndent] - how much to indent second lines.
  688. //
  689. // Returns: codes from CheckParamerterConsistency
  690. //
  691. // History: 07/28/92 Davey Created.
  692. //
  693. //-------------------------------------------------------------------
  694. INT CCmdline::SetIndent(USHORT usIndent)
  695. {
  696. _usIndent = usIndent;
  697. return(CheckParameterConsistency());
  698. }
  699. //+------------------------------------------------------------------
  700. //
  701. // Member: CCmdline::SetSwitchIndent, public
  702. //
  703. // Synoposis: Sets the switch indent parameter for usage display.
  704. //
  705. // Arguments: [usSwitchIndent] - how much to indent switch.
  706. //
  707. // Returns: codes from CheckParamerterConsistency
  708. //
  709. // History: 07/28/92 Davey Created.
  710. //
  711. //-------------------------------------------------------------------
  712. INT CCmdline::SetSwitchIndent(USHORT usSwitchIndent)
  713. {
  714. _usSwitchIndent = usSwitchIndent;
  715. return(CheckParameterConsistency());
  716. }
  717. //+------------------------------------------------------------------
  718. //
  719. // Member: CCmdline::SetDisplayWidth, public
  720. //
  721. // Synoposis: Sets the amount of line space available for the
  722. // usage display.
  723. //
  724. // Arguments: [usDisplayWidth] - line space available
  725. //
  726. // Returns: codes from CheckParamerterConsistency
  727. //
  728. // History: 07/28/92 Davey Created.
  729. //
  730. //-------------------------------------------------------------------
  731. INT CCmdline::SetDisplayWidth(USHORT usDisplayWidth)
  732. {
  733. _usDisplayWidth = usDisplayWidth;
  734. return(CheckParameterConsistency());
  735. }
  736. //+------------------------------------------------------------------
  737. //
  738. // Member: CCmdline::CheckParameterConsistency, private const
  739. //
  740. // Synoposis: Checks to make sure the display parameters are
  741. // useable.
  742. //
  743. // Arguments: None.
  744. //
  745. // Returns: CMDLINE_NO_ERROR
  746. // CMDLINE_ERROR_DISPLAY_PARAMS
  747. //
  748. // History: 07/28/92 Davey Created.
  749. //
  750. //-------------------------------------------------------------------
  751. INT CCmdline::CheckParameterConsistency(void) const
  752. {
  753. if (_usSwitchIndent >= _usIndent)
  754. {
  755. return(CMDLINE_ERROR_DISPLAY_PARAMS);
  756. }
  757. if (_usIndent >= _usDisplayWidth)
  758. {
  759. return(CMDLINE_ERROR_DISPLAY_PARAMS);
  760. }
  761. return(CMDLINE_NO_ERROR);
  762. }
  763. //+------------------------------------------------------------------
  764. //
  765. // Member: CCmdline::QueryDisplayParameters, public
  766. //
  767. // Synoposis: Returns the three parameters which control the
  768. // usage display output.
  769. //
  770. // Arguments: [pusDisplayWidth] - returns display width
  771. // [pusSwitchIndent] - returns switch indent
  772. // [pusIndent] - returns indent of second lines
  773. //
  774. // History: 07/28/92 Davey Created.
  775. //
  776. //-------------------------------------------------------------------
  777. void CCmdline::QueryDisplayParameters(
  778. USHORT *pusDisplayWidth,
  779. USHORT *pusSwitchIndent,
  780. USHORT *pusIndent) const
  781. {
  782. *pusDisplayWidth = _usDisplayWidth;
  783. *pusSwitchIndent = _usSwitchIndent;
  784. *pusIndent = _usIndent;
  785. }
  786. //+------------------------------------------------------------------
  787. //
  788. // Member: CCmdline::DisplayUsage, public
  789. //
  790. // Synoposis: Displays usage of switches,
  791. //
  792. // Arguments: apExpectedArgs: the array of command line objects
  793. // uiMaxArgs: number of elements in the array
  794. //
  795. // Returns: Error code from QueryError
  796. //
  797. // History: 07/28/92 Davey Created.
  798. //
  799. // Notes: Looks like:
  800. //
  801. // Usage Instructions
  802. // test2 /mc:<worker> [/ml:<log_server>] [/mt:<test>]
  803. // [/mn:<tester-email>] [/mp:<path>] [/mo:<obj_name>]
  804. // [/md:<dispatcher>] [/?]
  805. //
  806. // /mc Takes a list of strings specifying worker names.
  807. // These workers should have full names.
  808. // Don't you think so?
  809. // The strings in the list are separated by one of the following
  810. // character(s): ",; ".
  811. // /ml Takes a string specifying log server name.
  812. // /mt Takes a string specifying name of the test.
  813. // /mn Takes a string specifying email name of the tester.
  814. // /mp Takes a string specifying path name to log to.
  815. // /mo Takes a string specifying name of the object.
  816. // /md Takes a string specifying name of the dispatcher to use.
  817. // /? Flag specifying command line usage. It defaults to FALSE.
  818. //
  819. // ***following is whatever is output by the function pfnExtraUsage****
  820. //
  821. //-------------------------------------------------------------------
  822. INT CCmdline::DisplayUsage(
  823. CBaseCmdlineObj * const apExpectedArgs[],
  824. UINT uiMaxArgs)
  825. {
  826. USHORT usWidth = _usDisplayWidth;
  827. USHORT StrLen;
  828. INT iRC;
  829. USHORT cchProgName;
  830. PNSTR pnszLine;
  831. USHORT i;
  832. (*_pfnDisplay)(_TEXTN("\nUsage Instructions\n"));
  833. // Start creating usage line.
  834. //
  835. // Determine length to make line buffer - if the size of the program
  836. // name is less than the indentation, the buffer needs to be at
  837. // least as big as the indentation, less 1 for an extra space that
  838. // gets printed.
  839. cchProgName = (USHORT) _ncslen(_pnszProgName);
  840. if (cchProgName < _usIndent)
  841. {
  842. // Add 1 for null term
  843. StrLen = (USHORT) (_usIndent + 1);
  844. }
  845. else
  846. {
  847. // Add 1 for null term, 1 for new line and indent in next line
  848. StrLen = (USHORT) (cchProgName + _usIndent + 2);
  849. }
  850. // copy program name
  851. //
  852. pnszLine = new NCHAR[StrLen];
  853. if (NULL == pnszLine)
  854. {
  855. return(CMDLINE_ERROR_OUT_OF_MEMORY);
  856. }
  857. _ncscpy(pnszLine, _pnszProgName);
  858. // if the size of the program name is less than indentation then
  859. // add in padding; else add in a new line and then padding.
  860. //
  861. if (cchProgName >= _usIndent)
  862. {
  863. _ncscat(pnszLine, nszClNewLine);
  864. }
  865. // fill in with spaces until get to indent position
  866. for (i = (USHORT) _ncslen(pnszLine); i < StrLen - 1; i++)
  867. {
  868. _ncscat(pnszLine, nszClSpace);
  869. }
  870. // output program name
  871. //
  872. (*_pfnDisplay)(pnszLine);
  873. // update remaining display width
  874. //
  875. usWidth = (USHORT) (usWidth - _usIndent);
  876. // done with pnszLine - delete it now to avoid leaks in case of
  877. // errors
  878. //
  879. delete pnszLine;
  880. // display the command line usage statement, required ones first
  881. //
  882. for (i=0; i<uiMaxArgs; i++)
  883. {
  884. if (apExpectedArgs[i]->IsRequired() == TRUE)
  885. {
  886. iRC = apExpectedArgs[i]->DisplayUsageLine(
  887. &usWidth,
  888. _usDisplayWidth,
  889. _usIndent);
  890. if (iRC)
  891. {
  892. SetError(iRC);
  893. return(iRC);
  894. }
  895. }
  896. }
  897. for (i=0; i<uiMaxArgs; i++)
  898. {
  899. if (apExpectedArgs[i]->IsRequired() == FALSE)
  900. {
  901. iRC = apExpectedArgs[i]->DisplayUsageLine(
  902. &usWidth,
  903. _usDisplayWidth,
  904. _usIndent);
  905. if (iRC)
  906. {
  907. SetError(iRC);
  908. return(iRC);
  909. }
  910. }
  911. }
  912. // print out if using internal usage.
  913. //
  914. if (_pbcInternalUsage != NULL)
  915. {
  916. iRC = _pbcInternalUsage->DisplayUsageLine(
  917. &usWidth,
  918. _usDisplayWidth,
  919. _usIndent);
  920. if (iRC)
  921. {
  922. SetError(iRC);
  923. return(iRC);
  924. }
  925. }
  926. // separate the line and descriptions
  927. (*_pfnDisplay)(_TEXTN("\n\n"));
  928. // display the switch descriptions, required ones first
  929. for (i=0; i<uiMaxArgs; i++)
  930. {
  931. if (apExpectedArgs[i]->IsRequired() == TRUE)
  932. {
  933. iRC = apExpectedArgs[i]->DisplayUsageDescr(
  934. _usSwitchIndent,
  935. _usDisplayWidth,
  936. _usIndent);
  937. if (iRC)
  938. {
  939. SetError(iRC);
  940. return(iRC);
  941. }
  942. }
  943. }
  944. for (i=0; i<uiMaxArgs; i++)
  945. {
  946. if (apExpectedArgs[i]->IsRequired() == FALSE)
  947. {
  948. iRC = apExpectedArgs[i] -> DisplayUsageDescr(
  949. _usSwitchIndent,
  950. _usDisplayWidth,
  951. _usIndent);
  952. if (iRC)
  953. {
  954. SetError(iRC);
  955. return(iRC);
  956. }
  957. }
  958. }
  959. // print out if using internal usage.
  960. //
  961. if (_pbcInternalUsage != NULL)
  962. {
  963. iRC = _pbcInternalUsage->DisplayUsageDescr(
  964. _usSwitchIndent,
  965. _usDisplayWidth,
  966. _usIndent);
  967. if (iRC)
  968. {
  969. SetError(iRC);
  970. return(iRC);
  971. }
  972. }
  973. (*_pfnDisplay)(_TEXTN("\n\n"));
  974. // invoke the special usage function if defined.
  975. if (_pfExtraUsage != NULL)
  976. {
  977. _pfExtraUsage();
  978. }
  979. return(CMDLINE_NO_ERROR);
  980. }
  981. //+------------------------------------------------------------------
  982. //
  983. // Member: CCmdline::SetInternalUsage, private
  984. //
  985. // Synposis: Sets the internal usage flag
  986. //
  987. // Effects: Modifies _pcbInternalUsage, so that it displays
  988. //
  989. // Arguments: none
  990. //
  991. // Returns: CMDLINE_NO_ERROR
  992. // CMDLINE_ERROR_OUT_OF_MEMORY
  993. //
  994. //
  995. // History: 02/06/95 jesussp Created
  996. //
  997. // Notes:
  998. //-------------------------------------------------------------------
  999. INT CCmdline::SetInternalUsage(void)
  1000. {
  1001. INT iError; // Error to be returned
  1002. _pbcInternalUsage = new CBoolCmdlineObj(_TEXTN("?"), _TEXTN("command line usage."));
  1003. if (NULL == _pbcInternalUsage)
  1004. {
  1005. SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
  1006. }
  1007. else
  1008. {
  1009. SetError(_pbcInternalUsage->QueryError());
  1010. }
  1011. iError = QueryError();
  1012. if (CMDLINE_NO_ERROR != iError)
  1013. {
  1014. // Since QueryError always sets CMDLINE_NO_ERROR, we need to
  1015. // call SetError to put the error back.
  1016. SetError(iError);
  1017. }
  1018. return iError;
  1019. }
  1020. //+------------------------------------------------------------------
  1021. //
  1022. // Function: DelimitWithNulls, private
  1023. //
  1024. // Synposis: Delimits an argument string, putting a null byte
  1025. // between parameters and an additional extra null byte
  1026. // at the end of the string. For use by CCmdline
  1027. // constructor.
  1028. //
  1029. // Effects: Copies pnszArgLine to pnszDest. Space must be
  1030. // allocated for both strings. Modifies uiArgc.
  1031. //
  1032. // Arguments: [pnszArgLine] The line with arguments
  1033. // [pnszDest] Null-delimited destination
  1034. // [puiArgc] Pointer to an argument count
  1035. //
  1036. // Returns: Nothing
  1037. //
  1038. //
  1039. // History: 02/10/95 jesussp Created
  1040. //
  1041. // Notes: Inspired on parse_cmdline(), crt32\startup\stdargv.c
  1042. // pnszArgLine and pnszDest must point to valid memory
  1043. // allocations. Also, the space allocation for
  1044. // pnszDest must be greater than that of pnszArgLine.
  1045. //-------------------------------------------------------------------
  1046. static void DelimitWithNulls(LPNSTR pnszArgLine,
  1047. LPNSTR pnszDest,
  1048. UINT *puiArgc)
  1049. {
  1050. LPNSTR pncSrc = pnszArgLine;
  1051. LPNSTR pncDst = pnszDest;
  1052. BOOL fInQuote = 0;
  1053. USHORT ucBackSlash = 0;
  1054. USHORT ucQuotes = 0;
  1055. *puiArgc = 0;
  1056. // Loop through the entire argument line
  1057. for (;;)
  1058. {
  1059. // skip blanks
  1060. while (*pncSrc && _isnspace(*pncSrc))
  1061. {
  1062. ++pncSrc;
  1063. }
  1064. // Reached the end of the string?
  1065. if (nchClNull == *pncSrc)
  1066. {
  1067. // Put a null byte at the end only if we haven't had
  1068. // any argument...
  1069. if (0 == *puiArgc)
  1070. {
  1071. *pncDst++ = nchClNull;
  1072. }
  1073. break;
  1074. }
  1075. // We now have one more argument...
  1076. ++(*puiArgc);
  1077. // process one argument
  1078. for (;;)
  1079. {
  1080. ucBackSlash = 0;
  1081. while (nchClBackSlash == *pncSrc)
  1082. {
  1083. ++pncSrc; ++ucBackSlash;
  1084. }
  1085. if (nchClQuote == *pncSrc)
  1086. {
  1087. if (ucBackSlash % 2 == 0) // Even number of backslashes?
  1088. {
  1089. fInQuote = !fInQuote;
  1090. ++pncSrc; // Eat up quote
  1091. }
  1092. ucBackSlash /= 2;
  1093. }
  1094. while (ucBackSlash--)
  1095. {
  1096. *pncDst++ = nchClBackSlash;
  1097. }
  1098. if (nchClNull == *pncSrc || (!fInQuote && _isnspace(*pncSrc)))
  1099. {
  1100. break;
  1101. }
  1102. *pncDst++ = *pncSrc++;
  1103. }
  1104. if (fInQuote)
  1105. {
  1106. *pncDst++ = nchClSpace;
  1107. }
  1108. else
  1109. {
  1110. *pncDst++ = nchClNull;
  1111. }
  1112. }
  1113. // Complete the string, adding an extra nul character
  1114. *pncDst = nchClNull;
  1115. }