home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume17 / parseargs / part12 < prev    next >
Encoding:
Internet Message Format  |  1991-03-18  |  61.0 KB

  1. From: brad@hcx1.ssd.csd.harris.com (Brad Appleton)
  2. Newsgroups: comp.sources.misc
  3. Subject: v17i057:  parseargs - functions to parse command line arguments, Part12/12
  4. Message-ID: <1991Mar18.155817.2348@sparky.IMD.Sterling.COM>
  5. Date: 18 Mar 91 15:58:17 GMT
  6. Approved: kent@sparky.imd.sterling.com
  7. X-Checksum-Snefru: a4640e70 c2c7ef08 c902a69f 568cc2ba
  8.  
  9. Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com>
  10. Posting-number: Volume 17, Issue 57
  11. Archive-name: parseargs/part12
  12.  
  13. This is part 12 of parseargs
  14.  
  15. #!/bin/sh
  16. # this is Part.12 (part 12 of a multipart archive)
  17. # do not concatenate these parts, unpack them in order with /bin/sh
  18. # file parseargs/xparse.c continued
  19. #
  20. if test ! -r _shar_seq_.tmp; then
  21.     echo 'Please unpack part 1 first!'
  22.     exit 1
  23. fi
  24. (read Scheck
  25.  if test "$Scheck" != 12; then
  26.     echo Please unpack part "$Scheck" next!
  27.     exit 1
  28.  else
  29.     exit 0
  30.  fi
  31. ) < _shar_seq_.tmp || exit 1
  32. if test ! -f _shar_wnt_.tmp; then
  33.     echo 'x - still skipping parseargs/xparse.c'
  34. else
  35. echo 'x - continuing file parseargs/xparse.c'
  36. sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/xparse.c' &&
  37. **
  38. ** ^SIDE-EFECTS:
  39. **    None.
  40. **
  41. ** ^RETURN-VALUE:
  42. **    TRUE if fd is associated with a terminal, FALSE otherwise.
  43. **
  44. ** ^ALGORITHM:
  45. **    Trivial - just use isatty and restore errno if necessary
  46. ***^^**********************************************************************/
  47. #ifdef __ANSI_C__
  48. X   static BOOL is_interactive( int fd )
  49. #endif
  50. {
  51. #ifdef unix
  52. X   EXTERN int isatty  ARGS((int));
  53. X   extern int errno;
  54. X   int saverr = errno;  /* save errno */
  55. X
  56. X   if ( isatty(fd) )
  57. X      return  TRUE;
  58. X   else {
  59. X      errno = saverr;
  60. X      return  FALSE;
  61. X   }
  62. #else
  63. #ifdef vms
  64. X   EXTERN int isatty  ARGS((int));
  65. X   int ret = isatty( fd );
  66. X
  67. X   if ( ret == -1 ) /* error with fd */
  68. X      syserr( "error on file descriptor #%d", fd );
  69. X   else if ( ret )
  70. X      return  TRUE;
  71. X   else
  72. X      return  FALSE;
  73. #else
  74. X   return  TRUE;
  75. #endif
  76. #endif
  77. }
  78. X
  79. X
  80. /***************************************************************************
  81. ** ^FUNCTION: get_description - get the description portion of a string
  82. **
  83. ** ^SYNOPSIS:
  84. */
  85. #ifndef __ANSI_C__
  86. X   static char *get_description( str )
  87. /*
  88. ** ^PARAMETERS:
  89. */
  90. X   char *str;
  91. /*    -- the string to parse for a description
  92. */
  93. #endif  /* !__ANSI_C__ */
  94. X
  95. /* ^DESCRIPTION:
  96. **    Get_description null terminates the first portion of the string and
  97. **    returns a pointer to the second portion.
  98. **
  99. **    Two "portions" must be either separated by whitespace or the second
  100. **    portion may be within "(),{},[], or <>" delimiters. The second
  101. **    portion is assumed to begin with the first alphabetic following
  102. **    separator.
  103. **
  104. ** ^REQUIREMENTS:
  105. **    str should be non-null and non-empty
  106. **
  107. ** ^SIDE-EFECTS:
  108. **    The characters which separated the two portions of <str> are
  109. **    replaced with a single '\0'.
  110. **
  111. ** ^RETURN-VALUE:
  112. **    Address of the description (or NULL if the string has no description).
  113. **
  114. ** ^ALGORITHM:
  115. **    - locate the end of the first portion by scanning for whitespace or
  116. **      balanced delimiters.
  117. **    - locate the beginning of the second portion by scanning for the first
  118. **      alpha-numeric following the end of the first portion.
  119. **    - return the address of the description.
  120. ***^^**********************************************************************/
  121. #ifdef __ANSI_C__
  122. X   static char *get_description( char *str )
  123. #endif
  124. {
  125. X   register char  *description = CHARNULL;
  126. X   BOOL  is_end = FALSE, is_balanced = FALSE;
  127. X   char  *p;
  128. X   static CONST char  whitespace[]  = " \t\n\r\f\v";
  129. X   static CONST char  beg_portion[] = "(<{[";
  130. X   static CONST char  end_portion[] = ")>}]";
  131. X
  132. X   description = strpbrk( str, whitespace );
  133. X   if ( description ) {
  134. X      is_end = TRUE;
  135. X      *description++ = '\0';  /* null terminate the 1st portion */
  136. X      while ( isspace(*description) )  ++description;  /* trim leading ' ' */
  137. X   }
  138. X
  139. X   if ( !is_end ) {
  140. X       p = strpbrk( str, beg_portion );
  141. X       if ( p )  description = p;
  142. X   }
  143. X
  144. X   if ( description ) {
  145. X      if ( !is_end ) {  /* null terminate and skip leading '(' */
  146. X         is_end = is_balanced = TRUE;
  147. X         *description++ = '\0';
  148. X      }
  149. X      else if ( strchr(beg_portion, *description) ) {
  150. X            is_balanced = TRUE;
  151. X            ++description;
  152. X      }
  153. X      if ( is_balanced ) {  /* remove trailing ')' */
  154. X         p = description + (strlen( description ) - 1);
  155. X         if ( strchr(end_portion, *p) )  *p = '\0';
  156. X      }
  157. X   }/*end-if*/
  158. X
  159. X   if ( description  &&  !is_balanced ) {
  160. X      while ( !isalnum(*description) )  ++description;
  161. X   }
  162. X
  163. X   return  description;
  164. }
  165. X
  166. X
  167. /***************************************************************************
  168. ** ^FUNCTION: init_args - Initialize the command object
  169. **
  170. ** ^SYNOPSIS:
  171. */
  172. #ifndef __ANSI_C__
  173. X   VOID init_args( argd )
  174. /*
  175. ** ^PARAMETERS:
  176. */
  177. X   ARGDESC argd[];
  178. /*    -- the array of command-arguments to initialize.
  179. */
  180. #endif  /* !__ANSI_C__ */
  181. X
  182. /* ^DESCRIPTION:
  183. **    Init_args performs all the actions that are required to prepare an
  184. **    argdesc-array for use by any of the parseargs functions. Storrage
  185. **    is allocated and initialized and argument descriptions are compiled.
  186. **
  187. ** ^REQUIREMENTS:
  188. **    <argd> must point to an array that has been declared using the CMD_XXXX
  189. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  190. **
  191. ** ^SIDE-EFECTS:
  192. **    The argd is initialized for use.
  193. **
  194. ** ^RETURN-VALUE:
  195. **    None.
  196. **
  197. ** ^ALGORITHM:
  198. **    - compile the command name, purpose, and description if they were given
  199. **    - if ENDOFARGS was used without STARTOFARGS, then shift each item in
  200. **      the array down by one position.
  201. **    - initialize the parse-flags to the default settings
  202. **    - initialize the default argument search-list
  203. **    - allocate and initialize the command-context
  204. **    - for each command-line argument in argd
  205. **      - compile the ad_prompt field and set the default initial ARGXXX flags
  206. **      end-for
  207. ***^^**********************************************************************/
  208. #ifdef __ANSI_C__
  209. X   void init_args( ARGDESC argd[] )
  210. #endif
  211. {
  212. X   register ARGDESC *ad, *anchor;
  213. X   int  ad_count = 0;
  214. X   BOOL old_style = FALSE;
  215. X   char *description = (char *)NULL, *purpose = (char *)NULL;
  216. X
  217. X   if ( !argd )  return;
  218. X
  219. X      /* dont initialize if its already been done */
  220. X   if ( CMD_isINIT(argd) )  return;
  221. X
  222. X   if ( !ARG_isEND(argd) )  old_style = TRUE;
  223. X
  224. X      /* determine the argument count and preprocess each ad-entry */
  225. X   anchor = ( old_style ) ? argd : ARG_FIRST(argd);
  226. X   for ( ad = anchor ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) )  {
  227. X      ad_count++;
  228. X
  229. X         /* set-up any positional args that we know of */
  230. X      if ( ARG_isPOSONLY(ad) )  BSET( arg_flags(ad), ARGPOS );
  231. X
  232. X         /* set-up any default ARGNOVALs that we know of */
  233. X      if (ARG_isBOOLEAN(ad) || ARG_isPSEUDOARG(ad))
  234. X         BSET( arg_flags(ad), ARGNOVAL );
  235. X
  236. X      description = get_description( (char *)arg_sname(ad) );
  237. X      if ( description )  {
  238. X         BSET(arg_flags(ad), ARGDESCRIBED);
  239. X         strcpy( (char *)arg_sdesc(ad), description );
  240. X      }
  241. X   }
  242. X
  243. X      /* shift all the entries down one to make room for a new 1st-entry
  244. X      ** It would've been nice to just swap the 1st and last entries but
  245. X      ** I have to preserve the order that all positional parameters are
  246. X      ** given in.
  247. X      */
  248. X   if ( old_style  &&  ad_count > 0 ) {
  249. X      anchor = ad + 1;  /* save this position */
  250. X      for ( ; ad > argd ; ARG_RETREAT(ad) ) {
  251. X         memcpy( (ARBPTR)ad, (ARBPTR)(ad - 1), sizeof(ARGDESC) );
  252. X      }/*for*/
  253. X      memcpy( (ARBPTR)argd, (ARBPTR)anchor, sizeof(ARGDESC) );
  254. X   }
  255. X   else  anchor = ad;
  256. X
  257. X     /* set default parse-flags */
  258. X   cmd_flags(argd) = pa_DEFAULTS;
  259. X
  260. X     /* if new-style, get the purpose from the command name */
  261. X   if ( !old_style  &&  cmd_name(argd) ) {
  262. X      purpose = get_description( (char *)cmd_name(argd) );
  263. X   }
  264. X
  265. X      /* set the program name */
  266. X   if ( ProgName  &&  *ProgName  &&  !cmd_name(argd) ) {
  267. X      cmd_name(argd) = ProgName;
  268. X   }
  269. X
  270. X      /* set context */
  271. X   if ( !cmd_context(argd) )  {
  272. X      cmd_context(argd)  = (CTXDESC *) anchor;
  273. X      cmd_state(argd)    = ( old_style ) ? ps_OLDSTYLE : (ps_flags_t) 0;
  274. X      cmd_argv0(argd)    = cmd_name(argd);
  275. X      cmd_purpose(argd)  = purpose;
  276. X      cmd_ptrs(argd) = (ARGDPTRS *)malloc( sizeof(ARGDPTRS) );
  277. X      if ( !cmd_ptrs(argd) ) {
  278. X         syserr( "malloc failed in init_args()" );
  279. X      }
  280. X      if ( argd == Default_ArgDesc ) {
  281. X         cmd_defargs(argd)  = ARGDESCNULL;
  282. X      }
  283. X      else {
  284. X         if ( !CMD_isINIT(Default_ArgDesc) )  init_args( Default_ArgDesc );
  285. X         cmd_defargs(argd)  = Default_ArgDesc;
  286. X      }
  287. X      cmd_list(argd) = ARGDESCNULL;
  288. #ifdef amiga_style
  289. X      cmd_prev(argd) = ARGDESCNULL;
  290. #endif
  291. X   }
  292. }
  293. X
  294. X
  295. /***************************************************************************
  296. ** ^FUNCTION: reset_args - (re)set a command for parsing
  297. **
  298. ** ^SYNOPSIS:
  299. */
  300. #ifndef __ANSI_C__
  301. X   static VOID reset_args( argd )
  302. /*
  303. ** ^PARAMETERS:
  304. */
  305. X   ARGDESC argd[];
  306. /*    -- array or command-line arguments to be reset
  307. */
  308. #endif  /* !__ANSI_C__ */
  309. X
  310. /* ^DESCRIPTION:
  311. **    Reset_args will prepare a command for parsing. The command-state is
  312. **    initialized and each argument is reset to "unmatched".
  313. **
  314. ** ^REQUIREMENTS:
  315. **    <argd> must point to an array that has been declared using the CMD_XXXX
  316. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  317. **
  318. ** ^SIDE-EFECTS:
  319. **    resets the ARG flags of each argument and the command-state of argd.
  320. **
  321. ** ^RETURN-VALUE:
  322. **    None.
  323. **
  324. ** ^ALGORITHM:
  325. **    - reset the command-context to be (as of yet) unparsed
  326. **    - reset the ARG flags of the programmer & default argument descriptors
  327. ***^^**********************************************************************/
  328. X
  329. X   /* arg-flags to be reset before parsing a new command-line */
  330. #define  ARGDEFAULTS  ( ARGGIVEN | ARGVALGIVEN | ARGKEYWORD | ARGVALSEP )
  331. X
  332. #ifdef __ANSI_C__
  333. X   static void reset_args( ARGDESC argd[] )
  334. #endif
  335. {
  336. X   register ARGDESC *args, *ad;
  337. X
  338. X   if ( !CMD_isINIT(argd) )  init_args(argd);
  339. X
  340. X      /* reset the command context */
  341. X   BCLEAR( cmd_state(argd), (ps_NOFLAGS|ps_NOCMDENV|ps_NOPARSECNTL) );
  342. X   cmd_list(argd) = ARGDESCNULL;
  343. #ifdef amiga_style
  344. X   cmd_prev(argd) = ARGDESCNULL; /* No argument requested */
  345. #endif
  346. X
  347. X      /* clear out any cruft in the argument descriptors */
  348. X   for ( args = argd ; args ; args = cmd_defargs(args) ) {
  349. X      for ( ad = ARG_FIRST(args) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad) ) {
  350. X         BCLEAR( arg_flags(ad), ARGDEFAULTS );
  351. X
  352. #if ( defined(vms_style)  ||  defined(amiga_style) )
  353. X            /* vms and amiga use keywords only */
  354. X         BSET( arg_flags(ad), ARGKEYWORD );
  355. #endif
  356. X      }/*for*/
  357. X   }/*for*/
  358. }
  359. X
  360. #undef ARGDEFAULTS
  361. X
  362. X
  363. /***************************************************************************
  364. ** ^FUNCTION: prompt_user - prompt the user for a missing argument
  365. **
  366. ** ^SYNOPSIS:
  367. */
  368. #ifndef __ANSI_C__
  369. X   static BOOL prompt_user( ad, argname )
  370. /*
  371. ** ^PARAMETERS:
  372. */
  373. X   ARGDESC *ad;
  374. /*    -- pointer to the argument to be supplied by the user
  375. */
  376. X   char *argname;
  377. /*    -- name of the argument to be supplied by the user
  378. */
  379. #endif  /* !__ANSI_C__ */
  380. X
  381. /* ^DESCRIPTION:
  382. **    Prompt_user will attempt to prompt the user to supply (on standard input)
  383. **    the value for the given argument. If the argument is a list, then each
  384. **    item of the list should be given on a separate line (with a blank line
  385. **    terminating the list).
  386. **
  387. **    No special "escaping" or translation is performed on the resulting user
  388. **    input, hence any whitespace ro quotes will be considered as part of the
  389. **    argument value.
  390. **
  391. **    If stdin is NOT associated with a terminal then no prompting is performed
  392. **    and FALSE is returned.  If the user enters in invalid value for the
  393. **    argument then the value is discarded and FALSE is returned (so you
  394. **    better get it right the first time).
  395. **
  396. ** ^REQUIREMENTS:
  397. **    Only the first 255 characters of user input is used.
  398. **
  399. ** ^SIDE-EFECTS:
  400. **    Modifies <ad> accordingly.
  401. **
  402. ** ^RETURN-VALUE:
  403. **    FALSE if there is an error, TRUE otherwise.
  404. **
  405. ** ^ALGORITHM:
  406. **    - if stdin is not connected to a terminal return FALSE
  407. **    - if argument is not a list then
  408. **      - get string from user
  409. **      - attempt to convert the string
  410. **      - return TRUE upon success and FALSE upon failure
  411. **    - else (the argument is a list)
  412. **      - while (user has not entered a blank line) do
  413. **        - get string from user
  414. **        - attempt to convert the string
  415. **        - return FALSE upon failure (otherwise continue to loop)
  416. **        end-while
  417. **      end-if
  418. ***^^**********************************************************************/
  419. #ifdef __ANSI_C__
  420. X   static BOOL prompt_user( ARGDESC *ad, const char *argname )
  421. #endif
  422. {
  423. X   BOOL  error = FALSE, first = TRUE;
  424. X   int   end;
  425. X   char  buf[ MAXLINE ];
  426. X
  427. X   if ( !is_interactive(STDIN) )  return  FALSE;
  428. X
  429. X   if ( ARG_isMULTIVAL(ad) ) {
  430. X      fprintf(stderr, "Enter one %s per-line ", argname);
  431. X      fprintf(stderr, "(enter a blank line to stop).\n");
  432. X   }
  433. X
  434. X   do {  /* need repeated prompting for an ARGLIST or an ARGVEC */
  435. X      /* get argument from user */
  436. X      *buf='\0';
  437. #ifdef vms_style
  438. X      fprintf(stderr, "\r%s> ", argname);
  439. #else
  440. X      fprintf(stderr, "\rEnter %s: ", argname);
  441. #endif
  442. X      fflush( stderr );
  443. X      if ( !fgets(buf, MAXLINE, stdin) )  *buf = '\0';
  444. X
  445. X      /* discard the newline */
  446. X      end = strlen(buf) - 1;
  447. X      if ( end >= 0  &&  buf[end] == '\n' )
  448. X         buf[ end ] = '\0';
  449. X
  450. X      /* make sure we read something! */
  451. X      if ( *buf == '\0' ) {
  452. X         if ( first ) {
  453. X            usrerr( "error - no %s given", argname );
  454. X            error = TRUE;
  455. X         }
  456. X         continue;
  457. X      }
  458. X
  459. X      /* try to convert what we read (remember - buf is transient) */
  460. X      if (  HANDLE( ad, buf, TRUE ) )
  461. X         BSET(arg_flags(ad), ARGGIVEN | ARGVALGIVEN);
  462. X      else
  463. X         error = TRUE;
  464. X
  465. X      first = FALSE;
  466. X   } while ( !error &&  ARG_isMULTIVAL(ad)  &&  *buf );
  467. X
  468. X   return  (error) ? FALSE : TRUE;
  469. }
  470. X
  471. X
  472. /***************************************************************************
  473. ** ^FUNCTION: verify_argreqs - check for any missing required arguments
  474. **
  475. ** ^SYNOPSIS:
  476. */
  477. #ifndef __ANSI_C__
  478. X   static BOOL verify_argreqs( cmd, parse_status )
  479. /*
  480. ** ^PARAMETERS:
  481. */
  482. X   ARGDESC *cmd;
  483. /*    -- the argdesc-array of command-line arguments.
  484. */
  485. X   int *parse_status;
  486. /*    -- address of current parse_status
  487. */
  488. #endif  /* !__ANSI_C__ */
  489. X
  490. /* ^DESCRIPTION:
  491. **    Verify_argreqs will reset parse_status to a success-value if it 
  492. **    previously indicated a syntax error but pa_IGNORE is set to ignore
  493. **    syntax error.  Verify_argreqs will then verify that any required
  494. **    command-line arguments have been supplied (if they were not and
  495. **    pa_PROMPT is set, then the missing values will be prompted for).
  496. **
  497. ** ^REQUIREMENTS:
  498. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  499. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  500. **
  501. **    parse_status should be a pointer to the result of a previous call to
  502. **    {f,l,s,v,}parseargs.
  503. **
  504. ** ^SIDE-EFECTS:
  505. **    The arg-descs for missing arguments may be modified by prompt_user.
  506. **    parse_status will be modified to indicate whether or not a syntax
  507. **    error really has occurred (it will be pe_SUCCESS if all is hunky-dory).
  508. **
  509. ** ^RETURN-VALUE:
  510. **    FALSE if there is an error, TRUE otherwise.
  511. **
  512. ** ^ALGORITHM:
  513. **    - if parse_status is a syntax error but pa_IGNORE is set
  514. **      - ignore the error by resetting the status to pe_SUCCESS
  515. **      end-if
  516. **    - if pa_NOCHECK is not set then check for any missing required
  517. **      arguments (using prompt_user to get the values if pa_PROMPT is set).
  518. ***^^**********************************************************************/
  519. #ifdef __ANSI_C__
  520. X   static BOOL verify_argreqs( ARGDESC *cmd, int *parse_status )
  521. #endif
  522. {
  523. X   register ARGDESC *ad;
  524. X   BOOL error;
  525. X   argName_t  s;
  526. X
  527. X   if ( !CMD_isINIT(cmd) )  init_args(cmd);
  528. X
  529. X   if ( *parse_status == pe_SYNTAX  &&  BTEST(cmd_flags(cmd), pa_IGNORE) ) {
  530. X      *parse_status = pe_SUCCESS;
  531. X   }
  532. X   error = ( *parse_status != pe_SUCCESS ) ? TRUE : FALSE;
  533. X
  534. X   if ( !BTEST(cmd_flags(cmd), pa_NOCHECK) ) {
  535. X      for (ad = ARG_FIRST(cmd) ; !ARG_isEND(ad) ; ARG_ADVANCE(ad)) {
  536. X         if (arg_type(ad) == argDummy)  continue;
  537. X
  538. X         if ( ARG_isREQUIRED(ad)  &&  !ARG_isGIVEN(ad) ) {
  539. X               /* still didn't get a value... sigh */
  540. X            if ( ARG_isPOSITIONAL(ad) ) {
  541. X               (VOID) get_name( arg_sname(ad), s );
  542. X               usrerr("%s required", s);
  543. X            }
  544. X            else {
  545. #ifdef amiga_style
  546. X               (VOID) get_keyword( arg_sname(ad), s );
  547. X               usrerr("argument required for %s keyword", s);
  548. #endif
  549. #ifdef ibm_style
  550. X               (VOID) get_name( arg_sname(ad), s );
  551. X               {
  552. X                  char c, *pfx = getenv( "SWITCHAR" );
  553. X                  c = ( pfx &&  *pfx ) ? *pfx : '/';
  554. X                  usrerr("%s required for %c%c switch", s, c, arg_cname(ad));
  555. X               }
  556. #endif
  557. #ifdef unix_style
  558. X               (VOID) get_name( arg_sname(ad), s );
  559. X               usrerr("%s required for %c%c flag", s, c_OPT_PFX, arg_cname(ad));
  560. #endif
  561. #ifdef vms_style
  562. X               (VOID) get_keyword( arg_sname(ad), s );
  563. X               usrerr("value required for %c%s qualifier", *s_KWD_PFX, s);
  564. #endif
  565. X            }
  566. X
  567. X            if ( !error  &&  BTEST(cmd_flags(cmd), pa_PROMPT) ) {
  568. X               if ( !prompt_user(ad, s) )  error = TRUE;
  569. X            }
  570. X            else if ( !error ) {
  571. X               error = TRUE;
  572. X            }
  573. X         }/*if*/
  574. X      }/*for*/
  575. X   }/*if*/
  576. X
  577. X   return  (error) ? FALSE : TRUE;
  578. }
  579. X
  580. X
  581. /***************************************************************************
  582. ** ^FUNCTION: read_flags - read bitmask-flags in a string
  583. **
  584. ** ^SYNOPSIS:
  585. */
  586. #ifndef __ANSI_C__
  587. X   static argMask_t read_flags( str, c_tbl, m_tbl )
  588. /*
  589. ** ^PARAMETERS:
  590. */
  591. X   char *str;
  592. /*    -- the string to parse
  593. */
  594. X   char c_tbl[];
  595. /*    -- a (NUL-terminated) array of alpha-numeric characters Each character
  596. **    is the first letter/number of a token.  If the character is lowercase,
  597. **    then the corresponding mask is set, if it is uppercase, the corresponding
  598. **    mask is cleared.
  599. */
  600. X   argMask_t m_tbl[];
  601. /*    -- a table of bitmasks for the given character-table. The mask to use
  602. **       for c_tbl[i] is m_tbl[i].
  603. */
  604. #endif  /* !__ANSI_C__ */
  605. X
  606. /* ^DESCRIPTION:
  607. **    Read_flags will parse the given string for any flags present in c_tbl[].
  608. **    When a flag is matched, its corresponding entry in m_tbl[] is set (or
  609. **    cleared) in the currently matched set of flags.
  610. **
  611. **    When the given string has been completely scanned, the resulting
  612. **    combination of masks that were matched is returned.
  613. **
  614. ** ^REQUIREMENTS:
  615. **    A '\0' or a ';' indicates the end of parsing. A token/mask may be negated
  616. **    by preceding it with one of '!', '^', or '~'. Tokens must be separated by
  617. **    one or more non-alphanumerics (other than '!', '~', and '^').
  618. **
  619. ** ^SIDE-EFECTS:
  620. **    None.
  621. **
  622. ** ^RETURN-VALUE:
  623. **    The combination of bitmasks indicated by the given string.
  624. **
  625. ** ^ALGORITHM:
  626. **    - set masks = 0
  627. **    - for each "token" in str
  628. **      - if token doesnt match any entries in c_tbl then continue
  629. **      - let i = index of matched token in c_tbl
  630. **      - if c_tbl[i] is lowercase then OR masks with m_tbl[i]
  631. **      - if c_tbl[i] is uppercase then AND masks with NOT(m_tbl[i])
  632. **      end-for
  633. **    - return masks
  634. ***^^**********************************************************************/
  635. X
  636. X      /* macros for parsing flags */
  637. #define  IS_NEGCHAR(c)  ( c == '!' || c == '^' || c == '~' )
  638. #define  SKIP_TOK(s)    while ( isalnum(*s) || *s == '_' || *s == '-' ) s++
  639. #define  SKIP_SEP(s)    while ( *s && !IS_NEGCHAR(*s) && !isalnum(*s) ) s++
  640. #define  NEXT_TOK(s)    SKIP_TOK(s) ; SKIP_SEP(s)
  641. #define  TOGGLE(x)      x = (x) ? FALSE : TRUE
  642. X
  643. #ifdef __ANSI_C__
  644. X   static argMask_t read_flags(
  645. X      const char *str, const char c_tbl[], const argMask_t m_tbl[]
  646. X   )
  647. #endif
  648. {
  649. X   char *pos = CHARNULL;
  650. X   BOOL  negated = FALSE;
  651. X   argMask_t  mask = 0, flags = 0;
  652. X
  653. X   while ( *str  &&  *str != ';' ) {
  654. X      mask = 0;
  655. X      negated = FALSE;
  656. X
  657. X      while ( IS_NEGCHAR(*str) ) {
  658. X         TOGGLE(negated);
  659. X         ++str;
  660. X         SKIP_SEP(str);
  661. X      }
  662. X
  663. X      if ( (pos = strchr( c_tbl, TOLOWER(*str) )) != CHARNULL ) {
  664. X         mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
  665. X      }
  666. X      else if ( (pos = strchr( c_tbl, TOUPPER(*str) )) != CHARNULL ) {
  667. X         TOGGLE(negated);
  668. X         mask = m_tbl[ (int)(pos - (char *)c_tbl) ];
  669. X      }
  670. X      else {
  671. X         negated = FALSE;
  672. X      }
  673. X
  674. X      NEXT_TOK(str);
  675. X
  676. X      if ( mask || negated ) {
  677. X         if   (   negated  )  BCLEAR(flags, mask);
  678. X         else /* !negated */  BSET(flags, mask);
  679. X      }/*if*/
  680. X   }/*while*/
  681. X
  682. X   return  flags;
  683. }
  684. X
  685. #undef  IS_NEGCHAR
  686. #undef  SKIP_TOK
  687. #undef  SKIP_SEP
  688. #undef  NEXT_TOK
  689. #undef  TOGGLE
  690. X
  691. X
  692. /***************************************************************************
  693. ** ^FUNCTION: get_usage_flags - determine user-defined usage-message format
  694. **
  695. ** ^SYNOPSIS:
  696. */
  697. #ifndef __ANSI_C__
  698. X   static argMask_t get_usage_flags( cmd )
  699. /*
  700. ** ^PARAMETERS:
  701. */
  702. X   ARGDESC *cmd;
  703. /*    -- command-structure to determine usage-flags for
  704. */
  705. #endif  /* !__ANSI_C__ */
  706. X
  707. /* ^DESCRIPTION:
  708. **    Through the use of an environment variable (or a VMS symbol), the user
  709. **    may control the syntax and the verbosity of the command-usage messages
  710. **    that are printed by parseargs. The desired level of verbosity may be
  711. **    set by defining the environment variable "USAGECNTL" to be a com-
  712. **    bination of strings (case insensitive). The value of each string con-
  713. **    trols one of three different "modes" of behavior in the displaying
  714. **    of usage messages:  The first "mode" is "verbose" mode, which con-
  715. **    trols whether or not a detailed description of each argument should
  716. **    accompany the usual command-line sysnopsis. If verbose mode is
  717. **    "off", then only a command-line synopsis is printed (this is also
  718. **    refferred to as "terse" mode). The other two "modes" control the
  719. **    displaying of option syntax and long-option syntax. A mode may be
  720. **    explicitly disabled by preceding its corresponding string with the `!'
  721. **    character. The "modes" which correspond to the possible values of the
  722. **    "USAGECNTL" environment variable are given by the following table.
  723. **
  724. **         "Quiet"        No usage message of any kind is displayed.
  725. **
  726. **         "Silent"       Same as Quiet.
  727. **
  728. **         "Paged"        The usage message is piped to a pager. The pager
  729. **                        used is named by the "USAGE_PAGER" environment
  730. **                        variable. If this variable is unset or empty (or
  731. **                        is not the name of an executable program), the
  732. **                        pager named by the "PAGER" environment variable
  733. **                        us used.  If this variable is unset or empty (or
  734. **                        is not the name of an executable program) then
  735. **                        the program "/usr/ucb/more" is used.
  736. **         "Description"  The command description is printed.
  737. **
  738. **         "Terse"        Terse mode, just print command-line synopsis.
  739. **
  740. **         "Verbose"      Verbose mode, print descriptions for each argument
  741. **
  742. **         "Options"      Option syntax is displayed.
  743. **
  744. **         "LongOpts"     Long-option syntax is displayed.
  745. **
  746. **         "KeyWords"     Same as "LongOpts".
  747. **
  748. **    If the environment variable "USAGECNTL" is empty or undefined, then
  749. **    the default usage level (which is presently "Verbose + Options")
  750. **    will be used.
  751. **
  752. ** ^REQUIREMENTS:
  753. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  754. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  755. **
  756. ** ^SIDE-EFECTS:
  757. **    None.
  758. **
  759. ** ^RETURN-VALUE:
  760. **    The usage-flags corresponding to the value of $USAGECNTL
  761. **
  762. ** ^ALGORITHM:
  763. **    - If $USAGECNTL is NULL or empty, return the default flags
  764. **    - get the value of $USAGECNTL and call read_flags to parse it.
  765. **    - return the resulting flags.
  766. ***^^**********************************************************************/
  767. #ifdef __ANSI_C__
  768. X   static argMask_t get_usage_flags( const ARGDESC *cmd )
  769. #endif
  770. {
  771. X   register  char  *usage_env;
  772. X   argMask_t  usg_ctl = 0;
  773. X   static CONST char  usage_tokens[] = {
  774. X      'n', 'q', 's',
  775. X      'T', 'v',
  776. X      'o',
  777. X      'l', 'k',
  778. X      'd',
  779. X      'p',
  780. X      '\0'
  781. X   };
  782. X   static CONST argMask_t  usage_masks[] = {
  783. X      usg_NONE,     usg_NONE,     usg_NONE,
  784. X      usg_VERBOSE,  usg_VERBOSE,
  785. X      usg_OPTS,
  786. X      usg_LONGOPTS, usg_LONGOPTS,
  787. X      usg_DESCRIPTION,
  788. X      usg_PAGED
  789. X   };
  790. X
  791. X   usage_env = getenv("USAGECNTL");
  792. X   if ( !usage_env ) {
  793. X      usg_ctl = usg_VERBOSE;
  794. X   }
  795. X   else {
  796. X      usg_ctl = read_flags( usage_env, usage_tokens, usage_masks );
  797. X   }
  798. X
  799. X   envfree( usage_env );
  800. X
  801. #ifdef unix_style
  802. X      /* make sure we print at least one of options/keywords */
  803. X   if ( !BTEST(usg_ctl, usg_OPTS|usg_LONGOPTS) ) {
  804. X      if ( BTEST(cmd_flags(cmd), pa_KWDSONLY) ) {
  805. X         BSET(usg_ctl, usg_LONGOPTS);
  806. X      }
  807. X      else {
  808. X         BSET(usg_ctl, usg_OPTS);
  809. X      }
  810. X   }
  811. #endif /* unix_style */
  812. X
  813. X   return  usg_ctl;
  814. }
  815. X
  816. X
  817. /***************************************************************************
  818. ** ^FUNCTION: get_parse_flags - determine user-defined parsing behavior
  819. **
  820. ** ^SYNOPSIS:
  821. */
  822. #ifndef __ANSI_C__
  823. X   static VOID get_parse_flags( cmd )
  824. /*
  825. ** ^PARAMETERS:
  826. */
  827. X   ARGDESC *cmd;
  828. /*    -- command-structure to determine parse-flags for
  829. */
  830. #endif  /* !__ANSI_C__ */
  831. X
  832. /* ^DESCRIPTION:
  833. **    The programmer may control parsing behavior through the use of
  834. **    parsecntl(3). The user may set this behavior through the use of the
  835. **    PARSECNTL environment variable.  By indicating any number of flags
  836. **    (possibly negated) the user will directly modify the behavior of the
  837. **    parseargs library. Flags may be combined by placing a `+' or `|'
  838. **    character in between flags. A switch is negated by immediately preceding
  839. **    it with a `!' or `-' character.  The possible "flags" are given by
  840. **    the following table. Flags are case-insensitive.
  841. **
  842. **         "Prompt"      Prompt the user for any missing arguments that are
  843. **                       required on the command-line. No special escaping
  844. **                       or quoting is performed on the user input. Required
  845. **                       arguments that expect a list of values will be
  846. **                       repeatedly prompted for (one item per line) until a
  847. **                       blank line (followed by a carriage return) is
  848. **                       entered.
  849. **
  850. **         "Ignore"      Ignore any unrecognized or improperly specified
  851. **                       command-line arguments and continue execution of
  852. **                       the program. Normally, if an argument is unmatched
  853. **                       (or is improperly specified), a usage message is
  854. **                       printed program execution is terminated.
  855. **
  856. **         "OptsOnly"    Under UNIX, setting this flag will disable the
  857. **                       parsing of long-option syntax. This will cause all
  858. **                       arguments starting with '+' to always be treated as
  859. **                       a positional parameter (instead of a long-option).
  860. **
  861. **         "KwdsOnly"    Under UNIX, setting this flag disables the parsing
  862. **                       of single-character options.  This will cause all
  863. **                       arguments starting with '-' to always be treated as
  864. **                       a positional parameter (instead of an option).
  865. **
  866. **         "LoptsOnly"   Same as KwdsOnly.
  867. **
  868. **         "Flags1st"    Setting this flag causes the parseargs library to
  869. **                       force any and all non-positional arguments to be
  870. **                       specified before any positional ones.  As an exam-
  871. **                       ple, under UNIX, if this flag is SET then parseargs
  872. **                       will consider the command line "cmd -x arg" to con-
  873. **                       sist of one option and one positional argument;
  874. **                       however the command line "cmd arg -x" would be con-
  875. **                       sidered to consist of two positional arguments (the
  876. **                       -x option will be unmatched).
  877. **
  878. **                       If this flag is UNSET, then both of the previous
  879. **                       examples are considered to consist of one option
  880. **                       and one positional argument.
  881. **
  882. **         "CaseIgnore"  Setting this flag cause character-case to be
  883. **                       ignored when attempting to match single-character
  884. **                       argument names (i.e. causes "-i" and "-I" will be
  885. **                       considered equivalent).
  886. **
  887. **    If the environment variable "PARSECNTL" is empty or undefined, then
  888. **    parsing behavior set by the programmer is used.  If the programmer has
  889. **    not explicitly used parsecntl(3) to modify the parsing behavior will
  890. **    be "!Prompt + !Ignore" for Unix and AmigaDOS systems, "Prompt" for
  891. **    VMS systems, and "CaseIgnore" for MS-DOS and OS/2 systems.
  892. **
  893. ** ^REQUIREMENTS:
  894. **    <cmd> must point to an array that has been declared using the CMD_XXXX
  895. **    macros, or using the ENDOFARGS (and optionally STARTOFARGS) macro.
  896. **
  897. ** ^SIDE-EFECTS:
  898. **    Modifies the parse-flags of <cmd> is $PARSECNTL is non-null & non-empty.
  899. **
  900. ** ^RETURN-VALUE:
  901. **    None.
  902. **
  903. ** ^ALGORITHM:
  904. **    - If $PARSECNTL is NULL or empty, return
  905. **    - Else turn off all flags that may be set by $PARSECNTL
  906. **    - get the value of $PARSECNTL and call read_flags to parse it.
  907. **    - set the returned flags in the cmd-structure.
  908. **    - return the resulting flags.
  909. ***^^**********************************************************************/
  910. X
  911. X   /* the combination of parse-flags that may be set via $PARSECNTL */
  912. #define  pa_USRFLAGS \
  913. X    (pa_PROMPT|pa_IGNORE|pa_ANYCASE|pa_OPTSONLY|pa_KWDSONLY|pa_FLAGS1ST)
  914. X
  915. #ifdef __ANSI_C__
  916. X   static void get_parse_flags( ARGDESC *cmd )
  917. #endif
  918. {
  919. X   register argMask_t  flags = 0;
  920. X   char *parse_env;
  921. X   static CONST char  parse_tokens[] = {
  922. X      'c',
  923. X      'd',
  924. X      'f',
  925. X      'i',
  926. X      'p',
  927. X      'o',
  928. X      'l','k',
  929. X      '\0'
  930. X   };
  931. X   static CONST argMask_t  parse_masks[] = {
  932. X      pa_ANYCASE,
  933. X      pa_DEFAULTS,
  934. X      pa_FLAGS1ST,
  935. X      pa_IGNORE,
  936. X      pa_PROMPT,
  937. X      pa_OPTSONLY,
  938. X      pa_KWDSONLY, pa_KWDSONLY
  939. X   };
  940. X
  941. X   if ( !CMD_isINIT(cmd) )  init_args( cmd );
  942. X
  943. X   if ( (parse_env = getenv("PARSECNTL"))  &&  *parse_env ) {
  944. X      flags = read_flags( parse_env, parse_tokens, parse_masks );
  945. X
  946. X         /*
  947. X         ** mask off all the flags that may be set by $PARSECNTL
  948. X         ** (including any default flags).
  949. X         */
  950. X      BCLEAR( cmd_flags(cmd), pa_USRFLAGS );
  951. X
  952. X      cmd_flags(cmd) |= flags;
  953. X   }/*if*/
  954. X
  955. X   envfree( parse_env );
  956. }
  957. X
  958. #undef  pa_USRFLAGS
  959. X
  960. X
  961. /***************************************************************************
  962. ** ^FUNCTION: parse_user_defaults - parse user-supplied default arguments
  963. **
  964. ** ^SYNOPSIS:
  965. */
  966. #ifndef __ANSI_C__
  967. X   static VOID parse_user_defaults( cmd )
  968. /*
  969. ** ^PARAMETERS:
  970. */
  971. X   ARGDESC *cmd;
  972. /*    -- the command-line object to parse
  973. */
  974. #endif  /* !__ANSI_C__ */
  975. X
  976. /* ^DESCRIPTION:
  977. **    Programs that use parseargs may be given default arguments under
  978. **    Unix and PCs through the use of environment variables (symbols
  979. **    are used for VMS systems). If a  C-program or shell-script uses
  980. **    parseargs to implement a command named "cmd" then the environment
  981. **    variable CMD_ARGS will be parsed for any "default" arguments before
  982. **    the command-line is parsed.  The command-line will over-ride any
  983. **    options that are specified in this environment variable (except that
  984. **    ARGLISTs and ARGVECs set in "CMD_ARGS" will be appended from the
  985. **    command-line if they are selected).
  986. **
  987. **    It is important to note that the contents of the CMD_ARGS environ-
  988. **    ment variable are NOT expanded by the shell and hence any special
  989. **    characters (such as quotes or back-slashes) will NOT be escaped or
  990. **    removed by parseargs. Furthermore, it will not be possible to try and
  991. **    use a tab, space, or newline character in the environment variable as
  992. **    anything other than an argument separator.
  993. **
  994. **    Lastly, parts of an option specification in CMD_ARGS may NOT be
  995. **    continued on the command-line. As an example, if -f requires an
  996. **    argument and CMD_ARGS="-f", then the command-line "cmd  bah" will
  997. **    NOT assign "bah" as the argument to -f but will instead complain
  998. **    about a missing argument for -f. Similarly, if -l takes a list of
  999. **    arguments and CMD_ARGS="-l item1 item2", then the command-line
  1000. **    "cmd  bah", will NOT assign "bah" to the end of the list containing
  1001. **    "item1" and "item2" but will instead treat "bah" as the first
  1002. **    positional parameter on the command-line.
  1003. **
  1004. ** ^REQUIREMENTS:
  1005. **    <cmd> should be an array of ARGDESCs declared using the CMD_XXXX macros
  1006. **    or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1007. **
  1008. ** ^SIDE-EFECTS:
  1009. **    Any matched arguments have their ARGDESCs modified accordingly.
  1010. **    Also, the command-state is changed to reflect the fact that the
  1011. **    environment variable has been parsed. Also, after parsing the
  1012. **    variable, the command is partially parsed. Hence the pa_CONTINUE
  1013. **    is set to prevent arguments from being reset when the actual command
  1014. **    line is parsed.
  1015. **
  1016. ** ^RETURN-VALUE:
  1017. **    None.
  1018. **
  1019. ** ^ALGORITHM:
  1020. **    - Get the value of the environment variable
  1021. **    - if the variable is null or empty then return.
  1022. **    - turn OFF the VARIABLE-NOT-YET-PARSED flag
  1023. **    - turn on the NOCHECK flag so we dont check for missing required
  1024. **      arguments in the variable (there may be more on the command-line).
  1025. **    - parse the string and convert any arguments matched
  1026. **    - set the NOCHECK flag back to its previous setting
  1027. **    - set the CONTINUE flag since the command-line is now partially parsed.
  1028. **    - return
  1029. ***^^**********************************************************************/
  1030. X
  1031. #define  ENV_SUFFIX  "_ARGS"  /* suffix for env-variable with default args */
  1032. X
  1033. #ifdef __ANSI_C__
  1034. X   static VOID parse_user_defaults( ARGDESC *cmd )
  1035. #endif
  1036. {
  1037. X   int  rc;
  1038. X   char      *env_args;
  1039. X   argName_t  env_name;
  1040. X   VOID usage();
  1041. X
  1042. X   if ( !CMD_isINIT(cmd) )  init_args( cmd );
  1043. X
  1044. X   if ( BTEST(cmd_state(cmd), ps_NOCMDENV) )  return;
  1045. X
  1046. X   strucpy( env_name, ProgName );
  1047. X   strcat( env_name, ENV_SUFFIX );
  1048. X
  1049. X   env_args = getenv(env_name);
  1050. X   if ( env_args ) {
  1051. X      char **argv = (char **)NULL;
  1052. X      char *env_val = strdup( env_args );
  1053. X      argMask_t  saveflags = cmd_flags(cmd);
  1054. X
  1055. X      BSET( cmd_flags(cmd), pa_NOCHECK | pa_ARGV0 | pa_COPYF );
  1056. X      BSET( cmd_state(cmd), ps_NOCMDENV );
  1057. X
  1058. X          /* split line up into whitespace separated tokens */
  1059. X      if ( !strsplit( &argv, env_val, (char *)NULL ) ) {
  1060. X         free( argv );
  1061. X         return;
  1062. X      }
  1063. X
  1064. X      rc = parse_argv_style( argv, cmd );
  1065. X      free( argv );
  1066. X      cmd_list(cmd) = ARGDESCNULL;  /* dont allow lists to continue on */
  1067. #ifdef amiga_style
  1068. X      cmd_prev(cmd) = ARGDESCNULL;
  1069. #endif
  1070. X
  1071. X      if ( rc  &&  !BTEST(cmd_flags(cmd), pa_IGNORE) ) {
  1072. X         eprintf( "%s: syntax-error in %s \"%s\".\n",
  1073. X                  ProgName, USER_VARIABLE, env_name );
  1074. X         eprintf( "\t%s = \"%s\"\n\n", env_name, env_args );
  1075. X         free( env_val );
  1076. X         usage( cmd );
  1077. X         exit( 2 );
  1078. X      }
  1079. X
  1080. X      free( env_val );
  1081. X      cmd_flags(cmd) = (saveflags | pa_CONTINUE);
  1082. X   }
  1083. X
  1084. X   envfree( env_args );
  1085. }
  1086. X
  1087. #undef ENV_SUFFIX
  1088. X
  1089. X
  1090. /***************************************************************************
  1091. ** ^FUNCTION: parse_init -- initialize an ARGDESC for parsing
  1092. **
  1093. ** ^SYNOPSIS:
  1094. */
  1095. #ifndef __ANSI_C__
  1096. X   static ARGDESC *parse_init( argdp )
  1097. /*
  1098. ** ^PARAMETERS:
  1099. */
  1100. X   ARGDESC *argdp[];
  1101. /*    -- pointer to the argument descriptor array.
  1102. */
  1103. #endif  /* !__ANSI_C__ */
  1104. X
  1105. /* ^DESCRIPTION:
  1106. **    Parse_init will perform the usual pre-parsing actions such as checking
  1107. **    $PARSECNTL, parsing $<NAME>_ARGS, and resetting the command-line.
  1108. **
  1109. ** ^REQUIREMENTS:
  1110. **    <argdp> should point to an array of ARGDESCs declared using the CMD_XXXX
  1111. **    macros or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1112. **
  1113. ** ^SIDE-EFECTS:
  1114. **      Initialize argd and parses any default arguments.
  1115. **
  1116. ** ^RETURN-VALUE:
  1117. **    pointer to the arg-descriptors
  1118. **
  1119. ** ^ALGORITHM:
  1120. **    - check for a NULL argdesc-array
  1121. **    - initialize the command-object if necessary
  1122. **    - set ProgName
  1123. **    - read $PARSECNTL
  1124. **    - reset the command-line if necessary (and parse $<NAME>_ARGS)
  1125. **    - return the command-object
  1126. ***^^**********************************************************************/
  1127. #ifdef __ANSI_C__
  1128. X   static ARGDESC *parse_init( ARGDESC *argdp[] )
  1129. #endif
  1130. {
  1131. X   register ARGDESC *cmd;
  1132. X   BOOL  unnamed = FALSE;
  1133. X
  1134. X      /* allow null argument descriptor */
  1135. X   if ( !(*argdp) )  *argdp = Empty_ArgDesc;
  1136. X
  1137. X      /* initialize command-structure */
  1138. X   if ( !CMD_isINIT(*argdp) )  init_args( *argdp );
  1139. X   cmd = *argdp;
  1140. X
  1141. X      /* save the name of this program (for error messages) */
  1142. X   if ( cmd_argv0(cmd)  &&  *(cmd_argv0(cmd)) ) {
  1143. X      ProgName = cmd_argv0(cmd);
  1144. X   }
  1145. X   else if ( cmd_name(cmd)  &&  *(cmd_name(cmd)) ) {
  1146. X      ProgName = cmd_name(cmd);
  1147. X   }
  1148. X   else {
  1149. X      unnamed = TRUE;
  1150. X   }
  1151. X
  1152. X      /* read PARSECNTL environment variable */
  1153. X   if ( !BTEST(cmd_state(cmd), ps_NOPARSECNTL) ) {
  1154. X      get_parse_flags(*argdp);
  1155. X   }
  1156. X
  1157. X      /* reset argd if necessary */
  1158. X   if ( !BTEST(cmd_flags(cmd), pa_CONTINUE) )  {
  1159. X      reset_args( *argdp );
  1160. X
  1161. X         /* get any options from <CMDNAME>_ARGS environment variable */
  1162. X      if ( !BTEST(cmd_flags(cmd), pa_NOCMDENV)  &&  !unnamed )  {
  1163. X         parse_user_defaults( *argdp );
  1164. X      }
  1165. X   }
  1166. X
  1167. X   return  *argdp;
  1168. }
  1169. X
  1170. X
  1171. /***************************************************************************
  1172. ** ^FUNCTION: usage -- print a usage message
  1173. **
  1174. ** ^SYNOPSIS:
  1175. */
  1176. #ifndef __ANSI_C__
  1177. X   VOID usage( argd )
  1178. /*
  1179. ** ^PARAMETERS:
  1180. */
  1181. X   ARGDESC argd[];
  1182. /*    -- the description of expected arguments.
  1183. */
  1184. #endif  /* !__ANSI_C__ */
  1185. X
  1186. /* ^DESCRIPTION:
  1187. **    Given an argdesc array, usage will print the usage for the given
  1188. **    command in the format specified by the user's USAGECNTL environment
  1189. **    variable.
  1190. **
  1191. ** ^REQUIREMENTS:
  1192. **    <argd> should be an array of ARGDESCs declared using the CMD_XXXX macros
  1193. **    or with the ENDOFARGS (and possible STARTOFARGS) macros.
  1194. **
  1195. ** ^SIDE-EFECTS:
  1196. **    Prints on stderr.
  1197. **
  1198. ** ^RETURN-VALUE:
  1199. **    None.
  1200. **
  1201. ** ^ALGORITHM:
  1202. **    - initialize the command-object
  1203. **    - set ProgName
  1204. **    - read $USAGECNTL
  1205. **    - call <os>_usage()
  1206. ***^^**********************************************************************/
  1207. #ifdef __ANSI_C__
  1208. X   void usage( const ARGDESC argd[] )
  1209. #endif
  1210. {
  1211. X   register CONST ARGDESC *cmd;
  1212. X   argMask_t  usg_ctl = 0;
  1213. X
  1214. X      /* allow null argument descriptor */
  1215. X   if ( !argd )  argd = Empty_ArgDesc;
  1216. X
  1217. X   if ( !CMD_isINIT(argd) )   init_args( (ARGDESC *)argd );
  1218. X   cmd = argd;
  1219. X
  1220. X   if ( cmd_argv0(cmd) && *(cmd_argv0(cmd)) ) {
  1221. X      ProgName = cmd_argv0(cmd);
  1222. X   }
  1223. X   else if ( cmd_name(cmd) && *(cmd_name(cmd)) ) {
  1224. X      ProgName = cmd_name(cmd);
  1225. X   }
  1226. X
  1227. X   usg_ctl = get_usage_flags(cmd);
  1228. X   if ( !BTEST(usg_ctl, usg_NONE) )  print_usage_style( argd, usg_ctl );
  1229. }
  1230. X
  1231. X
  1232. /***************************************************************************
  1233. ** ^FUNCTION: parsecntl - control various aspects of command-line parsing
  1234. **
  1235. ** ^SYNOPSIS:
  1236. */
  1237. #ifndef __ANSI_C__
  1238. X   int  parsecntl( argd, cntl, mode, va_alist )
  1239. /*
  1240. ** ^PARAMETERS:
  1241. */
  1242. X   ARGDESC *argd;
  1243. /*    --  The command-object to operate upon
  1244. */
  1245. X   parsecntl_t cntl;
  1246. /*    --  The control-code corresponding to the desired operation
  1247. */
  1248. X   parsemode_t mode;
  1249. /*    --  The mode of the operation (read, write, or both)
  1250. */
  1251. X   va_dcl
  1252. /*    -- any further args followed by the item to be read/written
  1253. */
  1254. #endif  /* !__ANSI_C__ */
  1255. X  
  1256. /* ^DESCRIPTION:
  1257. **    Parsecntl will read and/or write the desired attributes of the given
  1258. **    command-object. The attributes to be operated upon are specified by
  1259. **    the second parameter to parsecntl. The desired mode (read, write, or
  1260. **    both) are specified by the third parameter to parsecntl. If the
  1261. **    operation to be performed is pc_ARGFLAGS, then the fourth argument to
  1262. **    parsecntl should be the keyword name of the argument whose flags are to
  1263. **    be retrieved.  The last parameter to parsecntl is always the object to
  1264. **    contain the attribute(s) to be read/written.  If the attribute(s) are
  1265. **    to be read (regardless of whether or not they are also being changed)
  1266. **    then the last argument should be a pointer to an object, otherwise the
  1267. **    last argument should be the object itself.
  1268. **
  1269. **    If mode is pc_READ, then the desired attributes are copied into the
  1270. **    object pointed to by the last parameter. If the mode is pc_WRITE, then
  1271. **    the attributes from the last parameter are copied to the command-
  1272. **    object. If mode is pc_RDWR, then the attributes pointed to by the last
  1273. **    parameter are copied to the command-object, and then the previous
  1274. **    value of these attributes (before they were overwritten) is copied
  1275. **    into the object pointed to by the last parameter.
  1276. **
  1277. **    If cntl is pc_ARGFLAGS, then the only valid mode pc_READ. All other
  1278. **    attributes may be written or read by parsecntl.
  1279. **
  1280. ** ^REQUIREMENTS:
  1281. **    If mode is READ or READ+WRITE then the last argument should be the
  1282. **    address of the desired object, otherwise it should be the object itself.
  1283. **
  1284. ** ^SIDE-EFECTS:
  1285. **    None if the mode is READ, otherwise, the desired attibutes are (re)set
  1286. **
  1287. ** ^RETURN-VALUE:
  1288. **    pe_SYSTEM
  1289. **       -- If a system error occurred
  1290. ** 
  1291. **    pe_SUCCESS
  1292. **       -- success, no errors encountered.
  1293. ** 
  1294. **    pe_DEFARGS
  1295. **       -- an attempt (using parsecntl()) was made to change the
  1296. **          default arg-search list of a command to point to an argdesc-array
  1297. **          which already has the given command on its default arg-search list
  1298. **          (which would cause an infinite loop when attempting to match an
  1299. **          unknown command-line argument).
  1300. ** 
  1301. **    pe_NOMATCH
  1302. **       -- unable to match the named argument for the pc_ARGFLAGS cntl
  1303. ** 
  1304. **    pe_BADMODE
  1305. **       -- bad mode for given command in parsecntl()
  1306. ** 
  1307. **    pe_BADCNTL
  1308. **       -- bad command for parsecntl
  1309. **
  1310. ** ^ALGORITHM:
  1311. **    - if cntl is pc_ARGFLAGS
  1312. **      - if mode is pc_WRITE or pc_RDWR then return pe_BADMODE
  1313. **      - get the argument name and try to match it.
  1314. **      - if no match, then return pe_NOMATCH
  1315. **      - copy the flags for the matched argument
  1316. **      - return pe_SUCCESS
  1317. **    - else if cntl is pc_PARSEFLAGS
  1318. **      - save the current parseflags.
  1319. **      - if requested, (re)set the current flags
  1320. **      - if requested, copy the old flags
  1321. **      - return pe_SUCCESS
  1322. **    - else if cntl is pc_DEFARGS
  1323. **      - save the current default-args
  1324. **      - make sure the given default-args does not already contain argd
  1325. **      - if the above assertion failed, return pe_DEFARGS
  1326. **      - if requested, (re)set the default-args
  1327. **      - if requested, copy the old default-args
  1328. **      - return pe_SUCCESS
  1329. **    - else ( cntl must be in {pc_NAME, pc_PURPOSE, pc_DESCRIPTION} )
  1330. **      - save the current setting of the attribute-string
  1331. **      - if requested, (re)set the current attribute-string
  1332. **      - if requested, copy the old attribute-string
  1333. **      - return pe_SUCCESS
  1334. **      endif
  1335. ***^^**********************************************************************/
  1336. X
  1337. X   /*
  1338. X   ** define some convenience macros to determine if we are
  1339. X   ** reading/writing from/to the argdesc-array.
  1340. X   */
  1341. #define isREADING(pmode)  (pmode == pc_READ  || pmode == pc_RDWR)
  1342. #define isWRITING(pmode)  (pmode == pc_WRITE || pmode == pc_RDWR)
  1343. X
  1344. #ifdef __ANSI_C__
  1345. X   int parsecntl( ARGDESC *argd, parsecntl_t cntl, parsemode_t mode, ... )
  1346. #endif
  1347. {
  1348. X   register ARGDESC  *cmd;
  1349. X   register int  rc = pe_SUCCESS;
  1350. X   va_list   ap;
  1351. X
  1352. X   if ( !argd )   return   rc;
  1353. X   if ( !CMD_isINIT(argd) )   init_args(argd);
  1354. X   cmd = argd;
  1355. X   VA_START( ap, mode );   /* get argument to match */
  1356. X
  1357. X      /* now figure out what to do and go do it! */
  1358. X   switch( cntl ) {
  1359. X      case  pc_ARGFLAGS :
  1360. X         {
  1361. X            register ARGDESC *ad, *args;
  1362. X            char *name = VA_ARG( ap, char * );
  1363. X            int  *argflags;
  1364. X            BOOL  is_match = FALSE;
  1365. X
  1366. X            for (args = argd; !is_match  &&  args; args = cmd_defargs(argd) ) {
  1367. X               for (ad = ARG_FIRST(args); !ARG_isEND(ad); ARG_ADVANCE(ad) ) {
  1368. X                  if (arg_type(ad) == argDummy)   continue;
  1369. X                  if ( match(name, arg_sname(ad)) == 0 ) {
  1370. X                     is_match = TRUE;
  1371. X                     break;
  1372. X                  }
  1373. X               }/*foreach arg*/
  1374. X            }/*foreach argdesc*/
  1375. X
  1376. X            if ( !is_match ) {
  1377. X               VA_END(ap);
  1378. X               return  pe_NOMATCH;
  1379. X            }
  1380. X
  1381. X            if ( isREADING(mode) ) {
  1382. X              argflags = VA_ARG( ap, int * );
  1383. X              *argflags = (int) arg_flags(ad);
  1384. X            }
  1385. X            else {
  1386. X              rc = pe_BADMODE;  /* parsecntl() wont set ARGFLAGS */
  1387. X            }
  1388. X         }/*block*/
  1389. X         break;
  1390. X
  1391. X      case  pc_PARSEFLAGS :
  1392. X         {
  1393. X            int *pflags, flags;
  1394. X
  1395. X            if ( isREADING(mode) ) {
  1396. X              pflags = VA_ARG( ap, int * );
  1397. X              flags = (int) cmd_flags(cmd);
  1398. X            }
  1399. X            else {
  1400. X              flags = VA_ARG( ap, int );
  1401. X              pflags = &flags;
  1402. X            }
  1403. X
  1404. X            if ( isWRITING(mode) )  cmd_flags(cmd) = (argMask_t) *pflags;
  1405. X            if ( isREADING(mode) )  *pflags = flags;
  1406. X         }/*block*/
  1407. X         break;
  1408. X
  1409. X      case  pc_DEFARGS :
  1410. X         {
  1411. X            ARGDESC **pdefargd, *defargd;
  1412. X
  1413. X            if ( isREADING(mode) ) {
  1414. X               pdefargd = VA_ARG( ap, ARGDESC ** );
  1415. X               defargd = cmd_defargs(cmd);
  1416. X            }
  1417. X            else {
  1418. X               defargd = VA_ARG( ap, ARGDESC * );
  1419. X               pdefargd = &defargd;
  1420. X            }
  1421. X
  1422. X            if ( isWRITING(mode) ) {
  1423. X               ARGDESC *args;
  1424. X
  1425. X               if ( !CMD_isINIT(*pdefargd) )  init_args( *pdefargd );
  1426. X
  1427. X                  /* make sure we are not on the default-argdesc's
  1428. X                  ** default-argument hierarchy (or an infinite loop
  1429. X                  ** will result).
  1430. X                  */
  1431. X               for ( args = *pdefargd; rc && args; args = cmd_defargs(args) ) {
  1432. X                  if ( args == cmd )  rc = pe_DEFARGS;
  1433. X               }
  1434. X               if ( !rc )  cmd_defargs(cmd) = *pdefargd;  /* set new defaults */
  1435. X            }/*if*/
  1436. X
  1437. X            if ( isREADING(mode) )  *pdefargd = defargd;
  1438. X         }
  1439. X         break;
  1440. X
  1441. X      case  pc_NAME :
  1442. X      case  pc_PURPOSE :
  1443. X      case  pc_DESCRIPTION :
  1444. X         {
  1445. X            CONST char *str, **pstr;
  1446. X
  1447. X            if ( isREADING(mode) ) {
  1448. X               pstr = VA_ARG( ap, CONST char ** );
  1449. X               if      ( cntl == pc_NAME )        str = cmd_name(cmd);
  1450. X               else if ( cntl == pc_PURPOSE )     str = cmd_purpose(cmd);
  1451. X               else /* cntl == pc_DESCRIPTION */  str = cmd_description(cmd);
  1452. X            }
  1453. X            else {
  1454. X               str = VA_ARG( ap, CONST char * );
  1455. X               pstr = &str;
  1456. X            }
  1457. X
  1458. X            if ( isWRITING(mode) )  {
  1459. X               if      ( cntl == pc_NAME )        cmd_name(cmd) = *pstr;
  1460. X               else if ( cntl == pc_PURPOSE )     cmd_purpose(cmd) = *pstr;
  1461. X               else /* cntl == pc_DESCRIPTION */  cmd_description(cmd) = *pstr;
  1462. X            }
  1463. X            if ( isREADING(mode) )  *pstr = str;
  1464. X         }/*block*/
  1465. X         break;
  1466. X
  1467. X      default :
  1468. X         rc = pe_BADCNTL;
  1469. X         break;
  1470. X   }/*switch*/
  1471. X
  1472. X   VA_END( ap );
  1473. X   return   rc;
  1474. }
  1475. X
  1476. #undef  isREADING
  1477. #undef  isWRITING
  1478. X
  1479. X
  1480. /***************************************************************************
  1481. ** ^FUNCTION: sparseargs - parse arguments in a string
  1482. **
  1483. ** ^SYNOPSIS:
  1484. */
  1485. #ifndef __ANSI_C__
  1486. X   int sparseargs( str, argd )
  1487. /*
  1488. ** ^PARAMETERS:
  1489. */
  1490. X   char    *str;
  1491. /*    -- string to parse
  1492. */
  1493. X   ARGDESC *argd;
  1494. /*    -- pointer to argument descriptor table
  1495. */
  1496. #endif  /* !__ANSI_C__ */
  1497. X  
  1498. /* ^DESCRIPTION:
  1499. **    Given a single string and an argdesc array, sparseargs will parse
  1500. **    arguments from a string in much the same manner as parseargs.
  1501. **    Sparseargs will split the given string up into a vector of whitespace
  1502. **    separated tokens and then attempt to parse the resultant vector as if
  1503. **    it were given as argv[] on the command-line.  NO special treatment is
  1504. **    given to characters such as single-quotes, double-quotes, or anything
  1505. **    else. Sparseargs will always assume that any whitespace characters are
  1506. **    intended as argument separators.
  1507. **
  1508. ** ^REQUIREMENTS:
  1509. **    <str> should be non-NULL and non-empty
  1510. **
  1511. ** ^SIDE-EFECTS:
  1512. **    <str> is modified by strsplit().
  1513. **    <argd> is modified accordingly as arguments are matched.
  1514. **
  1515. ** ^RETURN-VALUE:
  1516. **    pe_SYSTEM
  1517. **       -- If a system error occurred
  1518. ** 
  1519. **    pe_SUCCESS
  1520. **       -- success, no errors encountered.
  1521. ** 
  1522. **    pe_SYNTAX
  1523. **       -- If a syntax error occurs in <str>
  1524. **
  1525. ** ^ALGORITHM:
  1526. **    - save current parse-flags
  1527. **    - add pa_ARGV0 to current parse-flags
  1528. **    - split string up into a vector of tokens
  1529. **    - call parse init and then parse the arguments
  1530. **    - if syntax-error, print usage and exit
  1531. **    - restore parse-flags
  1532. **    - return the status from parsing the vector
  1533. ***^^**********************************************************************/
  1534. #ifdef __ANSI_C__
  1535. X   int sparseargs( char *str, ARGDESC *argd )
  1536. #endif
  1537. {
  1538. X   argMask_t  saveflags;
  1539. X   char **argv;
  1540. X   int  rc = 0;
  1541. X
  1542. X   if ( !argd )  return  pe_SUCCESS;
  1543. X
  1544. X   if ( !CMD_isINIT(argd) )  init_args(argd);
  1545. X
  1546. X      /* save old flags & initialize set parse flags */
  1547. X   saveflags = cmd_flags(argd);
  1548. X   BSET(cmd_flags(argd), pa_ARGV0);
  1549. X
  1550. X      /* split line up into whitespace separated tokens */
  1551. X   if ( !strsplit( &argv, str, (char *)NULL ) ) {
  1552. X      free( argv );
  1553. X      return  rc;
  1554. X   }
  1555. X
  1556. X   rc = parse_argv_style( argv, parse_init( &argd ) );
  1557. X
  1558. X      /* reset previous parse flags */
  1559. X   cmd_flags(argd)  =  saveflags;
  1560. X
  1561. X      /* scan for missing required arguments */
  1562. X   if ( SYNTAX_ERROR(rc, argd) ) {
  1563. X      fputc( '\n', stderr );
  1564. X      usage( argd );
  1565. X      exit( 2 );
  1566. X   }
  1567. X
  1568. X   return  rc;
  1569. }
  1570. X
  1571. X
  1572. /***************************************************************************
  1573. ** ^FUNCTION: fparseargs - parse arguments from a file
  1574. **
  1575. ** ^SYNOPSIS:
  1576. */
  1577. #ifndef __ANSI_C__
  1578. X   int fparseargs( fp, argd )
  1579. /*
  1580. ** ^PARAMETERS:
  1581. */
  1582. X   FILE    *fp;
  1583. /*    -- pointer to file to read (must already be open)
  1584. */
  1585. X   ARGDESC *argd;
  1586. /*    -- pointer to argument descriptor table
  1587. */
  1588. #endif  /* !__ANSI_C__ */
  1589. X
  1590. /* ^DESCRIPTION:
  1591. **    Given a readable input stream and an argdesc array, fparseargs will
  1592. **    parse arguments in a file in much the same manner as parseargs.  A
  1593. **    maximum-line length of 255 characters is imposed.  NO "escaping" of
  1594. **    any kind is performed. Comments of a limited form are permitted: if
  1595. **    the first non-whitespace character on a line is a '#' (or '!' for VMS)
  1596. **    then that entire line is considered a comment and is ignored.  If a
  1597. **    value is provided for an argument that is NOT a list or a vector, then
  1598. **    the value MUST be on the same line as the argument (in other words,
  1599. **    "-v val" is fine but "-v\nval" is a not).
  1600. **
  1601. ** ^REQUIREMENTS:
  1602. **    <fp> should be non-NULL, already opened-for-reading, file-pointer
  1603. **
  1604. ** ^SIDE-EFECTS:
  1605. **    <argd> is modified accordingly as arguments are matched.
  1606. **
  1607. ** ^RETURN-VALUE:
  1608. **    pe_SYSTEM
  1609. **       -- If a system error occurred
  1610. ** 
  1611. **    pe_SUCCESS
  1612. **       -- success, no errors encountered.
  1613. ** 
  1614. **    pe_SYNTAX
  1615. **       -- if a syntax error occurs in the file
  1616. **
  1617. ** ^ALGORITHM:
  1618. **    - save current parse-flags
  1619. **    - add pa_ARGV0 to current parse-flags
  1620. **    - add pa_NOCHECK to current parse-flags (dont check until eof)
  1621. **    - call parse init
  1622. **    - parse the first line of the file
  1623. **    - add pa_CONTINUE to current parse-flags
  1624. **    - parse the rest of the file
  1625. **    - restore parse-flags
  1626. **    - now check for missing args if required
  1627. **    - if syntax-error, print usage and exit
  1628. **    - return
  1629. ***^^**********************************************************************/
  1630. X
  1631. #ifdef vms_style
  1632. #  define  c_COMMENT  '!'
  1633. #else
  1634. #  define  c_COMMENT  '#'
  1635. #endif
  1636. X
  1637. #ifdef __ANSI_C__
  1638. X   int fparseargs( FILE *fp, ARGDESC *argd )
  1639. #endif
  1640. {
  1641. X   int  rc;
  1642. X   char  text[ MAXLINE ];
  1643. X   argMask_t  saveflags;
  1644. X
  1645. X   if ( !argd )  return  0;
  1646. X
  1647. X   if ( !CMD_isINIT(argd) )  init_args(argd);
  1648. X
  1649. X      /* save old flags & initialize parse flags for first call */
  1650. X   saveflags = cmd_flags(argd);
  1651. X   BSET(cmd_flags(argd), pa_ARGV0 | pa_NOCHECK | pa_COPYF);
  1652. X
  1653. X   while ( !feof( fp ) ) {
  1654. X      if ( !fgets( text, MAXLINE, fp ) ) {
  1655. X         if ( ferror( fp ) )  {
  1656. X            cmd_flags(argd)  =  saveflags;
  1657. X            return  pe_SYSTEM;
  1658. X         }
  1659. X      }/*if*/
  1660. X
  1661. X         /* trim leading and trailing whitespace and check for comments */
  1662. X      (VOID) strtrim( text, (char *)NULL );
  1663. X      if ( !text  ||  !(*text)  ||  *text == c_COMMENT )  continue;
  1664. X
  1665. X      rc = sparseargs( text, argd );
  1666. X
  1667. X         /* set up parseflags for next call */
  1668. X      BSET(cmd_flags(argd), pa_CONTINUE);
  1669. X   }/*while !EOF*/
  1670. X
  1671. X      /* reset previous parse flags */
  1672. X   cmd_flags(argd)  =  saveflags;
  1673. X
  1674. X      /* scan for missing required args */
  1675. X   if ( SYNTAX_ERROR(rc, argd) ) {
  1676. X      fputc('\n', stderr);
  1677. X      usage( argd );
  1678. X      exit( 2 );
  1679. X   }
  1680. X
  1681. X   return  rc;
  1682. }
  1683. X
  1684. #undef  c_COMMENT
  1685. X
  1686. X
  1687. /***************************************************************************
  1688. ** ^FUNCTION: lparseargs - parse arguments from a list
  1689. **
  1690. ** ^SYNOPSIS:
  1691. */
  1692. #ifndef __ANSI_C__
  1693. X   int lparseargs( argls, argd )
  1694. /*
  1695. ** ^PARAMETERS:
  1696. */
  1697. X   ArgList *argls;
  1698. /*    -- linked list of args to parse
  1699. */
  1700. X   ARGDESC *argd;
  1701. /*    -- pointer to argument descriptor table
  1702. */
  1703. #endif  /* !__ANSI_C__ */
  1704. X  
  1705. /* ^DESCRIPTION:
  1706. **    Given an ArgList and an argdesc array, lparseargs will parse arguments
  1707. **    in a file in much the same manner as parseargs.
  1708. **
  1709. ** ^REQUIREMENTS:
  1710. **    <argls> should be an ArgList of strings
  1711. **
  1712. ** ^SIDE-EFECTS:
  1713. **    <argd> is modified accordingly as arguments are matched.
  1714. **
  1715. ** ^RETURN-VALUE:
  1716. **    pe_SYSTEM
  1717. **       -- If a system error occurred
  1718. ** 
  1719. **    pe_SUCCESS
  1720. **       -- success, no errors encountered.
  1721. ** 
  1722. **    pe_SYNTAX
  1723. **       -- if a syntax error occurs
  1724. **
  1725. ** ^ALGORITHM:
  1726. **    - save current parse-flags
  1727. **    - add pa_ARGV0 to current parse-flags
  1728. **    - make a vector out of the ArgList
  1729. **    - call parse init
  1730. **    - restore parse-flags
  1731. **    - if syntax-error, print usage and exit
  1732. **    - return
  1733. ***^^**********************************************************************/
  1734. #ifdef __ANSI_C__
  1735. X   int lparseargs( ArgList *argls, ARGDESC *argd )
  1736. #endif
  1737. {
  1738. X   int   i, argc = 0, rc = 0;
  1739. X   char **argv = (char **)NULL;
  1740. X   argMask_t   saveflags;
  1741. X   register    ArgList *ls;
  1742. X
  1743. X   if ( !argd )  return  0;
  1744. X   if ( !CMD_isINIT(argd) )   init_args(argd);
  1745. X
  1746. X      /* make 1st pass to count the args */
  1747. X   for ( ls = argls; ls ; ls = L_NEXT(ls) ) {
  1748. X      argc++;
  1749. X   }
  1750. X
  1751. X      /* allocate a NULL terminated arg-vector */
  1752. X   argv = (char **)malloc( (argc + 1) * sizeof(char *) );
  1753. X   if ( !argv )  return  pe_SYSTEM;
  1754. X   argv[ argc ] = (char *)NULL;
  1755. X
  1756. X      /* make 2nd pass to assign the elements of the vector */
  1757. X   for ( ls = argls, i = 0 ; ls ; ls = L_NEXT(ls), i++ ) {
  1758. X      argv[i] = L_STRING(ls);
  1759. X   }
  1760. X
  1761. X   saveflags = cmd_flags(argd);
  1762. X   BSET(cmd_flags(argd), pa_ARGV0);
  1763. X   rc = parse_argv_style( argv, parse_init( &argd ) );
  1764. X
  1765. X      /* reset previous parse-flags */
  1766. X   cmd_flags(argd) = saveflags;
  1767. X
  1768. X      /* scan for missing required arguments */
  1769. X   if ( SYNTAX_ERROR(rc, argd) ) {
  1770. X      fputc( '\n', stderr );
  1771. X      usage( argd );
  1772. X      exit( 2 );
  1773. X   }
  1774. X
  1775. X   return   rc;
  1776. }
  1777. X
  1778. X
  1779. /***************************************************************************
  1780. ** ^FUNCTION: vparseargs - parse a variable-argument list
  1781. **
  1782. ** ^SYNOPSIS:
  1783. */
  1784. #ifndef __ANSI_C__
  1785. X   int vparseargs( argd, argc, va_alist )
  1786. /*
  1787. ** ^PARAMETERS:
  1788. */
  1789. X   ARGDESC  *argd;
  1790. /*    -- 
  1791. */
  1792. X   int  argc;
  1793. /*    -- number of arguments to parse
  1794. */
  1795. X   va_dcl
  1796. /*    -- the variable-list of arguments to parse
  1797. */
  1798. #endif  /* !__ANSI_C__ */
  1799. X
  1800. /* ^DESCRIPTION:
  1801. **    Vparseargs takes an argdesc array, the number of arguments to parse,
  1802. **    and a (possibly NULL terminated) list of argument-strings and parses
  1803. **    them in much the same manner as parseargs.  Unlike sparseargs,
  1804. **    vparseargs assumes that all parameters are already split up into
  1805. **    tokens, hence any whitespace characters contained in any of the
  1806. **    string-parameters are used as is (and will be considered a part of
  1807. **    an argument name or value).
  1808. **
  1809. **
  1810. ** ^REQUIREMENTS:
  1811. **    argc must contain the number of arguments to be parsed (NOT including
  1812. **    any terminating NULL pointer).  If a NULL pointer is given as one of
  1813. **    the arguments, and this NULL pointer appears before argc indicated
  1814. **    the last argument would appear, then the NULL pointer will end the
  1815. **    the list of arguments and argc is ignored.
  1816. **    
  1817. ** ^SIDE-EFECTS:
  1818. **    <argd> is modified accordingly as arguments are matched.
  1819. **
  1820. ** ^RETURN-VALUE:
  1821. **    pe_SYSTEM
  1822. **       -- If a system error occurred
  1823. ** 
  1824. **    pe_SUCCESS
  1825. **       -- success, no errors encountered.
  1826. ** 
  1827. **    pe_SYNTAX
  1828. **       -- if a syntax error occurs
  1829. **
  1830. ** ^ALGORITHM:
  1831. **    - save current parse-flags
  1832. **    - add pa_ARGV0 to current parse-flags
  1833. **    - make a vector out of the variable list
  1834. **    - call parse init
  1835. **    - restore parse-flags
  1836. **    - if syntax-error, print usage and exit
  1837. **    - return
  1838. ***^^**********************************************************************/
  1839. #ifdef __ANSI_C__
  1840. X   int vparseargs( ARGDESC *argd, int argc, ... )
  1841. #endif
  1842. {
  1843. X   register char   *arg;
  1844. X   int   i, rc = 0;
  1845. X   argMask_t   saveflags;
  1846. X   char **argv = (char **)NULL;
  1847. X   va_list   ap;
  1848. X
  1849. X   if ( !argd )   return   0;
  1850. X   if ( !CMD_isINIT(argd) )   init_args(argd);
  1851. X
  1852. X   saveflags = cmd_flags(argd);
  1853. X   BSET(cmd_flags(argd), pa_ARGV0 | pa_COPYF);
  1854. X
  1855. X      /* allocate a NULL terminated arg-vector */
  1856. X   argv = (char **) malloc( (argc + 1) * sizeof(char *) );
  1857. X   if ( !argv )  return  pe_SYSTEM;
  1858. X   argv[ argc ] = (char *)NULL;
  1859. X
  1860. X   VA_START(ap, argc);
  1861. X   for ( i = 0; i < argc  &&  (arg = VA_ARG(ap, char *)) ; i++ ) {
  1862. X      argv[i] = arg;
  1863. X   }
  1864. X   VA_END(ap);
  1865. X
  1866. X   rc = parse_argv_style( argv, parse_init( &argd ) );
  1867. X
  1868. X      /* reset previous parse-flags */
  1869. X   cmd_flags(argd) = saveflags;
  1870. X
  1871. X      /* scan for missing required arguments */
  1872. X   if ( SYNTAX_ERROR(rc, argd) ) {
  1873. X      fputc( '\n', stderr );
  1874. X      usage( argd );
  1875. X      exit( 2 );
  1876. X   }
  1877. X
  1878. X   return   rc;
  1879. }
  1880. X
  1881. X
  1882. /***************************************************************************
  1883. ** ^FUNCTION: parseargs -- parse an argument vector
  1884. **
  1885. ** ^SYNOPSIS:
  1886. */
  1887. #ifndef __ANSI_C__
  1888. X   int parseargs( argv, argd )
  1889. /*
  1890. ** ^PARAMETERS:
  1891. */
  1892. X   char  *argv[];
  1893. /*    -- pointer to the argument vector as passed to main().
  1894. */
  1895. X  ARGDESC argd[];
  1896. /*    -- the argument descriptor array.
  1897. */
  1898. #endif  /* !__ANSI_C__ */
  1899. X
  1900. /* ^DESCRIPTION:
  1901. **    Given a vector of string-valued arguments such as that passed to main
  1902. **    and a vector describing the possible arguments, parseargs matches
  1903. **    actual arguments to possible arguments, converts values to the
  1904. **    desired type, and diagnoses problems such as missing arguments, extra
  1905. **    arguments, and argument values that are syntactically incorrect.
  1906. **
  1907. ** ^REQUIREMENTS:
  1908. **    <argv> must be non-NULL and have a NULL pointer as its last item.
  1909. **
  1910. ** ^SIDE-EFECTS:
  1911. **    <argd> is modified accordingly as arguments are matched.
  1912. **
  1913. ** ^RETURN-VALUE:
  1914. **    pe_SYSTEM
  1915. **       -- If a system error occurred
  1916. ** 
  1917. **    pe_SUCCESS
  1918. **       -- success, no errors encountered.
  1919. ** 
  1920. **    pe_SYNTAX
  1921. **       -- if a syntax error occurs
  1922. **
  1923. ** ^ALGORITHM:
  1924. **    - call parse init
  1925. **    - if syntax-error, print usage and exit
  1926. **    - return
  1927. ***^^**********************************************************************/
  1928. #ifdef __ANSI_C__
  1929. X   int parseargs( char *argv[], ARGDESC argd[] )
  1930. #endif
  1931. {
  1932. X   register ARGDESC *cmd;
  1933. X   register char **av = argv;
  1934. X   int rc = pe_SUCCESS;
  1935. X   argMask_t  saveflags;
  1936. X
  1937. X      /* allow null argument descriptor */
  1938. X   if ( !argd )  argd = Empty_ArgDesc;
  1939. X
  1940. X      /* initialize command-structure */
  1941. X   if ( !CMD_isINIT(argd) )  init_args( argd );
  1942. X   cmd = argd;
  1943. X   saveflags = cmd_flags(cmd);
  1944. X
  1945. X   if ( argv  &&  !BTEST(pa_ARGV0, cmd_flags(cmd)) ) {
  1946. X      cmd_argv0(cmd) = basename( *av++ );
  1947. X   }/*if*/
  1948. X
  1949. X   rc = parse_argv_style( av, parse_init( &argd ) );
  1950. X
  1951. X      /* reset previous parse-flags */
  1952. X   cmd_flags(cmd) = saveflags;
  1953. X
  1954. X      /* scan for missing required arguments */
  1955. X   if ( SYNTAX_ERROR(rc, argd) ) {
  1956. X      fputc( '\n', stderr );
  1957. X      usage( argd );
  1958. X      exit( 2 );
  1959. X   }
  1960. X
  1961. X   return  rc;
  1962. }
  1963. SHAR_EOF
  1964. echo 'File parseargs/xparse.c is complete' &&
  1965. chmod 0664 parseargs/xparse.c ||
  1966. echo 'restore of parseargs/xparse.c failed'
  1967. Wc_c="`wc -c < 'parseargs/xparse.c'`"
  1968. test 70930 -eq "$Wc_c" ||
  1969.     echo 'parseargs/xparse.c: original size 70930, current size' "$Wc_c"
  1970. rm -f _shar_wnt_.tmp
  1971. fi
  1972. rm -f _shar_seq_.tmp
  1973. echo You have unpacked the last part
  1974. exit 0
  1975. exit 0 # Just in case...
  1976. -- 
  1977. Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
  1978. Sterling Software, IMD           UUCP:     uunet!sparky!kent
  1979. Phone:    (402) 291-8300         FAX:      (402) 291-4362
  1980. Please send comp.sources.misc-related mail to kent@uunet.uu.net.
  1981.