cmdopt.c Source Code

Go to: Contents; Previous section; Beginning of section; Next file in section; Previous file in section.

Routines In This File (Alphabetical)

 Line Name
----- ----
   74 cmdopt_cmp
  122 kwdef_cmp
  189 kwstrleadlen
  165 kwstrlen
  315 process_keyword
  214 process_options
  454 process_options_file
  390 translate_keyword
   36 ustrncmp

BEGINNING OF FILE

     1: /****************************************************************************/
     2: /*									    */
     3: /*  FACILITY:	Generic Support Library					    */
     4: /*									    */
     5: /*  MODULE:	Command Line Keyword and Option Processing		    */
     6: /*									    */
     7: /*  AUTHOR:	Steve Branam, Network Product Support Group, Digital	    */
     8: /*		Equipment Corporation, Littleton, MA, USA.		    */
     9: /*									    */
    10: /*  DESCRIPTION: This module contains routines to process command line	    */
    11: /*  arguments, given a dispatch table of keywords and handlers. This can be */
    12: /*  used for main program command lines, or internal command lines parsed   */
    13: /*  in a similar manner.						    */
    14: /*									    */
    15: /*  REVISION HISTORY:							    */
    16: /*									    */
    17: /*  V0.1-00 24-AUG-1994 Steve Branam					    */
    18: /*									    */
    19: /*	Original version.						    */
    20: /*									    */
    21: /*  V0.2-00 23-SEP-1994 Steve Branam					    */
    22: /*									    */
    23: /*	Addded keyword translation code.				    */
    24: /*									    */
    25: /****************************************************************************/
    26: 
    27: #include <stdio.h>
    28: #include <ctype.h>
    29: #include "cmdopt.h"
    30: 
    31: static KEYWORD_DEFINITION			/* Option dispatch table,   */
    32: 	    *mOptionTable;			/* used by options file	    */
    33: 						/* processing.		    */
    34: 
    35: /*************************************************************************++*/

ROUTINE ustrncmp. Go to: Next routine in file; Routines in this file.

    36: int ustrncmp(
    37: /* Compares strings in upper-case, serving as a case-insensitive string	    */
    38: /* comparison; otherwise identical to strncmp.				    */
    39: 
    40:     char    *aStr1,
    41: 	    /* (READ, BY ADDR):						    */
    42: 	    /* First string to compare.					    */
    43: 
    44:     char    *aStr2,
    45: 	    /* (READ, BY ADDR):						    */
    46: 	    /* Second string to compare.				    */
    47: 
    48:     int	    vLength
    49: 	    /* (READ, BY VAL):						    */
    50: 	    /* Maximum number of characters to compare.			    */
    51: 
    52: )	/* Returns status value indicating comparison results:		    */
    53: 	/*    0	- Strings are equal.					    */
    54: 	/*  < 0 - String aStr1 is less than aStr2.			    */
    55: 	/*  > 0 - String aStr1 is greater than aStr2.			    */
    56: 	/*****************************************************************--*/
    57: 
    58: {
    59:     /*+									    */
    60:     /*	Compare upper-case version of each char in strings until unequal    */
    61:     /*	chars ard found, end of string is found, or maximum number of	    */
    62:     /*	characters exceeded.						    */
    63:     /*- 								    */
    64: 
    65:     for (; toupper(*aStr1) == toupper(*aStr2); aStr1++, aStr2++) {
    66: 	if (*aStr1 == '\0' || --vLength == 0) {
    67: 	    return 0;			    /* Strings are "equal".	    */
    68: 	}
    69:     }					    /* String are not equal.	    */
    70:     return toupper(*aStr1) - toupper(*aStr2);
    71: }
END ustrncmp. Go to: Beginning of routine.


    72: 
    73: /*************************************************************************++*/

