brad@hcx1.ssd.csd.harris.com (Brad Appleton) (03/18/91)
Submitted-by: Brad Appleton <brad@hcx1.ssd.csd.harris.com> Posting-number: Volume 17, Issue 54 Archive-name: parseargs/part09 This is part 9 of parseargs #!/bin/sh # this is Part.09 (part 9 of a multipart archive) # do not concatenate these parts, unpack them in order with /bin/sh # file parseargs/pgopen.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 9; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 if test ! -f _shar_wnt_.tmp; then echo 'x - still skipping parseargs/pgopen.c' else echo 'x - continuing file parseargs/pgopen.c' sed 's/^X//' << 'SHAR_EOF' >> 'parseargs/pgopen.c' && ** pager-file-pointer. ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** Zero if the pager is active; non-zero otherwise. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X int pgactive( const FILE *pager_fp ) #endif { X return ( (Pager_Type != PG_CLOSED) && (pager_fp) ); } X X /*************************************************************************** ** ^FUNCTION: pgclose - close the pager. ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X int pgclose( pager_fp ) /* ** ^PARAMETERS: */ X FILE *pager_fp; /* -- the file-pointer returned by pgopen() */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Pgclose will flush any remaining output and close the pager. ** ** ^REQUIREMENTS: ** pgopen() must first be called in order to obtain a valid ** pager-file-pointer. ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** Returns 0 if the pager was succesfully close; non-zero if it wasnt ** in use to begin with. ** ** ^ALGORITHM: ** - if ( pager-not-in-use ) return 1 ** - flush any remaining pager output ** - if we used popen() to open this pager then ** - call pclose to close the pager-program ** - unset the SIGPIPE signal-handler ** - wait for the pager-process to die ** end-if ** - reset the pager-file-pointer to NULL ** - set the pager-state to closed ** - return 0 ***^^**********************************************************************/ #ifdef __ANSI_C__ X int pgclose( FILE *pager_fp ) #endif { X if ( Pager_Type == PG_CLOSED || !Pager_FP || !pager_fp ) return 1; X X fflush( Pager_FP ); X X if ( Pager_Type == PG_ENV || Pager_Type == PG_DFLT ) { X signal( SIGPIPE, (void (*)())SIG_IGN ); X wait( (int *) 0 ); X } X X Pager_FP = (FILE *)NULL; X Pager_Type = PG_CLOSED; X X return 0; } X X /*************************************************************************** ** ^FUNCTION: pgopen - open the pager ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X FILE *pgopen( fp, pager_cmd ) /* ** ^PARAMETERS: */ X FILE *fp; /* -- the file pointer to use if popen() fails */ X char *pager_cmd; /* -- name of the pager-program to pass to popen() */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Pgopen will attempt to "redirect" output from the given file ** pointer to the pager specified by <pager_cmd>. If <fp> is NOT ** connected to a terminal or <pager_cmd> cannot be succesfully ** opened, then <fp> is resturned output is sent to it unpaginated. ** Otherwise, pgopen will try to open <pager_cmd> for input. If it ** cannot succeed it tries to open $PAGER for input. If this also ** fails then /usr/ucb/more is tried. If all else fails, return <fp>. ** ** ^REQUIREMENTS: ** pager_cmd should be the full-pathname name of a valid, executable ** program which reads from standard input and writes (one screenful ** at a time) to the terminal. ** ** ^SIDE-EFECTS: ** If popen() succeeds, the SIGPIPE signal is trapped. ** ** ^RETURN-VALUE: ** returns 0 upon success, non-zero if the pager is already in use. ** ** ^ALGORITHM: ** - if the pager is already in use, return 1 ** - if <fp> is not connected to a terminal, use it for output and return 0 ** - set up recovery point for SIGPIPE signal ** - if we can open the pager_cmd, check for a SIGPIPE error ** - if either of the above fails, then try the same with $PAGER ** - if either of the above fails, then try the same with /usr/ucb/more ** - if either of the above fails, just use <fp> and return ***^^**********************************************************************/ #ifdef __ANSI_C__ X FILE *pgopen( FILE *fp, const char *pager_cmd ) #endif { X pager_t pg_type; X char pg_name[ MAX_NAME_LEN ]; X X /* if a pager is already open - ignore this call */ X if ( Pager_Type != PG_CLOSED || Pager_FP ) return fp; X X /* X ** dont page output if it has been redirected X */ X if ( !isTERMINAL(fileno(fp)) ) { X Pager_Type = PG_FILE; X Pager_FP = fp; X return fp; X } X X *pg_name = '\0'; X if ( pager_cmd ) strcpy( pg_name, pager_cmd ); X pg_type = pg_pathname( pg_name ); X Pager_FP = (FILE *)NULL; X X /* jump here after pg_error fields SIGPIPE */ X if ( setjmp( pg_recover ) ) { X (void) pclose( Pager_FP ); X Pager_FP = (FILE *)NULL; X pg_type = ( pg_type == PG_ENV ) ? PG_DFLT : PG_NONE; X } X X /* keep trying till we get a valid file-pointer */ X while ( !Pager_FP ) { X switch( pg_type ) { X case PG_DFLT: X /* jump here if both $PAGER and DEFAULT-PAGER fail */ X if ( setjmp( pg_recover )) { X (void) pclose( Pager_FP ); X Pager_FP = (FILE *)NULL; X pg_type = PG_NONE; X continue; X } X /* fall through to next case */ X X case PG_ENV: X signal( SIGPIPE, pg_error ); X Pager_FP = (FILE *) popen( pg_name, "w" ); X if ( !Pager_FP ) { X if ( pg_type == PG_ENV ) { X Pager_FP = (FILE *)NULL; X pg_type = PG_DFLT; X } X else { X Pager_FP = fp; X pg_type = PG_NONE; X } X } X else { X /* X ** Sleep for a bit, just so we block, and the child X ** process can get moving. Then attempt to write to X ** the pager pipeline. If the pager is bad then the X ** pipe will be broken and pg_error() will handle X ** the broken-pipe signal. Othwerwise, the write will X ** succeed and we'll reset the handler to ignore the X ** broken pipe signal. X */ X sleep( 1 ); X fputc( '\n', Pager_FP ); X fflush( Pager_FP ); X signal( SIGPIPE, (void (*)())SIG_IGN ); X } X break; X X case PG_NONE: X case PG_FILE: X Pager_FP = fp; X break; X X default: X fprintf( stderr, "Unrecognized state [%d] in pgopen()\n", X pg_type ); X exit( -1 ); X }/*switch*/ X }/*while*/ X X Pager_Type = pg_type; X return Pager_FP; } X X /*************************************************************************** ** ^FUNCTION: pg_error - handle error when trying to open a pager ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static void pg_error() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Pgerror is called when the SIGPIPE signal is recieved. If SIGPIPE ** is recieved, it means that popen succesfully forked the shell to ** run the pager but the pager-command itself broke the read-end of ** the pipe between us (i.e. it is an invalid pager-program or it crashed). ** ** When SIGPIPE is recieved, this routine will reset the signal handler ** to its previous value and jump to the recovery point set up by pgopen. ** ** ^REQUIREMENTS: ** Pgopen must set up this function is the SIGPIPE handler function. ** ** Pgopen must place the address of the desired recovery point in ** pg_recover. ** ** ^SIDE-EFECTS: ** Resets SIGPIPE signal-handler and performs a non-local goto. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void pg_error( void ) #endif { X signal( SIGPIPE, (void (*)())SIG_IGN ); X longjmp( pg_recover, 1 ); } X X /*************************************************************************** ** ^FUNCTION: pg_pathname - get name of pager-program to use ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static pager_t pg_pathname( pager_cmd ) /* ** ^PARAMETERS: */ X char *pager_cmd; /* -- name of the pager-program to verify */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Pg_pathname will determine the name of the pager-program to attempt to ** open based on the values of pager_cmd, and $PAGER. ** ** ^REQUIREMENTS: ** pager_cmd must be non-null and be large enough to hold any of the ** possible pager program-names to be opened. ** ** ^SIDE-EFECTS: ** pager_cmd is over-written with the name of the pager-command to ** try to open for output ** ** ^RETURN-VALUE: ** Returns PG_DFLT if /usr/ucb/more is to be used. ** Returns PG_ENV if $PAGER or <pager_cmd> is to be used. ** ** ^ALGORITHM: ** - If pager_cmd is executable then compare it to /usr/ucb/more ** return PG_ENV if not-equal, else return PG_DFLT ** - Else ** - pager_cmd = $PAGER ** - If $PAGER is executable then compare it to /usr/ucb/more ** return PG_ENV if not-equal, else return PG_DFLT ** - Else ** pager_cmd = /usr/ucb/more ** return PG_DFLT ** End-if ** End-if ***^^**********************************************************************/ #ifdef __ANSI_C__ X static pager_t pg_pathname( char *pager_cmd ) #endif { X char *pg_name = pager_cmd, *getenv(); X pager_t pg_type; X X if ( Pager_Type != PG_NONE ) pg_type = Pager_Type; X X /* see if the given pager is okay */ X if ( !pg_name || !*pg_name || !access(pg_name, X_OK) ) { X pg_name = getenv("PAGER"); X } X else { X pg_type = ( strcmp(pager_cmd, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT; X return pg_type; X } X X /* see if $PAGER is ok */ X if ( !access(pg_name, X_OK) ) { X pg_type = ( strcmp(pg_name, DEFAULT_PAGER) ) ? PG_ENV : PG_DFLT; X strcpy( pager_cmd, pg_name ); X } X else { X pg_type = PG_DFLT; X strcpy( pager_cmd, DEFAULT_PAGER ); X } X X return pg_type; }/* pg_pathname */ X X #endif /* USE_PAGER */ SHAR_EOF echo 'File parseargs/pgopen.c is complete' && chmod 0664 parseargs/pgopen.c || echo 'restore of parseargs/pgopen.c failed' Wc_c="`wc -c < 'parseargs/pgopen.c'`" test 12588 -eq "$Wc_c" || echo 'parseargs/pgopen.c: original size 12588, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/pgopen.h ============== if test -f 'parseargs/pgopen.h' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/pgopen.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/pgopen.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/pgopen.h' && /************************************************************************* ** ^FILE: pgopen.h - include file for pgopen.c ** ** ^DESCRIPTION: ** If USE_PAGER is #define'd then this file will define the function ** prototypes needed to use the paging library implemented in pgopen.c; ** Otherwise it will #define macros with the same name as the paging ** library routines which do NOT perform any paging. ** ** ^HISTORY: ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> Created ***^^**********************************************************************/ X #ifndef NULL # include <stdio.h> #endif #include <useful.h> X #ifdef USE_PAGER X EXTERN FILE *pgopen ARGS(( FILE *, const char * )); X EXTERN int pgclose ARGS(( FILE * )); X EXTERN int pgactive ARGS(( const FILE * )); #else # define pgopen(fp,s) (( !fp ) ? fp : (fflush(fp), fp)) # define pgclose(fp) (( !fp ) ? 0 : (fflush(fp), 0)) # define pgactive(fp) ( fp ) #endif SHAR_EOF chmod 0664 parseargs/pgopen.h || echo 'restore of parseargs/pgopen.h failed' Wc_c="`wc -c < 'parseargs/pgopen.h'`" test 977 -eq "$Wc_c" || echo 'parseargs/pgopen.h: original size 977, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/stest.c ============== if test -f 'parseargs/stest.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/stest.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/stest.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/stest.c' && /************************************************************************* ** ^FILE: stest.c - test program for parseargs() ** ** ^DESCRIPTION: ** This file is the test program for the parseargs(3) function libarary. ** It is used to test parseargs for all command-line styles (which presently ** includes: UNIX, VMS, AmigaDOS, MS-DOS, and OS/2). ** ** ^HISTORY: ** --/--/-- Brad Appleton <brad@ssd.csd.harris.com> ** - Added structured block comments ** - Added an extra test for both old-style and new-style argument arrays ** - Added a test for triggers (ARGNOVAL arguments) ** - Added a test for arguments with optional values (using parsecntl()) ** - Added arg-vector arguments ** ** --/--/-- Peter da Silva <peter@ferranti.com> ** ** --/--/-- Eric P. Allman <eric@Berkeley.EDU> Created ***^^**********************************************************************/ X #include <useful.h> #include <parseargs.h> X VERSIONID("$Header: stest.c,v 2.0 89/12/24 00:56:29 eric Exp $"); X static char Mode[4] = "OFF"; X /*************************************************************************** ** ^FUNCTION: argMine - example ARGNOVAL argument translation routine ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X BOOL argMine( ad, vp, copyf ) /* ** ^PARAMETERS: */ X ARGDESC *ad; /* -- the argument descriptor for this parameter. */ X char *vp; /* -- a pointer to the string input value. */ X BOOL copyf; /* -- if TRUE, the value will be destroyed later, and so should be ** copied if it will be retained (as for a string). */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** This routine is provided as a (very) simple example of how to use ** the ARGNOVAL flag to set up "trigger" arguments. Depending upon the ** implementation of the "trigger" function (this routine), the position ** of the corresponding argument on the command line may (or may not) be ** important. ** ** ^REQUIREMENTS: ** ad should have ARGNOVAL set and this function as its ad_type. ** ** ^SIDE-EFFECTS: ** The static global variable Mode is (re)written. ** ** ^RETURN-VALUE: ** TRUE. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ /*ARGSUSED*/ #ifdef __ANSI_C__ X BOOL argMine( ARGDESC *ad, char *vp, BOOL copyf ) #endif { X strcpy(Mode, "ON"); X return TRUE; } X /*************************************************************************/ X /* declare variables to hold values from the command-line */ #define DEF_STR "Default String" X typedef ARGVEC_T(char *) strvec_t; X static int RepCount; static char *Name; static char *Str = DEF_STR; static char *DirName = "."; static BOOL XRated = FALSE; static BOOL XFlag = FALSE; static BOOL YFlag = TRUE; static BOOL ZFlag = FALSE; static char TabChar = ':'; static ARGVEC_T(int) Integers = ARGVEC_EMPTY(int); static strvec_t Groups = ARGVEC_EMPTY(char *); static ArgList *Argv = (ArgList *)NULL; X /*************************************************************************/ X /* declare a new style argument-descriptor array */ static CMD_OBJECT X Cmd X CMD_NAME X "stest -- test program for parseargs" X CMD_DESCRIPTION X "This program is used to test parseargs for each desired command-line \ style and for both old and new style argument-array declarations. The actual \ name of the command will be <os>_test. parseargs will be called twice (with \ the same command-line description) in order to test both the old and the new \ syntax for declaring argument arrays." X CMD_ARGUMENTS X 'n', ARGREQ|ARGPOS, argStr, __ &Name, "name (name to look for)", X 's', ARGVALOPT, argStr, __ &Str, "STRing (optional string to use)", X 'g', ARGVEC, argStr, __ &Groups, "newsGROUPS (newsgroups to test)", X 'c', ARGOPT, argInt, __ &RepCount, "REPcount (repeat count per group)", X 'd', ARGOPT, argStr, __ &DirName, "DIRname (work directory)", X 'i', ARGVEC, argInt, __ &Integers, "INTegerS (vector of numbers)", X '#', ARGHIDDEN, argBool, __ &XRated, "XratedMODE (naughty! naughty!)", X X 'x', ARGOPT, argSBool, __ &XFlag, "Xflag (set X flag)", X 'y', ARGOPT, argUBool, __ &YFlag, "Yflag (unset Y flag)", X 'z', ARGOPT, argTBool, __ &ZFlag, "Zflag (toggle Z flag)", X X 't', ARGOPT, argChar, __ &TabChar, "TABchar (field delimiter)", X 'r', ARGNOVAL, argMine, __ NULL, "raw (trigger raw-mode \ before processing any more arguments on the command-line)", X X ' ', ARGLIST, listStr, __ &Argv, "file (list of files)", X X END_ARGUMENTS CMD_END X X /*************************************************************************/ X /* declare an old style argument-descriptor array */ static ARGDESC Args[] = { X STARTOFARGS, X X 'n', ARGREQ|ARGPOS, argStr, __ &Name, "name (name to look for)", X 's', ARGVALOPT, argStr, __ &Str, "STRing (optional string to use)", X 'g', ARGVEC, argStr, __ &Groups, "newsGROUPS (newsgroups to test)", X 'c', ARGOPT, argInt, __ &RepCount, "REPcount (repeat count per group)", X 'd', ARGOPT, argStr, __ &DirName, "DIRname (work directory)", X 'i', ARGVEC, argInt, __ &Integers, "INTegerS (vector of numbers)", X '#', ARGHIDDEN, argBool, __ &XRated, "XratedMODE (naughty! naughty!)", X X 'x', ARGOPT, argSBool, __ &XFlag, "Xflag (set X flag)", X 'y', ARGOPT, argUBool, __ &YFlag, "Yflag (unset Y flag)", X 'z', ARGOPT, argTBool, __ &ZFlag, "Zflag (toggle Z flag)", X X 't', ARGOPT, argChar, __ &TabChar, "TABchar (field delimiter)", X 'r', ARGNOVAL, argMine, __ NULL, "raw (trigger raw-mode \ before processing any more arguments on the command-line)", X X ' ', ARGLIST, listStr, __ &Argv, "file (list of files)", X X ENDOFARGS }; X X /*************************************************************************** ** ^FUNCTION: reset_args - reset argument values for another pass ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID reset_args() #endif /* ** ^PARAMETERS: ** None. ** ** ^DESCRIPTION: ** Reset_args resets all the argument values to their corresponding ** default values so that we can (re)test the parseargs library. ** ** ^REQUIREMENTS: ** None. ** ** ^SIDE-EFECTS: ** All the static-global argument variables are rewritten. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void reset_args( void ) #endif { X RepCount = 0; X Name = CHARNULL; X Str = DEF_STR; X DirName = "."; X XRated = FALSE; X XFlag = FALSE; X YFlag = TRUE; X ZFlag = FALSE; X TabChar = ':'; X X vecFree(Integers, int); X vecDeepFree(Groups, char *); X listFree( Argv ); X Argv = ARGLISTNULL; } X X /*************************************************************************** ** ^FUNCTION: print_args - print current argument values ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static void print_args( argd ) /* ** ^PARAMETERS: */ X ARGDESC *argd; /* -- the command whose arg-values are to be printed */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Print the current values of all the command-line settings ** ** ^REQUIREMENTS: ** The command-line should have already been parsed by one of the ** Xparseargs functions. ** ** ^SIDE-EFECTS: ** Prints on stdout. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X static void print_args( const ARGDESC *argd ) #endif { X int i, flags; X ArgList *ls; X X printf( "Name = \"%s\", DirName = \"%s\", RepCount = %d,\n", X Name, DirName, RepCount ); X X printf( "XFlag = %d, YFlag = %d, ZFlag = %d, TabChar='%c'(%03o);\n", X XFlag, YFlag, ZFlag, TabChar, TabChar ); X X printf( "XRated=%d, Raw-Mode = \"%s\"\n", XRated, Mode ); X X /* to call parsecntl() to see if the optional value was supplied */ X (VOID) parsecntl( (ARGDESC *)argd, pc_ARGFLAGS, pc_READ, "string", &flags); X X if ( BTEST(flags, ARGGIVEN) && !BTEST(flags, ARGVALGIVEN) ) { X printf("String=!No Value Given on CmdLine!\n" ); X } X else { X printf("String=\"%s\"\n", Str); X } X X if (Groups.count) { X printf("Newsgroups:"); X for (i = 0 ; i < Groups.count ; i++ ) { X printf(" %s", Groups.array[i]); X } X putchar('\n'); X } X X if (Integers.count) { X printf("Integers:"); X for (i = 0 ; i < Integers.count ; i++ ) { X printf(" %d", Integers.array[i]); X } X putchar('\n'); X } X X if (Argv) printf("Remaining args: "); X for ( ls = Argv ; ls ; L_ADVANCE(ls) ) { X printf("%s", L_STRING(ls)); X if ( L_NEXT(ls) ) { X putchar(' '); X } X else { X putchar('\n'); X } X }/*for*/ } X X /*ARGSUSED*/ MAIN(argc, argv) { X parseargs(argv, Cmd); /* parse the command-line */ X print_args(Cmd); /* print what we found */ X X putchar('\n'); X reset_args(); /* reset args for another pass */ X X parseargs(argv, Args); /* parse same command-line using old-style argd */ X print_args(Args); /* print what we found (should be same as before) */ X X exit(0); /* wave bye-bye */ } SHAR_EOF chmod 0664 parseargs/stest.c || echo 'restore of parseargs/stest.c failed' Wc_c="`wc -c < 'parseargs/stest.c'`" test 9196 -eq "$Wc_c" || echo 'parseargs/stest.c: original size 9196, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/strfuncs.c ============== if test -f 'parseargs/strfuncs.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/strfuncs.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/strfuncs.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/strfuncs.c' && /************************************************************************** ** ^FILE: strfuncs.c - Miscellaneous string functions for parseargs ** ** ^DESCRIPTION: ** This file implements a wide variety of functions to manipulate ** strings in way way or another. Some of the functions may already ** be included in the standard library of some C compilers. ** ** The following functions are implemented: ** ** strucpy() -- copy a string and map to uppercase ** strlcpy() -- copy a string and map to lowercase ** strupr() -- convert a string to uppercase ** strlwr() -- convert a string to lowercase ** strdup() -- return a (newly allocated) copy of a string ** strpbrk() -- return first occurrence of character in a set ** strspn() -- return length of initial prefix of a character set ** strcspn() -- return length of initial prefix of a character set ** strltrim() -- trim leftmost (leading) characters in a string ** strrtrim() -- trim rightmost (trailing) characters in a string ** strtrim() -- trim leading and trailing characters in a string ** strsplit() -- split a string up into a vector of tokens ** strjoin() -- join a vector of token into a single string ** get_name() -- return the aname (argument-name) of an argument ** get_keyword() -- return the sname (keyword-name) of an argument ** match() -- match two keywords (case insensitive) upto a unique prefix ** stricmp() -- case insensitive comparison of strings ** strnicmp() -- case insensitive length-limited comparison of strings ** basename() -- remove the leading directories (and disks) from a path ** indent_para() -- print an indented hanging paragraph ** ** ^HISTORY: ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> Created ** - changed from file misc.c to this name and added all of the strxxx ** functions (plus got rid of some unused functions). ** ** --/--/-- Peter da Silva <peter@ferranti.com> ** ** --/--/-- Eric P. Allman <eric@Berkeley.EDU> Created ***^^**********************************************************************/ X #include <stdio.h> #include <ctype.h> #include <useful.h> X EXTERN VOID syserr ARGS((const char *, ...)); X static CONST char WhiteSpace[] = " \t\n\r\v\f"; X X #if ( defined(unix_style) || defined(ibm_style) ) # define TO_KWDCASE(c) TOLOWER(c) #else # define TO_KWDCASE(c) TOUPPER(c) #endif X X /*************************************************************************** ** ^FUNCTION: strucpy, strlcpy - copy dest to src, mapping to upper/lower case ** ** ^SYNOPSIS: ** ** char *strucpy( dest, src ) ** char *strlcpy( dest, src ) ** ** ^PARAMETERS: ** char *dest; ** -- the address to start copying to ** ** char *src; ** -- the address to start copying from ** ** ^DESCRIPTION: ** Strlcpy (strucpy) copies src into dest (upto and including the ** terminating NUL byte) and all uppercase (lowercase) characters in ** src are mapped to lowercase (uppercase) before being copied into dest. ** ** ^REQUIREMENTS: ** Dest must be non-null, and large enough to hold the copied result. ** ** ^SIDE-EFECTS: ** Dest is (re)written ** ** ^RETURN-VALUE: ** Address of dest. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *strucpy( char *dest, const char *src ) #else X char *strucpy( dest, src ) char *dest, *src; #endif { X register char *s1 = dest; X register CONST char *s2 = src; X X if ( !s2 ) return CHARNULL; X X for ( ; *s2 ; s1++, s2++ ) { X *s1 = TOUPPER( *s2 ); X } X *s1 = '\0'; X X return s1; } X X #ifdef __ANSI_C__ X char *strlcpy( char *dest, const char *src ) #else X char *strlcpy( dest, src ) char *dest, *src; #endif { X register char *s1 = dest; X register CONST char *s2 = src; X X if ( !s2 ) return CHARNULL; X X for ( ; *s2 ; s1++, s2++ ) { X *s1 = TOLOWER( *s2 ); X } X *s1 = '\0'; X X return s1; } X X /*************************************************************************** ** ^FUNCTION: strupr, strlwr - convert a string to all upper/lower case ** ** ^SYNOPSIS: ** char *strupr( str ) ** char *strlwr( str ) ** ** ^PARAMETERS: ** char *str; ** -- the string to be converted ** ** ^DESCRIPTION: ** Strupr (strlwr) converts all lowercase (uppercase) characters in <str> ** to uppercase (lowercase) and returns the address of <str>. ** ** ^REQUIREMENTS: ** str should be non-null and non-empty. ** ** ^SIDE-EFECTS: ** str is overwritten with the uppercase (lowercase) result. ** ** ^RETURN-VALUE: ** Address of str. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *strupr( char *str ) #else X char *strupr( str ) char *str; #endif { X char *p = str; X X for ( ; p && *p ; p++ ) { X if ( islower(*p) ) *p = toupper(*p); X } X X return str; } X X #ifdef __ANSI_C__ X char *strlwr( char *str ) #else X char *strlwr( str ) char *str; #endif { X char *p = str; X X for ( ; p && *p ; p++ ) { X if ( isupper(*p) ) *p = tolower(*p); X } X X return str; } X X /*************************************************************************** ** ^FUNCTION: stricmp, strnicmp - case insensitive string comparison ** ** ^SYNOPSIS: ** int stricmp( s1, s2 ) ** int strnicmp( s1, s2, n ) ** ** ^PARAMETERS: ** char *s1; ** -- first string to compare ** ** char *s2; ** -- second string to compare ** ** size_t n; ** -- The number of characters to compare ** ** ^DESCRIPTION: ** Stricmp (strnicmp) is identical to strcmp (strncmp) except that it ** it performs a case-insensitive comparison of characters. ** ** ^REQUIREMENTS: ** Both s1 and s2 should be non-null and non-empty ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** < 0 if s1 < s2 ** = 0 if s1 matches s2 ** > 0 if s1 > s2 ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X int stricmp( const char *str1, const char *str2 ) #else X int stricmp( str1, str2 ) char *str1, *str2; #endif { X register CONST char *s1 = str1, *s2 = str2; X register char c1, c2; X X if ( s1 == s2 ) return 0; X if ( !s1 ) return -1; X if ( !s2 ) return 1; X X for ( ; *s1 && *s2 ; s1++ , s2++ ) { X c1 = TOLOWER( *s1 ); X c2 = TOLOWER( *s2 ); X X if (c1 != c2) return (int)(c1 -c2); X } X return (*s1 == *s2) ? 0 : (int)(*s1 - *s2); } X X #ifdef __ANSI_C__ X int strnicmp( const char *str1, const char *str2, size_t len ) #else X int strnicmp( str1, str2, len ) char *str1, *str2; size_t len; #endif { X register CONST char *s1 = str1, *s2 = str2; X register char c1, c2; X X if ( s1 == s2 ) return 0; X if ( !s1 ) return -1; X if ( !s2 ) return 1; X X for ( ; *s1 && *s2 && len ; s1++ , s2++ , len-- ) { X c1 = TOLOWER( *s1 ); X c2 = TOLOWER( *s2 ); X X if (c1 != c2) return (int)(c1 -c2); X } X return (*s1 == *s2) ? 0 : (int)(*s1 - *s2); } X X #ifdef BSD X /*************************************************************************** ** ^FUNCTION: strdup - copy a string ** ** ^SYNOPSIS: */ # ifndef __ANSI_C__ X char *strdup( str ) /* ** ^PARAMETERS: */ X char *str; /* -- the string to replicate */ # endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Strdup allocates storrage and copies the given string into the ** newly acquired space (returning its address). The returned result ** should be deallocated using free(). ** ** ^REQUIREMENTS: ** str should be non-null ** ** ^SIDE-EFFECTS: ** None. ** ** ^RETURN-VALUE: ** Address of the newly allocated string. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ # ifdef __ANSI_C__ X char *strdup( const char *str ) # endif { X unsigned len = strlen(str) + 1; X char *p = malloc( len * sizeof(char) ); X X if ( !p ) syserr( "Fatal Error -- out of memory" ); X strcpy(p, str); X X return p; } X X /*************************************************************************** ** ^FUNCTION: strpbrk - return the first occurrence of characters in a string ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X char *strpbrk( str1, str2 ) /* ** ^PARAMETERS: */ X char *str1; /* -- the string to be searched */ X char *str2; /* -- the set of characters to be located */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Strpbrk will attempt to locate the first occurence in str1 of any ** character from str2 and return the address of the first such ** occurrence. If str1 contains NO characters from str2, then NULL ** is returned. ** ** ^REQUIREMENTS: ** Both str1 and str2 should be non-null and non-empty ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** A pointer to the first occurence in str1 of any char from str2. ** ** ^ALGORITHM: ** - foreach char in str1 ** - if char is in str2, return the address of char ** end-for ** - if we have reached the end of str1, return NULL ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *strpbrk( const char *str1, const char *str2 ) #endif { X register CONST char *s1 = str1, *s2 = str2; X X if ( !s1 || !*s1 || !s2 || !*s2 ) return CHARNULL; X X for ( ; *s1 ; s1++ ) { X if ( strchr(s2, *s1) ) return (char *)s1; X } X X return CHARNULL; } X X /*************************************************************************** ** ^FUNCTION: strspn, strcspn - identify leading runs of characters ** ** ^SYNOPSIS: ** char *strspn( str1, str2 ) ** char *strcspn( str1, str2 ) ** ** ^PARAMETERS: ** char *str1; ** -- the string to be searched ** ** char *str2; ** -- the string to be searched ** ** ^DESCRIPTION: ** Strspn (strcspn) attempts to determine the length of the longest ** leading prefix of str1 that consists entirely of character from ** (not from) str2. ** ** ^REQUIREMENTS: ** Both str1 and str2 should be non-null and non-empty. ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: ** The length of the initial prefix in str1 consisting entirely ** of characters from (not from) str2. ** ** ^ALGORITHM: ** - length = 0 ** - for each char in str1 ** - if char is in str2 (for strcspn) or not in str2 (for strcspn) ** then return length ** - else ** add 1 to length ** end-if ** end-for ** - if end-of-string then return length ** ***^^**********************************************************************/ #ifdef __ANSI_C__ X int strspn( const char *str1, const char *str2 ) #else X int strspn( str1, str2 ) char *str1, *str2; #endif { X register CONST char *s1 = str1, *s2 = str2; X int len = 0; X X if ( !s1 || !*s1 || !s2 || !*s2 ) return 0; X while ( *s1 && strchr(s2, *s1++) ) ++len; X return len; } X X #ifdef __ANSI_C__ X int strcspn( const char *str1, const char *str2 ) #else X int strcspn( str1, str2 ) char *str1, *str2; #endif { X register CONST char *s1 = str1, *s2 = str2; X int len = 0; X X if ( !s1 || !*s1 || !s2 || !*s2 ) return 0; X while ( *s1 && !strchr(s2, *s1++) ) ++len; X return len; } X #endif /* BSD */ X X /*************************************************************************** ** ^FUNCTION: strltrim, strrtrim, strtrim - trim leading/trailing characters ** ** ^SYNOPSIS: ** char *strltrim( str, charset ) ** char *strrtrim( str, charset ) ** char *strtrim( str, charset ) ** ** ^PARAMETERS: ** char *str; ** -- the string to be trimmed ** ** char *charset; ** -- the set of characters to be trimmed ** ** ^DESCRIPTION: ** Strltrim removes from str, all leftmost (leading) characters occurring ** in charset. ** ** Strrtrim removes from str, all rightmost (trailing) characters occurring ** in charset. ** ** Strtrim removes from str, all leading and trailing characters occurring ** in charset. ** ** For each of these functions, if charset is NULL or empty, then the set ** of whitespace characters (space, tab, newline, carriage-return, form-feed ** and vertical-tab) is assumed. ** ** ^REQUIREMENTS: ** str should be non-null and non-empty. ** ** ^SIDE-EFECTS: ** characters may be removed from the beginning and/or end of str. ** ** ^RETURN-VALUE: ** Address of str. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *strltrim( char *str, const char *charset ) #else X char *strltrim( str, charset ) char *str, *charset; #endif { X register int i; X X if ( !str || !*str ) return str; X /* if delim-string is NULL, whitespace is used */ X if ( !charset ) charset = WhiteSpace; X X i = strspn( str, charset ); X if ( i > 0 ) strcpy( str, &(str[i]) ); X X return str; } X X #ifdef __ANSI_C__ X char *strrtrim( char *str, const char *charset ) #else X char *strrtrim( str, charset ) char *str, *charset; #endif { X register int i; X X if ( !str || !*str ) return str; X if ( !charset ) charset = WhiteSpace; X for ( i = strlen(str) - 1 ; X ( i >= 0 ) && (strchr( charset, str[i] )) ; X i-- X ) ; X X str[i+1] = '\0'; X X return str; } X X #ifdef __ANSI_C__ X char *strtrim( char *str, const char *charset ) #else X char *strtrim( str, charset ) char *str, *charset; #endif { X register int i; X X if ( !str || !*str ) return str; X if ( !charset ) charset = WhiteSpace; X i = strspn( str, charset ); X if ( i > 0 ) strcpy( str, &(str[i]) ); X X for ( i = strlen(str) - 1 ; X ( i >= 0 ) && (strchr( charset, str[i] )) ; X i-- X ) ; X X str[i+1] = '\0'; X X return str; } X X /*************************************************************************** ** ^FUNCTION: strsplit - split a string into tokens ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X int strsplit( vec, token_str, separators ) /* ** ^PARAMETERS: */ X char **vec[]; /* -- pointer to the string vector to be allocated */ X char token_str[]; /* -- the string to be split up */ X char separators[]; /* -- the delimiters that separate tokens */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Strsplit will split token_str up into a vector of tokens that are ** separated by one or more characters from <separators>. The number ** of tokens found is returned and storage is allocated for the given ** vector (which may later be deallocated using free()). ** ** If <separators> is NULL or empty, then the set of whitespace characters ** is used as the token delimiters. ** ** ^REQUIREMENTS: ** vec must be non-NULL (it must be a valid address). ** token_str should be non-null and non-empty ** ** ^SIDE-EFECTS: ** All leading and trailing characters from <separators> are removed ** from token_str. Furthermore, all remaining sequences in token_str ** of characters from <separators> are replaced with a single NUL-byte. ** ** Token_str holds the actual storage for all the strings in the newly ** created vector. ** ** ^RETURN-VALUE: ** The number of tokens parsed. ** ** ^ALGORITHM: ** - count the number of tokens present while at the same time removing ** all leading and trailing delimiters, and replacing all other sequences ** of delimiters with the NUL character. ** - allocate a vector large enough to point to all the token strings. ** - for i in 0 .. (numtokens - 1) do ** - vector[i] = token_str ** - advance token_str to point at the next character past the ** righmost NUL-byte (which should be the start of the next token). ** end-for ** - return the number of tokens parsed. ***^^**********************************************************************/ #ifdef __ANSI_C__ X int strsplit( char **vec[], char token_str[], const char separators[] ) #endif { X register char c, *pread, *pwrite; X int i, count = 0; X X if ( !token_str ) return 0; X /* if delim-string is NULL, whitespace is used */ X if ( !separators ) separators = WhiteSpace; X X /* trim leading separators */ X pread = token_str; X while ( strchr(separators, *pread) ) ++pread; X token_str = pwrite = pread; X X /* X ** make first pass through string, counting # of tokens and X ** separating all tokens by a single '\0' X */ X while ( c = *pread++ ) { X if ( !strchr(separators, c) ) { X *pwrite++ = c; X } X else { X *pwrite++ = '\0'; /* null terminate this token */ X ++count; /* update token count */ X while ( strchr(separators, *pread) ) ++pread; X } X }/*while*/ X if ( *(pwrite - 1) ) { X ++count; /* dont forget last token */ X *pwrite = '\0'; /* null-terminate */ X } X X /* allocate space for the caller's vector (remember NULL at the end) */ X (*vec) = (char **)malloc( (1 + count) * sizeof( char * ) ); X if ( !*vec ) { X fprintf( stderr, "out of memory in strsplit() - aborting\n" ); X exit( -1 ); X } X X /* now go thru token-string again assigning pointers from vector */ X pread = token_str; X for ( i = 0 ; i < count ; i++ ) { X (*vec)[i] = pread; /* assign pointer */ X pread += strlen( pread ) + 1; X }/* end-for */ X X /* set up the trailing pointer to NULL at the end */ X (*vec)[ count ] = CHARNULL; X return count; } X X /*************************************************************************** ** ^FUNCTION: strjoin - join a vector of tokens together ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X char *strjoin( argv, separator ) /* ** ^PARAMETERS: */ X char *argv[]; /* -- pointer to the string vector to join together */ X char separator[]; /* -- the the string to use to separate tokens (if NULL, " " is used) */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Strjoin will make a single string out of the given vector by copying ** all the tokens from the given vector (in order) toa newly allocated ** string. Each token will be separated by an occurence of <separator>. ** ** If <separator> is NULL then a single space is used as the separator. ** If <separator> is empty, then no separator is used and the tokens are ** simply concatenated together. ** ** ^REQUIREMENTS: ** argv must be non-NULL (it must be a valid address), and must be terminated ** by a pointer to NULL (argv[last+1] == NULL). ** ** ^SIDE-EFECTS: ** Storage is allocated. ** ** ^RETURN-VALUE: ** The addres of the newly-joined results (which should be deallocated ** using free()). Returns NULL if nothing was joined. ** ** ^ALGORITHM: ** - count the number of characters to place in the joined-result. ** - allocate a string large-enough to copy the joined-result into. ** - copy each string into the string (with <separator> between tokens). ** - 0 return the result. ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *strjoin( const char *argv[], const char separator[] ) #endif { X size_t sz = 0; X register char *p; X register CONST char *a, **av; X register int seplen; X char *result; X X /* if argv is NULL, nothing to do */ X if ( !argv ) return CHARNULL; X if ( !separator ) separator = " "; X seplen = strlen( separator ); X X /* figure out how much space we need */ X for ( av = argv ; *av ; av++ ) { X if ( !**av ) continue; X sz += strlen( *av ); X if ( seplen && *(av + 1) ) sz += seplen; X } X X /* allocate space */ X result = (char *)malloc( (sz + 1) * sizeof(char) ); X if ( !result ) syserr( "malloc failed in strjoin()" ); X X /* join the strings together */ X *result = '\0'; X for ( av = argv, p = result ; (a = *av) ; av++ ) { X if ( !*a ) continue; X while ( (*p = *a++) ) ++p; /* copy token */ X if ( seplen && *(av + 1) ) { X a = separator; X while ( (*p = *a++) ) ++p; /* copy separator */ X }/*end-if*/ X }/*end-for*/ X X return result; } X X /*************************************************************************** ** ^FUNCTION: get_name - return the aname (argument-name) of an argument ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X char *get_name( s, buf ) /* ** ^PARAMETERS: */ X char *s; /* -- the ad_prompt field of an ARGDESC struct */ X char *buf; /* -- address to which the aname should be copied */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Get_name will get the full argument name of the given argument ** (not just the keyword name) and copy it to buf. ** ** ^REQUIREMENTS: ** Both s and buf must be non-null and non-empty. ** buf must be large enough to hold the result. ** ** ^SIDE-EFECTS: ** buf is overwritten. ** ** ^RETURN-VALUE: ** Address of the buffer containing the name. ** ** ^ALGORITHM: ** determine the name of an argument from its prompt ** and copy the result in the given buffer ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *get_name( const char *s, char *buf ) #endif { X /* <buf> must be large enough to hold the result! */ X strlcpy(buf, s); X return buf; } X X /*************************************************************************** ** ^FUNCTION: get_keyword - get the sname (keyword name) of an argument ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X char *get_keyword( s, buf ) /* ** ^PARAMETERS: */ X char *s; /* -- the ad_prompt field of an ARGDESC struct */ X char *buf; /* -- address to which the sname should be copied */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Get_name will get the keyword name of the given argument ** (not the entire argument name) and copy it to buf. ** ** The sname (keyword-name) consists only of all uppercase characters ** from the ad_prompt field (in the order they occur). If the ad_prompt ** field contains NO uppercase characters, than the aname and the sname ** are equivalent (the entire string). ** ** ^REQUIREMENTS: ** Both s and buf must be non-null and non-empty. ** buf must be large enough to hold the result. ** ** ^SIDE-EFECTS: ** buf is overwritten. * ** ^RETURN-VALUE: ** Address of the buffer containing the keyword. ** ** ^ALGORITHM: ** determine the keyword of an argument from its prompt ** and copy the result in the given buffer ***^^**********************************************************************/ #ifdef __ANSI_C__ X char *get_keyword( const char *s, char *buf ) #endif { X register char *p1 = (char *)s, *p2; X register int i, len = 0; X char *caps = CHARNULL; X X if ( !p1 ) return CHARNULL; X X /* find size to copy (use all caps if possible) */ X for ( p1 = (char *)s ; *p1 ; p1++ ) { X if ( !caps && isupper( *p1 ) ) caps = p1; X if ( caps && isupper( *p1 ) ) ++len; X } X if ( !caps ) len = (int) (p1 - (char *)s); X X /* copy string into buffer and convert it to desired case */ X /* <buf> must be large enough to hold the result! */ X p1 = buf; X if ( len ) { X if ( !caps ) { X for ( p1 = buf, p2 = (char *)s, i = 0 ; i < len ; p1++, p2++, i++ ) { X *p1 = TO_KWDCASE(*p2); X } X }/*if*/ X X else { X for ( p2 = caps, i = 0 ; i < len ; p2++ ) { X if ( isupper( *p2 ) ) { X *(p1++) = TO_KWDCASE(*p2); X ++i; X } X }/*for*/ X }/*else*/ X }/*if*/ X *p1 = '\0'; X X return buf; /* return buffer address */ } #ifndef amiga_style X /*************************************************************************** ** ^FUNCTION: match - match a keyword against a prospective argument ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X int match( candidate, target ) /* ** ^PARAMETERS: */ X char *candidate; /* -- the possible keyword argument */ X char *target; /* -- the keyword to be matched */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Match will attempt to see if the candidate string matches the ** target string (case insensitive). First a match is tried on the ** sname of the keyword, then on the aname. Candidate may be only ** a partial leading portion of the target as long as it is at least ** two characters long (unless the keyword is 1 character long). ** ** No "partial" matching is accepted for AmigaDOS command-lines. ** ** ^REQUIREMENTS: ** Both candidate and target should be non-null and non-empty. ** target should be the ad_prompt field of an ARGDESC structure. ** ** ^SIDE-EFECTS: ** None. ** ** ^RETURN-VALUE: * < 0 if candidate < target ** = 0 if candidate matches target ** > 0 if candidate > target ** ** ^ALGORITHM: ** - attempt a partial match against the sname and return 0 if we succeed ** - attempt a partial match against the aname and return 0 if we succeed ** - if both the above fail return non-zero (no match). ** ***^^**********************************************************************/ X /* rewritten 8/20/90 --BDA */ #define MINLEN 2 /* minimum # of characters to match */ X #ifdef __ANSI_C__ X int match ( const char *candidate, const char *target ) #endif { X int i, clen, tlen, too_short=0; X CONST char *full_targ; X char *up_targ; X X X full_targ = target; X X /* make up_targ the uppercase portion of target */ X up_targ = strdup( full_targ ); X (VOID) get_keyword( full_targ, up_targ ); X X /* match at least MINLEN characters if possible */ X tlen = strlen( up_targ ); X clen = strlen( candidate ); X if ( (tlen >= MINLEN) && (clen < MINLEN) ) { X ++too_short; /* not long enough -- no match */ X } X #ifdef vms_style X /* if first two chars are NO then match at least MINLEN+2 chars */ X if ( !strnicmp(up_targ, "NO", 2) ) { X if ( (tlen >= (MINLEN + 2)) && (clen < (MINLEN + 2)) ) { X ++too_short; /* not long enough -- no match */ X } X } #endif X X /* first try to match prefix of the uppercase portion */ X i = (too_short) ? -1 : strnicmp(up_targ, candidate, clen); X X free( up_targ ); X X /* did we match? */ X if ( !i ) return 0; /* yes! */ X X /* no! : compare the whole target X ** match at least MINLEN characters if possible X */ X tlen = strlen(full_targ); X if ( (tlen >= MINLEN) && (clen < MINLEN) ) { X return -1; /* not long enough -- no match */ X } X #ifdef vms_style X /* if first two chars are NO then match at least MINLEN+2 chars */ X if ( !strnicmp(full_targ, "no", 2) ) { X if ( (tlen >= (MINLEN + 2)) && (clen < (MINLEN + 2)) ) { X return -1; /* not long enough -- no match */ X } X } #endif X X return strnicmp(full_targ, candidate, clen); } X X /* here is the AmigaDOS version of match() */ #else X # ifdef __ANSI_C__ X int match( const char *candidate, const char *target ) # else X int match( candidate, target) char *candidate, *target; # endif { X int i, j; X char c; X X i = j = 0; X X while ( isgraph(target[i]) || isgraph(candidate[i]) ) { X while ( islower(target[i]) ) i++; X if ( !isgraph(target[i]) ) { X if ( !isgraph(candidate[j]) ) return 0; X return stricmp(target, candidate); X } X c = islower( candidate[j] ) ? toupper(candidate[j]) : candidate[j]; X if (target[i] != c) return stricmp(target, candidate); X i++; X j++; X } X return 0; } X #endif X X /*************************************************************************** ** ^FUNCTION: basename - return the last component of a pathname ** ** ^SYNOPSIS: ** char *basename( path ) ** ** ^PARAMETERS: ** path; ** -- the pathname to be truncated. ** ** ^DESCRIPTION: ** Basename takes a pathname and strips of all leading components ** (except for the very last one) which refer to directories or ** disk-drives. ** ** ^REQUIREMENTS: ** path should be non-null, non-empty, and should correspond to a valid ** pathname (absolute or relative). ** ** ^SIDE-EFECTS: ** None under Unix and AmigaDOS. ** ** Under VMS, the file version is removed and any .COM or .EXE extension ** is also removed. ** ** Under MS-DOS, any .EXE, .COM, or .BAT extension is removed. ** ** Under OS/2, any .EXE, .COM, or .CMD extension is removed. ** ** ^RETURN-VALUE: ** The address of the basename of the path. ** ** ^ALGORITHM: ** Trivial. ***^^**********************************************************************/ X /* should use '\\' for MS-DOS & OS/2 and use ']' for VMS */ #ifdef vms #define PATH_SEP ']' X X /* VAX/VMS version of basename */ # ifdef __ANSI_C__ X char *basename( char path[] ) # else X char *basename( path ) char path[]; # endif X { X char *base = strrchr( path, PATH_SEP ); X char *vers = strrchr( path, ';' ); X char *ext = strrchr( path, '.' ); X X if ( !base ) { X if ( !(base = strrchr( path, ':' )) ) { X base = path; X } X else { X ++base; X } X } X else { X ++base; X } X X if ( vers ) *vers ='\0'; X if ( ext && (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) { X ext = '\0'; X } X X return base; X } X #else #ifdef AmigaDOS X /* amiga version of basename() */ # ifdef __ANSI_C__ X char *basename( char path[] ) # else X char *basename( path ) char path[]; # endif X { X return path; X } #else #define PATH_SEP '/' /* default path-separator character */ X X /* default version of basename() */ # ifdef __ANSI_C__ X char *basename( char path[] ) # else X char *basename( path ) char path[]; # endif X { X char *base = strrchr( path, PATH_SEP ); X #if ( defined(MSDOS) || defined(OS2) ) X /* remove the extension from .EXE, .COM, .BAT, and .CMD files */ # ifdef OS2 X if ( ext && (!stricmp(ext, ".CMD") ) *ext = '\0'; # else X if ( ext && (!stricmp(ext, ".BAT") ) *ext = '\0'; # endif X X if ( ext && (!stricmp(ext, ".COM") || !stricmp(ext, ".EXE")) ) { X ext = '\0'; X } #endif X X if ( !base ) { #if ( defined(MSDOS) || defined(OS2) ) X base = strrchr( path, '\\' ); X if ( base ) return (base + 1); X if ( path[ 1 ] == ':' ) return (path + 2); /* just remove drive */ X return (base + 1); #endif X return path; X } X else { X return (base + 1); X } X } #endif #endif X X /*************************************************************************** ** ^FUNCTION: indent_para - print a hanging indented paragraph ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X VOID indent_para(fp, maxcols, margin, title, indent, text) /* ** ^PARAMETERS: */ X FILE *fp; /* -- the stream to which output is sent */ X int maxcols; /* -- the maximum width (in characters) of the output */ X int margin; /* -- the number of spaces to use as the left margin */ X char *title; /* -- the paragraph title */ X int indent; /* -- the distance between the title and the paragraph body */ X char *text; /* -- the body of the paragraph */ #endif /* !__ANSI_C__ */ X /* ^DESCRIPTION: ** Indent_para will print on fp, a titled, indented paragraph as follows: ** ** <----------------------- maxcols ---------------------------> ** <--- margin --> <-- indent --> ** title This is the first sentence ** of the paragraph. Etc ... ** ** ^REQUIREMENTS: ** maxcols and indent must be positive numbers with maxcols > indent ** ** ^SIDE-EFECTS: ** Output is printed to fp. ** ** ^RETURN-VALUE: ** None. ** ** ^ALGORITHM: ** Print the paragraph title and then print the text. ** Lines are automatically adjusted so that each one starts in the ** appropriate column. ***^^**********************************************************************/ #ifdef __ANSI_C__ X void indent_para( FILE *fp, int maxcols, int margin, X const char *title, int indent, const char *text ) #endif { X register int idx = 0; X BOOL first_line = TRUE; X int text_len = strlen(text); X char ch; X X /* print the title */ X fprintf( fp, "%*s%-*s", margin, "", indent, title ); X X idx = maxcols - margin - indent; X X if ( text_len <= idx ) X fprintf(fp, "%s\n", text); X else X do { X /* backup to end of previous word */ X while (idx && !isspace(text[idx])) --idx; X while (idx && isspace(text[idx])) --idx; X X /* print leading whitespace */ X if (!first_line) X fprintf(fp, "%*s%-*s", margin, "", indent, ""); X X ch = text[ ++idx ]; X *((char *)text + idx) = '\0'; X fprintf(fp, "%s\n", text); X *((char *)text + idx) = ch; X first_line = FALSE; X text = &(text[idx+1]); X text_len -= (idx+1); X X while (isspace(*text)) { /* goto next word */ X ++text; X --text_len; X } X X idx = maxcols - margin - indent; X X if ( text_len <= idx ) /* print-last line */ X fprintf(fp, "%*s%-*s%s\n", margin, "", indent, "", text); X } while ( text_len > idx ); } X X #ifdef STRTEST X #define WS " \t\n\v\r\f\"'" X static char string2[] = " oh what a beautiful - morning! "; X static char string[] = "\n\ \t' ', ARGREQ, argStr, Name, 'Name',\n\ \t'n', ARGOPT|ARGLIST, listStr, Groups, 'newsGROUP (newsgroups test)',\n\ \t'c', ARGOPT, argInt, RepCount, 'REPcount (number of reps)',\n\ \t'd', ARGOPT, argStr, DirName, 'DIRname',\n\ \t'x', ARGOPT, argBool, XFlag, 'Xflag (expand in X direction)',\n\ \t' ', ARGOPT|ARGLIST, listStr, Argv, 'File',\n\ \tENDOFARGS\n\ "; X static char word_str[] = "HELP (print help and quit)"; X main() #ifdef __ANSI_C__ #endif { X char **vector; X unsigned i, numtoks; X X printf( "test of strtrim() and strsplit():\n\n" ); X X printf( "unparsed string='%s'\n", string2 ); X printf( "ltrimmed string='%s'\n", strltrim( string2, WS ) ); X printf( "rtrimmed string='%s'\n", strrtrim( string2, WS ) ); X X numtoks = strsplit( &vector, string, "," ); X printf( "number of tokens=%d\n", numtoks ); X for ( i = 0 ; i < numtoks ; i++ ) { X printf( "trimmed token[%d] = '%s'\n", i, strtrim( vector[i], WS ) ); X } X X exit( 0 ); } X #endif SHAR_EOF chmod 0664 parseargs/strfuncs.c || echo 'restore of parseargs/strfuncs.c failed' Wc_c="`wc -c < 'parseargs/strfuncs.c'`" test 34399 -eq "$Wc_c" || echo 'parseargs/strfuncs.c: original size 34399, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/strfuncs.h ============== if test -f 'parseargs/strfuncs.h' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/strfuncs.h (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/strfuncs.h (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/strfuncs.h' && /*************************************************************************** ** ^FILE: strfuncs.h - string functions ** ** ^DESCRIPTION: ** External declarations for the functions implemented in strfuncs.c ** ** ^HISTORY: ** 01/07/91 Brad Appleton <brad@ssd.csd.harris.com> Created ***^^**********************************************************************/ X #ifndef STRFUNCS_H #define STRFUNCS_H X #include <useful.h> X EXTERN char *strucpy ARGS(( char *, const char * )); EXTERN char *strlcpy ARGS(( char *, const char * )); EXTERN char *strupr ARGS(( char * )); EXTERN char *strlwr ARGS(( char * )); EXTERN int stricmp ARGS(( const char *, const char * )); EXTERN int strnicmp ARGS(( const char *, const char *, size_t )); X #ifdef BSD X EXTERN char *strdup ARGS(( const char * )); X EXTERN char *strpbrk ARGS(( const char *, const char * )); X EXTERN int strspn ARGS(( const char *, const char * )); X EXTERN int strcspn ARGS(( const char *, const char * )); #endif X EXTERN char *strltrim ARGS(( char *, const char * )); EXTERN char *strrtrim ARGS(( char *, const char * )); EXTERN char *strtrim ARGS(( char *, const char * )); EXTERN int strsplit ARGS(( char ***, char *, const char * )); EXTERN char *strjoin ARGS(( const char **, const char * )); EXTERN char *get_name ARGS(( const char *, char * )); EXTERN char *get_keyword ARGS(( const char *, char * )); EXTERN int match ARGS(( const char *, const char * )); EXTERN char *basename ARGS(( char * )); EXTERN VOID indent_para ARGS(( FILE *, int, int, X const char *, int, const char * )); X #endif SHAR_EOF chmod 0664 parseargs/strfuncs.h || echo 'restore of parseargs/strfuncs.h failed' Wc_c="`wc -c < 'parseargs/strfuncs.h'`" test 1729 -eq "$Wc_c" || echo 'parseargs/strfuncs.h: original size 1729, current size' "$Wc_c" rm -f _shar_wnt_.tmp fi # ============= parseargs/syserr.c ============== if test -f 'parseargs/syserr.c' -a X"$1" != X"-c"; then echo 'x - skipping parseargs/syserr.c (File already exists)' rm -f _shar_wnt_.tmp else > _shar_wnt_.tmp echo 'x - extracting parseargs/syserr.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'parseargs/syserr.c' && /************************************************************************* ** ^FILE: syserr.c - error-message printing routines ** ** ^DESCRIPTION: ** This fill implements various routines for printing diagnostic ** messages on standard diagnostic output (stderr). The routines are: ** ** usrerr() -- print message and any system message(s) and return ** syserr() -- print message and any system message(s) and exit ** eprintf() -- print to stderr and return ** ** ^HISTORY: ** 01/02/91 Brad Appleton <brad@ssd.csd.harris.com> ** - Changed to use varargs/stdargs ** - Added structured comment blocks ** - Added eprintf() ** ** --/--/-- Peter da Silva <peter@ferranti.com> ** ** --/--/-- Eric P. Allman <eric@Berkeley.EDU> Created ***^^**********************************************************************/ X #include <stdio.h> #include <useful.h> /* #include <funclist.h> */ X VERSIONID("$Header: syserr.c,v 2.0 89/12/24 00:56:31 eric Exp $"); X extern char *ProgName; extern int errno; EXTERN int vfprintf ARGS((FILE *, const char *, va_list)); X X /*************************************************************************** ** ^FUNCTION: _error_message - generic message printing routine. ** ** ^SYNOPSIS: */ #ifndef __ANSI_C__ X static VOID _error_message( format, ap ) /* ** ^PARAMETERS: */ X char *format; SHAR_EOF true || echo 'restore of parseargs/syserr.c failed' fi echo 'End of part 9' echo 'File parseargs/syserr.c is continued in part 10' echo 10 > _shar_seq_.tmp exit 0 exit 0 # Just in case... -- Kent Landfield INTERNET: kent@sparky.IMD.Sterling.COM Sterling Software, IMD UUCP: uunet!sparky!kent Phone: (402) 291-8300 FAX: (402) 291-4362 Please send comp.sources.misc-related mail to kent@uunet.uu.net.