## root/clib/parse.c

/* [<][>][^][v][top][bottom][index][help] */

## DEFINITIONS

This source file includes following definitions.

1. trim
2. word
3. ll_word
4. wordlen
5. ll_wordlen
6. indxword
7. ll_indxword
8. words
9. ll_words
10. command_words
11. command_word
12. command_indxword
13. command_wordlen
14. sub_string
15. getyn
16. command_worddel
17. worddel
18. ll_worddel
19. ll_wordput
20. num
21. ucase
22. parse_extend
23. initstring
24. inittstring
25. initqstring
26. delbytes
27. qatoi
28. qatol
29. stricmp
30. strnicmp
31. logic_string_2_flag
32. flag_2_logic_string
33. pmatch
34. lpad
35. rpad
36. strip_space

    /* General parse/string module.
       Rick Smereka, Copyright (C) 1992-2005.

       This program is free software; you can redistribute it and/or modify
       it under the terms of the GNU General Public License as published by
       the Free Software Foundation; either version 2 of the License, or
       (at your option) any later version.

       This program is distributed in the hope that it will be useful,
       but WITHOUT ANY WARRANTY; without even the implied warranty of
       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       GNU General Public License for more details.

       You should have received a copy of the GNU General Public License
       along with this program; if not, get a copy via the Internet at
       http://gnu.org/copyleft/gpl.html or write to the Free Software
       Foundation, Inc., 59 Temple Place, Suite 330, Boston,
       MA 02111-1307 USA

       You can contact the author via email at rsmereka@future-lab.com

       Original version for DOS, Jan/92, Rick Smereka

       Added 'command' functions. Jan/97, Rick Smereka

       Ported to QNX V4.23a and added 'initstring' and
       'inittstring'. Jan/97, Rick Smereka

       Modified function 'll_word' to remove a possible
       uncontrolled memory access when ANY word in the
       string exceeds the allocated length of the output
       word. Added functions 'command_indxword' and
       'command_wordlen'. Re-wrote function 'command_word'
       to use new functions. Re-wrote function 'll_wordput'
       to remove slow speed. Nov/97, Rick Smereka

       Added function 'sub_string' from old C library. Feb/98,
       Rick Smereka

       Ported to 32bit Windows under CodeWarrior. Added
       functions 'stricmp' and 'strnicmp' as these were
       missing from the CodeWarrior library. Dec/98,
       Rick Smereka

       Changed 'getyn' function to use the function 'gets' instead
       of 'getche' since some platforms do not have this function.
       Ported to HP-UX under GNU C 2.8.1. Jan/99, Rick Smereka

       Added function 'is_valid_logic_flag'. Mar/99, Rick Smereka

       Ported to Red Hat Linux 5.2, Jul/99, Rick Smereka

       Added function 'isip'. Oct/99, Rick Smereka

       Fixed bug in 'command_indxword' which returned the wrong value
       if the first character in any word (except the first) was a
       quote. Nov/99, Rick Smereka

       Added function 'initqstring'. Mar/2000, Rick Smereka

       Changed function 'getyn' to use 'scanf' instead of 'gets'
       May/2000, Rick Smereka

       Removed the function 'isip' and placed it into the new
       TCP/IP utility library 'ip.c'. Jul/2000, Rick Smereka

       Changed functions 'll_indxword' and 'll_wordlen' to call
       'inittstring' only if the word delimiter is a space.  Added
       function 'pmatch'.  Feb/2001, Rick Smereka

       Added function 'get_time'. Oct/2001, Rick Smereka

       Fixed bug in 'll_wordput' which sometimes returned an
       empty string. Dec/2001, Rick Smereka

       Added functions 'lpad' and 'rpad'. Apr/2002,
       Rick Smereka

       Moved function 'get_time' to the module 'datime.c'.
       May/2002, Rick Smereka

       Added function 'strip_space'. Jul/2002, Rick Smereka

       Ported to Debian Linux. Nov/2002, Rick Smereka

       Fixed bug in functions 'lpad' and 'rpad' that returned
       an empty string if no padding was necessary. Re-wrote
       function 'll_worddel' to use function 'delbytes' to
       delete a portion of the string. Mar/2003, Rick Smereka

       Changed function 'getyn' to use 'fgets' instead of 'scanf'.
       May/2004, Rick Smereka

       Fixed logic error in the function 'rpad'. Dec/2004,
       Rick Smereka

       Plugged memory leak in 'll_worddel' (old 'malloc' call).
       Feb/2005, Rick Smereka */

    #include "stdhead.h"

    void trim(char *instr, char *outstr)
    {
       /* Trim a string by removing all spaces from the beginning
          and the end plus remove all extra spaces between words.
          'Outstr' must be a statically allocated string large
          enough to hold the output string. */

       int i, len, pos, done, outpos;
       char *tmp, *tmp2;

       len = strlen(instr);
       outstr[0] = EOS;

       if (!len)
          return;

       if ((tmp = initstring(instr)) == NULL)
          return;

       if ((tmp2 = (char *)malloc(len + 1)) == NULL)
          {
          free(tmp);
          return;
          }

       /* get position of first non-blank character */

       for(i = 0, pos = -1, done = FALSE; i < len && !done; i++)
          if (tmp[i] != ' ')
             {
             pos = i;
             done = TRUE;
             }

       /* check for all blank string */

       if (pos == -1)
          {
          free(tmp);
          free(tmp2);
          return;
          }

       i = pos;
       outpos = 0;
       tmp2[0] = EOS;

       while(i < len)
          {
          while(tmp[i] != ' '  && i < len)
             {
             tmp2[outpos++] = tmp[i];
             i++;
             }

          tmp2[outpos++] = ' ';

          if (i < len)
             while(instr[i] == ' ' && i < len)
                i++;
          }

       tmp2[outpos - 1] = EOS;
       strcpy(outstr, tmp2);
       free(tmp);
       free(tmp2);
    }

    int word(char *instr, char *outstr, int which)
    {
       /* Extract the 'which' space delimited word from 'instr'. */

       return(ll_word(instr, outstr, which, ' '));
    }

    int ll_word(char *instr, char *outstr, int which, char delim)
    {
       /* Extract the 'which' word of 'instr' and return it in 'outstr'.
          Each word is delimited by 'delim'
          'Outstr' must be a statically allocated string with enough
          length to accomidate the word. Function returns TRUE on
          success, FALSE otherwise. */

       int len;
       int pos;

       len = strlen(instr);
       outstr[0] = EOS;

       if (!len)
          return(FALSE);

       if (which <= 0 || which > ll_words(instr, delim))
          return(FALSE);

       pos = ll_indxword(instr, which, delim);
       len = ll_wordlen(instr, which, delim);
       strncpy(outstr, &instr[pos], len);
       outstr[len] = EOS;
       return(TRUE);
    }

    int wordlen(char *instr, int which)
    {
       /* Get the length of space delimited word 'which' in
          'instr'. Function returns length upon success,
          -1 otherwise. */

       return(ll_wordlen(instr, which, ' '));
    }

    int ll_wordlen(char *instr, int which, char delim)
    {
       /* Get the length of 'delim' delimited word 'which'
          in 'instr'. Functions returns length upon sucess,
          -1 otherwise. */

       char *tmpstr;
       int nwords;
       int len;
       int wrdlen;

       if ((len = strlen(instr)) == 0)
          return(-1);

       if (delim == ' ')
          {
          if ((tmpstr = inittstring(instr)) == NULL)
             return(-1);
          }
       else
          if ((tmpstr = initstring(instr)) == NULL)
             return(-1);

       len = strlen(tmpstr);
       nwords = ll_words(tmpstr, delim);

       if (!nwords)
          {
          free(tmpstr);
          return(-1);
          }

       if (which <= 0 || which > nwords)
          {
          free(tmpstr);
          return(-1);
          }

       if (which == nwords)
          wrdlen = len - ll_indxword(tmpstr, which, delim);
       else
          wrdlen = ll_indxword(tmpstr, which + 1, delim) -
                   (ll_indxword(tmpstr, which, delim) + 1);

       free(tmpstr);
       return(wrdlen);
    }

    int indxword(char *instr, int which)
    {
       /* Return the index of word 'which' within 'instr'. */

       return(ll_indxword(instr, which, ' '));
    }

    int ll_indxword(char *instr, int which, char delim)
    {
       /* Return the index of word 'which' within 'instr'
          delimited by 'delim'. Function returns the index
          on success, -1 otherwise. */

       int i, len, nwords, index;
       char *tmp;

       len = strlen(instr);

       if (!len)
          return(-1);

       if (delim == ' ')
          {
          if ((tmp = inittstring(instr)) == NULL)
             return(-1);
          }
       else
          if ((tmp = initstring(instr)) == NULL)
             return(-1);

       if (which <= 0 || which > ll_words(tmp, delim))
          {
          free(tmp);
          return(-1);
          }

       len = strlen(tmp);

       if (!len)
          {
          free(tmp);
          return(-1);
          }

       i = 0;
       nwords = 1;

       while(i <= len)
          {
          index = i;

          if (nwords == which)
             {
             free(tmp);
             return(index);
             }

          while(tmp[i] != delim && i <= len)
             i++;

          nwords++;
          i++;
          }

       free(tmp);
       return(-1);
    }

    int words(char *str)
    {
       /* Count the number of space delimited words in a string. */

       return(ll_words(str, ' '));
    }

    int ll_words(char *str, char delim)
    {
       /* Count the number of 'delim' delimited words in a string. */

       int i, len, nwords;
       char *tmp;

       len = strlen(str);

       if (!len)
          return(0);

       if ((tmp = initstring(str)) == NULL)
          return(0);

       /* trim string if delimiter is a space */

       if (delim == ' ')
          {
          trim(str, tmp);
          len = strlen(tmp);

          if (!len)
             {
             free(tmp);
             return(FALSE);
             }
          }

       /* count instances */

       for(i = 0, nwords = 0; i < len; i++)
          if (tmp[i] == delim)
             nwords++;

       free(tmp);
       return(nwords + 1);
    }

    int command_words(char *p_string)
    {
       /* Count the number of 'command' words in a string. A
          command word can be either a space delimited word or
          an entire paragraph delimited by matching quotes. Function
          returns the number of command words found. */

       char *t_string;
       char ch;
       char prev_ch;
       char delim;
       int len;
       int nwords;
       int pos;
       int found;

       if (!strlen(p_string))
          return(FALSE);

       if ((t_string = initstring(p_string)) == NULL)
          return(FALSE);

       len = strlen(t_string);
       nwords = 0;
       pos = 0;

       /* loop through all characters in string looking for delimiters */

       do
          {
          ch = t_string[pos];

          switch(ch)
             {
             case ' ':
                /* a space is an unconditional word */

                nwords++;
                pos++;
                break;

             case '\'':
             case '\"':
                if (pos > 0)
                   {
                   prev_ch = t_string[pos - 1];

                   if (prev_ch != ' ' && prev_ch != '\"' && prev_ch != '\'')
                      nwords++;
                   }

                delim = ch;
                found = FALSE;
                pos++;

                /* find matching quote */

                do
                   {
                   ch = t_string[pos];

                   if (delim == ch)
                      {
                      if (t_string[pos + 1] == ' ')
                         pos += 2;
                      else
                         pos++;

                      /* add another word as long as the quote is
                         not the last character */

                      if (pos < len)
                         nwords++;

                      found = TRUE;
                      }
                   else
                      pos++;
                   }
                while(!found && pos <= len);

                break;

             default:
                pos++;
             };
          }
       while(pos <= len);

       free(t_string);
       return(nwords + 1);
    }

    int command_word(char *instr, char *o_string, int which)
    {
       /* Parse a string and extract the which command word of
          'instr'. Output word will be placed into the
          string 'o_string' which already must have been
          allocated. Function returns 'TRUE' upon success,
          'FALSE' otherwise. */

       int len, pos;

       o_string[0] = EOS;

       if (which <= 0 || which > command_words(instr))
          return(FALSE);

       pos = command_indxword(instr, which);
       len = command_wordlen(instr, which);
       strncpy(o_string, &instr[pos], len);
       o_string[len] = EOS;
       return(TRUE);
    }

    int command_indxword(char *instr, int which)
    {
       /* Return the index of word 'which' within 'instr'
          command delimited. Function returns the index
          on success, -1 otherwise. */

       int pos, len, nword, done;
       int found;
       char ch, delim, prev_ch;

       len = strlen(instr);

       if (!len)
          return(-1);

       if (which <= 0 || which > command_words(instr))
          return(-1);

       pos = 0;
       nword = 1;
       done = FALSE;

       /* loop through all characters in string looking for delimiters */

       do
          {
          /* handle first word requested */

          if (nword == which)
             {
             if (instr[pos] == '\'' || instr[pos] == '\"')
                pos++;

             done = TRUE;
             continue;
             }

          ch = instr[pos];

          switch(ch)
             {
             case ' ':
                /* a space is an unconditional word */

                /* printf("ll_word:found space pos=%d,nword=%d\n", pos,
                          nword + 1); */
                nword++;
                pos++;

                if (nword == which)
                   done = TRUE;

                /* move position if the next character is a quote */

                if (done && pos < len)
                   if (instr[pos] == '\'' || instr[pos] == '\"')
                      pos++;

                break;

             case '\'':
             case '\"':
                /* printf("ll_word:found %c pos=%d\n", ch, pos); */

                /* if quote is to the right of a non-delimiter, add one word */

                if (pos > 0)
                   {
                   prev_ch = instr[pos - 1];

                   if (prev_ch != ' ' && prev_ch != '\"' && prev_ch != '\'')
                      {
                      nword++;
                      /* printf("ll_word:quote1, nword=%d\n", nword); */

                      if (nword == which)
                         {
                         done = TRUE;
                         /* printf("ll_word:quote1,done=TRUE\n"); */
                         break;
                         }
                      }
                   }

                delim = ch;
                found = FALSE;
                pos++;

                /* find matching quote */

                do
                   {
                   ch = instr[pos];

                   if (delim == ch)
                      {
                      if (instr[pos + 1] == ' ')
                         pos += 2;
                      else
                         pos++;

                      if (pos < len)
                         {
                         nword++;

                         if (nword == which)
                            {
                            done = TRUE;

                            if (instr[pos] == '\'' || instr[pos] == '"')
                               pos++;
                            }
                         }

                      /* printf("ll_word:match quote,pos=%d,nword=%d,done=%d\n",
                             pos, nword,done); */
                      found = TRUE;
                      }
                   else
                      pos++;
                   }
                while(!found && pos <= len);

                break;

             default:
                pos++;
                break;
             }
          }
       while(pos <= len && !done);

       if (done)
          return(pos);

       return(-1);
    }

    int command_wordlen(char *instr, int which)
    {
       /* Get and return the length of the 'which' command word
          in the string 'instr'. Function returns the word length
          upon success (which may be zero), -1 otherwise. */

       int indx, i, len, outlen;
       char delim;

       if (which <= 0 || which > command_words(instr))
          return(-1);

       indx = command_indxword(instr, which);
       len = strlen(instr);

       /* set delimiter */

       if (indx == 0)
          delim = ' ';
       else
          delim = instr[indx - 1];

       /* loop looking for the next delimiter or end of the string */

       for(i = indx, outlen = 0; i < len; i++, outlen++)
          if (instr[i] == delim)
             break;

       return(outlen);
    }

    int sub_string(char *string, char *sub)
    {
       /* Find the occurrance of 'sub' within 'string.' Returns
          a numeric index if found, -1 otherwise. */

       int i, j, k;

       for(i = 0; string[i] != EOS; i++)
          {
          for(j = i, k = 0; sub[k] == string[j] && sub[k] != EOS; j++, k++)
             ;

          if (sub[k] == EOS)
             return(i);
          }

       return(EOF);
    }

    int getyn(char *prompt)
    {
       /* Get a 'y'es or 'n'o answer from the user. Function
          returns 'TRUE' for 'y' and 'FALSE' for 'n'. */

       char ch[10];
       int ret;
       int done = FALSE;

       while(!done)
          {
          ch[0] = EOS;
          printf("%s?(y/n)", prompt);
          fgets(ch, 9, stdin);

          if (ch[0] != 'y' && ch[0] != 'Y' && ch[0] != 'n' && ch[0] != 'N')
             printf("please respond with 'y' or 'n'\n");
          else
             if (ch[0] == 'y' || ch[0] == 'Y')
                {
                ret = TRUE;
                done = TRUE;
                }
             else
                {
                ret = FALSE;
                done = TRUE;
                }
          }

       return(ret);
    }

    int command_worddel(char *instr, char *outstr, int which)
    {
       /* Delete the 'which' command word of 'instr'.
          Upon success, 'outstr' will be loaded with the new string.
          Function returns 'TRUE' upon success, 'FALSE' otherwise. */

       char *tmpout;
       char *tmpwrd;
       char quote;
       int nwords, nwords_out;
       int len;
       int pos;
       int i;

       outstr[0] = EOS;

       if (instr == NULL || outstr == NULL || which <= 0)
          return(FALSE);

       len = strlen(instr);

       if (!len)
          return(FALSE);

       if ((tmpout = (char *)malloc(len + 1)) == NULL)
          return(FALSE);

       if ((tmpwrd = (char *)malloc(len + 1)) == NULL)
          {
          free(tmpout);
          return(FALSE);
          }

       nwords = command_words(instr);
       nwords_out = 0;
       tmpwrd[0] = tmpout[0] = EOS;

       if (which > nwords)
          {
          free(tmpout);
          free(tmpwrd);
          return(FALSE);
          }

       /* loop to copy all words except the one to delete */

       for(i = 1; i <= nwords; i++)
          {
          if (i != which)
             {
             if (!command_word(instr, tmpwrd, i))
                {
                free(tmpout);
                free(tmpwrd);
                return(FALSE);
                }

             len = strlen(tmpout);
             pos = len + strlen(tmpwrd);  /* calc new end of string */

             /* if word contains multiple space delimited words,
                put the quotes back */

             if (words(tmpwrd) > 1)
                {
                /* decide which quotes to use based on if there
                   are any existing quotes */

                if (strchr(tmpwrd, '\''))
                   quote = '\"';
                else
                   quote = '\'';

                tmpout[len++] = quote;
                tmpout[len] = EOS;
                strcat(tmpout, tmpwrd);
                pos = len + strlen(tmpwrd);
                tmpout[pos++] = quote;
                tmpout[pos] = EOS;
                }
             else
                strcat(tmpout, tmpwrd);

             nwords_out++;

             /* concatenate the output string with a delimiter
                unless we are on the last word to be output */

             if (nwords_out < (nwords - 1))
                {
                strcat(tmpout, " ");
                tmpout[pos + 1] = EOS;
                }
             else
                tmpout[pos] = EOS;

             /* special logic to break if we are done before
                the natual end of the loop, this should occurr
                only if the last word is being deleted */

             if (nwords_out == (nwords - 1))
                break;
             }
          }

       strcpy(outstr, tmpout);
       free(tmpout);
       free(tmpwrd);
       return(TRUE);
    }

    int worddel(char *instr, char *outstr, int which)
    {
       /* Delete the 'which' space delimited word of 'instr'
          and place new string in 'outstr' which must be
          already allocated. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       return(ll_worddel(instr, outstr, which, ' '));
    }

    int ll_worddel(char *instr, char *outstr, int which, char delim)
    {
       /* Delete the 'which' word of 'instr' delimited by 'delim'.
          Upon success, 'outstr' will be loaded with the new string.
          Function returns 'TRUE' upon success, 'FALSE' otherwise. */

       int nwords, pos, num_bytes;

       if (instr == (char *)NULL || !strlen(instr) || outstr == (char *)NULL ||
           which <= 0)
          return(FALSE);

       outstr[0] = EOS;
       nwords = ll_words(instr, delim);

       if (which > nwords)
          return(FALSE);

       pos = ll_indxword(instr, which, delim);

       if (nwords == which)
          {
          // 'delbytes' uses zero len to indicate the rest of the string

          num_bytes = 0;

          /* if there is more than one word and we are deleting the last,
             back up one byte to remove the delimiter */

          if (nwords > 1)
             pos--;
          }
       else
          num_bytes = ll_indxword(instr, which + 1, delim) - pos;

       if (!delbytes(instr, outstr, pos, num_bytes))
          return(FALSE);

       return(TRUE);
    }

    int ll_wordput(char *instr, char *outstr, char *wdata, int which, char delim)
    {
       /* Place the word 'wdata' into 'instr' as the 'which' word. If the
          word already exists, it will be replaced, if it does not exist, the
          string will be extended out to the required word before copying.
          Each word is delimited by 'delim'. The new string will be returned in
          'outstr' upon success. 'outstr' must be already allocated large
          enough to hold the result string. Function returns 'TRUE' upon
          success, 'FALSE' otherwise. */

       char *new_str, *before, *after, *tmp;
       int existing_rec_len;
       int new_rec_len;
       int new_field_len;
       int existing_field_len;
       int nwords;
       int len;
       int indx;
       int process;

       if (instr == NULL || outstr == NULL || which <= 0)
          return(FALSE);

       outstr[0] = EOS;

       if (wdata == NULL)
          return(FALSE);

       len = strlen(instr);
       nwords = ll_words(instr, delim);

       /* create a copy of input (if present) or a new string
          large enough for the word plus the number of word
          delimiters plus a little extra */

       if (!len)
          {
          if ((new_str = (char *)malloc(strlen(wdata) + which + 200)) == NULL)
             return(FALSE);

          new_str[0] = EOS;
          }
       else
          if ((new_str = initstring(instr)) == NULL)
             return(FALSE);

       /* extend string out if required */

       if (which > nwords)
          {
          if ((tmp = (char *)malloc(len + strlen(wdata) + which + 200)) == NULL)
             {
             free(new_str);
             return(FALSE);
             }

          tmp[0] = EOS;

          if (!parse_extend(new_str, tmp, which, delim))
             {
             free(new_str);
             free(tmp);
             return(FALSE);
             }

          free(new_str);
          new_str = initstring(tmp);
          len = strlen(new_str);
          free(tmp);
          nwords = which;
          }

       if (!len)
          existing_field_len = existing_rec_len = 0;
       else
          {
          existing_field_len = ll_wordlen(new_str, which, delim);
          existing_rec_len = len;
          }

       new_field_len = strlen(wdata);

       /* if existing and new words are length zero, nothing more to do */

       if (new_field_len == 0 && existing_field_len == 0)
          {
          strcpy(outstr, new_str);
          free(new_str);
          return(TRUE);
          }

       /* if strings are equal, place new data over top of old */

       if (existing_field_len == new_field_len)
          {
          indx = ll_indxword(new_str, which, delim);
          memcpy(&new_str[indx], wdata, existing_field_len);
          strcpy(outstr, new_str);
          free(new_str);
          return(TRUE);
          }

       /* compute new length */

       if (new_field_len > existing_field_len)
          new_rec_len = existing_rec_len + (new_field_len -
                        existing_field_len);
       else
          new_rec_len = existing_rec_len - (existing_field_len -
                        new_field_len);

       /* 'tmp' will contain the result string */

       if ((tmp = (char *)malloc(new_rec_len + 1)) == NULL)
          {
          free(new_str);
          return(FALSE);
          }

       /* 'before' is all the data before the target word */

       if ((before = (char *)malloc(new_rec_len + 1)) == NULL)
          {
          free(tmp);
          free(new_str);
          return(FALSE);
          }

       /* 'after' is all the data after the target word */

       if ((after = (char *)malloc(new_rec_len + 1)) == NULL)
          {
          free(tmp);
          free(before);
          free(new_str);
          return(FALSE);
          }

       process = TRUE;

       /* figure out and copy before and after data */

       /* handle first word in string */

       if (which == 1)
          {
          before[0] = EOS;

          if (nwords == which)
             after[0] = EOS;
          else
             {
             /* load 'after' from start of second word to end (no delimiters) */

             indx = ll_indxword(new_str, 2, delim);
             strncpy(after, &new_str[indx], len - indx + 1);
             after[len - indx + 1] = EOS;
             }

          process = FALSE;
          }

       /* handle last word in string */

       if (process && which == nwords)
          {
          /* load 'before' from start to delimiter just before 'which' */

          indx = ll_indxword(new_str, which, delim);
          strncpy(before, new_str, indx);
          before[indx] = EOS;
          after[0] = EOS;
          process = FALSE;
          }

       /* handle 'which' in the middle of the string */

       if (process)
          {
          /* load 'before' from start to delimiter just before 'which' */

          indx = ll_indxword(new_str, which, delim);
          strncpy(before, new_str, indx);
          before[indx] = EOS;

          /* load 'after' from start of word after 'which' to the end
             (no delimiters) */

          indx = ll_indxword(new_str, which + 1, delim);
          strncpy(after, &new_str[indx], len - indx + 1);
          after[len - indx + 1] = EOS;
          }

       /* build final string from three parts */

       tmp[0] = EOS;
       len = 0;

       /* part one, 'before' string */

       if (strlen(before))
          {
          strcpy(tmp, before);
          len = strlen(tmp);
          }

       /* part two, new word data */

       strcat(tmp, wdata);
       tmp[len + new_field_len] = EOS;
       len = strlen(tmp);

       /* part three, 'after' string */

       if (strlen(after))
          {
          tmp[len] = delim;
          tmp[len + 1] = EOS;
          len++;
          strcat(tmp, after);
          tmp[len + strlen(after)] = EOS;
          }

       strcpy(outstr, tmp);
       free(new_str);
       free(tmp);
       free(before);
       free(after);
       return(TRUE);
    }

    int num(char *numstr)
    {
       /* Evaluate a string to determine whether it is a valid number.
          Numbers are the digits 0-9. A minus sign is allowed in the
          first character. Function returns 'TRUE' if the string is
          a valid number, 'FALSE' otherwise. */

       int len, i;

       if (numstr == NULL)
          return(FALSE);

       len = strlen(numstr);

       if (!len)
          return(FALSE);

       for(i = 0; i < len; i++)
          {
          if (numstr[i] == '-')
             {
             if (i > 0)
                return(FALSE);
             }
          else
             if (!isdigit(numstr[i]))
                return(FALSE);
          }

       return(TRUE);
    }

    void ucase(char *src, char *dest)
    {
       /* Copy a string to its uppercase version. Output string
          is copied into 'dest' which must be allocated to
          sufficient size. */

       int i, len;

       dest[0] = EOS;
       len = strlen(src);

       if (!len)
          return;

       for(i = 0; i < len; i++)
          dest[i] = toupper(src[i]);

       dest[len] = EOS;
    }

    int parse_extend(char *instr, char *outstr, int which, char delim)
    {
       /* Extend string data out to 'which' 'delim' delimited word.
          You may not extend using the space delimiter because of the
          nature of the space and the 'trim' function. Function returns
          'TRUE' upon success, 'FALSE' otherwise. Upon success, 'outstr'
          will be loaded with the extended string. */

       char *new_rec;
       int nwords;
       int i;
       int pos;
       int rec_len;
       int total_len;

       if (instr == NULL || outstr == NULL)
          return(FALSE);

       outstr[0] = EOS;
       nwords = ll_words(instr, delim);

       /* if the existing string is empty and we are adding the
          first word, do not extend */

       if (!nwords && which == 1)
          return(TRUE);

       if (which <= 0 || delim == ' ')
          return(FALSE);

       /* if word is already present, extend not necessary */

       if (nwords >= which)
          {
          strcpy(outstr, instr);
          return(TRUE);
          }

       if (!nwords)
          rec_len = 0;
       else
          rec_len = strlen(instr);

       total_len = rec_len + (which - nwords) + 200;

       if ((new_rec = (char *)malloc(total_len)) == NULL)
          return(FALSE);

       if (rec_len)
          strcpy(new_rec, instr);
       else
          new_rec[0] = EOS;

       /* extend string */

       for(pos = strlen(new_rec), i = nwords; i < which; i++, pos++)
          new_rec[pos] = delim;

       new_rec[pos] = EOS;
       strcpy(outstr, new_rec);
       free(new_rec);
       return(TRUE);
    }

    char *initstring(char *src)
    {
       /* Initialize and copy a string. Dynamic memory is used for
          the string and it is the responsibility of the caller
          to 'free' the string memory after use. Function returns
          a pointer to the beginning of the new string upon
          success, a 'NULL' pointer otherwise. */

       char *dest;
       int len;

       len = strlen(src);

       if (!src)
          return(NULL);

       if ((dest = (char *)malloc(len + 1)) == NULL)
          return(NULL);

       strcpy(dest, src);
       return(dest);
    }

    char *inittstring(char *src)
    {
       /* Initialize, copy and 'trim' a string. Dynamic memory is used for
          the string and it is the responsibility of the caller
          to 'free' the string memory after use. Function returns
          a pointer to the beginning of the new string upon
          success, a 'NULL' pointer otherwise. */

       char *dest;
       int len;

       len = strlen(src);

       if (!src)
          return(NULL);

       if ((dest = (char *)malloc(len + 1)) == NULL)
          return(NULL);

       trim(src, dest);
       return(dest);
    }

    char *initqstring(char *src)
    {
       /* Initialize, copy and quote a string. The destination string is
          only quoted if the source string contains more than one space
          delimited word. Dynamic memory is used for the string and it
          is the responsibility of the caller to 'free' the string
          memory after use. Function returns a pointer to the beginning
          of the new string upon success, a 'NULL' pointer otherwise. */

       char *dest;
       int len;

       if (src == (char *)NULL)
          return((char *)NULL);

       len = strlen(src);

       if (!len)
          return((char *)NULL);

       if ((dest = (char *)malloc(len + 3)) == (char *)NULL)
          return((char *)NULL);

       if (words(src) > 1)
          sprintf(dest, "'%s'", src);
       else
          strcpy(dest, src);

       return(dest);
    }

    int delbytes(char *instr, char *outstr, int pos, int numbytes)
    {
       /* Delete the 'numbytes' of 'instr' starting from byte 'pos'
          and place the result string into 'outstr' which must already
          be allocated to a sufficient size. Function returns 'TRUE'
          upon success with the result string in 'outstr', 'FALSE'
          otherwise. 'numbytes' may be zero which indicates the
          remainder of the string. */

       char *beg;
       char *end;
       int instrlen;
       int i, j;
       int slen;

       outstr[0] = EOS;
       instrlen = strlen(instr);

       if (instr == NULL || !instrlen || outstr == NULL || pos < 0 ||
           numbytes < 0 || (pos + numbytes) > instrlen)
          return(FALSE);

       if ((beg = (char *)malloc(instrlen + 1)) == (char *)NULL)
          return(FALSE);

       if ((end = (char *)malloc(instrlen + 1)) == (char *)NULL)
          {
          free(beg);
          return(FALSE);
          }

       beg[0] = EOS;
       end[0] = EOS;

       /* loop to copy all data before the start position 'pos' */

       for(i = 0; i < pos; i++)
          beg[i] = instr[i];

       beg[pos] = EOS;

       /* calc the number of bytes to delete */

       slen = (numbytes) ? numbytes : instrlen - pos;

       /* loop to copy all data after the removed section */

       for(i = pos + slen, j = 0; i < instrlen; i++, j++)
          if (i < (instrlen - 1))
             end[j] = instr[i];
          else
             {
             end[j] = instr[i];
             end[j + 1] = EOS;
             }

       sprintf(outstr, "%s%s", beg, end);
       free(beg);
       free(end);
       return(TRUE);
    }

    int qatoi(char *numstr, int *numval)
    {
       /* Convert a number in a string to an integer. This function
          differs from 'atoi' in the sense that non-numeric characters
          (other than the minus sign) are not allowed. Function returns
          'TRUE' if 'numstr' is a number and 'numval' will be loaded with
          the value. Upon failure, 'FALSE' will be returned and 'numval'
          will be zero. */

       *numval = 0;

       if (!num(numstr))
          return(FALSE);

       *numval = atoi(numstr);
       return(TRUE);
    }

    int qatol(char *numstr, long *numval)
    {
       /* Convert a number in a string to a long. This function
          differs from 'atol' in the sense that non-numeric characters
          (other than the minus sign) are not allowed. Function returns
          'TRUE' if 'numstr' is a number and 'numval' will be loaded with
          the value. Upon failure, 'FALSE' will be returned and 'numval'
          will be zero. */

       *numval = 0L;

       if (!num(numstr))
          return(FALSE);

       *numval = atol(numstr);
       return(TRUE);
    }

    /* functions missing from some C libraries including
       Codewarrior and Unix */

    #ifndef HAS_STRICMP
    int stricmp(char *s1, char *s2)
    {
       /* Case insensitive string comparison. Function
          returns the result from 'strcmp' after the
          two strings have been forced to upper case.
          Dec/98, Rick Smereka */

       char *us1, *us2;
       int ret;

       if (s1 == (char *)NULL || s2 == (char *)NULL)
          return(4);  // return positive number on failure

       if (!strlen(s1) || !strlen(s2))
          return(5);

       if ((us1 = (char *)malloc(strlen(s1) + 1)) == (char *)NULL)
          return(6);

       if ((us2 = (char *)malloc(strlen(s2) + 1)) == (char *)NULL)
          {
          free(us1);
          return(7);
          }

       // covert both strings to upper case

       ucase(s1, us1);
       ucase(s2, us2);

       // normal compare

       ret = strcmp(us1, us2);
       free(us1);
       free(us2);
       return(ret);
    }

    int strnicmp(char *s1, char *s2, int len)
    {
       /* Case insensitive string comparison for a
          specific number of bytes. Function
          returns the result from 'strncmp' after the
          two strings have been forced to upper case.
          Dec/98, Rick Smereka */

       char *us1, *us2;
       int ret;

       if (s1 == (char *)NULL || s2 == (char *)NULL)
          return(4);  // return positive number on failure

       if (!strlen(s1) || !strlen(s2))
          return(5);

       if ((us1 = (char *)malloc(strlen(s1) + 1)) == (char *)NULL)
          return(6);

       if ((us2 = (char *)malloc(strlen(s2) + 1)) == (char *)NULL)
          {
          free(us1);
          return(7);
          }

       // covert both strings to upper case

       ucase(s1, us1);
       ucase(s2, us2);

       // normal compare

       ret = strncmp(us1, us2, len);
       free(us1);
       free(us2);
       return(ret);
    }
    #endif

    int logic_string_2_flag(char *lstring, int *flag)
    {
       /* Convert a string to a boolean value.
          Acceptable values of 'lstring' are:

             yes, no, true, false, on, off, 0, 1

          String comparison is case insensitive.
          Function returns 'TRUE' upon success
          with the flag value loaded into 'flag'.
          Function returns false otherwise. */

       char *tmp;
       int ret = -1;

       if (lstring == (char *)NULL || !strlen(lstring))
          return(FALSE);

       if (flag == (int *)NULL)
          return(FALSE);

       *flag = 0;

       if ((tmp = initstring(lstring)) == (char *)NULL)
          return(-1);

       trim(lstring, tmp);

       if (!stricmp(tmp, "YES"))
          ret = TRUE;

       if (ret == -1 && !stricmp(tmp, "NO"))
          ret = FALSE;

       if (ret == -1 && !stricmp(tmp, "TRUE"))
          ret = TRUE;

       if (ret == -1 && !stricmp(tmp, "FALSE"))
          ret = FALSE;

       if (ret == -1 && !stricmp(tmp, "ON"))
          ret = TRUE;

       if (ret == -1 && !stricmp(tmp, "OFF"))
          ret = FALSE;

       if (ret == -1 && !strcmp(tmp, "1"))
          ret = TRUE;

       if (ret == -1 && !strcmp(tmp, "0"))
          ret = FALSE;

       free(tmp);
       *flag = (ret != -1) ? ret : 0;
       return(ret != -1 ? TRUE : FALSE);
    }

    int flag_2_logic_string(int flag, char *str)
    {
       /* Convert a integer flag to a string. 'ON' is used
          to represent 1 and 'OFF' is used to represent
          0. The 'flag' must be zero or one. Function
          returns 'TRUE' upon success with the string
          loaded into 'str', 'FALSE' is returned
          otherwise. */

       if (flag != TRUE && flag != FALSE)
          return(FALSE);

       if (flag)
          strcpy(str, "on");
       else
          strcpy(str, "off");

       return(TRUE);
    }

    int pmatch(char *pattern, char *fname)
    {
       /* Match a (file)name to a pattern. Two wildcards are
          allowed in the 'pattern'. An asterisk ('*') will
          match all characters up to the next non-wildcard
          and the question mark ('?') will match any
          single character. Function returns 'TRUE' if a
          match is found, 'FALSE' otherwise. */

       char pat_ch, fname_ch;
       int pat_len, fname_len, pat_pos, fname_pos;
       int done = FALSE, found;

       if (pattern == NULL || fname == NULL)
          return(FALSE);

       pat_len = strlen(pattern);
       fname_len = strlen(fname);
       pat_pos = fname_pos = 0;

       if (!pat_len || !fname_len)
          return(FALSE);

       while(!done)
          {
          pat_ch = pattern[pat_pos];

          switch(pat_ch)
             {
             case '?':
                pat_pos++;
                fname_pos++;

                if (pat_pos >= pat_len && fname_pos >= fname_len)
                   return(TRUE);

                if (pat_pos >= pat_len || fname_pos >= fname_len)
                   return(FALSE);

                break;

             case '*':
                pat_pos++;

                if (pat_pos >= pat_len)
                   return(TRUE);

                pat_ch = pattern[pat_pos];
                found = FALSE;

                while(!found)
                   {
                   fname_ch = fname[fname_pos];

                   if (fname_ch == pat_ch)
                      {
                      found = TRUE;
                      fname_pos++;
                      pat_pos++;
                      }
                   else
                      {
                      fname_pos++;

                      if (fname_pos >= fname_len)
                         if (pat_pos >= pat_len)
                            return(TRUE);
                         else
                            return(FALSE);
                      }
                   }

                break;

             default:
                if (pattern[pat_pos] != fname[fname_pos])
                   return(FALSE);

                pat_pos++;
                fname_pos++;

                if (pat_pos >= pat_len && fname_pos >= fname_len)
                   return(TRUE);

                if (pat_pos >= pat_len || fname_pos >= fname_len)
                   return(FALSE);
             };
          }

       return(FALSE);
    }

    void lpad(char *istr, char *ostr, int padlen, char padch)
    {
       /* Pad a string on the left with the character 'padch'
          to the length 'padlen'. Function returns the padded
          string in 'ostr' which must already be allocated to
          sufficient size by the caller. */

       int i, ch2pad, outpos, len;

       if (istr == (char *)NULL)
          return;

       if (ostr == (char *)NULL)
          return;

       if (padlen <= 0)
          return;

       ostr[0] = EOS;
       len = strlen(istr);
       ch2pad = padlen - len;

       /* if chars to pad is zero or less, just copy input to output */

       if (ch2pad <= 0)
          {
          strcpy(ostr, istr);
          return;
          }

       for(i = 1, outpos = 0; i <= ch2pad; i++, outpos++)
          {
          ostr[outpos] = padch;
          ostr[outpos + 1] = EOS;
          }

       for(i = 0; i < len; i++, outpos++)
          {
          ostr[outpos] = istr[i];
          ostr[outpos + 1] = EOS;
          }
    }

    void rpad(char *istr, char *ostr, int padlen, char padch)
    {
       /* Pad a string on the right with the character 'padch'
          to the length 'padlen'. Function returns the padded
          string in 'ostr' which must already be allocated to
          sufficient size by the caller. */

       int i, ch2pad, outpos, len;

       if (istr == (char *)NULL)
          return;

       if (ostr == (char *)NULL)
          return;

       if (padlen <= 0)
          return;

       ostr[0] = EOS;
       len = strlen(istr);
       ch2pad = padlen - len;

       /* if chars to pad is zero or less, just copy input to output */

       if (ch2pad <= 0)
          {
          strcpy(ostr, istr);
          return;
          }

       strcpy(ostr, istr);

       for(i = 0, outpos = len; i < ch2pad; i++, outpos++)
          {
          ostr[outpos] = padch;
          ostr[outpos + 1] = EOS;
          }
    }

    void strip_space(char *instr, char *outstr)
    {
       /* Strip all spaces from 'instr' and place the result
          into 'outstr' which must already be allocated to
          sufficient size by the caller. */

       char ch;
       int i, len, out_pos;

       if (instr == (char *)NULL || !strlen(instr))
          return;

       len = strlen(instr);

       for(i = 0, out_pos = 0; i < len; i++)
          {
          ch = instr[i];

          if (ch != ' ')
             {
             outstr[out_pos++] = ch;
             outstr[out_pos] = EOS;
             }
          }
    }

/* [<][>][^][v][top][bottom][index][help] */