ROUTINE cmdopt_cmp. Go to: Next routine in file; Routines in this file.

    74: static int cmdopt_cmp(
    75: /* Compares an argument string to a command line option using a case-	    */
    76: /* insensitive string comparison.  The argument matches the option if the   */
    77: /* keyword portion is at least as long as the minimum option keyword	    */
    78: /* length, and matches the keyword for all specified characters.	    */
    79: 
    80:     KEYWORD_DEFINITION
    81: 	    *aOption,
    82: 	    /* (READ, BY ADDR):						    */
    83: 	    /* Command line option keyword definition to compare.	    */
    84: 
    85:     char    *aArgStr
    86: 	    /* (READ, BY ADDR):						    */
    87: 	    /* Argument string to compare. Options are assumed to be	    */
    88: 	    /* specified in one of two forms, either just the option switch */
    89: 	    /* char followed by the keyword (for toggle options), or the    */
    90: 	    /* option switch char and keyword followed by an equal sign and */
    91: 	    /* the option value string (for value options).		    */
    92: 
    93: )	/* Returns length of keyword portion of aArgstr (including switch   */
    94: 	/* char) to indicate successful match, or 0 if argument does not    */
    95: 	/* match option.						    */
    96: 	/********************************************************************/
    97: 
    98: {
    99:     int	    kwlen;			    /* Length of argument keyword   */
   100: 					    /* portion (including switch    */
   101: 					    /* char).			    */
   102: 
   103:     /*+									    */
   104:     /*	Find length of argument keyword portion. If it is less than minimum */
   105:     /*	required length, no match. Otherwise, compare argument to option    */
   106:     /*	keyword to determine match.					    */
   107:     /*-									    */
   108: 
   109:     if ((kwlen = kwstrlen(&aArgStr[1])) < kwdef_minlen(aOption)) {  
   110: 	return 0;			    /* Too short, no match.	    */
   111:     }
   112:     else if (ustrncmp(kwdef_keyword(aOption), &aArgStr[1],
   113: 		max(kwlen, kwdef_minlen(aOption))) == 0) {
   114: 	return kwlen + 1;			    /* Argument matches option.	    */
   115:     }
   116:     else {
   117: 	return 0;			    /* No match.		    */
   118:     }
   119: }
END cmdopt_cmp. Go to: Beginning of routine.


   120: 
   121: /*************************************************************************++*/

ROUTINE kwdef_cmp. Go to: Next routine in file; Routines in this file.

   122: static int kwdef_cmp(
   123: /* Compares a string to a keyword using a case- insensitive string	    */
   124: /* comparison.  The string matches the keyword if it is at least as long as */
   125: /* the minimum keyword length, and matches the keyword for all specified    */
   126: /* characters.								    */
   127: 
   128:     KEYWORD_DEFINITION
   129: 	    *aKwDef,
   130: 	    /* (READ, BY ADDR):						    */
   131: 	    /* Keyword definition to compare.				    */
   132: 
   133:     char    *aKwStr,
   134: 	    /* (READ, BY ADDR):						    */
   135: 	    /* Keyword string to compare.				    */
   136: 
   137:     int	    vLength
   138: 	    /* (READ, BY VAL):						    */
   139: 	    /* Keyword string length.					    */
   140: 
   141: )	/* Returns status flag:						    */
   142: 	/*  1 - Successful match					    */
   143: 	/*  0 - String does not match keyword.				    */
   144: 	/* ******************************************************************	*/
   145: 
   146: {
   147:     /*+									    */
   148:     /*	If string length is less than minimum required length, no match.    */
   149:     /*	Otherwise, compare argument to option keyword to determine match.   */
   150:     /*									    */
   151: 
   152:     if (vLength < kwdef_minlen(aKwDef)) {  
   153: 	return 0;			    /* Too short, no match.	    */
   154:     }
   155:     else if (ustrncmp(kwdef_keyword(aKwDef), aKwStr,
   156: 		max(vLength, kwdef_minlen(aKwDef))) == 0) {
   157: 	return 1;			    /* String matches keyword.	    */
   158:     }
   159:     else {
   160: 	return 0;			    /* No match.		    */
   161:     }
   162: }
END kwdef_cmp. Go to: Beginning of routine.


   163: 
   164: /*************************************************************************++*/

ROUTINE kwstrlen. Go to: Next routine in file; Routines in this file.

   165: int kwstrlen(
   166: /* Finds the length of a keyword string. A keyword is a contiguous string   */
   167: /* of alphanumerics and the underscore character '_'.			    */
   168: 
   169:     char    *aKwStr
   170: 	    /* (READ, BY ADDR):						    */
   171: 	    /* Keyword string to find length of. It is assumed to contain   */
   172: 	    /* no leading non-keyword characters (if it does, the length    */
   173: 	    /* returned will be 0).					    */
   174: 
   175: )	/* Returns length of string, or 0 if the first character is not a   */
   176: 	/* valid keyword string character.				    */
   177: 	/********************************************************************/
   178: 
   179: {
   180:     int	    kwlen;				/* Length of keyword.	    */
   181: 
   182:     for (kwlen = 0;
   183: 	isalnum(aKwStr[kwlen]) || aKwStr[kwlen] == '_';
   184: 	kwlen++);
   185:     return kwlen;
   186: }
