/* 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;
         }
      }
}
