home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume29 / parseargs / part07 / strfuncs.c
C/C++ Source or Header  |  1992-05-19  |  41KB  |  1,514 lines

  1. /**************************************************************************
  2. ** ^FILE: strfuncs.c - Miscellaneous string functions for parseargs
  3. **
  4. ** ^DESCRIPTION:
  5. **    This file implements a wide variety of functions to manipulate
  6. **    strings in way way or another. Some of the functions may already
  7. **    be included in the standard library of some C compilers.
  8. **
  9. **    The following functions are implemented:
  10. **
  11. **       strucpy() -- copy a string and map to uppercase
  12. **       strlcpy() -- copy a string and map to lowercase
  13. **       strupr() -- convert a string to uppercase
  14. **       strlwr() -- convert a string to lowercase
  15. **       stricmp() -- case insensitive comparison of strings
  16. **       strnicmp() -- case insensitive length-limited comparison of strings
  17. **       strdup() -- return a (newly allocated) copy of a string
  18. **       strndup() -- return a (newly allocated) copy of a prefix of a string
  19. **       strpbrk() -- return first occurrence of character in a set
  20. **       strspn() -- return length of initial prefix of a character set
  21. **       strcspn() -- return length of initial prefix of a character set
  22. **       strltrim() -- trim leftmost (leading) characters in a string
  23. **       strrtrim() -- trim rightmost (trailing) characters in a string
  24. **       strtrim() -- trim leading and trailing characters in a string
  25. **       strsplit() -- split a string up into a vector of tokens
  26. **       strjoin() -- join a vector of tokens into a single string
  27. **       get_argpfx() -- return the length of the first part of the string
  28. **       get_argdesc() -- return the description (second part) of the string
  29. **       get_argname() -- return the aname (argument-name) of an argument
  30. **       get_kwdname() -- return the sname (keyword-name) of an argument
  31. **       match() -- match two keywords (case insensitive) upto a unique prefix
  32. **       basename() -- remove the leading directories (and disks) from a path
  33. **       indent_para() -- print an indented hanging paragraph
  34. **
  35. ** ^HISTORY:
  36. **
  37. **    12/16/91     Brad Appleton     <brad@ssd.csd.harris.com>
  38. **    - add strndup()
  39. **
  40. **    11/26/91     Brad Appleton     <brad@ssd.csd.harris.com>
  41. **    - added the following to indent_para(). If last arg is 0,
  42. **      then the whole length is used.
  43. **
  44. **    08/27/91     Earl Chew     <cechew@bruce.cs.monash.edu.au>
  45. **    - add extra length argument to indent_para().
  46. **    - add FORCE_KWDCASE() macro
  47. **    - add non-writable strings support to get_argname() and
  48. **      get_kwdname()
  49. **    - add get_argpfx() and get_argdesc() for non-writable strings
  50. **      support
  51. **    - allow zero length string for strsplit()
  52. **
  53. **    01/02/91     Brad Appleton     <brad@ssd.csd.harris.com>     Created
  54. **    - changed from file misc.c to this name and added all of the strxxx
  55. **      functions (plus got rid of some unused functions).
  56. **
  57. **    --/--/--    Peter da Silva    <peter@ferranti.com>    
  58. **
  59. **    --/--/--    Eric P. Allman    <eric@Berkeley.EDU>     Created
  60. ***^^**********************************************************************/
  61.  
  62. #include <stdio.h>
  63. #include <ctype.h>
  64. #include <useful.h>
  65.  
  66. #ifdef vms
  67. # include <ssdef.h>
  68. #endif
  69.  
  70. #include "strfuncs.h"
  71.  
  72. EXTERN  VOID   syserr  ARGS((const char *, ...));
  73.  
  74. static CONST char WhiteSpace[] = " \t\n\r\v\f";
  75.  
  76. #define c_ARG_SEP '='
  77. #if ( defined(unix_style)  ||  defined(ibm_style) )
  78. # define  FORCE_KWDCASE(s)       strlwr(s)
  79. # define  TO_KWDCASE(c)          TOLOWER(c)
  80. # define  KWDCASECOPY(dest,src)  strlcpy(dest,src)
  81. #else
  82. # define  FORCE_KWDCASE(s)       strupr(s)
  83. # define  TO_KWDCASE(c)          TOUPPER(c)
  84. # define  KWDCASECOPY(dest,src)  strucpy(dest,src)
  85. #endif
  86.  
  87.  
  88. /***************************************************************************
  89. ** ^FUNCTION: strucpy, strlcpy - copy dest to src, mapping to upper/lower case
  90. **
  91. ** ^SYNOPSIS:
  92. **
  93. **    char *strucpy( dest, src )
  94. **    char *strlcpy( dest, src )
  95. **
  96. ** ^PARAMETERS:
  97. **    char *dest;
  98. **    -- the address to start copying to
  99. **
  100. **    char *src;
  101. **    -- the address to start copying from
  102. **
  103. ** ^DESCRIPTION:
  104. **    Strlcpy (strucpy) copies src into dest (upto and including the
  105. **    terminating NUL byte) and all uppercase (lowercase) characters in
  106. **    src are mapped to lowercase (uppercase) before being copied into dest.
  107. **
  108. ** ^REQUIREMENTS:
  109. **    Dest must be non-null, and large enough to hold the copied result.
  110. **
  111. ** ^SIDE-EFFECTS:
  112. **    Dest is (re)written
  113. **
  114. ** ^RETURN-VALUE:
  115. **    Address of dest.
  116. **
  117. ** ^ALGORITHM:
  118. **    Trivial.
  119. ***^^**********************************************************************/
  120. #ifdef __ANSI_C__
  121.    char *strucpy( char *dest, const char *src )
  122. #else
  123.    char *strucpy( dest, src )  char *dest, *src;
  124. #endif
  125. {
  126.    register char  *s1 = dest;
  127.    register CONST char  *s2 = src;
  128.  
  129.    if ( !s2 )  return  CHARNULL;
  130.  
  131.    for ( ; *s2 ; s1++, s2++ ) {
  132.       *s1 = TOUPPER( *s2 );
  133.    }
  134.    *s1 = '\0';
  135.  
  136.    return   s1;
  137. }
  138.  
  139.  
  140. #ifdef __ANSI_C__
  141.    char *strlcpy( char *dest, const char *src )
  142. #else
  143.    char *strlcpy( dest, src )  char *dest, *src;
  144. #endif
  145. {
  146.    register char  *s1 = dest;
  147.    register CONST char  *s2 = src;
  148.  
  149.    if ( !s2 )  return  CHARNULL;
  150.  
  151.    for ( ; *s2 ; s1++, s2++ ) {
  152.       *s1 = TOLOWER( *s2 );
  153.    }
  154.    *s1 = '\0';
  155.  
  156.    return   s1;
  157. }
  158.  
  159.  
  160. /***************************************************************************
  161. ** ^FUNCTION: strupr, strlwr - convert a string to all upper/lower case
  162. **
  163. ** ^SYNOPSIS:
  164. **    char *strupr( str )
  165. **    char *strlwr( str )
  166. **
  167. ** ^PARAMETERS:
  168. **    char *str;
  169. **    -- the string to be converted
  170. **
  171. ** ^DESCRIPTION:
  172. **    Strupr (strlwr) converts all lowercase (uppercase) characters in <str>
  173. **    to uppercase (lowercase) and returns the address of <str>.
  174. **
  175. ** ^REQUIREMENTS:
  176. **    str should be non-null and non-empty.
  177. **
  178. ** ^SIDE-EFFECTS:
  179. **    str is overwritten with the uppercase (lowercase) result.
  180. **
  181. ** ^RETURN-VALUE:
  182. **    Address of str.
  183. **
  184. ** ^ALGORITHM:
  185. **    Trivial.
  186. ***^^**********************************************************************/
  187. #ifdef __ANSI_C__
  188.    char *strupr( char *str )
  189. #else
  190.    char *strupr( str )  char *str;
  191. #endif
  192. {
  193.    char *p = str;
  194.  
  195.    for ( ; p && *p ; p++ ) {
  196.       if ( islower(*p) )  *p = toupper(*p);
  197.    }
  198.  
  199.    return   str;
  200. }
  201.  
  202.  
  203. #ifdef __ANSI_C__
  204.    char *strlwr( char *str )
  205. #else
  206.    char *strlwr( str )  char *str;
  207. #endif
  208. {
  209.    char *p = str;
  210.  
  211.    for ( ; p && *p ; p++ ) {
  212.       if ( isupper(*p) )  *p = tolower(*p);
  213.    }
  214.  
  215.    return   str;
  216. }
  217.  
  218.  
  219. /***************************************************************************
  220. ** ^FUNCTION: stricmp, strnicmp - case insensitive string comparison
  221. **
  222. ** ^SYNOPSIS:
  223. **    int stricmp( s1, s2 )
  224. **    int strnicmp( s1, s2, n )
  225. **
  226. ** ^PARAMETERS:
  227. **    char *s1;
  228. **    -- first string to compare
  229. **
  230. **    char *s2;
  231. **    -- second string to compare
  232. **
  233. **    size_t  n;
  234. **    -- The number of characters to compare
  235. **
  236. ** ^DESCRIPTION:
  237. **    Stricmp (strnicmp) is identical to strcmp (strncmp) except that it
  238. **    it performs a case-insensitive comparison of characters.
  239. **
  240. ** ^REQUIREMENTS:
  241. **    Both s1 and s2 should be non-null and non-empty
  242. **
  243. ** ^SIDE-EFFECTS:
  244. **    None.
  245. **
  246. ** ^RETURN-VALUE:
  247. **    < 0    if s1 < s2
  248. **    = 0    if s1 matches s2
  249. **    > 0    if s1 > s2
  250. **
  251. ** ^ALGORITHM:
  252. **    Trivial.
  253. ***^^**********************************************************************/
  254. #ifdef __ANSI_C__
  255.    int stricmp( const char *str1, const char *str2 )
  256. #else
  257.    int stricmp( str1, str2 ) char *str1, *str2;
  258. #endif
  259. {
  260.    register  CONST char *s1 = str1, *s2 = str2;
  261.    register  char  c1, c2;
  262.  
  263.    if ( s1 == s2 )  return   0;
  264.    if ( !s1 )       return  -1;
  265.    if ( !s2 )       return   1;
  266.  
  267.    for ( ; *s1 && *s2 ; s1++ , s2++ ) {
  268.       c1 = TOLOWER( *s1 );
  269.       c2 = TOLOWER( *s2 );
  270.  
  271.       if (c1 != c2)  return  (int)(c1 -c2);
  272.    }
  273.    return   (*s1 == *s2) ? 0 : (int)(*s1 - *s2);
  274. }
  275.  
  276.  
  277. #ifdef __ANSI_C__
  278.    int strnicmp( const char *str1, const char *str2, size_t len )
  279. #else
  280.    int strnicmp( str1, str2, len ) char *str1, *str2; size_t  len;
  281. #endif
  282. {
  283.    register  CONST char *s1 = str1, *s2 = str2;
  284.    register  char  c1, c2;
  285.  
  286.    if ( s1 == s2 )  return   0;
  287.    if ( !s1 )       return  -1;
  288.    if ( !s2 )       return   1;
  289.  
  290.    for ( ; *s1 && *s2 && len ; s1++ , s2++ , len-- ) {
  291.       c1 = TOLOWER( *s1 );
  292.       c2 = TOLOWER( *s2 );
  293.  
  294.       if (c1 != c2)  return  (int)(c1 -c2);
  295.    }
  296.    return   (!len  ||  (*s1 == *s2)) ? 0 : (int)(*s1 - *s2);
  297. }
  298.  
  299.  
  300. /***************************************************************************
  301. ** ^FUNCTION: strdup - copy a string
  302. **
  303. ** ^SYNOPSIS:
  304. */
  305. # ifndef __ANSI_C__
  306.    char *strdup( str )
  307. /*
  308. ** ^PARAMETERS:
  309. */
  310.    char *str;
  311. /*    -- the string to replicate
  312. */
  313. # endif  /* !__ANSI_C__ */
  314.  
  315. /* ^DESCRIPTION:
  316. **    Strdup allocates storrage and copies the given string into the
  317. **    newly acquired space (returning its address). The returned result
  318. **    should be deallocated using free().
  319. **
  320. ** ^REQUIREMENTS:
  321. **    str should be non-null
  322. **
  323. ** ^SIDE-EFFECTS:
  324. **    None.
  325. **
  326. ** ^RETURN-VALUE:
  327. **    Address of the newly allocated string.
  328. **
  329. ** ^ALGORITHM:
  330. **    Trivial.
  331. ***^^**********************************************************************/
  332. # ifdef __ANSI_C__
  333.    char *strdup( const char *str )
  334. # endif
  335. {
  336.   unsigned len = strlen(str) + 1;
  337.   char *p = (char *)malloc( len * sizeof(char) );
  338.  
  339.   if ( !p )  syserr( "malloc failed in strdup()" );
  340.   strcpy(p, str);
  341.  
  342.   return p;
  343. }
  344.  
  345.  
  346. /***************************************************************************
  347. ** ^FUNCTION: strndup - copy a prefix of a string
  348. **
  349. ** ^SYNOPSIS:
  350. */
  351. # ifndef __ANSI_C__
  352.    char *strndup( str, len )
  353. /*
  354. ** ^PARAMETERS:
  355. */
  356.    char *str;
  357. /*    -- the string to replicate
  358. */
  359.    unsigned  len;
  360. /*    -- the number of characters to be replicated
  361. */
  362. # endif  /* !__ANSI_C__ */
  363.  
  364. /* ^DESCRIPTION:
  365. **    Strndup allocates storrage and copies the the first "len" characters
  366. **    of the given string into the newly acquired space (returning its
  367. **    address). The returned result should be deallocated using free().
  368. **
  369. ** ^REQUIREMENTS:
  370. **    str should be non-null
  371. **
  372. ** ^SIDE-EFFECTS:
  373. **    None.
  374. **
  375. ** ^RETURN-VALUE:
  376. **    Address of the newly allocated string.
  377. **
  378. ** ^ALGORITHM:
  379. **    Trivial.
  380. ***^^**********************************************************************/
  381. # ifdef __ANSI_C__
  382.    char *strndup( const char *str, unsigned len )
  383. # endif
  384. {
  385.   char *p = (char *)malloc( (len + 1) * sizeof(char) );
  386.  
  387.   if ( !p )  syserr( "malloc failed in strndup()" );
  388.   strncpy(p, str, len);
  389.  
  390.   return p;
  391. }
  392.  
  393.  
  394. #ifdef BSD
  395.  
  396. /***************************************************************************
  397. ** ^FUNCTION: strpbrk - return the first occurrence of characters in a string
  398. **
  399. ** ^SYNOPSIS:
  400. */
  401. #ifndef __ANSI_C__
  402.    char *strpbrk( str1, str2 )
  403. /*
  404. ** ^PARAMETERS:
  405. */
  406.    char *str1;
  407. /*    -- the string to be searched
  408. */
  409.    char *str2;
  410. /*    -- the set of characters to be located
  411. */
  412. #endif  /* !__ANSI_C__ */
  413.  
  414. /* ^DESCRIPTION:
  415. **    Strpbrk will attempt to locate the first occurence in str1 of any 
  416. **    character from str2 and return the address of the first such
  417. **    occurrence. If str1 contains NO characters from str2, then NULL
  418. **    is returned.
  419. **
  420. ** ^REQUIREMENTS:
  421. **    Both str1 and str2 should be non-null and non-empty
  422. **
  423. ** ^SIDE-EFFECTS:
  424. **    None.
  425. **
  426. ** ^RETURN-VALUE:
  427. **    A pointer to the first occurence in str1 of any char from str2.
  428. **
  429. ** ^ALGORITHM:
  430. **    - foreach char in str1
  431. **       - if char is in str2, return the address of char
  432. **      end-for
  433. **    - if we have reached the end of str1, return NULL
  434. ***^^**********************************************************************/
  435. #ifdef __ANSI_C__
  436.    char *strpbrk( const char *str1, const char *str2 )
  437. #endif
  438. {
  439.    register CONST char *s1 =  str1, *s2 = str2;
  440.  
  441.    if ( !s1 || !*s1 || !s2 || !*s2 )  return  CHARNULL;
  442.  
  443.    for ( ; *s1 ; s1++ )  {
  444.       if ( strchr(s2, *s1) )  return (char *)s1;
  445.    }
  446.  
  447.    return  CHARNULL;
  448. }
  449.  
  450.  
  451. /***************************************************************************
  452. ** ^FUNCTION: strspn, strcspn - identify leading runs of characters
  453. **
  454. ** ^SYNOPSIS:
  455. **    char *strspn( str1, str2 )
  456. **    char *strcspn( str1, str2 )
  457. **
  458. ** ^PARAMETERS:
  459. **    char *str1;
  460. **    -- the string to be searched
  461. **
  462. **    char *str2;
  463. **    -- the string to be searched
  464. **
  465. ** ^DESCRIPTION:
  466. **    Strspn (strcspn) attempts to determine the length of the longest
  467. **    leading prefix of str1 that consists entirely of character from
  468. **    (not from) str2.
  469. **
  470. ** ^REQUIREMENTS:
  471. **    Both str1 and str2 should be non-null and non-empty.
  472. **
  473. ** ^SIDE-EFFECTS:
  474. **    None.
  475. **
  476. ** ^RETURN-VALUE:
  477. **    The length of the initial prefix in str1 consisting entirely
  478. **    of characters from (not from) str2.
  479. **
  480. ** ^ALGORITHM:
  481. **    - length = 0
  482. **    - for each char in str1
  483. **       - if char is in str2 (for strcspn) or not in str2 (for strcspn)
  484. **            then return length
  485. **       - else
  486. **            add 1 to length
  487. **         end-if
  488. **      end-for
  489. **    - if end-of-string then return length
  490. **
  491. ***^^**********************************************************************/
  492. #ifdef __ANSI_C__
  493.    int strspn( const char *str1, const char *str2 )
  494. #else
  495.    int strspn( str1, str2 )  char *str1, *str2;
  496. #endif
  497. {
  498.    register CONST char  *s1 = str1, *s2 = str2;
  499.    int len = 0;
  500.  
  501.    if ( !s1 || !*s1 || !s2 || !*s2 )  return  0;
  502.    while ( *s1  &&  strchr(s2, *s1++) )  ++len;
  503.    return  len;
  504. }
  505.  
  506.  
  507. #ifdef __ANSI_C__
  508.    int strcspn( const char *str1, const char *str2 )
  509. #else
  510.    int strcspn( str1, str2 )  char *str1, *str2;
  511. #endif
  512. {
  513.    register CONST char  *s1 = str1, *s2 = str2;
  514.    int len = 0;
  515.  
  516.    if ( !s1 || !*s1 || !s2 || !*s2 )  return  0;
  517.    while ( *s1  &&  !strchr(s2, *s1++) )  ++len;
  518.    return  len;
  519. }
  520.  
  521. #endif  /* BSD */
  522.  
  523.  
  524. /***************************************************************************
  525. ** ^FUNCTION: strltrim, strrtrim, strtrim - trim leading/trailing characters
  526. **
  527. ** ^SYNOPSIS:
  528. **    char *strltrim( str, charset )
  529. **    char *strrtrim( str, charset )
  530. **    char *strtrim( str, charset )
  531. **
  532. ** ^PARAMETERS:
  533. **    char *str;
  534. **    -- the string to be trimmed
  535. **
  536. **    char *charset;
  537. **    -- the set of characters to be trimmed
  538. **
  539. ** ^DESCRIPTION:
  540. **    Strltrim removes from str, all leftmost (leading) characters occurring
  541. **    in charset.
  542. **
  543. **    Strrtrim removes from str, all rightmost (trailing) characters occurring
  544. **    in charset.
  545. **
  546. **    Strtrim removes from str, all leading and trailing characters occurring
  547. **    in charset.
  548. **
  549. **    For each of these functions, if charset is NULL or empty, then the set
  550. **    of whitespace characters (space, tab, newline, carriage-return, form-feed
  551. **    and vertical-tab) is assumed.
  552. **
  553. ** ^REQUIREMENTS:
  554. **    str should be non-null and non-empty.
  555. **
  556. ** ^SIDE-EFFECTS:
  557. **    characters may be removed from the beginning and/or end of str.
  558. **
  559. ** ^RETURN-VALUE:
  560. **    Address of str.
  561. **
  562. ** ^ALGORITHM:
  563. **    Trivial.
  564. ***^^**********************************************************************/
  565. #ifdef __ANSI_C__
  566.    char *strltrim( char *str, const char *charset )
  567. #else
  568.    char *strltrim( str, charset )  char *str, *charset;
  569. #endif
  570. {
  571.    register   int   i;
  572.  
  573.    if ( !str  ||  !*str )   return   str;
  574.       /* if delim-string is NULL, whitespace is used */
  575.    if ( !charset )   charset = WhiteSpace;
  576.  
  577.    i = strspn( str, charset );
  578.    if ( i > 0 )  strcpy( str, &(str[i]) );
  579.  
  580.    return   str;
  581. }
  582.  
  583.  
  584. #ifdef __ANSI_C__
  585.    char *strrtrim( char *str, const char *charset )
  586. #else
  587.    char *strrtrim( str, charset )  char *str, *charset;
  588. #endif
  589. {
  590.    register   int   i;
  591.  
  592.    if ( !str  ||  !*str )   return   str;
  593.    if ( !charset )   charset = WhiteSpace;
  594.    for ( i = strlen(str) - 1 ;
  595.             ( i >= 0 ) && (strchr( charset, str[i] )) ;
  596.             i--
  597.          ) ;
  598.  
  599.    str[i+1] = '\0';
  600.  
  601.    return   str;
  602. }
  603.  
  604.  
  605. #ifdef __ANSI_C__
  606.    char *strtrim( char *str, const char *charset )
  607. #else
  608.    char *strtrim( str, charset )  char *str, *charset;
  609. #endif
  610. {
  611.    register   int   i;
  612.  
  613.    if ( !str  ||  !*str )   return   str;
  614.    if ( !charset )   charset = WhiteSpace;
  615.    i = strspn( str, charset );
  616.    if ( i > 0 )  strcpy( str, &(str[i]) );
  617.  
  618.    for ( i = strlen(str) - 1 ;
  619.             ( i >= 0 ) && (strchr( charset, str[i] )) ;
  620.             i--
  621.          ) ;
  622.  
  623.    str[i+1] = '\0';
  624.  
  625.    return   str;
  626. }
  627.  
  628.  
  629. /***************************************************************************
  630. ** ^FUNCTION: strsplit - split a string into tokens
  631. **
  632. ** ^SYNOPSIS:
  633. */
  634. #ifndef __ANSI_C__
  635.    int  strsplit( vec, token_str, separators )
  636. /*
  637. ** ^PARAMETERS:
  638. */
  639.    char **vec[];
  640. /*    -- pointer to the string vector to be allocated
  641. */
  642.    char token_str[];
  643. /*    -- the string to be split up
  644. */
  645.    char separators[];
  646. /*    -- the delimiters that separate tokens
  647. */
  648. #endif  /* !__ANSI_C__ */
  649.  
  650. /* ^DESCRIPTION:
  651. **    Strsplit will split token_str up into  a vector of tokens that are
  652. **    separated by one or more characters from <separators>. The number
  653. **    of tokens found is returned and storage is allocated for the given
  654. **    vector (which may later be deallocated using free()).
  655. **
  656. **    If <separators> is NULL or empty, then the set of whitespace characters
  657. **    is used as the token delimiters.
  658. **
  659. ** ^REQUIREMENTS:
  660. **    vec must be non-NULL (it must be a valid address).
  661. **    token_str should be non-null
  662. **
  663. ** ^SIDE-EFFECTS:
  664. **    All leading and trailing characters from <separators> are removed
  665. **    from token_str. Furthermore, all remaining sequences in token_str
  666. **    of characters from <separators> are replaced with a single NUL-byte.
  667. **
  668. **    Token_str holds the actual storage for all the strings in the newly
  669. **    created vector.
  670. **
  671. ** ^RETURN-VALUE:
  672. **    The number of tokens parsed.
  673. **
  674. ** ^ALGORITHM:
  675. **    - count the number of tokens present while at the same time removing
  676. **      all leading and trailing delimiters, and replacing all other sequences
  677. **      of delimiters with the NUL character.
  678. **    - allocate a vector large enough to point to all the token strings.
  679. **    - for i in 0 .. (numtokens - 1) do
  680. **         - vector[i] = token_str
  681. **         - advance token_str to point at the next character past the
  682. **           rightmost NUL-byte (which should be the start of the next token).
  683. **      end-for
  684. **    - vector[numtokens] = NUL
  685. **    - return the number of tokens parsed.
  686. ***^^**********************************************************************/
  687. #ifdef __ANSI_C__
  688.    int strsplit( char **vec[], char token_str[], const char separators[] )
  689. #endif
  690. {
  691.    register   char c, *pread, *pwrite;
  692.    int   i, count = 0;
  693.  
  694.    if ( !token_str )    token_str = "";
  695.       /* if delim-string is NULL, whitespace is used */
  696.    if ( !separators )   separators = WhiteSpace;
  697.  
  698.       /* trim leading separators */
  699.    pread = token_str;
  700.    while ( strchr(separators, *pread) )   ++pread;
  701.    if ( ! *pread )  return  0;
  702.    token_str = pwrite = pread;
  703.  
  704.       /*
  705.       ** make first pass through string, counting # of tokens and
  706.       ** separating all tokens by a single '\0'
  707.       */
  708.    for ( c = *pread++ ; c ; ) {
  709.       if ( !strchr(separators, c) )   {
  710.          do {
  711.             *pwrite++ = c;
  712.          } while ( (c = *pread++) && !strchr(separators, c) );
  713.         *pwrite++ = '\0';
  714.         ++count;
  715.       }/*if*/
  716.       while ( c && strchr(separators, c) )   c = *pread++;
  717.    }/*for*/
  718.  
  719.       /* allocate space for the caller's vector (remember NULL at the end) */
  720.    (*vec) = (char **)malloc( (1 + count) * sizeof( char * ) );
  721.    if ( !*vec )  syserr( "malloc failed in strsplit()" );
  722.  
  723.       /* now go thru token-string again assigning pointers from vector */
  724.    pread = token_str;
  725.    for ( i = 0 ; i < count ; i++ ) {
  726.       (*vec)[i] = pread;   /* assign pointer */
  727.       pread += strlen( pread ) + 1;
  728.    }/* end-for */
  729.  
  730.       /* set up the trailing pointer to NULL at the end */
  731.    (*vec)[ count ] = CHARNULL;
  732.    return   count;
  733. }
  734.  
  735.  
  736. /***************************************************************************
  737. ** ^FUNCTION: strjoin - join a vector of tokens together
  738. **
  739. ** ^SYNOPSIS:
  740. */
  741. #ifndef __ANSI_C__
  742.    char  *strjoin( argv, separator )
  743. /*
  744. ** ^PARAMETERS:
  745. */
  746.    char *argv[];
  747. /*    -- pointer to the string vector to join together
  748. */
  749.    char separator[];
  750. /*    -- the the string to use to separate tokens (if NULL, " " is used)
  751. */
  752. #endif  /* !__ANSI_C__ */
  753.  
  754. /* ^DESCRIPTION:
  755. **    Strjoin will make a single string out of the given vector by copying
  756. **    all the tokens from the given vector (in order) to a newly allocated
  757. **    string. Tokens will be separated by a single occurence of <separator>.
  758. **
  759. **    If <separator> is NULL then a single space is used as the separator.
  760. **    If <separator> is empty, then no separator is used and the tokens are
  761. **    simply concatenated together.
  762. **
  763. ** ^REQUIREMENTS:
  764. **    argv must be non-NULL (it must be a valid address), and must be
  765. **    terminated by a pointer to NULL (argv[last+1] == NULL).
  766. **
  767. ** ^SIDE-EFFECTS:
  768. **    Storage is allocated.
  769. **
  770. ** ^RETURN-VALUE:
  771. **    The address of the newly-joined result (which should be deallocated
  772. **    using free()). Returns NULL if nothing was joined.
  773. **
  774. ** ^ALGORITHM:
  775. **    - count the number of characters to place in the joined-result.
  776. **    - allocate a string large-enough to copy the joined-result into.
  777. **    - copy each string into the string (with <separator> between tokens).
  778. **    - 0 return the result.
  779. ***^^**********************************************************************/
  780. #ifdef __ANSI_C__
  781.    char *strjoin( const char *argv[], const char separator[] )
  782. #endif
  783. {
  784.    size_t  sz = 0;
  785.    register char *p;
  786.    register CONST char *a, **av;
  787.    register int  seplen;
  788.    char *result;
  789.  
  790.       /* if argv is NULL, nothing to do */
  791.    if ( !argv )  return  CHARNULL;
  792.    if ( !separator )  separator = " ";
  793.    seplen = strlen( separator );
  794.  
  795.       /* figure out how much space we need */
  796.    for ( av = argv ; *av ; av++ ) {
  797.       if ( !**av )  continue;
  798.       sz += strlen( *av );
  799.       if ( seplen  &&  *(av + 1) )  sz += seplen;
  800.    }
  801.  
  802.       /* allocate space */
  803.    result = (char *)malloc( (sz + 1) * sizeof(char) );
  804.    if ( !result )  syserr( "malloc failed in strjoin()" );
  805.  
  806.       /* join the strings together */
  807.    *result = '\0';
  808.    for ( av = argv, p = result ; (a = *av) ; av++ ) {
  809.       if ( !*a )  continue;
  810.       while ( (*p = *a++) ) ++p;  /* copy token */
  811.       if ( seplen  &&  *(av + 1) ) {
  812.          a = separator;
  813.          while ( (*p = *a++) ) ++p;  /* copy separator */
  814.       }/*end-if*/
  815.    }/*end-for*/
  816.  
  817.    return  result;
  818. }
  819.  
  820.  
  821. /***************************************************************************
  822. ** ^FUNCTION: get_argpfx - get the prefix portion of a string
  823. **
  824. ** ^SYNOPSIS:
  825. */
  826. #ifndef __ANSI_C__
  827.    int get_argpfx( str )
  828. /*
  829. ** ^PARAMETERS:
  830. */
  831.    char *str;
  832. /*    -- the string to parse for a description
  833. */
  834. #endif  /* !__ANSI_C__ */
  835.  
  836. /* ^DESCRIPTION:
  837. **    Get_argdesc returns the length of the first portion of the string.
  838. **
  839. **    Two "portions" must be either separated by whitespace or the second
  840. **    portion may be within "(),{},[], or <>" delimiters. The second
  841. **    portion is assumed to begin with the first alphabetic following
  842. **    separator.
  843. **
  844. ** ^REQUIREMENTS:
  845. **    str should be non-null and non-empty
  846. **
  847. ** ^SIDE-EFFECTS:
  848. **    None.
  849. **
  850. ** ^RETURN-VALUE:
  851. **    The length of the first portion.
  852. **
  853. ** ^ALGORITHM:
  854. **    - locate the end of the first portion by scanning for whitespace or
  855. **      balanced delimiters.
  856. ***^^**********************************************************************/
  857. #ifdef __ANSI_C__
  858.    int get_argpfx( const char *str )
  859. #endif
  860. {
  861.    register char  *description;
  862.    static CONST char  whitespace[]  = " \t\n\r\f\v";
  863.    static CONST char  beg_portion[] = "(<{[";
  864.  
  865.    description = strpbrk( str, whitespace );
  866.    if ( !description ) {
  867.        description = strpbrk( str, beg_portion );
  868.    }
  869.  
  870.    return description ? description - str : strlen(str);
  871. }
  872.  
  873.  
  874. /***************************************************************************
  875. ** ^FUNCTION: get_argdesc - get the description portion of a string
  876. **
  877. ** ^SYNOPSIS:
  878. */
  879. #ifndef __ANSI_C__
  880.    char *get_argdesc( str, len )
  881. /*
  882. ** ^PARAMETERS:
  883. */
  884.    char *str;
  885. /*    -- the string to parse for a description
  886. */
  887.    int *len;
  888. /*    -- the pointer to the length
  889. */
  890. #endif  /* !__ANSI_C__ */
  891.  
  892. /* ^DESCRIPTION:
  893. **    Get_argdesc returns a pointer to the second portion of the string
  894. **    and also indicates how long it is.
  895. **
  896. **    Two "portions" must be either separated by whitespace or the second
  897. **    portion may be within "(),{},[], or <>" delimiters. The second
  898. **    portion is assumed to begin with the first alphabetic following
  899. **    separator.
  900. **
  901. ** ^REQUIREMENTS:
  902. **    str should be non-null and non-empty
  903. **
  904. ** ^SIDE-EFFECTS:
  905. **    The length of the description is written to *len.
  906. **
  907. ** ^RETURN-VALUE:
  908. **    Address of the description (or NULL if the string has no description).
  909. **
  910. ** ^ALGORITHM:
  911. **    - locate the end of the first portion by scanning for whitespace or
  912. **      balanced delimiters.
  913. **    - locate the beginning of the second portion by scanning for the first
  914. **      alpha-numeric following the end of the first portion.
  915. **    - return the address of the description.
  916. ***^^**********************************************************************/
  917. #ifdef __ANSI_C__
  918.    char *get_argdesc( const char *str, int *len )
  919. #endif
  920. {
  921.    register char  *description = CHARNULL;
  922.    BOOL  is_end = FALSE, is_balanced = FALSE;
  923.    char  *p;
  924.    static CONST char  whitespace[]  = " \t\n\r\f\v";
  925.    static CONST char  beg_portion[] = "(<{[";
  926.    static CONST char  end_portion[] = ")>}]";
  927.  
  928.    description = strpbrk( str, whitespace );
  929.    if ( description ) {
  930.       is_end = TRUE;
  931.       while ( isspace(*++description) )  continue;  /* trim leading ' ' */
  932.       if ( strchr(beg_portion, *description) ) {
  933.             is_balanced = TRUE;
  934.             ++description;
  935.       }
  936.    }
  937.    else {
  938.        description = strpbrk( str, beg_portion );
  939.        if ( description ) {  /* skip leading '(' */
  940.           is_end = is_balanced = TRUE;
  941.           ++description;
  942.        }
  943.    }
  944.  
  945.    if ( !description ) {
  946.       *len = 0;
  947.    }
  948.    else {
  949.       if ( is_balanced ) {  /* remove trailing ')' */
  950.          p = description + (strlen( description ) - 1);
  951.          if ( !strchr(end_portion, *p) )  ++p;
  952.          *len = p - description;
  953.       }
  954.       else {
  955.          while ( !isalnum(*description) )  ++description;
  956.          *len = strlen( description );
  957.       }
  958.    }
  959.  
  960.    return  description;
  961. }
  962.  
  963.  
  964. /***************************************************************************
  965. ** ^FUNCTION: get_argname - return the aname (argument-name) of an argument
  966. **
  967. ** ^SYNOPSIS:
  968. */
  969. #ifndef __ANSI_C__
  970.    char  *get_argname( s, buf )
  971. /*
  972. ** ^PARAMETERS:
  973. */
  974.    char *s;
  975. /*    -- the ad_prompt field of an ARGDESC struct
  976. */
  977.    char *buf;
  978. /*    -- address to which the aname should be copied
  979. */
  980. #endif  /* !__ANSI_C__ */
  981.  
  982. /* ^DESCRIPTION:
  983. **    Get_argname will get the full argument name of the given argument
  984. **    (not just the keyword name) and copy it to buf.
  985. **
  986. ** ^REQUIREMENTS:
  987. **    Both s and buf must be non-null and non-empty.
  988. **    buf must be large enough to hold the result.
  989. **
  990. ** ^SIDE-EFFECTS:
  991. **    buf is overwritten.
  992. **
  993. ** ^RETURN-VALUE:
  994. **    Address of the buffer containing the name.
  995. **
  996. ** ^ALGORITHM:
  997. **    determine the name of an argument from its prompt
  998. **    and copy the result in the given buffer
  999. ***^^**********************************************************************/
  1000. #ifdef __ANSI_C__
  1001.    char *get_argname( const char *s, char *buf )
  1002. #endif
  1003. {
  1004.    register CONST char *p2;
  1005.    int len;
  1006.  
  1007.       /* see if sname and aname are separated by c_ARG_SEP
  1008.       ** <buf> must be large enough to hold the result!
  1009.       */
  1010.    len = get_argpfx(s);
  1011.    p2 = strchr( s, c_ARG_SEP );
  1012.    if ( p2 && p2 < &s[len]) {
  1013.       ++p2;
  1014.       strncpy( buf, p2, len-(p2-s) );
  1015.       buf[len-(p2-s)] = 0;
  1016.       strlwr(buf);
  1017.    }
  1018.    else {
  1019.       strncpy( buf, s, len );
  1020.       buf[len] = 0;
  1021.       strlwr(buf);
  1022.    }
  1023.    return   buf;
  1024. }
  1025.  
  1026.  
  1027. /***************************************************************************
  1028. ** ^FUNCTION: get_kwdname - get the sname (keyword name) of an argument
  1029. **
  1030. ** ^SYNOPSIS:
  1031. */
  1032. #ifndef __ANSI_C__
  1033.    char  *get_kwdname( s, buf )
  1034. /*
  1035. ** ^PARAMETERS:
  1036. */
  1037.    char *s;
  1038. /*    -- the ad_prompt field of an ARGDESC struct
  1039. */
  1040.    char *buf;
  1041. /*    -- address to which the sname should be copied
  1042. */
  1043. #endif  /* !__ANSI_C__ */
  1044.  
  1045. /* ^DESCRIPTION:
  1046. **    Get_kwdname will get the keyword name of the given argument
  1047. **    (not the entire argument name) and copy it to buf.
  1048. **
  1049. **    The sname (keyword-name) consists only of all uppercase characters
  1050. **    from the ad_prompt field (in the order they occur). If the ad_prompt
  1051. **    field contains NO uppercase characters, than the aname and the sname
  1052. **    are equivalent (the entire string).
  1053. **
  1054. ** ^REQUIREMENTS:
  1055. **    Both s and buf must be non-null and non-empty.
  1056. **    buf must be large enough to hold the result.
  1057. **
  1058. ** ^SIDE-EFFECTS:
  1059. **    buf is overwritten.
  1060. *
  1061. ** ^RETURN-VALUE:
  1062. **    Address of the buffer containing the keyword.
  1063. **
  1064. ** ^ALGORITHM:
  1065. **    determine the keyword of an argument from its prompt
  1066. **    and copy the result in the given buffer
  1067. ***^^**********************************************************************/
  1068. #ifdef __ANSI_C__
  1069.    char *get_kwdname( const char *s, char *buf )
  1070. #endif
  1071. {
  1072.    register char *p1 = (char *)s, *p2, ch;
  1073.    int len;
  1074.    BOOL caps = FALSE;
  1075.  
  1076.    if ( !p1 )  return  CHARNULL;
  1077.  
  1078.       /* see if sname and aname are separated by c_ARG_SEP */
  1079.    len = get_argpfx( p1 );
  1080.    p2 = strchr( p1, c_ARG_SEP );
  1081.    if ( p2 && p2 < &p1[len]) {
  1082.       strncpy( buf, p1, p2-p1);
  1083.       buf[p2-p1] = 0;
  1084.       FORCE_KWDCASE(buf);
  1085.       return  buf;
  1086.    }
  1087.  
  1088.       /* copy string into buffer and convert it to desired case */
  1089.       /* <buf> must be large enough to hold the result! */
  1090.    for ( p2 = buf; *p1 && len != 0; p1++, len-- ) {
  1091.       if ( isupper(*p1) ) {
  1092.          if ( !caps ) {
  1093.             caps = TRUE;
  1094.             p2 = buf;
  1095.          }
  1096.          *p2++ = TO_KWDCASE(*p1);
  1097.       }
  1098.       else if ( !caps ) {
  1099.          *p2++ = TO_KWDCASE(*p1);
  1100.       }
  1101.    }
  1102.    *p2 = '\0';
  1103.  
  1104.    return   buf;   /* return buffer address */
  1105. }
  1106.  
  1107. #ifndef amiga_style
  1108.  
  1109. /***************************************************************************
  1110. ** ^FUNCTION: match - match a keyword against a prospective argument
  1111. **
  1112. ** ^SYNOPSIS:
  1113. */
  1114. #ifndef __ANSI_C__
  1115.    int match( candidate, target )
  1116. /*
  1117. ** ^PARAMETERS:
  1118. */
  1119.    char *candidate;
  1120. /*    -- the possible keyword argument
  1121. */
  1122.    char *target;
  1123. /*    -- the keyword to be matched
  1124. */
  1125. #endif  /* !__ANSI_C__ */
  1126.  
  1127. /* ^DESCRIPTION:
  1128. **    Match will attempt to see if the candidate string matches the
  1129. **    target string (case insensitive). First a match is tried on the
  1130. **    sname of the keyword, then on the aname.  Candidate may be only
  1131. **    a partial leading portion of the target as long as it is at least
  1132. **    two characters long (unless the keyword is 1 character long).
  1133. **
  1134. **    No "partial" matching is accepted for AmigaDOS command-lines.
  1135. **
  1136. ** ^REQUIREMENTS:
  1137. **    Both candidate and target should be non-null and non-empty.
  1138. **    target should be the ad_prompt field of an ARGDESC structure.
  1139. **
  1140. ** ^SIDE-EFFECTS:
  1141. **    None.
  1142. **
  1143. ** ^RETURN-VALUE:
  1144. *     < 0  if candidate < target
  1145. **    = 0  if candidate matches target
  1146. **    > 0  if candidate > target
  1147. **
  1148. ** ^ALGORITHM:
  1149. **    - attempt a partial match against the sname and return 0 if we succeed
  1150. **    - attempt a partial match against the aname and return 0 if we succeed
  1151. **    - if both the above fail return non-zero (no match).
  1152. **    
  1153. ***^^**********************************************************************/
  1154.  
  1155. /* rewritten 8/20/90 --BDA */
  1156. #define MINLEN  2     /* minimum # of characters to match */
  1157.  
  1158. #ifdef __ANSI_C__
  1159.    int match ( const char *candidate, const char *target )
  1160. #endif
  1161. {
  1162.    int  i, clen, tlen, too_short=0;
  1163.    char  arg_targ[ 256 ], kwd_targ[ 256 ];
  1164.  
  1165.  
  1166.       /* make kwd_targ the keyword portion of target */
  1167.    (VOID) get_kwdname( target, kwd_targ );
  1168.  
  1169.       /* match at least MINLEN characters if possible */
  1170.    tlen = strlen( kwd_targ );
  1171.    clen = strlen( candidate );
  1172.    if ( (tlen >= MINLEN)   &&   (clen < MINLEN) ) {
  1173.       ++too_short;      /* not long enough -- no match */
  1174.    }
  1175.  
  1176. #ifdef vms_style
  1177.       /* if first two chars are NO then match at least MINLEN+2 chars */
  1178.    if ( !strnicmp(kwd_targ, "NO", 2) ) {
  1179.       if ( (tlen >= (MINLEN + 2))   &&   (clen < (MINLEN + 2)) ) {
  1180.          ++too_short;      /* not long enough -- no match */
  1181.       }
  1182.    }
  1183. #endif
  1184.  
  1185.       /* first try to match prefix of the keyword portion */
  1186.    i = (too_short) ? -1 : strnicmp(kwd_targ, candidate, clen);
  1187.  
  1188.       /* did we match? */
  1189.    if ( !i )  return  0;   /* yes! */
  1190.  
  1191.    /* no! : compare the argument portion
  1192.    **       match at least MINLEN characters if possible
  1193.    */
  1194.       /* make arg_targ the argument portion of target */
  1195.    (VOID) get_argname( target, arg_targ );
  1196.  
  1197.    tlen = strlen(arg_targ);
  1198.    if ( (tlen >= MINLEN)   &&   (clen < MINLEN) ) {
  1199.       return   -1;      /* not long enough -- no match */
  1200.    }
  1201.  
  1202. #ifdef vms_style
  1203.    /* if first two chars are NO then match at least MINLEN+2 chars */
  1204.    if ( !strnicmp(arg_targ, "no", 2) ) {
  1205.       if ( (tlen >= (MINLEN + 2))   &&   (clen < (MINLEN + 2)) ) {
  1206.          return   -1;      /* not long enough -- no match */
  1207.       }
  1208.    }
  1209. #endif
  1210.  
  1211.    return  strnicmp(arg_targ, candidate, clen);
  1212. }
  1213.  
  1214.  
  1215. /* here is the AmigaDOS version of match() */
  1216. #else
  1217.  
  1218. # ifdef __ANSI_C__
  1219.    int match( const char *candidate, const char *target )
  1220. # else
  1221.    int match( candidate, target) char *candidate, *target;
  1222. # endif
  1223. {
  1224.    char kwd_targ[ 256 ], arg_targ[ 256 ];
  1225.    int rc;
  1226.  
  1227.    (VOID) get_kwdname( target, kwd_targ );
  1228.    rc = stricmp( kwd_targ, candidate );
  1229.  
  1230.    if ( rc == 0 )  return  0;
  1231.  
  1232.    (VOID) get_argname( target, arg_targ );
  1233.    rc = stricmp( arg_targ, candidate );
  1234.  
  1235.    return  rc;
  1236. }
  1237.  
  1238. #endif
  1239.  
  1240.  
  1241. /***************************************************************************
  1242. ** ^FUNCTION: basename - return the last component of a pathname
  1243. **
  1244. ** ^SYNOPSIS:
  1245. **    char *basename( path )
  1246. **
  1247. ** ^PARAMETERS:
  1248. **    path;
  1249. **    -- the pathname to be truncated.
  1250. **
  1251. ** ^DESCRIPTION:
  1252. **    Basename takes a pathname and strips of all leading components
  1253. **    (except for the very last one) which refer to directories or
  1254. **    disk-drives.
  1255. **
  1256. ** ^REQUIREMENTS:
  1257. **    path should be non-null, non-empty, and should correspond to a valid
  1258. **    pathname (absolute or relative).
  1259. **
  1260. ** ^SIDE-EFFECTS:
  1261. **    None under Unix and AmigaDOS.
  1262. **
  1263. **    Under VMS, the file version is removed and any .COM or .EXE extension
  1264. **    is also removed.
  1265. **
  1266. **    Under MS-DOS, any .EXE, .COM, or .BAT extension is removed.
  1267. **
  1268. **    Under OS/2, any .EXE, .COM, or .CMD extension is removed.
  1269. **    
  1270. ** ^RETURN-VALUE:
  1271. **    The address of the basename of the path.
  1272. **
  1273. ** ^ALGORITHM:
  1274. **    Trivial.
  1275. ***^^**********************************************************************/
  1276.    /* should use '\\' for MS-DOS & OS/2 and use ']' for VMS */
  1277. #ifdef vms
  1278. #define PATH_SEP ']'
  1279.  
  1280.       /* VAX/VMS version of basename */
  1281. # ifdef __ANSI_C__
  1282.    char *basename( char  path[] )
  1283. # else
  1284.    char *basename( path ) char  path[];
  1285. # endif
  1286.    {
  1287.       char *base = strrchr( path, PATH_SEP );
  1288.       char *vers = strrchr( path, ';' );
  1289.       char *ext  = strrchr( path, '.' );
  1290.  
  1291.       if ( !base ) {
  1292.          if ( !(base = strrchr( path, ':' )) ) {
  1293.             base = path;
  1294.          }
  1295.          else {
  1296.             ++base;
  1297.          }
  1298.       }
  1299.       else {
  1300.          ++base;
  1301.       }
  1302.  
  1303.       if ( vers )  *vers ='\0';
  1304.       if ( ext  &&  (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) {
  1305.           *ext = '\0';
  1306.       }
  1307.  
  1308.       return  base;
  1309.    }
  1310.  
  1311. #else
  1312. #ifdef AmigaDOS
  1313.       /* amiga version of basename() */
  1314. # ifdef __ANSI_C__
  1315.    char *basename( char  path[] )
  1316. # else
  1317.    char *basename( path ) char  path[];
  1318. # endif
  1319.    {
  1320.       return   path;
  1321.    }
  1322. #else
  1323. #define PATH_SEP  '/'     /* default path-separator character */
  1324.  
  1325.       /* default version of basename() */
  1326. # ifdef __ANSI_C__
  1327.    char *basename( char  path[] )
  1328. # else
  1329.    char *basename( path ) char  path[];
  1330. # endif
  1331.    {
  1332.       char *base = strrchr( path, PATH_SEP );
  1333.  
  1334. #if ( defined(MSDOS) || defined(OS2) )
  1335.       /* remove the extension from .EXE, .COM, .BAT, and .CMD files */
  1336. # ifdef OS2
  1337.       if ( ext  &&  (!stricmp(ext, ".CMD") )  *ext = '\0';
  1338. # else
  1339.       if ( ext  &&  (!stricmp(ext, ".BAT") )  *ext = '\0';
  1340. # endif
  1341.  
  1342.       if ( ext  &&  (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) {
  1343.           ext = '\0';
  1344.       }
  1345. #endif
  1346.  
  1347.       if ( !base ) {
  1348. #if ( defined(MSDOS) || defined(OS2) )
  1349.          base = strrchr( path, '\\' );
  1350.          if ( base )  return  (base + 1);
  1351.          if ( path[ 1 ] == ':' )  return  (path + 2);  /* just remove drive */
  1352.          return  (base + 1);
  1353. #endif
  1354.          return  path;
  1355.       }
  1356.       else {
  1357.          return  (base + 1);
  1358.       }
  1359.    }
  1360. #endif
  1361. #endif
  1362.  
  1363.  
  1364. /***************************************************************************
  1365. ** ^FUNCTION: indent_para - print a hanging indented paragraph
  1366. **
  1367. ** ^SYNOPSIS:
  1368. */
  1369. #ifndef __ANSI_C__
  1370.    VOID indent_para(fp, maxcols, margin, title, indent, text, textlen)
  1371. /*
  1372. ** ^PARAMETERS:
  1373. */
  1374.    FILE *fp;
  1375. /*    -- the stream to which output is sent
  1376. */
  1377.    int maxcols;
  1378. /*    -- the maximum width (in characters) of the output
  1379. */
  1380.    int margin;
  1381. /*    -- the number of spaces to use as the left margin
  1382. */
  1383.    char *title;
  1384. /*    -- the paragraph title
  1385. */
  1386.    int indent;
  1387. /*    -- the distance between the title and the paragraph body
  1388. */
  1389.    char *text;
  1390. /*    -- the body of the paragraph
  1391. */
  1392.    int textlen;
  1393. /*    -- the length of the body of the paragraph
  1394. */
  1395. #endif  /* !__ANSI_C__ */
  1396.  
  1397. /* ^DESCRIPTION:
  1398. **    Indent_para will print on fp, a titled, indented paragraph as follows:
  1399. **
  1400. **    <----------------------- maxcols --------------------------->
  1401. **    <--- margin -->     <-- indent -->
  1402. **                   title              This is the first sentence
  1403. **                                      of the paragraph. Etc ...
  1404. **
  1405. ** ^REQUIREMENTS:
  1406. **    maxcols and indent must be positive numbers with maxcols > indent
  1407. **
  1408. ** ^SIDE-EFFECTS:
  1409. **    Output is printed to fp.
  1410. **
  1411. ** ^RETURN-VALUE:
  1412. **    None.
  1413. **
  1414. ** ^ALGORITHM:
  1415. **    Print the paragraph title and then print the text.
  1416. **    Lines are automatically adjusted so that each one starts in the
  1417. **    appropriate column.
  1418. ***^^**********************************************************************/
  1419. #ifdef __ANSI_C__
  1420.    void indent_para( FILE *fp, int maxcols, int margin,
  1421.                      const char *title, int indent, const char *text,
  1422.                      int textlen )
  1423. #endif
  1424. {
  1425.    register int idx = 0;
  1426.    BOOL first_line = TRUE;
  1427.    char ch;
  1428.  
  1429.    if ( ! textlen )  textlen = strlen( text );
  1430.  
  1431.    /* print the title */
  1432.    fprintf( fp, "%*s%-*s", margin, "", indent, title );
  1433.  
  1434.    idx = maxcols - margin - indent;
  1435.  
  1436.    if ( textlen <= idx )
  1437.       fprintf(fp, "%.*s\n", textlen, text);
  1438.    else
  1439.       do {
  1440.                /* backup to end of previous word */
  1441.          while (idx  &&  !isspace(text[idx]))  --idx;
  1442.          while (idx  &&  isspace(text[idx]))   --idx;
  1443.          idx++;
  1444.  
  1445.             /* print leading whitespace */
  1446.          if (!first_line)
  1447.             fprintf(fp, "%*s%-*s", margin, "", indent, "");
  1448.  
  1449.          fprintf(fp, "%.*s\n", idx, text);
  1450.  
  1451.          first_line = FALSE;
  1452.          text = &(text[idx+1]);
  1453.          textlen -= (idx+1);
  1454.  
  1455.          while (isspace(*text)) {  /* goto next word */
  1456.             ++text;
  1457.             --textlen;
  1458.          }
  1459.  
  1460.          idx = maxcols - margin - indent;
  1461.  
  1462.          if ( textlen <= idx )  /* print-last line */
  1463.             fprintf(fp, "%*s%-*s%.*s\n", margin, "", indent, "", textlen, text);
  1464.       } while ( textlen > idx );
  1465. }
  1466.  
  1467.  
  1468. #ifdef STRTEST
  1469.  
  1470. #define WS " \t\n\v\r\f\"'"
  1471.  
  1472. static char string2[] =  "      oh what      a beautiful -    morning!        ";
  1473.  
  1474. static char string[] =  "\n\
  1475. \t' ',  ARGREQ,          argStr,   Name,     'Name',\n\
  1476. \t'n',  ARGOPT|ARGLIST,  listStr,  Groups,   'newsGROUP (newsgroups test)',\n\
  1477. \t'c',  ARGOPT,          argInt,   RepCount, 'REPcount (number of reps)',\n\
  1478. \t'd',  ARGOPT,          argStr,   DirName,  'DIRname',\n\
  1479. \t'x',  ARGOPT,          argBool,  XFlag,    'Xflag (expand in X direction)',\n\
  1480. \t' ',  ARGOPT|ARGLIST,  listStr,  Argv,     'File',\n\
  1481. \tENDOFARGS\n\
  1482. ";
  1483.  
  1484. static char word_str[] = "HELP (print help and quit)";
  1485.  
  1486. main()
  1487. #ifdef __ANSI_C__
  1488. #endif
  1489. {
  1490.    char **vector;
  1491.    unsigned i, numtoks;
  1492.  
  1493.    printf( "test of strtrim() and strsplit():\n\n" );
  1494.  
  1495.    printf( "unparsed string='%s'\n", string2 );
  1496.    printf( "ltrimmed string='%s'\n", strltrim( string2, WS ) );
  1497.    printf( "rtrimmed string='%s'\n", strrtrim( string2, WS ) );
  1498.  
  1499.    numtoks = strsplit( &vector, string, "," );
  1500.    printf( "number of tokens=%d\n", numtoks );
  1501.    for ( i = 0 ; i < numtoks ; i++ ) {
  1502.       printf( "trimmed token[%d] = '%s'\n", i, strtrim( vector[i], WS ) );
  1503.    }
  1504.  
  1505. #ifdef vms
  1506.    exit( SS$_NORMAL );
  1507. #else
  1508.    exit( 0 );
  1509. #endif
  1510.  
  1511. }
  1512.  
  1513. #endif
  1514.