END kwstrlen. Go to: Beginning of routine.


   187: 
   188: /*************************************************************************++*/

ROUTINE kwstrleadlen. Go to: Next routine in file; Routines in this file.

   189: int kwstrleadlen(
   190: /* Finds the length of leading non-keyword characters in a keyword string,  */
   191: /* which may subsequently be treated as the offset to the beginning of the  */
   192: /* keyword.								    */
   193: 
   194:     char    *aKwStr
   195: 	    /* (READ, BY ADDR):						    */
   196: 	    /* Keyword string that may contain leading non-keyword	    */
   197: 	    /* characters.						    */
   198: 
   199: )	/* Returns length of leading characters, or 0 if the string	    */
   200: 	/* contains no leading characters (or is a null string).	    */
   201: 	/********************************************************************/
   202: 
   203: {
   204:     int	    leadlen;				/* Length of leading chars. */
   205: 
   206:     for (leadlen = 0;
   207: 	!isalnum(aKwStr[leadlen])
   208: 	&& aKwStr[leadlen] != '_' && aKwStr[leadlen] != '\0';
   209: 	leadlen++);
   210:     return leadlen;
   211: }
END kwstrleadlen. Go to: Beginning of routine.


   212: 
   213: /*************************************************************************++*/

ROUTINE process_options. Go to: Next routine in file; Routines in this file.

   214: int process_options(
   215: /* Parses user command line arguments for options and dispatches option	    */
   216: /* handler routines. Processing will terminate if any handler indicates	    */
   217: /* failure.								    */
   218: 
   219:     int	    vArgc,
   220: 	    /* (READ, BY VAL):						    */
   221: 	    /* Number of program argument strings in aArgv.		    */
   222: 
   223:     char    *aArgv[],
   224: 	    /* (READ, BY ADDR):						    */
   225: 	    /* List of program argument strings.			    */
   226: 
   227:     int	    vFirstOption,
   228: 	    /* (READ, BY VAL):						    */
   229: 	    /* Starting argument number for option. All preceding arguments */
   230: 	    /* are assumed to be required, not options.			    */
   231: 
   232:     KEYWORD_DEFINITION
   233: 	    *aOptionTable
   234: 	    /* (READ, BY ADDR):						    */
   235: 	    
   236: 	    /* Option definition table, containing option keywords (not	    */
   237: 	    /* including option switch character), handler routine ptrs,    */
   238: 	    /* and keyword translation codes.  The table is terminated by   */
   239: 	    /* an entry containing a NULL keyword ptr. The interface for a  */
   240: 	    /* handler routine is:					    */
   241: 	    /*								    */
   242: 	    /*	    int handler(code, valstr, n)			    */
   243: 	    /*								    */
   244: 	    /* where code is the translation code for the matched keyword,  */
   245: 	    /* valstr is the ptr to the remainder of the argument string    */
   246: 	    /* following the matched argument characters, and n is the	    */
   247: 	    /* option's argument position on the command line; however, a   */
   248: 	    /* handler is free to ignore (and therefore not declare) any    */
   249: 	    /* trailing subset of these parameters. The handler returns 1   */
   250: 	    /* if the argument is handled successfully, or 0 otherwise.	    */
   251: 	    /* This interface supports two styles of usage: common handlers */
   252: 	    /* may handle several different options, discriminating the	    */
   253: 	    /* matched option via the translation code; or individual	    */
   254: 	    /* handlers may be used for each option, ignoring the	    */
   255: 	    /* translation codes.					    */
   256: 
   257: )	/* Returns status indicating processing success:		    */
   258: 	/*  1	- All options handled successfully (any unrecognized	    */
   259: 	/*	  arguments/options ignored).				    */
   260: 	/*  0	- An argument could not be processed.			    */
   261: 	/********************************************************************/
   262: 
   263: {
   264:     int	    arg;				/* Argument number.	    */
   265:     KEYWORD_DEFINITION				/* Current keyword def.	    */
   266: 	    *kwdef;
   267:     int	    kwlen;				/* Length of argument	    */
   268: 						/* keyword portion	    */
   269: 						/* (including switch char). */
   270: 
   271:     /*+									    */
   272:     /*	Save option table ptr in case we run into an option to process an   */
   273:     /*	options file, then attempt to process each command line argument    */
   274:     /*	following the required ones as an option.			    */
   275:     /*-									    */
   276: 
   277:     mOptionTable = aOptionTable;
   278:     for (arg = vFirstOption; arg < vArgc; arg++) {
   279: 
   280: 	/*+								    */
   281: 	/*  If argument starts with option switch character, go through	    */
   282: 	/*  option table trying to match it. On match, call option handler  */
   283: 	/*  and skip the rest of the option table; if the handler return    */
   284: 	/*  failure, abort. If no match, issue warning and ignore the	    */
   285: 	/*  argument. If it did not start with switch char, issue warning   */
   286: 	/*  and ignore it.						    */
   287: 	/*-								    */
   288: 						
   289: 	if (*aArgv[arg] == CMDLINE_OPTION_SWITCH) {
   290: 	    for (kwdef = aOptionTable; kwdef_keyword(kwdef) != NULL; kwdef++) {
   291: 		if ((kwlen = cmdopt_cmp(kwdef, aArgv[arg]))) {
   292: 
   293: 						/* Match, call handler.	    */
   294: 		    if (!(kwdef_handler(kwdef))(kwdef_code(kwdef),
   295: 			    &aArgv[arg][kwlen], arg)) {
   296: 			return 0;		/* Abort, handler failed!   */
   297: 		    }
   298: 		    break;			/* Skip rest of table.	    */
   299: 		}
   300: 	    }
   301: 	    if (!kwlen) {			/* No option matches.	    */
   302: 		printf("ERROR: Unrecognized/ambiguous option %s\n", aArgv[arg]);
   303: 		return 0;
   304: 	    }
   305: 	}
   306: 	else {					/* Not an option argument.  */
   307: 	    printf("ERROR: Unexpected command line argument %s\n", aArgv[arg]);
   308: 	    return 0;
   309: 	}
   310:     }						/* All options processed    */
   311:     return 1;					/* successfully.	    */
   312: }
