home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / comp / sources / misc / 3777 < prev    next >
Encoding:
Text File  |  1992-07-26  |  51.2 KB  |  1,610 lines

  1. Newsgroups: comp.sources.misc
  2. Path: sparky!kent
  3. From: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
  4. Subject:  v31i053:  cmdline - C++ Library for parsing command-line arguments, Part06/07
  5. Message-ID: <1992Jul27.020852.29820@sparky.imd.sterling.com>
  6. Followup-To: comp.sources.d
  7. X-Md4-Signature: 3e3a4734168e6b97ec467667ead4e62a
  8. Sender: kent@sparky.imd.sterling.com (Kent Landfield)
  9. Reply-To: brad@travis.csd.harris.com
  10. Organization: Harris Computer Systems
  11. References: <csm-v31i047=cmdline.205609@sparky.IMD.Sterling.COM>
  12. Date: Mon, 27 Jul 1992 02:08:52 GMT
  13. Approved: kent@sparky.imd.sterling.com
  14. Lines: 1594
  15.  
  16. Submitted-by: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
  17. Posting-number: Volume 31, Issue 53
  18. Archive-name: cmdline/part06
  19. Environment: C++
  20.  
  21. #! /bin/sh
  22. # This is a shell archive.  Remove anything before this line, then unpack
  23. # it by saving it into a file and typing "sh file".  To overwrite existing
  24. # files, type "sh file -c".  You can also feed this as standard input via
  25. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  26. # will see the following message at the end:
  27. #        "End of archive 6 (of 7)."
  28. # Contents:  src/cmd/cmdparse.c src/lib/unix.c
  29. # Wrapped by brad@hcx1 on Mon Jul 20 10:41:32 1992
  30. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  31. if test -f 'src/cmd/cmdparse.c' -a "${1}" != "-c" ; then 
  32.   echo shar: Will not clobber existing file \"'src/cmd/cmdparse.c'\"
  33. else
  34. echo shar: Extracting \"'src/cmd/cmdparse.c'\" \(22245 characters\)
  35. sed "s/^X//" >'src/cmd/cmdparse.c' <<'END_OF_FILE'
  36. X//------------------------------------------------------------------------
  37. X// ^FILE: cmdparse.c - implementation of the CmdParseCommand
  38. X//
  39. X// ^DESCRIPTION:
  40. X//    This file implements the member functions of the CmdParseCommand
  41. X//    class.
  42. X//
  43. X// ^HISTORY:
  44. X//    04/26/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  45. X//-^^---------------------------------------------------------------------
  46. X
  47. X#include <stdlib.h>
  48. X#include <iostream.h>
  49. X#include <fstream.h>
  50. X#include <strstream.h>
  51. X#include <string.h>
  52. X#include <ctype.h>
  53. X
  54. X#include "argtypes.h"
  55. X#include "cmdparse.h"
  56. X#include "syntax.h"
  57. X#include "quoted.h"
  58. X
  59. Xenum { SUCCESS = 0, FAILURE = -1 } ;
  60. X
  61. Xenum { MAX_IDENT_LEN = 64, MAX_DESCRIPTION_LEN = 1024 } ;
  62. X
  63. X
  64. Xextern "C" {
  65. X   int  isatty(int fd);
  66. X}
  67. X
  68. X//------------------------------------------------------------------------ copy
  69. X
  70. X//-------------------
  71. X// ^FUNCTION: copy - copy a string
  72. X//
  73. X// ^SYNOPSIS:
  74. X//    copy(dest, src)
  75. X//
  76. X// ^PARAMETERS:
  77. X//    char * & dest;
  78. X//    -- where to put the copy we make
  79. X//
  80. X//    const char * src;
  81. X//    -- the string to copy
  82. X//
  83. X// ^DESCRIPTION:
  84. X//    This function duplicates its second parameter to its first parameter.
  85. X//
  86. X// ^REQUIREMENTS:
  87. X//    None.
  88. X//
  89. X// ^SIDE-EFFECTS:
  90. X//    "dest" is modified to point to the newly allocated and copied result.
  91. X//
  92. X// ^RETURN-VALUE:
  93. X//    None.
  94. X//
  95. X// ^ALGORITHM:
  96. X//    Trivial.
  97. X//-^^----------------
  98. Xstatic  void
  99. Xcopy(char * & dest, const char * src)
  100. X{
  101. X   if (src == NULL)  return ;
  102. X   dest = new char[::strlen(src) + 1] ;
  103. X   if (dest)  ::strcpy(dest, src);
  104. X}
  105. X
  106. X//------------------------------------------------------------------ CmdArgVers
  107. X
  108. XCmdArgVers::CmdArgVers(char opt, const char * kwd, const char * description)
  109. X   : CmdArg(opt, kwd, description, CmdArg::isOPT)
  110. X{
  111. X}
  112. X
  113. XCmdArgVers::~CmdArgVers(void)
  114. X{
  115. X}
  116. X
  117. Xint
  118. XCmdArgVers::operator()(const char * & , CmdLine & cmd)
  119. X{
  120. X   cerr << cmd.name() << "\trelease " << cmd.release()
  121. X        << " at patchlevel " << cmd.patchlevel() << endl ;
  122. X   ::exit(e_VERSION);
  123. X   return  0;  // shutup the compiler about not returning a value
  124. X}
  125. X
  126. X//------------------------------------------------------------- CmdParseCommand
  127. X
  128. X//-------------------
  129. X// ^FUNCTION: CmdParseCommand::parse_declarations - parse user arguments
  130. X//
  131. X// ^SYNOPSIS:
  132. X//    CmdParseCommand::parse_declarations()
  133. X//
  134. X// ^PARAMETERS:
  135. X//    None.
  136. X//
  137. X// ^DESCRIPTION:
  138. X//    This routine will go through all the input sources that were supplied
  139. X//    on the command-line. Each of these "input sources" is something that
  140. X//    contains user argument declarations that need to be parsed.  If no
  141. X//    input sources were given and cin is not connected to a terminal, then
  142. X//    we try to read the declarations from cin.
  143. X//
  144. X//    If input sources were given, they are parsed in the following order:
  145. X//       1) from a string
  146. X//       2) from an environment variable
  147. X//       3) from a file
  148. X//
  149. X//    If more than one input source is specified then they are processed in
  150. X//    the above order and each argument is appended to the user's CmdLine
  151. X//    object in the order that it was seen.
  152. X//
  153. X// ^REQUIREMENTS:
  154. X//    This routine should be called by CmdParseCommand::operator() after
  155. X//    the command-line has been parsed.
  156. X//
  157. X// ^SIDE-EFFECTS:
  158. X//    If input comes from a file, then the file is read (until eof or an
  159. X//    error condition occurs).
  160. X//
  161. X//    Any arguments found are "compiled" and appended to "usr_cmd", the user's
  162. X//    CmdLine object.
  163. X//
  164. X// ^RETURN-VALUE:
  165. X//    0 upon success, non-zero upon failure.
  166. X//
  167. X// ^ALGORITHM:
  168. X//    Follow along - its pretty straightforward.
  169. X//-^^----------------
  170. Xint
  171. XCmdParseCommand::parse_declarations(void)
  172. X{
  173. X   const char * str = input_str ;
  174. X   const char * varname = input_var ;
  175. X   const char * filename = input_file ;
  176. X
  177. X      // If no "input sources" were specified, try cin.
  178. X   if ((str == NULL)  &&  (varname == NULL)  &&  (filename == NULL)) {
  179. X      // Make sure cin is NOT interactive!
  180. X      int fd = ((filebuf *)(cin.rdbuf()))->fd();
  181. X      if (::isatty(fd)) {
  182. X         error() << "Can't read argument declarations from a terminal."
  183. X                 << endl ;
  184. X         return  -1;
  185. X      }
  186. X      return  parse_declarations(cin);
  187. X   }
  188. X
  189. X   int  rc = 0;
  190. X
  191. X      // First - parse from the string
  192. X   if (str) {
  193. X      rc += parse_declarations(str);
  194. X   }
  195. X
  196. X      // Second - parse from the environment variable
  197. X   if (varname) {
  198. X      const char * contents = ::getenv(varname);
  199. X      if (contents) {
  200. X         rc += parse_declarations(contents);
  201. X      } else {
  202. X         error() << varname << " is empty or is undefined." << endl ;
  203. X         return  -1;
  204. X      }
  205. X   }
  206. X
  207. X      // Third - parse from the file. If the filename is "-" then it
  208. X      //         means that standard input should be used.
  209. X      //
  210. X   if (filename) {
  211. X      if (::strcmp(filename, "-") == 0) {
  212. X         rc += parse_declarations(cin);
  213. X      } else {
  214. X         ifstream  ifs(filename);
  215. X         if (ifs) {
  216. X            rc += parse_declarations(ifs);
  217. X         } else {
  218. X            error() << "Unable to read from " << filename << '.' << endl ;
  219. X            return  -1;
  220. X         }
  221. X      }
  222. X   }
  223. X
  224. X   return  rc;
  225. X}
  226. X
  227. X
  228. X//-------------------
  229. X// ^FUNCTION: CmdParseCommand::usr_append - add a user argument
  230. X//
  231. X// ^SYNOPSIS:
  232. X//    CmdParseCommand::usr_append(type, varname, arg, description)
  233. X//
  234. X// ^PARAMETERS:
  235. X//    const char * type;
  236. X//    -- the name of the desired argument type (which should correspond
  237. X//       to either CmdArgUsage, CmdArgDummy, or one of the types defined
  238. X//       in "argtypes.h").
  239. X//
  240. X//    const char * varname;
  241. X//    -- the name of the shell variable that will be receiving the value
  242. X//       that was supplied to this argument on the command-line.
  243. X//
  244. X//    ArgSyntax & arg;
  245. X//    -- the argument syntax corresponding to the "syntax-string" supplied
  246. X//       by the user.
  247. X//
  248. X//    const char * description;
  249. X//    -- the user's description of this argument.
  250. X//
  251. X// ^DESCRIPTION:
  252. X//    This member function will create a corresponding instance of a derived
  253. X//    class of CmdArg named by "type" and will append it to the user's CmdLine
  254. X//    object (named usr_cmd).
  255. X//
  256. X//    "type" may be one of the following (the leading "CmdArg" prefix may be
  257. X//    omitted):
  258. X//
  259. X//       CmdArgInt, CmdArgFloat, CmdArgChar, CmdArgStr, CmdArgBool,
  260. X//       CmdArgSet, CmdArgClear, CmdArgToggle, CmdArgUsage, CMdArgDummy
  261. X//
  262. X//    It is NOT necessary to use the name of a "List" CmdArg type in order
  263. X//    to indicate a list, that will have been inferred from the syntax string
  264. X//    and the appropriate ShellCmdArg<Type> object that we create will know
  265. X//    how to handle it.
  266. X//
  267. X// ^REQUIREMENTS:
  268. X//    This function should be called after an argument declaration has been
  269. X//    completely parsed.
  270. X//
  271. X// ^SIDE-EFFECTS:
  272. X//    If "type" is invalid - an error is printed on cerr, otherwise
  273. X//    we create an appopriate CmdArg<Type> object and append it to usr_cmd.
  274. X//
  275. X// ^RETURN-VALUE:
  276. X//    0 for success; non-zero for failure.
  277. X//
  278. X// ^ALGORITHM:
  279. X//    Trivial - there's just a lot of type-names to check for.
  280. X//-^^----------------
  281. Xint
  282. XCmdParseCommand::usr_append(const char * type,
  283. X                            const char * varname,
  284. X                            ArgSyntax  & arg,
  285. X                            const char * description)
  286. X{
  287. X   char * name = NULL ;
  288. X   char * kwd  = NULL ;
  289. X   char * val  = NULL ;
  290. X   char * desc = NULL ;
  291. X   unsigned  flags = arg.syntax() ;
  292. X   char  opt = arg.optchar() ;
  293. X
  294. X      // Need to make copies of some things because we cant assume they
  295. X      // will be sticking around.  We assume that ShellCmdArg::~ShellCmdArg
  296. X      // will deallocate this storage.
  297. X      //
  298. X   ::copy(name, varname);
  299. X   ::copy(kwd,  arg.keyword());
  300. X   ::copy(val,  arg.value());
  301. X   ::copy(desc, description);
  302. X
  303. X       // Skip any leading "Cmd", "Arg", or "CmdArg" prefix in the type-name.
  304. X   if (CmdLine::strmatch("Cmd", type, 3) == CmdLine::str_EXACT)  type += 3;
  305. X   if (CmdLine::strmatch("Arg", type, 3) == CmdLine::str_EXACT)  type += 3;
  306. X
  307. X       // Create an argument for the appropriate type and append it
  308. X       // to usr_cmd.
  309. X       //
  310. X   if (CmdLine::strmatch("Usage", type) == CmdLine::str_EXACT) {
  311. X      delete [] name ;
  312. X      usr_cmd.append(new CmdArgUsage(opt, kwd, desc)) ;
  313. X   }
  314. X   else if (CmdLine::strmatch("Dummy", type) == CmdLine::str_EXACT) {
  315. X      delete [] name ;
  316. X      usr_cmd.append(new CmdArgDummy(opt, kwd, val, desc, flags));
  317. X   }
  318. X   else if (CmdLine::strmatch("Set", type) == CmdLine::str_EXACT) {
  319. X      usr_cmd.append(new ShellCmdArgBool(name, opt, kwd, desc, flags));
  320. X   }
  321. X   else if (CmdLine::strmatch("Clear", type) == CmdLine::str_EXACT) {
  322. X      usr_cmd.append(new ShellCmdArgClear(name, opt, kwd, desc, flags));
  323. X   }
  324. X   else if (CmdLine::strmatch("Toggle", type) == CmdLine::str_EXACT) {
  325. X      usr_cmd.append(new ShellCmdArgToggle(name, opt, kwd, desc, flags));
  326. X   }
  327. X   else if (CmdLine::strmatch("Boolean", type) != CmdLine::str_NONE) {
  328. X      usr_cmd.append(new ShellCmdArgBool(name, opt, kwd, desc, flags));
  329. X   }
  330. X   else if (CmdLine::strmatch("Integer", type) != CmdLine::str_NONE) {
  331. X      usr_cmd.append(new ShellCmdArgInt(name, opt, kwd, val, desc, flags));
  332. X   }
  333. X   else if (CmdLine::strmatch("Float", type) != CmdLine::str_NONE) {
  334. X      usr_cmd.append(new ShellCmdArgFloat(name, opt, kwd, val, desc, flags));
  335. X   }
  336. X   else if (CmdLine::strmatch("Character", type) != CmdLine::str_NONE) {
  337. X      usr_cmd.append(new ShellCmdArgChar(name, opt, kwd, val, desc, flags));
  338. X   }
  339. X   else if (CmdLine::strmatch("String", type) != CmdLine::str_NONE) {
  340. X      usr_cmd.append(new ShellCmdArgStr(name, opt, kwd, val, desc, flags));
  341. X   }
  342. X   else {
  343. X      cerr << "Unknown argument type \"" << type << "\"." << endl ;
  344. X      delete [] kwd ;
  345. X      delete [] val ;
  346. X      delete [] desc ;
  347. X      return  FAILURE ;
  348. X   }
  349. X
  350. X   return  SUCCESS ;
  351. X}
  352. X
  353. X
  354. X//-------------------
  355. X// ^FUNCTION: CmdParseCommand::parse_declarations - parse from a string
  356. X//
  357. X// ^SYNOPSIS:
  358. X//    CmdParseCommand::parse_declarations(str);
  359. X//
  360. X// ^PARAMETERS:
  361. X//    const char * str;
  362. X//    -- the string containing the argument declarations.
  363. X//
  364. X// ^DESCRIPTION:
  365. X//    Parse the user's argument declarations from an input string.
  366. X//
  367. X// ^REQUIREMENTS:
  368. X//    This member function should only be called by parse_declarations(void).
  369. X//
  370. X// ^SIDE-EFFECTS:
  371. X//    - modifies usr_cmd by appending to it any valid arguments that we parse.
  372. X//
  373. X// ^RETURN-VALUE:
  374. X//    0 for success; non-zero for failure.
  375. X//
  376. X// ^ALGORITHM:
  377. X//    Just turn the string into an instream and parse the instream.
  378. X//-^^----------------
  379. Xint
  380. XCmdParseCommand::parse_declarations(const char * str)
  381. X{
  382. X   int  rc = 0;
  383. X   char * strbuf = new char[::strlen(str) + 1] ;
  384. X   (void) ::strcpy(strbuf, str);
  385. X   istrstream  iss(strbuf);
  386. X   rc = parse_declarations(iss);
  387. X   delete  strbuf ;
  388. X   return  rc ;
  389. X}
  390. X
  391. X
  392. X
  393. X//-------------------
  394. X// ^FUNCTION: CmdParseCommand::parse_declarations - parse from an instream
  395. X//
  396. X// ^SYNOPSIS:
  397. X//    CmdParseCommand::parse_declarations(is);
  398. X//
  399. X// ^PARAMETERS:
  400. X//    istream & is;
  401. X//    -- the instream containing the argument declarations.
  402. X//
  403. X// ^DESCRIPTION:
  404. X//    Parse the user's argument declarations from an input steam.
  405. X//
  406. X// ^REQUIREMENTS:
  407. X//    This member function should only be called by parse_declarations(void).
  408. X//
  409. X// ^SIDE-EFFECTS:
  410. X//    - modifies usr_cmd by appending to it any valid arguments that we parse.
  411. X//
  412. X// ^RETURN-VALUE:
  413. X//    0 for success; non-zero for failure.
  414. X//
  415. X// ^ALGORITHM:
  416. X//    while not eof do
  417. X//       - read the type
  418. X//       - read the name
  419. X//       - read the syntax
  420. X//       - read the description
  421. X//       - convert (type, name, syntax, description) into something we can
  422. X//           append to usr_cmd.
  423. X//    done
  424. X//-^^----------------
  425. Xint
  426. XCmdParseCommand::parse_declarations(istream & is)
  427. X{
  428. X      // Keep track of the number of declarations that we parse.
  429. X   unsigned  nargs = 0;
  430. X
  431. X   if (is.eof())  return  SUCCESS;
  432. X
  433. X   char  arg_type[MAX_IDENT_LEN];
  434. X   char  arg_name[MAX_IDENT_LEN];
  435. X   QuotedString  arg_description(MAX_DESCRIPTION_LEN);
  436. X
  437. X   while (is) {
  438. X      ++nargs;
  439. X
  440. X         // Skip all non-alpha-numerics
  441. X     int c = is.peek() ;
  442. X     while ((c != EOF) && (c != '_') && (! isalnum(c))) {
  443. X        (void) is.get();
  444. X        c = is.peek();
  445. X     }
  446. X  
  447. X         // First parse the argument type
  448. X      is.width(sizeof(arg_type) - 1);
  449. X      is >> arg_type ;
  450. X      if (! is) {
  451. X         if (is.eof()) {
  452. X            return  SUCCESS;  // end of args
  453. X         } else {
  454. X            error() << "Unable to extract type for argument #" << nargs
  455. X                    << '.' << endl ;
  456. X            return  FAILURE;
  457. X         }
  458. X      }
  459. X
  460. X         // Now parse the argument name
  461. X      is.width(sizeof(arg_name) - 1);
  462. X      is >> arg_name ;
  463. X      if (! is) {
  464. X         if (is.eof()) {
  465. X            error() << "Premature end of input.\n"
  466. X                    << "\texpecting a name for argument #" << nargs
  467. X                    << '.' << endl ;
  468. X         } else {
  469. X            error() << "Unable to extract name of argument #" << nargs
  470. X                    << '.' << endl ;
  471. X         }
  472. X         return  FAILURE;
  473. X      }
  474. X
  475. X         // Now parse the argument syntax
  476. X      ArgSyntax  arg;
  477. X      is >> arg;
  478. X      if (! is) {
  479. X         error() << "Unable to get syntax for \"" << arg_name << "\" argument."
  480. X                 << endl ;
  481. X         return  FAILURE ;
  482. X      }
  483. X
  484. X         // Now parse the argument description
  485. X      is >> arg_description ;
  486. X      if (! is) {
  487. X         error() << "Unable to get description for \"" << arg_name
  488. X                 << "\" argument." << endl ;
  489. X         return  FAILURE ;
  490. X      }
  491. X
  492. X      if (usr_append(arg_type, arg_name, arg, arg_description)) {
  493. X         error() << "Unable to append \"" << arg_name << "\" argument "
  494. X                 << "to the list." << endl ;
  495. X         return  FAILURE;
  496. X      }
  497. X   }
  498. X   return  SUCCESS;
  499. X}
  500. X
  501. X
  502. X//-------------------
  503. X// ^FUNCTION: CmdParseCommand::set_args - set the user's arguments.
  504. X//
  505. X// ^SYNOPSIS:
  506. X//    CmdParseCommand::set_args(shell)
  507. X//
  508. X// ^PARAMETERS:
  509. X//    UnixShell * shell;
  510. X//    -- the command-interpreter (shell) that we need to output
  511. X//       variable settings for.
  512. X//
  513. X// ^DESCRIPTION:
  514. X//    For each argument that was given on the user's command-line, we need to
  515. X//    output a variable setting using the sepcified shell's syntax to indicate
  516. X//    the value that was specified.
  517. X//
  518. X// ^REQUIREMENTS:
  519. X//    This member function should only be called by CmdParseCommand::operator()
  520. X//
  521. X// ^SIDE-EFFECTS:
  522. X//    All the variable settings or sent to cout (standard output), the invoking
  523. X//    user is responsible for evaluating what this member function outputs.
  524. X//
  525. X// ^RETURN-VALUE:
  526. X//    None.
  527. X//
  528. X// ^ALGORITHM:
  529. X//    For each of the user's command-argument objects
  530. X//       - if the argument is a dummy, then skip it
  531. X//       - if the argument corresponds to the positional parameters for
  532. X//           this shell but was not given a value on the command-line then
  533. X//           unset this shell's positional parameters.
  534. X//       - if the argument was given then
  535. X//           - if the argument took a value and no value was given, then
  536. X//               set the variable <argname>_FLAG to be TRUE.
  537. X//           - else set the corresponding shell variable.
  538. X//           endif
  539. X//       endif
  540. X//    endfor
  541. X//-^^----------------
  542. Xvoid
  543. XCmdParseCommand::set_args(UnixShell * shell)
  544. X{
  545. X   unsigned  flags, syntax;
  546. X   CmdLineCmdArgIter  iter(usr_cmd);
  547. X
  548. X   for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
  549. X      flags  = cmdarg->flags();
  550. X      syntax = cmdarg->syntax();
  551. X
  552. X      if (cmdarg->is_dummy())  continue;
  553. X
  554. X      ShellCmdArg * sh_cmdarg = (ShellCmdArg *)cmdarg;
  555. X
  556. X      if ((syntax & CmdArg::isPOS) && (! (flags & CmdArg::VALGIVEN))) {
  557. X         // if these are the positional-parameters then unset them!
  558. X         if (shell->is_positionals(sh_cmdarg->name())) {
  559. X            shell->unset_args(sh_cmdarg->name());
  560. X         }
  561. X      }
  562. X
  563. X      if (! (flags & CmdArg::GIVEN))  continue;
  564. X
  565. X      if ((syntax & CmdArg::isVALTAKEN) && (! (flags & CmdArg::VALGIVEN))) {
  566. X         // flag was given without its value - we need to record that
  567. X         char  var_name[256];
  568. X         (void) ::strcpy(var_name, sh_cmdarg->name());
  569. X         (void) ::strcat(var_name, suffix_str);
  570. X         ShellVariable  sh_var(var_name);
  571. X         sh_var.set(ShellCmdArgBool::True());
  572. X         shell->set(sh_var);
  573. X      } else {
  574. X         // output the value
  575. X         if (sh_cmdarg->is_array()) {
  576. X            shell->set(sh_cmdarg->array(), array_variant);
  577. X         } else {
  578. X            shell->set(sh_cmdarg->variable());
  579. X         }
  580. X      }
  581. X   } //for
  582. X}
  583. X
  584. X
  585. X//-------------------------------------------- CmdParseCommand::CmdParseCommand
  586. X
  587. XCmdParseCommand::CmdParseCommand(const char * name)
  588. X   : CmdLine(name),
  589. X     anywhere('a', "anywhere",
  590. X        "Allow options (and keywords) to follow positional parameters."
  591. X     ),
  592. X     anycase('i', "ignore-case",
  593. X        "Ignore character case on options."
  594. X     ),
  595. X     no_abort('n', "noabort",
  596. X        "Dont exit if bad syntax; try to continue parsing."
  597. X     ),
  598. X     no_guessing('g', "noguessing",
  599. X        "Dont \"guess\" for unmatched options/keywords."
  600. X     ),
  601. X     prompt('p', "prompt",
  602. X        "Prompt the user interactively for any missing required arguments."
  603. X     ),
  604. X     opts_only('o', "options-only",
  605. X        "Dont match keywords (long-options)."
  606. X     ),
  607. X     kwds_only('k', "keywords-only",
  608. X        "Dont match options."
  609. X     ),
  610. X     quiet('q', "quiet",
  611. X        "Dont print command-line syntax error messages."
  612. X     ),
  613. X     array_variant('A', "arrays",
  614. X        "Use alternative syntax for arrays."
  615. X     ),
  616. X     usage('u', "usage",
  617. X        "Print command-line usage and exit."
  618. X     ),
  619. X     version('v', "version",
  620. X        "Print version information and exit."
  621. X     ),
  622. X     true_str('T', "true", "string",
  623. X        "The string to use for boolean arguments that are turned ON \
  624. X(default=\"TRUE\")."
  625. X     ),
  626. X     false_str('F', "false", "string",
  627. X        "The string to use for boolean arguments that are turned OFF \
  628. X(default=\"\")."
  629. X     ),
  630. X     suffix_str('S', "suffix", "string",
  631. X        "The suffix to use for missing optional values. (default=\"_FLAG\")."
  632. X     ),
  633. X     usr_shell('s', "shell", "shellname",
  634. X
  635. X        "Set program arguments using the syntax of the given shell \
  636. X(default=\"sh\")."
  637. X     ),
  638. X     input_file('f', "file", "filename",
  639. X        "The file from which program argument declarations are read."
  640. X     ),
  641. X     input_var('e', "env", "varname",
  642. X        "The environment variable containing the program argument declarations."
  643. X     ),
  644. X     input_str('d', "decls", "string",
  645. X        "The string that contains the program argument declarations."
  646. X     ),
  647. X     dummy_arg("--",
  648. X        "Indicates the end of options/keywords."
  649. X     ),
  650. X     usr_prog('N', "name", "program-name",
  651. X        "The name of the program whose arguments are to be parsed.",
  652. X        (CmdArg::isPOS | CmdArg::isREQ | CmdArg::isVALREQ)
  653. X     ),
  654. X     usr_args("[arguments ...]",
  655. X        "The program-arguments to be parsed",
  656. X     )
  657. X{
  658. X      // Append options.
  659. X   (*this) << anywhere << anycase << no_abort << no_guessing << prompt
  660. X           << opts_only << kwds_only << quiet << array_variant << usage
  661. X           << version << true_str << false_str << suffix_str << usr_shell
  662. X           << input_file << input_var << input_str << dummy_arg ;
  663. X
  664. X      // Append positional parameters.
  665. X   (*this) << usr_prog << usr_args ;
  666. X
  667. X   set(CmdLine::KWDS_ONLY);
  668. X
  669. X      // Set up defaults
  670. X   usr_shell  = "sh" ;
  671. X   true_str   = "TRUE" ;
  672. X   false_str  = "" ;
  673. X   suffix_str = "_FLAG" ;
  674. X}
  675. X
  676. X//------------------------------------------- CmdParseCommand::~CmdParseCommand
  677. X
  678. XCmdParseCommand::~CmdParseCommand(void)
  679. X{
  680. X   CmdLineCmdArgIter  iter(usr_cmd);
  681. X
  682. X   for (CmdArg * cmdarg = iter() ; cmdarg ; cmdarg = iter()) {
  683. X      delete  cmdarg ;
  684. X   }
  685. X}
  686. X
  687. X
  688. X//-------------------
  689. X// ^FUNCTION: CmdParseCommand::operator()
  690. X//
  691. X// ^SYNOPSIS:
  692. X//    CmdParseCommand::operator(iter)
  693. X//
  694. X// ^PARAMETERS:
  695. X//    CmdLineArgIter & iter;
  696. X//    -- an object to iterate over the arguments on the command-line.
  697. X//
  698. X// ^DESCRIPTION:
  699. X//    This member function is the "guts" of a CmdParseCommand object.
  700. X//    We perform all the actions necessary to read the user's argument
  701. X//    declaratins, read the user's command-line, and set the corresponding
  702. X//    shell variables.
  703. X//
  704. X// ^REQUIREMENTS:
  705. X//    None.
  706. X//
  707. X// ^SIDE-EFFECTS:
  708. X//    - Modifies all parts of the corresponding CmdParseCommand object.
  709. X//    - prints variable settings on cout
  710. X//    - prints usage/error messages on cerr
  711. X//
  712. X// ^RETURN-VALUE:
  713. X//    e_SUCCESS   --  no errors
  714. X//    e_USAGE     --  no errors - usage printed
  715. X//    e_VERSION   --  no errors - version printed
  716. X//    e_CMDSYNTAX --  command-line syntax error
  717. X//    e_BADSHELL  --  invalid shell specified
  718. X//    e_BADDECLS  --  invalid declaration(s) given
  719. X//
  720. X// ^ALGORITHM:
  721. X//    It gets complicated so follow along.
  722. X//-^^----------------
  723. Xint
  724. XCmdParseCommand::operator()(CmdLineArgIter & iter)
  725. X{
  726. X      // Parse arguments
  727. X   parse(iter);
  728. X
  729. X      // Use the specified shell
  730. X   UnixShell * shell = new UnixShell(usr_shell);
  731. X   if (! shell->is_valid()) {
  732. X      error() << "\"" << usr_shell
  733. X              << "\" is not a known command interpreter." << endl ;
  734. X      return  e_BADSHELL ;
  735. X   }
  736. X
  737. X      // Handle "-true" and "-false" options
  738. X   if (true_str.flags()  & CmdArg::GIVEN)  ShellCmdArgBool::True(true_str);
  739. X   if (false_str.flags() & CmdArg::GIVEN)  ShellCmdArgBool::False(false_str);
  740. X
  741. X      // Intitialize user's command-line
  742. X   usr_cmd.name(usr_prog);
  743. X   if (parse_declarations())  return  e_BADDECLS ;
  744. X
  745. X      // Set user parsing preferences
  746. X   if (anywhere)     usr_cmd.clear(CmdLine::OPTS_FIRST);
  747. X   if (anycase)      usr_cmd.set(CmdLine::ANY_CASE_OPTS);
  748. X   if (no_abort)     usr_cmd.set(CmdLine::NO_ABORT);
  749. X   if (no_guessing)  usr_cmd.set(CmdLine::NO_GUESSING);
  750. X   if (prompt)       usr_cmd.set(CmdLine::PROMPT_USER);
  751. X   if (opts_only)    usr_cmd.set(CmdLine::OPTS_ONLY);
  752. X   if (kwds_only)    usr_cmd.set(CmdLine::KWDS_ONLY);
  753. X   if (quiet)        usr_cmd.set(CmdLine::QUIET);
  754. X
  755. X      // Just print usage if thats all that is desired
  756. X   if (usage) {
  757. X      usr_cmd.usage(cout);
  758. X      return  e_USAGE ;
  759. X   }
  760. X
  761. X      // Parse user's command-line
  762. X   usr_cmd.prologue();
  763. X   for (int i = 0 ; i < usr_args.count() ; i++) {
  764. X      usr_cmd.parse_arg(usr_args[i]);
  765. X   }
  766. X   usr_cmd.epilogue();
  767. X
  768. X      // Set user's variables
  769. X   set_args(shell);
  770. X
  771. X   delete  shell ;
  772. X   return  0;
  773. X}
  774. X
  775. END_OF_FILE
  776. if test 22245 -ne `wc -c <'src/cmd/cmdparse.c'`; then
  777.     echo shar: \"'src/cmd/cmdparse.c'\" unpacked with wrong size!
  778. fi
  779. # end of 'src/cmd/cmdparse.c'
  780. fi
  781. if test -f 'src/lib/unix.c' -a "${1}" != "-c" ; then 
  782.   echo shar: Will not clobber existing file \"'src/lib/unix.c'\"
  783. else
  784. echo shar: Extracting \"'src/lib/unix.c'\" \(26093 characters\)
  785. sed "s/^X//" >'src/lib/unix.c' <<'END_OF_FILE'
  786. X//------------------------------------------------------------------------
  787. X// ^FILE: unix.c - implement the unix-specific portions of CmdLine
  788. X//
  789. X// ^DESCRIPTION:
  790. X//     This file implements the public and private CmdLine library functions
  791. X//  that are specific to the native command-line syntax of Unix.
  792. X//
  793. X//  The following functions are implemented:
  794. X//
  795. X//     CmdLine::parse_option() -- parse an option
  796. X//     CmdLine::parse_keyword() -- parse a keyword
  797. X//     CmdLine::parse_value() -- parse a value
  798. X//     CmdLine::parse_arg() -- parse a single argument
  799. X//     CmdLine::arg_error() -- format an argument for error messages
  800. X//     CmdLine::fmt_arg()   -- format an argument for usage messages
  801. X//
  802. X// ^HISTORY:
  803. X//    01/09/92    Brad Appleton    <brad@ssd.csd.harris.com>    Created
  804. X//-^^---------------------------------------------------------------------
  805. X
  806. X#include <iostream.h>
  807. X#include <strstream.h>
  808. X#include <stdlib.h>
  809. X#include <string.h>
  810. X
  811. X#include "exits.h"
  812. X#include "cmdline.h"
  813. X#include "states.h"
  814. X
  815. X  // Prefix string used for short options
  816. Xstatic const char OPT_PFX[] = "-" ;
  817. X
  818. X  // Function to tell us if an argument looks like an option
  819. Xinline static int
  820. XisOPTION(const char * s)  {
  821. X   return  ((*(s) == '-') && ((*((s)+1) != '-')) && ((*((s)+1) != '\0'))) ;
  822. X}
  823. X
  824. X  // Need a prefix string for a long-option and a function to tell us
  825. X  // if an argument looks like a long-option as well.
  826. X  //
  827. X#ifdef  USE_PLUS
  828. X   static const char KWD_PFX[] = "+" ;
  829. X
  830. X   inline static int
  831. X   isKEYWORD(const char *s) {
  832. X      return   ((*(s) == '+') && ((*((s)+1) != '\0'))) ;
  833. X   }
  834. X#else
  835. X   static const char KWD_PFX[] = "--" ;
  836. X
  837. X   inline static int
  838. X   isKEYWORD(const char *s) {
  839. X      return  ((*(s) == '-') && (*((s)+1) == '-')  && (*((s)+2) != '\0')) ;
  840. X   }
  841. X#endif
  842. X
  843. X   // Need to know when an argument means "end-of-options"
  844. Xinline static int
  845. XisENDOPTIONS(const char *s) {
  846. X   return  ((*(s) == '-') && (*((s)+1) == '-')  && (*((s)+2) == '\0')) ;
  847. X}
  848. X
  849. X
  850. X//-------
  851. X// ^FUNCTION: CmdLine::parse_option - parse a Unix option
  852. X//
  853. X// ^SYNOPSIS:
  854. X//    unsigned CmdLine::parse_option(arg);
  855. X//
  856. X// ^PARAMETERS:
  857. X//    const char * arg;
  858. X//    -- the command-line argument containing the prospective option
  859. X//
  860. X// ^DESCRIPTION:
  861. X//    This routine will attempt to "handle" all options specified in
  862. X//    the string "arg". For each option found, its compile-function
  863. X//    is called and the corresponding state of both the command
  864. X//    and of the matched option(s) is (are) updated.
  865. X//
  866. X// ^REQUIREMENTS:
  867. X//    "arg" should point past any leading option prefix (such as "-").
  868. X//
  869. X// ^SIDE-EFFECTS:
  870. X//    "cmd" is modified accordingly as each option is parsed (as are its
  871. X//    constituent arguments). If there are syntax errors then error messages
  872. X//    are printed if QUIET is NOT set.
  873. X//
  874. X// ^RETURN-VALUE:
  875. X//    NO_ERROR is returned if no errors were encountered. Otherwise the
  876. X//    return value is a combination of bitmasks of type CmdLine::CmdStatus
  877. X//    (defined in <cmdline.h>) indicating all the problems that occurred.
  878. X//
  879. X// ^ALGORITHM:
  880. X//    see if we left an option dangling without parsing its value.
  881. X//    for each option bundled in "arg"
  882. X//       try to match the option
  883. X//       if no match issue a message (unless QUIET is set)
  884. X//       else
  885. X//          if the option takes NO argument than handle that
  886. X//          else if the option takes an argument
  887. X//             if the rest or arg is not empty then
  888. X//                call the option's compile-function using the rest of
  889. X//                                                    arg as the value.
  890. X//                skip past whatever portion of value was used
  891. X//             else
  892. X//                update the state of the command to show that we are expecting
  893. X//                       to see the value for this option as the next argument.
  894. X//             endif
  895. X//          endif
  896. X//          update the state of the argument.
  897. X//       endif
  898. X//    endfor
  899. X//-^^----
  900. Xunsigned
  901. XCmdLine::parse_option(const char * arg)
  902. X{
  903. X   const  char * save_arg = arg;
  904. X   unsigned  save_flags = 0, rc = 0 ;
  905. X   CmdArg * cmdarg = NULL;
  906. X   int  bad_val;
  907. X
  908. X   // see if we left an argument dangling without a value
  909. X   ck_need_val() ;
  910. X
  911. X   do {  // loop over bundled options
  912. X      cmdarg = opt_match(*arg);
  913. X      if (cmdarg == NULL) {
  914. X         // If we were in the middle of a guess - sorry no cigar, otherwise
  915. X         // guess that maybe this is a keyword and not an keyword.
  916. X         //
  917. X         if (cmd_state & cmd_GUESSING) {
  918. X            if (arg == save_arg)  return  BAD_OPTION;
  919. X         } else {
  920. X            if (! (cmd_flags & NO_GUESSING)) {
  921. X               cmd_state |= cmd_GUESSING;
  922. X               rc = parse_keyword(arg);
  923. X               cmd_state &= ~cmd_GUESSING;
  924. X               if (rc != BAD_KEYWORD)  return  rc;
  925. X            }
  926. X         }
  927. X         if (! (cmd_flags & QUIET)) {
  928. X            error() << "unknown option \"" << OPT_PFX << char(*arg)
  929. X                    << "\"." << endl ;
  930. X         }
  931. X         rc |= BAD_OPTION ;
  932. X         ++arg ;  // skip bad option
  933. X         continue ;
  934. X      }
  935. X      ++arg ;  // skip past option character
  936. X      save_flags = cmdarg->flags() ;
  937. X      cmdarg->clear();
  938. X      cmdarg->set(CmdArg::OPTION) ;
  939. X      if ((! *arg) && (cmdarg->syntax() & CmdArg::isVALTAKEN)) {
  940. X         // End of string -- value must be in next arg
  941. X         // Save this cmdarg-pointer for later and set the parse_state to
  942. X         // indicate that we are expecting a value.
  943. X         //
  944. X
  945. X         if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  946. X            // If this argument is sticky we already missed our chance
  947. X            // at seeing a value.
  948. X            //
  949. X            if (cmdarg->syntax() & CmdArg::isVALREQ) {
  950. X               if (! (cmd_flags & QUIET)) {
  951. X                  error() << "value required in same argument for "
  952. X                          << OPT_PFX << char(cmdarg->char_name())
  953. X                          << " option." << endl;
  954. X               }
  955. X               rc |= (VAL_MISSING | VAL_NOTSTICKY) ;
  956. X               cmdarg->flags(save_flags);
  957. X            } else {
  958. X               // The value is optional - set the GIVEN flag and call
  959. X               // handle_arg with NULL (and check the result).
  960. X               //
  961. X               const char * null_str = NULL;
  962. X               cmdarg->set(CmdArg::GIVEN) ;
  963. X               cmd_parse_state = cmd_START_STATE ;
  964. X               bad_val = handle_arg(cmdarg, null_str);
  965. X               if (bad_val) {
  966. X                  if (! (cmd_flags & QUIET)) {
  967. X                     arg_error("bad value for", cmdarg) << "." << endl ;
  968. X                  }
  969. X                  rc |= BAD_VALUE ;
  970. X                  cmdarg->flags(save_flags);
  971. X               }
  972. X            }
  973. X         } else {
  974. X            // Wait for the value to show up next time around
  975. X            cmdarg->set(CmdArg::GIVEN) ;
  976. X            cmd_matched_arg = cmdarg ;
  977. X            cmd_parse_state = cmd_WANT_VAL ;
  978. X            if (cmdarg->syntax() & CmdArg::isVALREQ) {
  979. X               cmd_parse_state += cmd_TOK_REQUIRED ;
  980. X            }
  981. X         }
  982. X         return  rc ;
  983. X      }
  984. X
  985. X      // If this option is an isVALSEP and "arg" is not-empty then we
  986. X      // have an error.
  987. X      //
  988. X      if ((cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  989. X          (cmdarg->syntax() & CmdArg::isVALSEP)) {
  990. X         if (! (cmd_flags & QUIET)) {
  991. X            error() << "value required in separate argument for "
  992. X                    << OPT_PFX << char(cmdarg->char_name())
  993. X                    << " option." << endl;
  994. X         }
  995. X         rc |= (VAL_MISSING | VAL_NOTSEP) ;
  996. X         cmdarg->flags(save_flags);
  997. X         return  rc;
  998. X      } else {
  999. X         // handle the option
  1000. X         const char * save_arg = arg;
  1001. X         bad_val = handle_arg(cmdarg, arg);
  1002. X         if (bad_val) {
  1003. X            if (! (cmd_flags & QUIET)) {
  1004. X               arg_error("bad value for", cmdarg) << "." << endl ;
  1005. X            }
  1006. X            rc |= BAD_VALUE ;
  1007. X            cmdarg->flags(save_flags);
  1008. X         }
  1009. X         cmdarg->set(CmdArg::GIVEN);
  1010. X         if (arg != save_arg)  cmdarg->set(CmdArg::VALGIVEN);
  1011. X      }
  1012. X   } while (arg && *arg) ;
  1013. X
  1014. X   return  rc ;
  1015. X}
  1016. X
  1017. X
  1018. X//-------
  1019. X// ^FUNCTION: CmdLine::parse_keyword - parse a Unix keyword
  1020. X//
  1021. X// ^SYNOPSIS:
  1022. X//    unsigned CmdLine::parse_keyword(arg);
  1023. X//
  1024. X// ^PARAMETERS:
  1025. X//    const char * arg;
  1026. X//    -- the command-line argument containing the prospective keyword
  1027. X//
  1028. X// ^DESCRIPTION:
  1029. X//    This routine will attempt to "handle" the keyword specified in
  1030. X//    the string "arg". For any keyword found, its compile-function
  1031. X//    is called and the corresponding state of both the command
  1032. X//    and of the matched keyword(s) is (are) updated.
  1033. X//
  1034. X// ^REQUIREMENTS:
  1035. X//    "arg" should point past any leading keyword prefix (such as "--").
  1036. X//
  1037. X// ^SIDE-EFFECTS:
  1038. X//    "cmd" is modified accordingly as the keyword is parsed (as are its
  1039. X//    constituent arguments). If there are syntax errors then error messages
  1040. X//    are printed if QUIET is NOT set.
  1041. X//
  1042. X// ^RETURN-VALUE:
  1043. X//    NO_ERROR is returned if no errors were encountered. Otherwise the
  1044. X//    return value is a combination of bitmasks of type CmdLine::CmdStatus
  1045. X//    (defined in <cmdline.h>) indicating all the problems that occurred.
  1046. X//
  1047. X// ^ALGORITHM:
  1048. X//    see if we left an option dangling without parsing its value.
  1049. X//    look for a possible value for this keyword denoted by ':' or '='
  1050. X//    try to match "arg" as a keyword
  1051. X//    if no match issue a message (unless QUIET is set)
  1052. X//    else
  1053. X//       if the keyword takes NO argument than handle that
  1054. X//       else if the keyword takes an argument
  1055. X//          if a value was found "arg"
  1056. X//             call the keyword's compile-function with the value found.
  1057. X//          else
  1058. X//             update the state of the command to show that we are expecting
  1059. X//                    to see the value for this option as the next argument.
  1060. X//          endif
  1061. X//       endif
  1062. X//       update the state of the argument.
  1063. X//    endif
  1064. X//-^^----
  1065. Xunsigned
  1066. XCmdLine::parse_keyword(const char * arg)
  1067. X{
  1068. X   unsigned  save_flags = 0, rc = 0 ;
  1069. X   CmdArg * cmdarg = NULL ;
  1070. X   int  ambiguous = 0, len = -1, bad_val;
  1071. X   const char * val = NULL ;
  1072. X
  1073. X   // see if we left an argument dangling without a value
  1074. X   ck_need_val() ;
  1075. X
  1076. X   // If there is a value with this argument, get it now!
  1077. X   val = ::strpbrk(arg, ":=") ;
  1078. X   if (val) {
  1079. X      len = val - arg ;
  1080. X      ++val ;
  1081. X   }
  1082. X
  1083. X   cmdarg = kwd_match(arg, len, ambiguous);
  1084. X   if (cmdarg == NULL) {
  1085. X      // If we were in the middle of a guess - sorry no cigar, otherwise
  1086. X      // guess that maybe this is an option and not a keyword.
  1087. X      //
  1088. X      if (cmd_state & cmd_GUESSING) {
  1089. X         return  BAD_KEYWORD;
  1090. X      } else if ((! ambiguous) || (len == 1)) {
  1091. X         if (! (cmd_flags & NO_GUESSING)) {
  1092. X            cmd_state |= cmd_GUESSING;
  1093. X            rc = parse_option(arg);
  1094. X            cmd_state &= ~cmd_GUESSING;
  1095. X            if (rc != BAD_OPTION)  return  rc;
  1096. X         }
  1097. X      }
  1098. X      if (! (cmd_flags & QUIET)) {
  1099. X         error() << ((ambiguous) ? "ambiguous" : "unknown") << " option "
  1100. X                 << "\"" << ((cmd_flags & KWDS_ONLY) ? OPT_PFX : KWD_PFX)
  1101. X                 << arg << "\"." << endl ;
  1102. X      }
  1103. X      rc |= ((ambiguous) ? KWD_AMBIGUOUS : BAD_KEYWORD) ;
  1104. X      return  rc ;
  1105. X   }
  1106. X
  1107. X   save_flags = cmdarg->flags() ;
  1108. X   cmdarg->clear();
  1109. X   cmdarg->set(CmdArg::KEYWORD) ;
  1110. X   if ((cmdarg->syntax() & CmdArg::isVALTAKEN) && (val == NULL)) {
  1111. X      // Value must be in the next argument.
  1112. X      // Save this cmdarg for later and indicate that we are
  1113. X      // expecting a value.
  1114. X      //
  1115. X      if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  1116. X         // If this argument is sticky we already missed our chance
  1117. X         // at seeing a value.
  1118. X         //
  1119. X         if (cmdarg->syntax() & CmdArg::isVALREQ) {
  1120. X            if (! (cmd_flags & QUIET)) {
  1121. X               error() << "value required in same argument for "
  1122. X                       << ((cmd_flags & KWDS_ONLY) ? OPT_PFX : KWD_PFX)
  1123. X                       << cmdarg->keyword_name() << " option." << endl;
  1124. X            }
  1125. X            rc |= (VAL_MISSING | VAL_NOTSTICKY) ;
  1126. X            cmdarg->flags(save_flags);
  1127. X         } else {
  1128. X            // The value is optional - set the GIVEN flag and call
  1129. X            // handle_arg with NULL (and check the result).
  1130. X            //
  1131. X            const char * null_str = NULL;
  1132. X            cmdarg->set(CmdArg::GIVEN) ;
  1133. X            cmd_parse_state = cmd_START_STATE ;
  1134. X            bad_val = handle_arg(cmdarg, null_str);
  1135. X            if (bad_val) {
  1136. X               if (! (cmd_flags & QUIET)) {
  1137. X                  arg_error("bad value for", cmdarg) << "." << endl ;
  1138. X               }
  1139. X               rc |= BAD_VALUE ;
  1140. X               cmdarg->flags(save_flags);
  1141. X            }
  1142. X         }
  1143. X      } else {
  1144. X         // Wait for the value to show up next time around
  1145. X         cmdarg->set(CmdArg::GIVEN) ;
  1146. X         cmd_matched_arg = cmdarg ;
  1147. X         cmd_parse_state = cmd_WANT_VAL ;
  1148. X         if (cmdarg->syntax() & CmdArg::isVALREQ) {
  1149. X            cmd_parse_state += cmd_TOK_REQUIRED ;
  1150. X         }
  1151. X      }
  1152. X      return  rc ;
  1153. X   }
  1154. X
  1155. X   // If this option is an isVALSEP and "val" is not-NULL then we
  1156. X   // have an error.
  1157. X   //
  1158. X   if (val  &&  (cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  1159. X       (cmdarg->syntax() & CmdArg::isVALSEP)) {
  1160. X      if (! (cmd_flags & QUIET)) {
  1161. X         error() << "value required in separate argument for "
  1162. X                 << ((cmd_flags & KWDS_ONLY) ? OPT_PFX : KWD_PFX)
  1163. X                 << cmdarg->keyword_name() << " option." << endl;
  1164. X      }
  1165. X      rc |= (VAL_MISSING | VAL_NOTSEP) ;
  1166. X      cmdarg->flags(save_flags);
  1167. X      return  rc;
  1168. X   }
  1169. X   // handle the keyword
  1170. X   bad_val = handle_arg(cmdarg, val);
  1171. X   if (bad_val) {
  1172. X      if (! (cmd_flags & QUIET)) {
  1173. X         arg_error("bad value for", cmdarg) << "." << endl ;
  1174. X      }
  1175. X      rc |= BAD_VALUE ;
  1176. X      cmdarg->flags(save_flags);
  1177. X   }
  1178. X
  1179. X   return  rc ;
  1180. X}
  1181. X
  1182. X
  1183. X//-------
  1184. X// ^FUNCTION: CmdLine::parse_value - parse a Unix value
  1185. X//
  1186. X// ^SYNOPSIS:
  1187. X//    unsigned CmdLine::parse_value(arg);
  1188. X//
  1189. X// ^PARAMETERS:
  1190. X//    const char * arg;
  1191. X//    -- the command-line argument containing the prospective value
  1192. X//
  1193. X// ^DESCRIPTION:
  1194. X//    This routine will attempt to "handle" the value specified in
  1195. X//    the string "arg". The compile-function of the corresponding
  1196. X//    argument-value is called and the corresponding state of both
  1197. X//    the command and of the matched option(s) is (are) updated.
  1198. X//    If the value corresponds to a multi-valued argument, then that
  1199. X//    is handled here.
  1200. X//
  1201. X// ^REQUIREMENTS:
  1202. X//
  1203. X// ^SIDE-EFFECTS:
  1204. X//    "cmd" is modified accordingly for the value that is parsed (as are its
  1205. X//    constituent arguments). If there are syntax errors then error messages
  1206. X//    are printed if QUIET is NOT set.
  1207. X//
  1208. X// ^RETURN-VALUE:
  1209. X//    NO_ERROR is returned if no errors were encountered. Otherwise the
  1210. X//    return value is a combination of bitmasks of type CmdLine::CmdStatus
  1211. X//    (defined in <cmdline.h>) indicating all the problems that occurred.
  1212. X//
  1213. X// ^ALGORITHM:
  1214. X//    If the command-state says we are waiting for the value of an option
  1215. X//       find the option that we matched last
  1216. X//    else
  1217. X//       match the next positional parameter in "cmd"
  1218. X//       if there isnt one issue a "too many args" message
  1219. X//               (unless cmd_QUIETs is set) and return.
  1220. X//    endif
  1221. X//    handle the given value and update the argument and command states.
  1222. X//-^^----
  1223. Xunsigned
  1224. XCmdLine::parse_value(const char * arg)
  1225. X{
  1226. X   unsigned  save_flags = 0, rc = 0 ;
  1227. X   int  bad_val;
  1228. X   CmdArg * cmdarg = NULL;
  1229. X
  1230. X   if (cmd_parse_state & cmd_WANT_VAL) {
  1231. X      if (cmd_matched_arg == NULL) {
  1232. X         cerr << "*** Internal error in class CmdLine.\n"
  1233. X              << "\tparse-state is inconsistent with last-matched-arg."
  1234. X              << endl ;
  1235. X         ::exit(e_INTERNAL);
  1236. X      }
  1237. X      // get back the cmdarg that we saved for later
  1238. X      // - here is the value it was expecting
  1239. X      //
  1240. X      cmdarg = cmd_matched_arg ;
  1241. X      save_flags = cmdarg->flags() ;
  1242. X   } else {
  1243. X      // argument is positional - find out which one it is
  1244. X      cmdarg = pos_match() ;
  1245. X      if (cmdarg == NULL) {
  1246. X         if (! (cmd_flags & QUIET)) {
  1247. X            error() << "too many arguments given." << endl ;
  1248. X         }
  1249. X         rc |= TOO_MANY_ARGS ;
  1250. X         return  rc ;
  1251. X      }
  1252. X      save_flags = cmdarg->flags() ;
  1253. X      cmdarg->clear();
  1254. X      cmdarg->set(CmdArg::POSITIONAL) ;
  1255. X      if (cmd_flags & OPTS_FIRST) {
  1256. X         cmd_state |= cmd_END_OF_OPTIONS ;
  1257. X      }
  1258. X   }
  1259. X
  1260. X   // handle this value
  1261. X   cmdarg->set(CmdArg::VALSEP) ;
  1262. X   bad_val = handle_arg(cmdarg, arg);
  1263. X   if (bad_val) {
  1264. X      if (! (cmd_flags & QUIET)) {
  1265. X         arg_error("bad value for", cmdarg) << "." << endl ;
  1266. X      }
  1267. X      rc |= BAD_VALUE ;
  1268. X      cmdarg->flags(save_flags);
  1269. X      if (! (cmdarg->syntax() & CmdArg::isLIST)) {
  1270. X         cmd_parse_state = cmd_START_STATE;
  1271. X      }
  1272. X   }
  1273. X
  1274. X   // If the value was okay and we were requiring a value, then
  1275. X   // a value is no longer required.
  1276. X   //
  1277. X   if ((! bad_val) && (cmdarg->syntax() & CmdArg::isLIST)) {
  1278. X      cmd_parse_state &= ~cmd_TOK_REQUIRED ;
  1279. X   }
  1280. X
  1281. X   return  rc ;
  1282. X}
  1283. X
  1284. X
  1285. X//-------
  1286. X// ^FUNCTION: CmdLine::parse_arg - parse an argv[] element unix-style
  1287. X//
  1288. X// ^SYNOPSIS:
  1289. X//    unsigned CmdLine::parse_arg(arg)
  1290. X//
  1291. X// ^PARAMETERS:
  1292. X//    const char * arg;
  1293. X//    -- an argument string (argv[] element) from the command-line
  1294. X//
  1295. X// ^DESCRIPTION:
  1296. X//    This routine will determine whether "arg" is an option, a long-option,
  1297. X//    or a value and call the appropriate parse_xxxx function defined above.
  1298. X//
  1299. X// ^REQUIREMENTS:
  1300. X//
  1301. X// ^SIDE-EFFECTS:
  1302. X//    "cmd" is modified accordingly for the string that is parsed (as are its
  1303. X//    constituent arguments). If there are syntax errors then error messages
  1304. X//    are printed if QUIET is NOT set.
  1305. X//
  1306. X// ^RETURN-VALUE:
  1307. X//    NO_ERROR is returned if no errors were encountered. Otherwise the
  1308. X//    return value is a combination of bitmasks of type CmdLine::CmdStatus
  1309. X//    (defined in <cmdline.h>) indicating all the problems that occurred.
  1310. X//
  1311. X// ^ALGORITHM:
  1312. X//    if we are expecting a required value
  1313. X//       call parse_value()
  1314. X//    else if "arg" is an option
  1315. X//       skip past the option prefix
  1316. X//       call parse_option()
  1317. X//    else if "arg" is a keyword
  1318. X//       skip past the kewyord prefix
  1319. X//       call parse_keyword()
  1320. X//    else if "arg" is "--" (meaning end of options)
  1321. X//       see that we didnt leave an option dangling without a value
  1322. X//       indicate end-of-options in the command-state
  1323. X//    else
  1324. X//       call parse_value()
  1325. X//    endif
  1326. X//-^^----
  1327. Xunsigned
  1328. XCmdLine::parse_arg(const char * arg)
  1329. X{
  1330. X   if (arg == NULL)  return  cmd_status ;
  1331. X
  1332. X   if (cmd_parse_state & cmd_TOK_REQUIRED) {
  1333. X      // If a required value is expected, then this argument MUST be
  1334. X      // the value (even if it looks like an option
  1335. X      //
  1336. X      cmd_status |= parse_value(arg) ;
  1337. X   } else if (isOPTION(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  1338. X      ++arg ;  // skip '-' option character
  1339. X      if (cmd_flags & KWDS_ONLY) {
  1340. X         cmd_state  |= cmd_KEYWORDS_USED ;
  1341. X         cmd_status |=  parse_keyword(arg) ;
  1342. X      } else {
  1343. X         cmd_state  |= cmd_OPTIONS_USED ;
  1344. X         cmd_status |=  parse_option(arg) ;
  1345. X      }
  1346. X   } else if ((! (cmd_flags & OPTS_ONLY))
  1347. X              && isKEYWORD(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  1348. X      cmd_state |= cmd_KEYWORDS_USED ;
  1349. X      ++arg ;  // skip over '+' keyword prefix
  1350. X#ifndef USE_PLUS
  1351. X      ++arg ;  // skip over '--' keyword prefix
  1352. X#endif
  1353. X      cmd_status |= parse_keyword(arg) ;
  1354. X   } else if (isENDOPTIONS(arg) && (! (cmd_state & cmd_END_OF_OPTIONS))) {
  1355. X      cmd_state |= cmd_END_OF_OPTIONS ;
  1356. X      // see if we left an argument dangling without a value
  1357. X      ck_need_val() ;
  1358. X   } else {
  1359. X      cmd_status |= parse_value(arg) ;
  1360. X   }
  1361. X
  1362. X   return  cmd_status ;
  1363. X}
  1364. X
  1365. X//-------
  1366. X// ^FUNCTION: CmdLine::arg_error - format an argument for error messages
  1367. X//
  1368. X// ^SYNOPSIS:
  1369. X//    ostream & arg_error(error_str, cmdarg);
  1370. X//
  1371. X// ^PARAMETERS:
  1372. X//    const char * error_str;
  1373. X//    -- the problem with the argument
  1374. X//
  1375. X//    const CmdArg * cmdarg;
  1376. X//    -- the argument to be formatted
  1377. X//
  1378. X// ^DESCRIPTION:
  1379. X//    This function will write to "os" the argument corresponding to
  1380. X//    "cmdarg" as we would like it to appear in error messages that pertain
  1381. X//    to this argument.
  1382. X//
  1383. X// ^REQUIREMENTS:
  1384. X//    None.
  1385. X//
  1386. X// ^SIDE-EFFECTS:
  1387. X//    writes to "os"
  1388. X//
  1389. X// ^RETURN-VALUE:
  1390. X//    A reference to os.
  1391. X//
  1392. X// ^ALGORITHM:
  1393. X//    Pretty straightforward, just print to os the way we
  1394. X//    want the argument to appear in usage messages.
  1395. X//-^^----
  1396. Xostream &
  1397. XCmdLine::arg_error(const char * error_str, const CmdArg * cmdarg) const
  1398. X{
  1399. X   ostream & os = error() << error_str << char(' ') ;
  1400. X
  1401. X   if (cmdarg->flags() & CmdArg::GIVEN) {
  1402. X       if (cmdarg->flags() & CmdArg::KEYWORD) {
  1403. X          os << ((cmd_flags & KWDS_ONLY) ? OPT_PFX : KWD_PFX)
  1404. X             << cmdarg->keyword_name() << " option" ;
  1405. X       } else if (cmdarg->flags() & CmdArg::OPTION) {
  1406. X          os << OPT_PFX << (char)cmdarg->char_name() << " option" ;
  1407. X       } else {
  1408. X          os << cmdarg->value_name() << " argument" ;
  1409. X       }
  1410. X   } else {
  1411. X       if (cmdarg->syntax() & CmdArg::isPOS) {
  1412. X          os << cmdarg->value_name() << " argument" ;
  1413. X       } else {
  1414. X          if (cmd_flags & KWDS_ONLY) {
  1415. X             os << OPT_PFX << cmdarg->keyword_name() << " option" ;
  1416. X          } else {
  1417. X             os << OPT_PFX << (char)cmdarg->char_name() << " option" ;
  1418. X          }
  1419. X       }
  1420. X   }
  1421. X   return  os;
  1422. X}
  1423. X
  1424. X
  1425. X//-------
  1426. X// ^FUNCTION: CmdLine::fmt_arg - format an argument for usage messages
  1427. X//
  1428. X// ^SYNOPSIS:
  1429. X//    unsigned CmdLine::fmt_arg(cmdarg, buf, bufsize, syntax, level);
  1430. X//
  1431. X// ^PARAMETERS:
  1432. X//    const CmdArg * cmdarg;
  1433. X//    -- the argument to be formatted
  1434. X//
  1435. X//    char * buf;
  1436. X//    -- where to print the formatted result
  1437. X//
  1438. X//    unsigned bufsize;
  1439. X//    -- number of bytes allocated for buf.
  1440. X//
  1441. X//    CmdLine::CmdLineSyntax syntax;
  1442. X//    -- the syntax to use (option, long-option, or both).
  1443. X//
  1444. X//    CmdLine::CmdUsageLevel;
  1445. X//    -- the usage-level corresponding to this portion of the
  1446. X//       usage message.
  1447. X//
  1448. X// ^DESCRIPTION:
  1449. X//    This function will write into "buf" the argument corresponding to
  1450. X//    "cmdarg" as we would like it to appear in usage messages.
  1451. X//
  1452. X// ^REQUIREMENTS:
  1453. X//    "buf" must be large enough to hold the result.
  1454. X//
  1455. X// ^SIDE-EFFECTS:
  1456. X//    writes to "buf"
  1457. X//
  1458. X// ^RETURN-VALUE:
  1459. X//    the length of the formatted result.
  1460. X//
  1461. X// ^ALGORITHM:
  1462. X//    Its kind of tedious so follow along.
  1463. X//-^^----
  1464. Xunsigned
  1465. XCmdLine::fmt_arg(const CmdArg           * cmdarg,
  1466. X                 char                   * buf,
  1467. X                 unsigned                 bufsize,
  1468. X                 CmdLine::CmdLineSyntax   syntax,
  1469. X                 CmdLine::CmdUsageLevel   level) const
  1470. X{
  1471. X   ostrstream  oss(buf, bufsize);
  1472. X   *buf = '\0';
  1473. X
  1474. X   char optchar = cmdarg->char_name();
  1475. X   const char * keyword = cmdarg->keyword_name();
  1476. X
  1477. X   // Need to adjust the syntax if optchar or keyword is empty
  1478. X   if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  1479. X       ((! optchar) || (keyword == NULL))) {
  1480. X      if (keyword == NULL) {
  1481. X         if ((cmd_flags & KWDS_ONLY) && (cmd_flags & NO_GUESSING)) {
  1482. X            return  0;
  1483. X         } else {
  1484. X            syntax = cmd_OPTS_ONLY;
  1485. X         }
  1486. X      }
  1487. X      if (! optchar) {
  1488. X         if ((cmd_flags & OPTS_ONLY) && (cmd_flags & NO_GUESSING)) {
  1489. X            return  0;
  1490. X         } else {
  1491. X            syntax = cmd_KWDS_ONLY;
  1492. X         }
  1493. X      }
  1494. X   }
  1495. X
  1496. X   // If the argument is optional - print the leading '['
  1497. X   if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  1498. X      oss << char('[') ;
  1499. X   }
  1500. X
  1501. X   // If we have a sticky-argument and usage is cmd_BOTH then it gets
  1502. X   // really hairy so we just treat this as a special case right here
  1503. X   // and now.
  1504. X   //
  1505. X   if ((syntax == cmd_BOTH) &&
  1506. X       (! (cmdarg->syntax() & CmdArg::isPOS)) &&
  1507. X       (cmdarg->syntax() & CmdArg::isVALTAKEN) &&
  1508. X       (cmdarg->syntax() & CmdArg::isVALSTICKY))
  1509. X   {
  1510. X      if (cmdarg->syntax() & CmdArg::isVALOPT) {
  1511. X         oss << OPT_PFX << char(optchar) << char('[') << cmdarg->value_name()
  1512. X             << "]|" << KWD_PFX << keyword << "[=" << cmdarg->value_name()
  1513. X             << char(']') ;
  1514. X      } else {
  1515. X         oss << OPT_PFX << optchar << cmdarg->value_name() << char('|')
  1516. X             << KWD_PFX << keyword << char('=') << cmdarg->value_name() ;
  1517. X      }
  1518. X      if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) {
  1519. X         oss << " ..." ;
  1520. X      }
  1521. X      if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  1522. X         oss << char(']') ;
  1523. X      }
  1524. X      oss << ends ;
  1525. X      return  (oss.pcount() - 1);
  1526. X   }
  1527. X
  1528. X   if (! (cmdarg->syntax() & CmdArg::isPOS)) {
  1529. X      switch(syntax) {
  1530. X         case cmd_OPTS_ONLY :
  1531. X            oss << OPT_PFX << char(optchar) ;
  1532. X            break ;
  1533. X
  1534. X         case cmd_KWDS_ONLY :
  1535. X            oss << ((cmd_flags & KWDS_ONLY) ? OPT_PFX : KWD_PFX) << keyword ;
  1536. X            break ;
  1537. X
  1538. X         case cmd_BOTH :
  1539. X            oss << OPT_PFX << char(optchar) << char('|')
  1540. X                << KWD_PFX << keyword ;
  1541. X            break ;
  1542. X
  1543. X         default :
  1544. X            cerr << "*** Internal error in class CmdLine.\n"
  1545. X                 << "\tunknown CmdLineSyntax value (" << int(syntax) << ")."
  1546. X                 << endl ;
  1547. X            ::exit(e_INTERNAL);
  1548. X      } //switch
  1549. X      if (cmdarg->syntax() & CmdArg::isVALTAKEN) {
  1550. X         if (! (cmdarg->syntax() & CmdArg::isVALSTICKY)) {
  1551. X            oss << char(' ') ;
  1552. X         }
  1553. X      }
  1554. X   }
  1555. X
  1556. X   // If the argument takes a value then print the value
  1557. X   if (cmdarg->syntax() & CmdArg::isVALTAKEN) {
  1558. X      if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  1559. X          (cmdarg->syntax() & CmdArg::isVALOPT))
  1560. X      {
  1561. X         oss << char('[') ;
  1562. X      }
  1563. X      if (cmdarg->syntax() & CmdArg::isVALSTICKY) {
  1564. X         if (syntax == cmd_KWDS_ONLY)  oss << char('=') ;
  1565. X      }
  1566. X      oss << cmdarg->value_name() ;
  1567. X      if ((level == VERBOSE_USAGE) && (cmdarg->syntax() & CmdArg::isLIST)) {
  1568. X         oss << " ..." ;
  1569. X      }
  1570. X      if ((! (cmdarg->syntax() & CmdArg::isPOS)) &&
  1571. X          (cmdarg->syntax() & CmdArg::isVALOPT))
  1572. X      {
  1573. X         oss << char(']') ;
  1574. X      }
  1575. X   }
  1576. X
  1577. X   if ((level == VERBOSE_USAGE) && (! (cmdarg->syntax() & CmdArg::isREQ))) {
  1578. X      oss << char(']') ;
  1579. X   }
  1580. X   oss << ends ;
  1581. X
  1582. X   return  (oss.pcount() - 1) ;
  1583. X}
  1584. X
  1585. END_OF_FILE
  1586. if test 26093 -ne `wc -c <'src/lib/unix.c'`; then
  1587.     echo shar: \"'src/lib/unix.c'\" unpacked with wrong size!
  1588. fi
  1589. # end of 'src/lib/unix.c'
  1590. fi
  1591. echo shar: End of archive 6 \(of 7\).
  1592. cp /dev/null ark6isdone
  1593. MISSING=""
  1594. for I in 1 2 3 4 5 6 7 ; do
  1595.     if test ! -f ark${I}isdone ; then
  1596.     MISSING="${MISSING} ${I}"
  1597.     fi
  1598. done
  1599. if test "${MISSING}" = "" ; then
  1600.     echo You have unpacked all 7 archives.
  1601.     rm -f ark[1-9]isdone
  1602. else
  1603.     echo You still need to unpack the following archives:
  1604.     echo "        " ${MISSING}
  1605. fi
  1606. ##  End of shell archive.
  1607. exit 0
  1608.  
  1609. exit 0 # Just in case...
  1610.