home *** CD-ROM | disk | FTP | other *** search
/ The Developer Connection…ice Driver Kit for OS/2 3 / DEV3-D1.ISO / source / util2src / cfaccess.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-07-24  |  30.4 KB  |  744 lines

  1. /*============================================================================*
  2.  * module: cfaccess.c - Configuration file access subroutines.
  3.  *
  4.  * (C)Copyright IBM Corporation, 1990, 1991, 1992.        Brian E. Yoder
  5.  *
  6.  * This module contains the following externally-available subroutines:
  7.  *
  8.  *      cfopen()        cfsetfile()
  9.  *      cfread()        cfclose()       cfgetbyname()   cfreadfile()
  10.  *      cfstrcmpi()
  11.  *
  12.  * The cfopen() subroutine opens a configuration file.  Only one configuration
  13.  * file at a time may be processed.  If a configuration file is still open
  14.  * from a previous call, cfopen() closes it before it opens the new file.
  15.  *
  16.  * The cfsetfile() subroutine allows you to process a file that is already
  17.  * open (such as stdin).  Again, if a configuration file is already open,
  18.  * cfsetfile() closes it before storing the FILE pointer of the new file.
  19.  *
  20.  * The cfread() subroutine reads the next line from the configuration file
  21.  * that contains tokens. It returns the number of tokens in the line and
  22.  * copies a pointer to an array of pointers to the null-terminated tokens
  23.  * to the calling program.
  24.  *
  25.  * The cfreadfile() subroutine reads the next 'file' line from the configuration
  26.  * file.  It returns a pointer to the name of the file.
  27.  *
  28.  * The cfclose() subroutine closes the configuration file.
  29.  *
  30.  * The cfgetbyname() subroutine searches a configuration file for the first
  31.  * line that begins with a specified token.  It returns the number of tokens
  32.  * in this line and copies two pointers to the calling program: a pointer to
  33.  * the array of token pointers, and one to the first token past the first '='.
  34.  * The cfgetbyname() subroutine calls cfopen() for you, so you cannot use
  35.  * cfsetfile() to specify an already-opened file.
  36.  *
  37.  * The cfline() subroutine returns the current line number within the
  38.  * configuration file.  It can be called after cfread(), cfreadfile(), or
  39.  * cfgetbyname().
  40.  *
  41.  * NOTES:
  42.  *
  43.  * The cfread(), cfreadfile(), and cfgetbyname() subroutines copy
  44.  * pointers to static data that is overwritten with each subsequent call.
  45.  *
  46.  * The cfstrcmpi() subroutine performs a case-insensitive string comparison.
  47.  * It is only needed for AIX -- the C/2 (DOS, OS/2) library has its own.
  48.  *
  49.  * 03/21/90 - Created.
  50.  * 03/26/90 - Initial version, for PS/2 AIX.
  51.  * 05/25/90 - Added cfline() subroutine to return current line number.
  52.  * 07/24/90 - Fixed gettoken() to handle unterminated strings properly.
  53.  * 11/02/90 - Added cfstrcmpi() subroutine.
  54.  * 03/25/91 - Added the cfsetfile() subroutine, to allow processing of stdin.
  55.  *            Also, various comments were updated.
  56.  * 04/03/91 - Ported from AIX to DOS C/2.  Files are opened in text mode.
  57.  * 11/17/91 - Strings may now be enclosed in square brackets as well as quotes.
  58.  *            Comments may begin with a semicolon as well as with a # character.
  59.  *            These changes allow these routines to process clear-text .INI
  60.  *            files that are often used by DOS, OS/2, and MS Windows software.
  61.  * 04/13/92 - Removed a benign nested comment so ccmt won't complain.
  62.  * 07/24/92 - Changed "rt" to "r" for fopen(). C Set/2 doesn't allow "rt".
  63.  *============================================================================*/
  64.  
  65. #include <stdio.h>
  66. #include <stdlib.h>
  67. #include <string.h>
  68. #include <memory.h>
  69. #include <sys/types.h>
  70.  
  71. #include "util.h"
  72.  
  73. /*============================================================================*
  74.  * Function prototypes for subroutines used only by this module
  75.  *============================================================================*/
  76.  
  77. static char *strlwr       ( char * );
  78. static void  parseline    ( void );
  79. static char *gettoken     ( void );
  80.  
  81. /*============================================================================*
  82.  * Static data for this module only
  83.  *============================================================================*/
  84.  
  85. #define LINE     1024               /* Maximum line length */
  86. #define MAX_TOK   128               /* Maximum pointers in tokv array */
  87.  
  88. static FILE   *cfile = NULL;        /* Currently open file */
  89.  
  90. static char   *tnext;               /* Set by parseline(), updated by gettoken() */
  91. static char   *nexttok;             /* Set by parseline(), updated by gettoken() */
  92. static char    tline[LINE+1];       /* Buffer to hold line from file */
  93. static ulong   lineno = 0L;         /* Current line number */
  94.  
  95. static char    eqsign[] = "=";      /* Equal (assignment) '=' sign */
  96.  
  97. static int     tokc;                /* No. of tokens found in line */
  98. static char   *tokv[MAX_TOK+1];     /* Array of pointers to tokens */
  99.  
  100. static int     IsApp = FALSE;       /* Is line an application in an .INI file? */
  101.  
  102. /*============================================================================*
  103.  * cfopen() - Opens a configuration file.
  104.  *
  105.  * PURPOSE:
  106.  *   This subroutine opens a configuration file for reading.
  107.  *
  108.  * REMARKS:
  109.  *   You should use the subroutines provided in this API set for actually
  110.  *   reading data from the configuration file.
  111.  *
  112.  *   If a configuration file is already open, this subroutine closes it
  113.  *   before opening the new file.
  114.  *
  115.  *   When you are done reading the file, you should call the cfclose()
  116.  *   subroutine.
  117.  *
  118.  * RETURNS:
  119.  *   0, if the file was opened successfully.
  120.  *   1, if the file could not be opened.
  121.  *============================================================================*/
  122. int cfopen(cfname)
  123.  
  124. char  *cfname;
  125.  
  126. {
  127.   if (cfile != NULL)                /* If a file is already open: */
  128.      fclose(cfile);                 /*    Close it! */
  129.  
  130.   tokc = 0;                         /* Initialize token count and array to 'none' */
  131.   *tokv = NULL;
  132.  
  133.   lineno = 0L;                      /* Set line number to zero */
  134.   IsApp = FALSE;                    /* Reset IsApp */
  135.  
  136.   cfile = fopen(cfname, "r");       /* Attempt to open the file */
  137.  
  138.   if (cfile == NULL)                /* Return with appropriate success status */
  139.      return(1);
  140.   else
  141.      return(0);
  142. }
  143.  
  144. /*============================================================================*
  145.  * cfsetfile() - Sets the FILE * for an open file.
  146.  *
  147.  * PURPOSE:
  148.  *   This subroutine allows the configuration file access subroutines to
  149.  *   process a file that is already open.  You use this subroutine instead
  150.  *   of the cfopen() subroutine.
  151.  *
  152.  *   Pass this subroutine the FILE pointer for a file that has already been
  153.  *   opened for reading.  You can process standard input by passing the
  154.  *   'stdin' FILE pointer.
  155.  *
  156.  * REMARKS:
  157.  *   You should use the subroutines provided in this API set for actually
  158.  *   reading data from the configuration file.
  159.  *
  160.  *   If a configuration file is already open, this subroutine closes it
  161.  *   before setting the new file.
  162.  *
  163.  *   When you are done reading the file, you should call the cfclose()
  164.  *   subroutine.
  165.  *
  166.  *   This subroutine does not check the file pointer (fp) for validity.
  167.  *   You must be sure that you never pass a NULL or invalid pointer to
  168.  *   this subroutine.
  169.  *
  170.  * RETURNS:
  171.  *   0, always.
  172.  *============================================================================*/
  173. int cfsetfile(fp)
  174.  
  175. FILE  *fp;
  176.  
  177. {
  178.   if (cfile != NULL)                /* If a file is already open: */
  179.      fclose(cfile);                 /*    Close it! */
  180.  
  181.   tokc = 0;                         /* Initialize token count and array to 'none' */
  182.   *tokv = NULL;
  183.  
  184.   lineno = 0L;                      /* Set line number to zero */
  185.   IsApp = FALSE;                    /* Reset IsApp */
  186.  
  187.   cfile = fp;                       /* Save pointer to open file */
  188.   return(0);                        /* Return */
  189. }
  190.  
  191. /*============================================================================*
  192.  * cfline() - Gets current line number within the configuration file.
  193.  *
  194.  * PURPOSE:
  195.  *   This subroutine get the number of the line within the configuration
  196.  *   file that the last access took place.
  197.  *
  198.  * REMARKS:
  199.  *   This subroutine returns zero when called after cfopen() or cfsetfile()
  200.  *   but before any calls to cfread(), cfreadfile(), or cfgetbyname().
  201.  *
  202.  * RETURNS:
  203.  *   The number of the current line within the current configuration file.
  204.  *============================================================================*/
  205. ulong cfline()
  206. {
  207.   return(lineno);                   /* Return current line number */
  208. }
  209.  
  210. /*============================================================================*
  211.  * cfisapp() - Is the current line in the configuration file an application?
  212.  *
  213.  * PURPOSE:
  214.  *   This subroutine checks to see whether or not the current line in the
  215.  *   configuration file is an application in an .INI file.
  216.  *
  217.  *   An .INI file "application" is line whose first token is enclosed within
  218.  *   square brackets, such as:
  219.  *
  220.  *        [Board 0]
  221.  *
  222.  * RETURNS:
  223.  *   TRUE if the current line is an application, or FALSE if it's not.
  224.  *============================================================================*/
  225. int   cfisapp()
  226. {
  227.   return(IsApp);
  228. }
  229.  
  230. /*============================================================================*
  231.  * cfread() - Reads the next information line from the configuration file.
  232.  *
  233.  * PURPOSE:
  234.  *   This subroutine reads the next line that contains one or more tokens.
  235.  *
  236.  * REMARKS:
  237.  *   The pointer returned by this subroutine points to a static data area.
  238.  *   This data area is overwritten by each call to this subroutine or by
  239.  *   each call to the cfreadfile() subroutine.
  240.  *
  241.  * RETURNS:
  242.  *   The number of tokens found in the line.  If the file is not open,
  243.  *   a read error occurred, or we're at the end of the file, then
  244.  *   zero is returned.  Normally, if a zero is returned, it is safe to
  245.  *   assume that the end of the file has been reached.
  246.  *
  247.  *   On entry, 'tokvp' must point to a location that is to contain a pointer
  248.  *   to an array of pointers to token strings.
  249.  *
  250.  *   The array contains pointers to each token found in the line.  A NULL
  251.  *   pointer is stored in the array position just past the last token's
  252.  *   pointer.
  253.  *
  254.  * EXAMPLE:
  255.  *   The calling program can make the following data declarations on the stack:
  256.  *
  257.  *        int     tokc;
  258.  *        char  **tokv;
  259.  *
  260.  *   The calling program can invoke this subroutine as follows:
  261.  *
  262.  *        tokc = cfread(&tokv);
  263.  *
  264.  *   The calling program can then use 'tokc' and 'tokv' just like the
  265.  *   main() subroutine uses 'argc' and 'argv'.
  266.  *============================================================================*/
  267. int cfread(tokvp)
  268.  
  269. char  **tokvp[];                    /* Pointer to pointer to array of token pointers */
  270.  
  271. {
  272.   char  *s;                         /* Returned by fgets() */
  273.  
  274.   /* Copy pointer to token array to caller */
  275.  
  276.   *tokvp = tokv;                    /* Copy pointer to array of token pointers */
  277.  
  278.   /* Be sure file is open */
  279.  
  280.   if (cfile == NULL)                /* If file is not open: */
  281.   {
  282.      tokc = 0;                            /* Set tokc = 'no tokens' */
  283.      *tokv = NULL;                        /* Store no pointers in array */
  284.      return(tokc);                        /* Return number of tokens found */
  285.   }
  286.  
  287.   /* Loop to find next line of file that contains tokens */
  288.  
  289.   for (;;)                          /* Loop reading lines from file: */
  290.   {
  291.      s = fgets(tline, LINE,            /* Get next line from file */
  292.                 cfile);
  293.      if (s == NULL)                    /* If at end of file: */
  294.      {
  295.         tokc = 0;                         /* Initialize token count and array to 'none' */
  296.         *tokv = NULL;
  297.         cfclose();                           /* Close file */
  298.         return(tokc);                        /* Return number of tokens found */
  299.      }
  300.  
  301.      lineno++;                         /* Increment line number */
  302.  
  303.      parseline();                      /* Parse the line */
  304.      if (tokc != 0)                    /* If one or more tokens were found: */
  305.         break;                              /* Break out of loop! */
  306.   }                                 /* End of loop to look for lines with tokens */
  307.  
  308.   return(tokc);                     /* Return number of tokens found */
  309. }
  310.  
  311. /*============================================================================*
  312.  * cfreadfile() - Reads the 'file' line from the configuration file.
  313.  *
  314.  * PURPOSE:
  315.  *   This subroutine reads the next valid line that contains 'file' as its
  316.  *   first token.
  317.  *
  318.  * REMARKS:
  319.  *   The pointer returned by this subroutine points to a static data area.
  320.  *   This data area is overwritten by each call to this subroutine.
  321.  *
  322.  *   Valid lines must be formatted as follows:
  323.  *
  324.  *      file ftag = filename
  325.  *
  326.  *   where: 'file' is a constant, 'ftag' is ignored, and 'filename' is the
  327.  *   name of the file.
  328.  *
  329.  *   Any line not formatted as shown is ignored.
  330.  *
  331.  * RETURNS:
  332.  *   A pointer to the name of the file.  If a NULL pointer is returned,
  333.  *   then it is normally safe to assume that we reached the end of the file.
  334.  *============================================================================*/
  335.  
  336. #define FileTag        "file"       /* String that defines the 'file' tag */
  337. #define FileTokCnt     4            /* Minimum no. of tokens in 'file' entry */
  338. #define FileEqIndex    2            /* Index of '=' token */
  339. #define FileNameIndex  3            /* Index of file name */
  340.  
  341. char *cfreadfile()
  342.  
  343. {
  344.   int    count;                     /* No. of tokens pointers in array */
  345.   char **vector;                    /* Pointer to array of token pointers */
  346.  
  347.   for (;;)                          /* Loop to find the next 'file' entry: */
  348.   {
  349.      count = cfread(&vector);            /* Get next line with tokens */
  350.      if (count == 0)                     /* If at end of file: */
  351.         return(NULL);                         /* Done: return NULL pointer */
  352.  
  353.      if (count < FileTokCnt)             /* Ignore line with too few tokens */
  354.         continue;
  355.  
  356.      if (cfstrcmpi(*vector, FileTag) != 0)  /* Ignore line with wrong tag (1st token) */
  357.         continue;
  358.  
  359.      if (strcmp(vector[FileEqIndex],     /* Ignore line with no '=' in proper place */
  360.                 eqsign) != 0)
  361.         continue;
  362.  
  363.      return(vector[FileNameIndex]);      /* Ok!  Return pointer to file name */
  364.  
  365.   } /* end of for loop to find the next 'file' entry */
  366. }
  367.  
  368. /*============================================================================*
  369.  * cfclose() - Closes the previously opened configuration file.
  370.  *
  371.  * PURPOSE:
  372.  *   This subroutine closes the previously opened configuration file.
  373.  *   If the file is already closed, then no action is taken.
  374.  *
  375.  * REMARKS:
  376.  *   This subroutine should be called whenever you are finished reading
  377.  *   the configuration file.
  378.  *
  379.  * RETURNS:
  380.  *   0, always (for now).
  381.  *============================================================================*/
  382. int cfclose()
  383.  
  384. {
  385.   if (cfile != NULL)                /* If a file is open: */
  386.      fclose(cfile);                 /*    Close it */
  387.  
  388.   cfile = NULL;                     /* Reset file pointer to NULL */
  389.   IsApp = FALSE;                    /* Reset IsApp */
  390.  
  391.   return(0);                        /* For now: always return 0 */
  392. }
  393.  
  394. /*============================================================================*
  395.  * cfgetbyname() - Gets the tokens associated with a specified tag name.
  396.  *
  397.  * PURPOSE:
  398.  *   This subroutine opens the configuration file and searches for the first
  399.  *   line whose first token matches the specified "tagname".  The subroutine
  400.  *   then closes the configuration file and returns pointers to the token
  401.  *   information for the line.
  402.  *
  403.  * REMARKS:
  404.  *   The pointer returned by this subroutine points to a static data area
  405.  *   that is overwritten by each call to one of these subroutines.
  406.  *
  407.  *   Since this subroutine calls cfopen() for you, you cannot use the
  408.  *   cfsetfile() subroutine that allows you to process an already-opened
  409.  *   file such as stdin.
  410.  *
  411.  * RETURNS:
  412.  *   The number of tokens found in the line.  If the file cannot be opened
  413.  *   or no line begins with the specified "tagname", a zero is returned.
  414.  *
  415.  *   This subroutine also copies two additional pointers to the caller:
  416.  *   A pointer to the array of pointers to tokens in the line, and
  417.  *   a pointer to the first token immediately past the first '=' in the
  418.  *   line.  If there is no '=' or if the '=' is the last token in the line,
  419.  *   a NULL pointer is copied to the calling program.
  420.  *
  421.  * EXAMPLE:
  422.  *   The calling program can make the following data declarations on the stack:
  423.  *
  424.  *        int     tokc;
  425.  *        char  **tokv;
  426.  *        char   *value;
  427.  *
  428.  *   The calling program can invoke this subroutine as follows to retrieve the
  429.  *   information for the tag named "temp" in the specified "cfgfile":
  430.  *
  431.  *        tokc = cfgetbyname("cfgfile", "temp", &tokv, &value);
  432.  *
  433.  *   The calling program can then use 'tokc' and 'tokv' just like the
  434.  *   main() subroutine uses 'argc' and 'argv'.
  435.  *============================================================================*/
  436. int cfgetbyname(fname, tagname, tokvp, value)
  437.  
  438. char   *fname;                      /* Pointer to name of configuration file */
  439. char   *tagname;                    /* Pointer to (lowercase!) name of tag */
  440. char  **tokvp[];                    /* Pointer to pointer to array of token pointers */
  441. char  **value;                      /* Pointer to pointer to tag's value */
  442.  
  443. {
  444.   int   rc;                         /* Return code storage */
  445.   char *tagval;                     /* Pointer to tag value */
  446.   int    count;                     /* No. of tokens pointers in array */
  447.   char **vector;                    /* Pointer to array of token pointers */
  448.  
  449.   *tokvp = tokv;                    /* Store pointer to array of token pointers */
  450.  
  451.   rc = cfopen(fname);               /* Open the configuration file */
  452.   if (rc != 0)                      /* If we can't open the file: */
  453.   {
  454.      *value = NULL;                      /* Copy NULL pointer to value */
  455.      tokc = 0;                           /* Initialize token count and array to 'none' */
  456.      *tokv = NULL;
  457.      cfclose();                          /* Close the file */
  458.      return(0);                          /* Return 'tag not found' */
  459.   }
  460.  
  461.   for (;;)                          /* Scan file for 'tagname': */
  462.   {
  463.      count = cfread(&vector);            /* Get next line with tokens */
  464.      if (count == 0)                     /* If at end of file: */
  465.      {
  466.         tokc = 0;                             /* Initialize token count and array to 'none' */
  467.         *tokv = NULL;
  468.         cfclose();                            /* Close the file */
  469.         return(0);                            /* Done: return 'not found' */
  470.      }
  471.  
  472.      if (cfstrcmpi(*vector, tagname) == 0)  /* If we found the tag: */
  473.         break;                                /* Break out! */
  474.   }
  475.  
  476.   for (;;)                          /* Look for first token past first '=': */
  477.   {
  478.      vector++;                           /* Point to next token */
  479.  
  480.      if (*vector == NULL)                /* If at end of array: */
  481.         break;                                /* Break out */
  482.  
  483.      if (strcmp(*vector, eqsign) == 0)   /* If we found a '=': */
  484.      {
  485.         vector++;                             /* Point vector to next token pointer */
  486.         break;                                /* Break out */
  487.      }
  488.   }
  489.  
  490.   cfclose();                        /* Close the configuration file */
  491.  
  492.   *value = *vector;                 /* Copy pointer to tag's value */
  493.   return(count);                    /* Return no. of tokens in line */
  494. }
  495.  
  496. /*============================================================================*
  497.  * strlwr() - Converts uppercase characters in a string to lowercase.
  498.  *
  499.  * PURPOSE:
  500.  *   Converts any uppercase characters in 'str' to lowercase.  Other
  501.  *   characters are not affected.
  502.  *
  503.  * RETURNS:
  504.  *   Pointer to 'str'.
  505.  *============================================================================*/
  506.  
  507. static char *strlwr(str)
  508.  
  509. char *str;                   /* Pointer to string */
  510.  
  511. {
  512.   while (*str != '\0')       /* Until we reach the end of the string: */
  513.   {
  514.      *str = tolower(*str);        /* Convert character to lowercase */
  515.      str++;                       /* Point 'str' to next character */
  516.   }
  517.  
  518.   return(str);
  519. }
  520.  
  521. /*============================================================================*
  522.  * parseline() - Parses a line into individual tokens.
  523.  *
  524.  * PURPOSE:
  525.  *   Parses the line pointed to by 'tline' and breaks it up into tokens.
  526.  *
  527.  * ON ENTRY: The following variables must be set by the caller:
  528.  *   tline = A complete line from the file.
  529.  *
  530.  * ON EXIT: The following variables are set by this subroutine:
  531.  *   tokc  = Number of tokens found in the line (0 = none).
  532.  *   tokv  = Array of pointers to the tokens.  A NULL pointer is stored
  533.  *           after the last token pointer.  The first token is converted
  534.  *           to lowercase.
  535.  *   tline = Modified during the parsing process.
  536.  *   IsApp = Is the current line an .INI file application?  This variable is
  537.  *           initialized to FALSE and may be set to TRUE by gettoken().
  538.  *============================================================================*/
  539.  
  540. static void parseline()
  541.  
  542. {
  543.   char **next;                      /* Pointer to next token in array */
  544.   char  *token;                     /* Pointer to a token */
  545.  
  546.   tokc = 0;                         /* Init: Set tokc = 'no tokens', and */
  547.   *tokv = NULL;                     /* Store no pointers in array */
  548.  
  549.   IsApp = FALSE;                    /* Initialize: line is not an .INI app */
  550.  
  551.   tnext = tline;                    /* Point 'tnext' to start of line buffer */
  552.   nexttok = NULL;                   /* NULL this pointer: see gettoken() */
  553.  
  554.   next = tokv;                      /* Point to first token pointer in array */
  555.  
  556.   while (tokc < MAX_TOK)            /* For each token found: */
  557.   {
  558.      token = gettoken();                /* Get the token */
  559.      if (token == NULL) break;          /* If no more: done */
  560.  
  561.      *next = token;                     /* Store pointer to it in array */
  562.      next++;                            /* Point to next entry in array */
  563.      tokc++;                            /* Update token count */
  564.   }
  565.  
  566.   *next = NULL;                     /* Terminate array with null pointer */
  567.  
  568. /*if (*tokv != NULL)  11/2/90*/     /* If first token pointer is not NULL: */
  569. /*   strlwr(*tokv);   (bey)  */          /* Convert token to lowercase */
  570.  
  571.   return;                           /* Return */
  572. }
  573.  
  574. /*============================================================================*
  575.  * gettoken() - Gets the next token from the input line.
  576.  *
  577.  * PURPOSE:
  578.  *   This subroutine starts searching at 'tnext' for the next token.
  579.  *
  580.  *   A token may consist of:
  581.  *      A single equal '=' sign, or:
  582.  *      One or more contiguous non-whitespace characters, or
  583.  *      A series of characters enclosed by either single or double quotes.
  584.  *      A series of characters enclosed in square brackets.
  585.  *
  586.  * RETURNS:
  587.  *   Pointer to the token.  If no token was found, then a NULL pointer
  588.  *   is returned.
  589.  *
  590.  * DATA USED:
  591.  *   tnext = Pointer to next character in 'tline' to scan.
  592.  *           The caller must initially set tnext = tline.  This subroutine
  593.  *           updates 'tnext' as it scans for tokens.
  594.  *   nexttok = Pointer to the next token, or NULL if there is none.
  595.  *           This subroutine terminates a nonstring token with a null character.
  596.  *           If the terminating character is a '=' instead of whitespace,
  597.  *           then 'nexttok' is set with a pointer to "=", so we don't
  598.  *           forget about it!
  599.  *   tokc  = Number of tokens found so far.
  600.  *   IsApp = If we're processing the first token (tokc==0) and it is delimited
  601.  *           by square brackets, then we set the IsApp flag to TRUE.
  602.  *============================================================================*/
  603.  
  604. static char *gettoken()
  605.  
  606. {
  607.   char  *starttok;                  /* Pointer to start of token */
  608.   char   ch;                        /* Character from the line */
  609.   char   strch;                     /* String delimiter character */
  610.  
  611.   if (nexttok != NULL)              /* If we have a previously stored token: */
  612.   {
  613.      starttok = nexttok;                 /* Store pointer to it */
  614.      nexttok = NULL;                     /* Then null it */
  615.      return(starttok);                   /* Return pointer to token */
  616.   }
  617.  
  618.   for (;;)                          /* To get the next token (if any), loop */
  619.   {                                 /* getting characters, stop at the end of */
  620.      ch = *tnext;                   /* line or comment, and skip leading white- */
  621.      switch (ch)                    /* space */
  622.      {
  623.                                     /*----------------------------------------*/
  624.         case '\0':                  /* If end of line buffer, or comment */
  625.         case '#':
  626.         case ';':
  627.            return(NULL);                 /* Done: no more tokens */
  628.            break;
  629.                                     /*----------------------------------------*/
  630.         case ' ':                   /* Skip over whitespace characters */
  631.         case '\t':
  632.         case '\n':
  633.         case '\f':
  634.            tnext++;                      /* Point to next character in line */
  635.            break;
  636.                                     /*----------------------------------------*/
  637.         case '=':                   /* Special case: '=' sign found: */
  638.            tnext++;                      /* Bump pointer past the '=' */
  639.            return(eqsign);               /* Return pointer to token string */
  640.            break;
  641.                                     /*----------------------------------------*/
  642.         case '[':                   /* Left bracket found: Change it to a right */
  643.            ch = ']';                     /* bracket and fall thru so it will be */
  644.            if (tokc == 0)                /* stored as the string end delimiter */
  645.               IsApp = TRUE;              /* If 1st token, it's an .INI "application" */
  646.         case '\'':                  /* Single or double quote found: */
  647.         case '"':
  648.            strch = ch;                   /* Store the string end delimiter char */
  649.            tnext++;                      /* Point tnext to 1st char of string */
  650.            starttok = tnext;             /* And store pointer */
  651.            for (;;)                      /* Loop to find end of string: */
  652.            {
  653.               ch = *tnext;                    /* Get next character */
  654.  
  655.               if ((ch == strch) ||            /* If ch is string delimiter */
  656.                   (ch == '\n'))               /* or the newline char: */
  657.               {
  658.                  *tnext = '\0';                  /* Null the string term char */
  659.                  tnext++;                        /* Set up for next call */
  660.                  return(starttok);               /* Return pointer to token */
  661.               }
  662.  
  663.               if (ch == '\0')                 /* If at the end of input:  */
  664.                  return(starttok);            /* string isn't terminated: return */
  665.  
  666.               tnext++;                        /* Point to next char and repeat */
  667.            }                             /* End of loop to get string token */
  668.            break;
  669.                                     /*----------------------------------------*/
  670.         default:                    /* Beginning of nonstring token found */
  671.            starttok = tnext;             /* Store pointer to start of token */
  672.            for (;;)                      /* Loop to find end of token: */
  673.            {
  674.               tnext++;                        /* Point to next character */
  675.               ch = *tnext;                    /* Get the character */
  676.               switch (ch)
  677.               {
  678.                  case ' ':                         /* If whitespace: */
  679.                  case '\t':
  680.                  case '\n':
  681.                  case '\f':
  682.                     *tnext = '\0';                      /* Null the character */
  683.                     tnext++;                            /* Set up for next call */
  684.                     return(starttok);                   /* Return pointer to token */
  685.                     break;
  686.  
  687.                  case '\0':                        /* If end of line: */
  688.                     return(starttok);                   /* Return pointer to token */
  689.                     break;
  690.  
  691.                  case '=':                         /* If '=' sign: it's a token too: */
  692.                     *tnext = '\0';                      /* Terminate the token */
  693.                     tnext++;                            /* Set up for next call */
  694.                     nexttok = eqsign;                   /* Let next iteration know about */
  695.                     return(starttok);                   /* the = token */
  696.                     break;
  697.               }
  698.            }                             /* End of loop to get non-string token */
  699.            break;
  700.      } /* end of switch (ch) statement */
  701.   } /* end of for loop to get the next token */
  702. } /* end of gettoken() */
  703.  
  704. /*============================================================================*
  705.  * cfstrcmpi() - Compares two strings (case-insensitive).
  706.  *
  707.  * PURPOSE:
  708.  *   This subroutine compares two strings without regard for case.  Therefore,
  709.  *   the following strings would compare as equal:
  710.  *          "AbcDef"  "abcdef"
  711.  *
  712.  * RETURNS:
  713.  *   0, if the two strings compare as equal.
  714.  *   1, if they are different.
  715.  *============================================================================*/
  716. int cfstrcmpi(s1, s2)
  717.  
  718. char  *s1;
  719. char  *s2;
  720.  
  721. {
  722.    register char c1, c2;            /* Storage for characters */
  723.  
  724.    for (;;)                         /* Loop to compare strings: */
  725.    {
  726.       c1 = *s1;                          /* Get next character from each string */
  727.       c2 = *s2;
  728.  
  729.       if ((c1 == 0) && (c2 == 0))        /* If at end of both: */
  730.          return(0);                           /* Strings are the same */
  731.  
  732.       if (c1 == 0) return(1);            /* If at end of only one string: */
  733.       if (c2 == 0) return(1);                 /* Strings are different */
  734.  
  735.       c1 = tolower(c1);                  /* Convert characters to lowercase */
  736.       c2 = tolower(c2);
  737.  
  738.       if (c2 != c1) return(1);           /* Return if strings are different */
  739.  
  740.       s1++;                              /* Bump pointers to next character */
  741.       s2++;                              /* in each string */
  742.    }
  743. }
  744.