END process_options. Go to: Beginning of routine.


   313: 
   314: /*************************************************************************++*/

ROUTINE process_keyword. Go to: Next routine in file; Routines in this file.

   315: int process_keyword(
   316: /* Parses keyword strings and dispatches to keyword handler routines.	    */
   317: /* Processing will terminate if any handler indicates failure. 		    */
   318: 
   319:     char    *aKwStr,
   320: 	    /* (READ, BY ADDR):						    */
   321: 	    /* Keyword string, with no leading, trailing, or enclosed	    */
   322: 	    /* whitespace. May be a single keyword, or a list of keywords,  */
   323: 	    /* separated by commas.					    */
   324: 
   325:     KEYWORD_DEFINITION
   326: 	    *aKwTable
   327: 	    /* (READ, BY ADDR):						    */
   328: 	    /* Keyword definition table, containing keywords, handler	    */
   329: 	    /* routine ptrs, and keyword translation codes. The table is    */
   330: 	    /* terminated by an entry containing a NULL keyword ptr. The    */
   331: 	    /* interface for a handler routine is:			    */
   332: 	    /*								    */
   333: 	    /*	    int handler(code)					    */
   334: 	    /*								    */
   335: 	    /* where code is the keyword translation code.  (there are no   */
   336: 	    /* arguments to the handler).  The handler returns 1 if the	    */
   337: 	    /* argument is handled successfully, or 0 otherwise. This	    */
   338: 	    /* interface supports two styles of usage: common handlers may  */
   339: 	    /* handle several different keywords, discriminating the	    */
   340: 	    /* matched keyword via the translation code; or individual	    */
   341: 	    /* handlers may be used for each keyword, ignoring the	    */
   342: 	    /* translation codes.					    */
   343: 
   344: )	/* Returns status indicating processing success:		    */
   345: 	/*  1	- All keywords handled successfully.			    */
   346: 	/*  0	- A keyword could not be processed.			    */
   347: 	/********************************************************************/
   348: 
   349: {
   350:     KEYWORD_DEFINITION				/* Current keyword def.	    */
   351: 	    *kwdef;
   352:     int	    kwlen;				/* Length of keyword.	    */
   353:     int	    found;				/* Flag indicating match.   */
   354:     char    *savestr = aKwStr;			/* Saved string ptr.	    */
   355: 
   356: 						/* For each keyword in list */
   357:     while (*aKwStr != '\0') {			/* (or just one)...	    */
   358: 
   359: 	if ((kwlen = kwstrlen(aKwStr)) == 0) {
   360: 	    printf("ERROR: Missing keyword %s\n", savestr);
   361: 	    return 0;
   362: 	}
   363: 	else {					/* Scan keyword table for   */
   364: 						/* match.		    */
   365: 	    for (kwdef = aKwTable; kwdef_keyword(kwdef) != NULL; kwdef++) {
   366: 		if ((found = kwdef_cmp(kwdef, aKwStr, kwlen))) {
   367: 
   368: 						/* Match, call handler with */
   369: 						/* translation code.	    */
   370: 		    if (!(kwdef_handler(kwdef))(kwdef_code(kwdef))) {
   371: 			return 0;		/* Abort, handler failed!   */
   372: 		    }
   373: 		    break;			/* Skip rest of table.	    */
   374: 		}
   375: 	    }
   376: 	    if (!found) {
   377: 		printf("ERROR: Unrecognized/ambiguous keyword %s\n", aKwStr);
   378: 		return 0;
   379: 	    }
   380: 	}
   381: 	if (aKwStr[kwlen] == KEYWORD_LIST_SEPARATOR) {
   382: 	    kwlen++;				/* Account for separator.   */
   383: 	}
   384: 	aKwStr += kwlen;			/* Advance past keyword.    */
   385:     }						
   386:     return 1;					/* All keywords processed   */
   387: }						/* successfully.	    */
