home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume31 / cmdline / part06 < prev    next >
Text File  |  1992-07-27  |  52KB  |  1,603 lines

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