END process_keyword. Go to: Beginning of routine.


   388: 
   389: /*************************************************************************++*/

ROUTINE translate_keyword. Go to: Next routine in file; Routines in this file.

   390: int translate_keyword(
   391: /* Parses a single keyword string and returns a translation code.	    */
   392: 
   393:     char    *aKwStr,
   394: 	    /* (READ, BY ADDR):						    */
   395: 	    /* Keyword string. If it contains any leading non-keyword	    */
   396: 	    /* characters, they will be ignored.  If it contains more than  */
   397: 	    /* one keyword, only the first one will be translated.	    */
   398: 
   399:     KEYWORD_DEFINITION
   400: 	    *aKwTable
   401: 	    /* (READ, BY ADDR):						    */
   402: 	    /* Keyword definition table, containing keywords, handler	    */
   403: 	    /* routine ptrs, and keyword translation codes. The table is    */
   404: 	    /* terminated by an entry containing a NULL keyword ptr. The    */
   405: 	    /* handler routine is called only if its ptr is non-NULL. The   */
   406: 	    /* interface for a handler routine is:			    */
   407: 	    /*								    */
   408: 	    /*	    int handler(code)					    */
   409: 	    /*								    */
   410: 	    /* where code is the keyword translation code.  (there are no   */
   411: 	    /* arguments to the handler).  The handler returns 1 if the	    */
   412: 	    /* argument is handled successfully, or 0 otherwise. This	    */
   413: 	    /* interface supports two styles of usage: common handlers may  */
   414: 	    /* handle several different keywords, discriminating the	    */
   415: 	    /* matched keyword via the translation code; or individual	    */
   416: 	    /* handlers may be used for each keyword, ignoring the	    */
   417: 	    /* translation codes.					    */
   418: 
   419: )	/* Returns matching keyword translation code, or 0 if no match	    */
   420: 	/* found or the matching keyword handler returns 0.		    */
   421: 	/********************************************************************/
   422: 
   423: {
   424:     KEYWORD_DEFINITION				/* Current keyword def.	    */
   425: 	    *kwdef;
   426:     int	    kwlen;				/* Length of keyword.	    */
   427:     char    *savestr = aKwStr;			/* Saved string ptr.	    */
   428: 
   429:     if ((kwlen = kwstrlen(aKwStr)) == 0) {
   430: 	return 0;
   431:     }
   432:     else {					/* Scan keyword table for   */
   433: 						/* match.		    */
   434: 	for (kwdef = aKwTable; kwdef_keyword(kwdef) != NULL; kwdef++) {
   435: 	    if (kwdef_cmp(kwdef, aKwStr, kwlen)) {
   436: 
   437: 						/* Match, if handler	    */
   438: 						/* defined, call it with    */
   439: 						/* translation code.	    */
   440: 		if (kwdef_handler(kwdef) != NULL
   441: 		    && !(kwdef_handler(kwdef))(kwdef_code(kwdef))) {
   442: 		    return 0;			/* Abort, handler failed!   */
   443: 		}
   444: 		else {				/* Handler ok (or not	    */
   445: 		    return kwdef_code(kwdef);	/* defined), return code.   */
   446: 		}
   447: 	    }
   448: 	}
   449: 	return 0;				/* No match.		    */
   450:     }
   451: }
END translate_keyword. Go to: Beginning of routine.


   452: 
   453: /*************************************************************************++*/

ROUTINE process_options_file. Go to: Next routine in file; Routines in this file.

   454: int process_options_file(
   455: /* Command line option handler to read an options file, where each line is  */
   456: /* a single command line option, just as if it were parsed from the command */
   457: /* line. Option files may be nested, just be careful of looped files or	    */
   458: /* running out of resources due to excessive nesting. Comments lines and    */
   459: /* trailing comment are allowed.					    */
   460: 
   461:     int	    vKwCode,
   462: 	    /* (READ, BY ADDR):						    */
   463: 	    /* Option keyword translation code, ignored by this routine.    */
   464: 
   465:     char    *aValStr
   466: 	    /* (READ, BY ADDR):						    */
   467: 	    /* Option value string, preceded by equal sign.		    */
   468:     
   469: )	/* Returns status code:						    */
   470: 	/*  1	- Successful processing of this option.			    */
   471: 	/*  0	- Option file name missing, or file cannot be opened.	    */
   472: 	/*****************************************************************--*/
   473: 
   474: {
   475:     FILE    *optfile;			    /* Options file ptr.	    */
   476:     int	    rcount;			    /* Routine name count.	    */
   477:     char    option[512];		    /* Option string buffer.	    */
   478:     char    *optend;			    /* End-of-option ptr.	    */
   479:     char    *optv;			    /* Option buffer ptr.	    */
   480: 
   481: 					    /* Check for equal sign and	    */
   482: 					    /* make sure there is a file.   */
   483:     if (*aValStr++ == CMDLINE_OPTION_SEPARATOR && *aValStr != '\0') {
   484: 	if ((optfile = fopen(aValStr, "r")) != NULL) {
   485: 	
   486: 					    /* For each line, locate	    */
   487: 					    /* beginning of text.	    */
   488: 	    while (fgets(option, sizeof(option), optfile) != NULL) {
   489: 		for (optv = option;
   490: 		    *optv != '\0' && *optv != CMDLINE_OPTION_SWITCH &&
   491: 		    *optv != CMDLINE_OPTION_COMMENT;
   492: 		    optv++);
   493: 					    /* Ignore comment lines. Find   */
   494: 					    /* end of option field and	    */
   495: 					    /* process option.		    */
   496: 		if (*optv != CMDLINE_OPTION_COMMENT) {
   497: 		    for (optend = optv;
   498: 			*optend > ' ' && *optend != CMDLINE_OPTION_COMMENT;
   499: 			optend++);
   500: 		    *optend = '\0';
   501: 		    if (!process_options(1, &optv, 0, mOptionTable)) {
   502: 			printf(
   503: 			    "ERROR: Failure processing option \"%s\" in %s\n",
   504: 			    option, aValStr);
   505: 			fclose(optfile);
   506: 			return 0;
   507: 		    }
   508: 		}
   509: 	    }
   510: 	    fclose(optfile);
   511: 	    return 1;
   512: 	}
   513: 	else {
   514: 	    printf("ERROR: Unable to open options file %s for input\n",
   515: 		aValStr);
   516: 	    return 0;
   517: 	}
   518:     }
   519:     else {
   520: 	printf("ERROR: %coptions option requires options file name\n",
   521: 	    CMDLINE_OPTION_SWITCH);
   522: 	return 0;
   523:     }
   524: }
END process_options_file. Go to: Beginning of routine.


   525: 

END OF FILE TOTAL: 9 routines, 53 Avg Length

Go to: Contents; Previous section; Beginning of section; Next file in section; Previous file in section.