[comp.sources.misc] v04i034: Config 1.0

kwok@iris.ucdavis.edu (Conrad Kwok) (08/24/88)

Posting-number: Volume 4, Issue 34
Submitted-by: "Conrad Kwok" <kwok@iris.ucdavis.edu>
Archive-name: config

Config 1.0 is a set of C routines to read configuration files.

--------------Cut Here------------------------
#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	Makefile
#	README
#	config.c
#	config.cfg
#	config.doc
#	config.h
#	main.c
# This archive created: Tue Aug 23 18:27:11 1988
export PATH; PATH=/bin:$PATH
if test -f 'Makefile'
then
	echo shar: will not over-write existing file "'Makefile'"
else
sed 's/^X//' << \SHAR_EOF > 'Makefile'
X# Makefile for CONFIG Version 1.0
X#
XOBJS	= main.o config.o
X#
X# Use of gcc is recommended whenever available. Include
X#	-DINT32BIT when int is 32 bits long
X#	-DNoVPRINTF when vprintf is not included in the library
X#	-DOldFashion when compiled using cc on Ultrix and BSD 4.x
XCC	= gcc
XCFLAGS	= -g -DINT32BIT
X
Xall:	main
X
Xmain:	$(OBJS)
X	$(CC) $(CFLAGS) -o main $(OBJS)
X
Xmain.o:	main.c config.h
X
Xconfig.o: 	config.c config.h
SHAR_EOF
fi # end of overwriting check
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \SHAR_EOF > 'README'
XCONFIG is  a set of  C routines for  reading configuration files.
XOne  of  the major objectives  of  CONFIG  is  to  allow existing
Xprograms which are not using configuration file to take advantage
Xof CONFIG with minimal changes.
X
XCONFIG is still in its very primitive form.  I don't have time to
Xfinish the programming section of CONFIG but it should not to
Xdifficult to follow the code (I think).
X
XCONFIG has been successfully compiled using:
X1. MSDOS MS C compiler 5.0 using small model
X2. Ultrix cc with -DOldFashion
X3. Ultrix gcc
X4. BSD 4.3 cc with -DOldFashion
X5. Encore gcc with -DNoVPRINTF
X6. Encore cc with -DNoVPRINTF
X7. SUN OS 3.4 cc
X
XConrad Kwok
Xinternet: kwok@iris.ucdavis.edu
Xcsnet   : kwok@ucd.csnet 
Xcsnet   : kwok%iris.ucdavis.edu@csnet.relay
Xuucp    : {ucbvax, uunet, ... }!ucdavis!iris!kwok
X
SHAR_EOF
fi # end of overwriting check
if test -f 'config.c'
then
	echo shar: will not over-write existing file "'config.c'"
else
sed 's/^X//' << \SHAR_EOF > 'config.c'
X/*==================================================================*/
X/*	CONFIG Version 1.0  <August 21, 1988>			    */
X/*								    */
X/*	Written by Conrad Kwok, Division of Computer Science, UCD   */
X/*								    */
X/*	Permission is granted for freely distribution of this file  */
X/*		provided that this message is included.		    */
X/*==================================================================*/
X
X#include <stdio.h>
X#include <ctype.h>
X#include <varargs.h>
X#include "config.h"
X
X#ifdef MSDOS
X#include "config.dcl"
X#endif
X
X#ifdef NoVPRINTF
X#define vfprintf(fp,fmt,arg) _doprnt(fmt,arg,fp)
X#endif
X
X#define SigKeyStrLen 20
X#define NuOfUserType 2
X#define NoMore EOF
X#define MinFloat -1.0e+38
X#define MaxFloat  1.0e+38
X#define MinDouble -1.0e+38
X#define MaxDouble  1.0e+38
X#define cfgMaxStrLen 512
X
X#define mywhite(ch) (isspace(ch) || ch=='{' || ch=='}')
X
Xint cfgLine=0, cfgCol=0;
Xchar StrQuChar = '"';
Xint KeyCaseSensitive = FALSE;
Xint cfgScan = FALSE;
Xint WasComma;
Xint cfgVerbose = FALSE;
Xchar KeyStr[SigKeyStrLen+1];
Xint (*UserDefHook)()=NULL;
Xint (*UTypeHook[NuOfUserType])() = {
X    NULL, NULL
X};
XFILE *cfgFile;
X
Xdouble cfgRange[8][2] = {
X    { -128  , 127   },
X    { 0     , 255   },
X    { -32768, 32767 },
X    { 0     , 65535 },
X#ifdef INT32BIT
X    { -2147483648.0, 2147483647.0 },
X    { 0          , 4294967295.0 },
X#else
X    { -32768, 32767 },
X    { 0     , 65535 },
X#endif
X    { -2147483648.0, 2147483647.0 },
X    { 0          , 4294967295.0 } };
X
Xvoid *malloc();
XKeyRec *searchkey();
X
Xint cfggetc()
X{
X    int ch;
X
Xreread:
X    ch = getc(cfgFile);
X    if (ch == '\n') {
X        cfgLine++;
X        cfgCol = 0;
X    } else if (ch == EOF) {
X        if (cfgCol!=0) {
X            cfgLine++;
X            cfgCol = 0;
X        }
X    } else if (ch=='#' || ch==';') {
X        do {
X            ch = getc(cfgFile);
X        } while (ch!=EOF && ch!='\n');
X        cfgLine++;
X        cfgCol = 0;
X        goto reread;
X    } else {
X        cfgCol++;
X    }
X    return(ch);
X}
X
Xcfgfldc()
X{
X    int ch;
X
X    ch=cfggetc();
X    if (cfgCol == 1 && !isspace(ch)) {
X        cfgungetc(ch);
X        return(EOF);
X    }
X    return(ch);
X}
X
X
Xcfgungetc(ch)
Xint ch;
X{
X    if (ch==EOF) return;
X    cfgCol--;
X    if (cfgCol < 0) {
X        cfgLine--;
X    }
X    ungetc(ch, cfgFile);
X}
X
X#ifndef MSDOS
Xstrnicmp(s, t, len)
Xchar *s, *t;
Xint len;
X{
X    char a, b;
X
X    for (; len > 0 ; len--) {
X        a= *s++; b= *t++;
X        a = islower(a) ? toupper(a) : a;
X        b = islower(b) ? toupper(b) : b;
X        if (a != b) break;
X        if (a == '\0') return(0);
X    }
X    return(a - b);
X}
X#endif
X
Xreadkey(str, strsize)
Xchar *str;
Xint strsize;
X{
X    int lencnt, ch;
X
X    lencnt=strsize-1;
X    do {
X        ch=cfggetc();
X    } while (isspace(ch));
X    if (ch==EOF) return(EOF);
X    if (cfgCol != 1) {
X        cfgerror("Configuration Keyword must begin at first column\n");
X    }
X    while (ch!=EOF && !isspace(ch)) {
X        if (lencnt-- > 0) {
X            *str++ = ch;
X        }
X        ch=cfggetc();
X    }
X    *str = NULL;
X    if (ch == EOF && lencnt+1 != strsize) {
X        cfgerror("Configuration File Ended Unexpectly\n");
X    }
X    return(ch);
X}
X
X#ifdef MSDOS
Xcfgerror(va_alist, ...)
X#else
Xcfgerror(va_alist)
X#endif
Xva_dcl
X{
X    char *fmt;
X    va_list arg_ptr;
X
X    va_start(arg_ptr);
X    fmt = va_arg(arg_ptr, char *);
X    fprintf(stderr,"CFG Error--L%d C%d:", cfgLine+1, cfgCol);
X    vfprintf(stderr, fmt, arg_ptr);
X    va_end(arg_ptr);
X    exit(1);
X}
X
X#ifdef MSDOS
Xcfgwarning(va_alist, ...)
X#else
Xcfgwarning(va_alist)
X#endif
Xva_dcl
X{
X    char *fmt;
X    va_list arg_ptr;
X
X    va_start(arg_ptr);
X    fmt = va_arg(arg_ptr, char *);
X    fprintf(stderr,"CFG Warning--L%d C%d:", cfgLine+1, cfgCol);
X    vfprintf(stderr, fmt, arg_ptr);
X    va_end(arg_ptr);
X}
X
Xreadconfig(filename)
Xchar *filename;
X{
X    KeyRec *idx;
X    int tmp;
X
X    if ((cfgFile=fopen(filename, "r")) == NULL) {
X        return FALSE;
X    }
X    while (readkey(KeyStr, sizeof(KeyStr)) != EOF) {
X        if ((idx=searchkey(KeyTable, KeyStr)) != NULL) {
X            if (idx->hook != NULL) {
X		if ((*idx->hook)(0, idx) == FALSE) {
X		    continue;
X		}
X            }
X            switch (idx->vtype) {
X            case V_byte:
X            case V_ubyte:
X            case V_short:
X            case V_ushort:
X            case V_int:
X            case V_uint:
X            case V_long:
X/*            case V_ulong: */
X                readint(idx);
X                break;
X            case V_float:
X            case V_double:
X            	readreal(idx);
X            	break;
X            case V_string:
X            case V_charptr:
X            	readstr(idx);
X            	break;
X            case V_char:
X            	readchar(idx);
X            	break;
X            case V_intkw:
X                readintkw(idx);
X                break;
X            case V_usertype0:
X            case V_usertype1:
X                tmp = idx->vtype - V_usertype0;
X                if (UTypeHook[tmp] != NULL) {
X                    (*UTypeHook[tmp])(idx);
X                    break;
X                }
X            default:
X                if (UserDefHook == NULL || (*UserDefHook)(idx)==FALSE) {
X		    cfgwarning("Unknown vtype %d\n", idx->vtype);
X                }
X                break;
X            }
X            if (idx->hook != NULL) {
X		if ((*idx->hook)(1, idx) == FALSE) {
X		    continue;
X		}
X            }
X            junkcheck(idx->val_set == cfgPreset);
X        } else {
X            if (!cfgScan) {
X                cfgwarning("Keyword '%s' *NOT* found\n", KeyStr);
X            }
X            junkcheck(FALSE);
X        }
X    }
X    return(TRUE);
X}
X
Xjunkcheck(skip)
Xint skip;
X{
X    int ch, echo;
X
X    echo = FALSE;
X    do {
X        if (echo) {
X	    putc(ch, stderr);
X        }
X        do {
X            ch=cfggetc();
X            if (ch == EOF) return;
X            if (echo && cfgCol != 1) {
X		putc(ch, stderr);
X	    } else if (cfgCol != 1 && !mywhite(ch)) {
X                if (!skip) {
X                    cfgwarning("The following data are ignored:\n");
X                    echo = TRUE;
X		    putc(ch, stderr);
X		}
X            }
X        } while (cfgCol != 1);
X    } while (isspace(ch));
X    cfgungetc(ch);
X    return;
X}
X
Xreadint(idx)
XKeyRec *idx;
X{
X    long val;
X    int loop, arraysz, ret, nettype;
X    void *num;
X    double lowerlimit, upperlimit;
X
X    if (idx->val_set == cfgPreset) return;
X    if (idx->val_set == TRUE) {
X        cfgwarning("%s already defined\n", KeyStr);
X        return;
X    }
X    WasComma = FALSE;
X    arraysz = idx->arraysize;
X    nettype = idx->vtype - V_byte;
X    if (arraysz <= 0) arraysz = 1;
X    num = idx->addr;
X    lowerlimit = idx->lower;
X    upperlimit = idx->upper;
X    if (lowerlimit >= upperlimit) {
X        lowerlimit=cfgRange[nettype][0];
X        upperlimit=cfgRange[nettype][1];
X    } else if (lowerlimit < cfgRange[nettype][0]) {
X        cfgwarning("User range of %s < lower limit. Default Used\n",
X                     KeyStr);
X        lowerlimit = cfgRange[nettype][0];
X    } else if (upperlimit > cfgRange[nettype][1]) {
X        cfgwarning("Usere range of %s > upper limit. Default Used\n",
X                     KeyStr);
X        upperlimit = cfgRange[nettype][1];
X    }
X    for (loop=0; loop < arraysz; loop++) {
X        if ((ret=readlong(&val)) == NoMore) {
X            cfgerror("Insufficient Field\n");
X        }
X        if (ret==TRUE) {
X            if (val < lowerlimit || val > upperlimit) {
X                cfgerror("Field %d out of range (%lg to %lg)\n", loop+1,
X                            lowerlimit, upperlimit);
X            }
X       	    if (cfgVerbose) {
X       	        printf("CFG: %s(%d)=%ld\n", KeyStr, loop, val);
X       	    }
X            switch (idx->vtype) {
X            case V_byte:
X                *((char *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (char *));
X#else
X                ((char *) num)++;
X#endif
X                break;
X            case V_ubyte:
X                *((unsigned char *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (unsigned char *));
X#else
X                ((unsigned char *) num)++;
X#endif
X                break;
X            case V_short:
X                *((short *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (short *));
X#else
X                ((short *) num)++;
X#endif
X                break;
X            case V_ushort:
X                *((unsigned short *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (unsigned short *));
X#else
X                ((unsigned short *) num)++;
X#endif
X                break;
X            case V_int:
X                *((int *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (int *));
X#else
X                ((int *) num)++;
X#endif
X                break;
X            case V_uint:
X                *((unsigned int *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (unsigned int *));
X#else
X                ((unsigned int *) num)++;
X#endif
X                break;
X            case V_long:
X                *((long *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (long *));
X#else
X                ((long *) num)++;
X#endif
X                break;
X            case V_ulong:
X                *((unsigned long *) num) = val;
X#ifdef OldFashion
X		num += (sizeof (unsigned long *));
X#else
X                ((unsigned long *) num)++;
X#endif
X                break;
X            default:
X               cfgerror("Almost Impossible Error. Unknown vtype");
X               break;
X            }
X	} else {
X	    if (idx->val_set != cfgDefault) {
X	        cfgerror("Field %d in '%s' missing\n", loop+1, KeyStr);
X	    } else {
X	        switch (idx->vtype) {
X                case V_byte:
X#ifdef OldFashion
X		    num += (sizeof (char *));
X#else
X                    ((char *) num)++;
X#endif
X                    break;
X                case V_ubyte:
X#ifdef OldFashion
X		    num += (sizeof (unsigned char *));
X#else
X                    ((unsigned char *) num)++;
X#endif
X                    break;
X                case V_short:
X#ifdef OldFashion
X		    num += (sizeof (short *));
X#else
X                    ((short *) num)++;
X#endif
X                    break;
X                case V_ushort:
X#ifdef OldFashion
X		    num += (sizeof (unsigned short *));
X#else
X                    ((unsigned short *) num)++;
X#endif
X                    break;
X                case V_int:
X#ifdef OldFashion
X		    num += (sizeof (int *));
X#else
X                    ((int *) num)++;
X#endif
X                    break;
X                case V_uint:
X#ifdef OldFashion
X		    num += (sizeof (unsigned int *));
X#else
X                    ((unsigned int *) num)++;
X#endif
X                    break;
X                case V_long:
X#ifdef OldFashion
X		    num += (sizeof (long *));
X#else
X                    ((long *) num)++;
X#endif
X                    break;
X                case V_ulong:
X#ifdef OldFashion
X		    num += (sizeof (unsigned long *));
X#else
X                    ((unsigned long *) num)++;
X#endif
X                    break;
X                default:
X                   cfgerror("Almost Impossible Error. Unknown vtype");
X                   break;
X                }
X            }
X	}
X    }
X    idx->val_set = TRUE;
X}
X
Xreadlong(l)
Xlong *l;
X{
X    int ch, nlen, neg;
X    long temp;
X
X    do {
X        ch = cfgfldc();
X    } while (mywhite(ch));
X    neg = FALSE;
X    if (ch == '-') {
X    	neg = TRUE;
X    	ch = cfgfldc();
X    } else if (ch == '+') {
X    	ch = cfgfldc();
X    }
X    temp = 0;
X    nlen = 0;
X    while (isdigit(ch)) {
X        nlen++;
X        temp = temp*10 + ch - '0';
X        ch = cfgfldc();
X    }
X    while (mywhite(ch)) ch=cfgfldc();
X    if (nlen==0) {
X        if ((ch==EOF && WasComma) || ch==',') {
X            return(FALSE);
X        } else {
X            return(NoMore);
X        }
X    } else {
X	*l = neg ? -temp : temp;
X        WasComma = (ch==',');
X        if (!WasComma) {
X            cfgungetc(ch);
X        }
X    }
X    return(TRUE);
X}
X
Xreadreal(idx)
XKeyRec *idx;
X{
X    double val;
X    int loop, arraysz, ret;
X    void *num;
X    double lowerlimit, upperlimit;
X
X    if (idx->val_set == cfgPreset) return;
X    if (idx->val_set == TRUE) {
X        cfgwarning("%s already defined\n", KeyStr);
X        return;
X    }
X    WasComma = FALSE;
X    arraysz = idx->arraysize;
X    if (arraysz <= 0) arraysz = 1;
X    num = idx->addr;
X    lowerlimit = idx->lower;
X    upperlimit = idx->upper;
X    if (lowerlimit >= upperlimit) {
X        switch (idx->vtype) {
X        case V_float:
X            lowerlimit = MinFloat;
X            upperlimit = MaxFloat;
X            break;
X	case V_double:
X            lowerlimit = MinFloat;
X            upperlimit = MaxFloat;
X            break;
X        default:
X            cfgerror("Almost impossible error. Unknown Type\n");
X        }
X    }
X    for (loop=0; loop < arraysz; loop++) {
X        if ((ret=readdouble(&val)) == NoMore) {
X            cfgerror("Insufficient Field\n");
X        }
X        if (ret==TRUE) {
X            if (val < lowerlimit || val > upperlimit) {
X                cfgerror("Field %d out of range (%lg to %lg)\n", loop+1,
X                            lowerlimit, upperlimit);
X            }
X       	    if (cfgVerbose) {
X       	        printf("CFG: %s(%d)=%lf\n", KeyStr, loop, val);
X       	    }
X            switch (idx->vtype) {
X            case V_float:
X                *((float *) num) = val;
X#ifdef OldFashion
X                num += (sizeof (float *));
X#else
X                ((float *) num)++;
X#endif
X                break;
X            case V_double:
X                *((double *) num) = val;
X#ifdef OldFashion
X                num += (sizeof (double *));
X#else
X                ((double *) num)++;
X#endif
X                break;
X            default:
X               cfgerror("Almost Impossible Error. Unknown vtype");
X               break;
X            }
X	} else {
X	    if (idx->val_set != cfgDefault) {
X	        cfgerror("Field %d in '%s' missing\n", loop+1, KeyStr);
X	    }
X	    switch (idx->vtype) {
X	    case V_float:
X#ifdef OldFashion
X                num += (sizeof (float *));
X#else
X                ((float *) num)++;
X#endif
X                break;
X            case V_double:
X#ifdef OldFashion
X                num += (sizeof (double *));
X#else
X                ((double *) num)++;
X#endif
X            default:
X               cfgerror("Almost Impossible Error. Unknown vtype");
X               break;
X            }
X	}
X    }
X    idx->val_set = TRUE;
X}
X
Xreaddouble(dbl)
Xdouble *dbl;
X{
X    int ch, nlen;
X    char dblstr[80];
X    double atof();
X
X    do {
X        ch = cfgfldc();
X    } while (mywhite(ch));
X    nlen=0;
X    while (isdigit(ch) || ch=='-' || ch=='+' || ch=='e' || ch=='E' ||
X		ch=='.') {
X        dblstr[nlen++] = ch;
X        ch = cfgfldc();
X    }
X    dblstr[nlen]=NULL;
X    while (mywhite(ch)) ch=cfgfldc();
X    if (nlen==0) {
X        if ((ch==EOF && WasComma) || ch==',') {
X            return(FALSE);
X        } else {
X            return(NoMore);
X        }
X    } else {
X	*dbl = atof(dblstr);
X        WasComma = (ch==',');
X        if (!WasComma) {
X            cfgungetc(ch);
X        }
X    }
X    return(TRUE);
X}
X
Xreadstr(idx)
XKeyRec *idx;
X{
X    char s[cfgMaxStrLen];
X    int loop, arraysz, ret, slen;
X    char *sptr;
X    int minlen, maxlen;
X
X    if (idx->val_set == cfgPreset) return;
X    if (idx->val_set == TRUE) {
X        cfgwarning("%s already defined\n", KeyStr);
X        return;
X    }
X    WasComma = FALSE;
X    arraysz = idx->arraysize;
X    if (arraysz <= 0) arraysz = 1;
X    sptr = idx->addr;
X    minlen = idx->lower;
X    maxlen = idx->upper;
X    if (minlen < 0) minlen = 0;
X    if (maxlen > cfgMaxStrLen) {
X        cfgwarning("Max string length is %d (config error)\n", cfgMaxStrLen);
X        maxlen = cfgMaxStrLen;
X    }
X    if (minlen > maxlen || (maxlen == 0 && minlen == 0) ) {
X        minlen = 0;
X        maxlen = cfgMaxStrLen;
X    }
X    for (loop=0; loop < arraysz; loop++) {
X        if ((ret=readstring(s, sizeof(s), StrQuChar)) == NoMore) {
X            cfgerror("Insufficient Field\n");
X        }
X        if (ret==TRUE) {
X            slen = strlen(s);
X            if (slen+1 > maxlen) {
X                cfgerror("Field %d too long (%d)\n", loop+1, maxlen);
X            }
X            if (cfgVerbose) {
X                printf("CFG: %s(%d)=%s\n", KeyStr, loop, s);
X            }
X            switch (idx->vtype) {
X            case V_string:
X                strcpy(sptr, s);
X                sptr += maxlen;
X                break;
X            case V_charptr:
X                if (slen == 0 && minlen <= 0) {
X                    *((char **) sptr) = NULL;
X                } else {
X                    if (slen < minlen) slen = minlen;
X                    if ((*((char **) sptr) = malloc(slen)) == NULL) {
X                        cfgerror("Error in allocating memory for string\n");
X                    }
X                    strcpy( *((char **) sptr), s);
X#ifdef OldFashion
X                    sptr += (sizeof (char **));
X#else
X                    ((char **) sptr)++;
X#endif
X                }
X                break;
X            }
X        } else {
X            if (idx->val_set != cfgDefault) {
X                cfgerror("Field %d in '%s' missing\n", loop+1, KeyStr);
X	    }
X            switch(idx->vtype) {
X            case V_string:
X                sptr += maxlen;
X                break;
X            case V_charptr:
X#ifdef OldFashion
X                    sptr += (sizeof (char **));
X#else
X                    ((char **) sptr)++;
X#endif
X                break;
X            }
X        }
X    }
X    idx->val_set = TRUE;
X}
X
Xreadstring(str, totalsize, qchar)
Xchar *str;
Xint totalsize;
Xchar qchar;
X{
X    static char special_ch[26] = {
X           0, '\b',    0,    0,    0, '\f',   7,   0,
X           0,    0,    0, '\f',    0, '\n',   0,   0,
X           0, '\r',    0, '\t',    0,    0,   0,   0,
X           0,    0};
X    int ch, slen, quoted, done;
X    char ch1;
X
X    do {
X        ch =cfgfldc();
X    } while (mywhite(ch));
X    slen=0;
X    done = FALSE;
X    quoted= ch == qchar;
X    if (quoted) ch = cfgfldc();
X    while (!done) {
X        if (ch=='\\') {
X            ch = cfgfldc();
X            if (ch==EOF) {
X                if (quoted) {
X                    cfgwarning("Missing closed quote!\n");
X                }
X                done =TRUE;
X                ch = '\\';
X            } else if (isalpha(ch)) {
X                    ch1 = islower(ch) ? toupper(ch) : ch;
X                    ch1 = special_ch[ch1-'A'];
X                    if (ch1 != NULL) ch=ch1;
X            }
X        } else if ((quoted && ch==qchar) ||
X                   (!quoted && (ch==',' || mywhite(ch)))) {
X            ch=cfgfldc();
X	    break;
X        }
X        if (ch == EOF) {
X            if (quoted) {
X                cfgwarning("Missing closed quote!\n");
X            }
X            break;
X        }
X        if (++slen < totalsize) {
X            *str++ = ch;
X        }
X        ch = cfgfldc();
X    }
X    *str = NULL;
X    while (mywhite(ch)) ch=cfgfldc();
X    if (slen==0 && !quoted) {
X        if ((ch==EOF && WasComma) || ch==',') {
X            return(FALSE);
X        } else {
X            return(NoMore);
X        }
X    } else {
X        WasComma = (ch==',');
X        if (!WasComma) {
X            cfgungetc(ch);
X        }
X    }
X    return(TRUE);
X}
X
Xreadchar(idx)
XKeyRec *idx;
X{
X    char s[3];
X    int loop, arraysz, ret, slen;
X    char *chptr;
X
X    if (idx->val_set == cfgPreset) return;
X    if (idx->val_set == TRUE) {
X        cfgwarning("%s already defined\n", KeyStr);
X        return;
X    }
X    WasComma = FALSE;
X    arraysz = idx->arraysize;
X    if (arraysz <= 0) arraysz = 1;
X    chptr= idx->addr;
X    for (loop=0; loop < arraysz; loop++) {
X        if ((ret=readstring(s, sizeof(s), '\'')) == NoMore) {
X            cfgerror("Insufficient Field\n");
X        }
X        if (ret==TRUE) {
X            slen = strlen(s);
X            if (slen > 1) {
X                cfgerror("Only 1 character is allowed in char constant\n");
X            }
X            if (cfgVerbose) {
X                printf("CFG: %s(%d)=%c\n", KeyStr, loop, s[0]);
X            }
X            *chptr = s[0];
X            chptr++;
X        } else {
X            if (idx->val_set != cfgDefault) {
X                cfgerror("Field %d in '%s' missing\n", loop+1, KeyStr);
X            }
X            chptr++;
X        }
X    }
X    idx->val_set = TRUE;
X}
X
Xreadintkw(idx)
XKeyRec *idx;
X{
X    char s[SigKeyStrLen+1];
X    int loop, arraysz, ret, slen;
X    int *val;
X
X    if (idx->val_set == cfgPreset) return;
X    if (idx->val_set == TRUE) {
X        cfgwarning("%s already defined\n", KeyStr);
X        return;
X    }
X    WasComma = FALSE;
X    arraysz = idx->arraysize;
X    if (arraysz <= 0) arraysz = 1;
X    val = idx->addr;
X    for (loop=0; loop < arraysz; loop++) {
X        if ((ret = readstring(s, sizeof(s), '\0')) == NoMore) {
X            cfgerror("Insufficient Field\n");
X        }
X        if (ret == TRUE) {
X            ret=searchkw((char **) idx->userdata, s,
X                            SigKeyStrLen, idx->userflag);
X            if (ret < 0) {
X                cfgerror("Keyword %s not found in table of %s\n", s, KeyStr);
X            }
X            if (cfgVerbose) {
X                printf("CFG: %s(%d)=%d+%d [%s]\n", KeyStr, loop, ret,
X                            (int) idx->lower, s);
X            }
X            *val = ret + (int) idx->lower;
X            val++;
X        } else {
X            if (idx->val_set != cfgDefault) {
X                cfgerror("Field %d in '%s' missing\n", loop+1, KeyStr);
X	    }
X            *val++;
X        }
X    }
X    idx->val_set = TRUE;
X}
X
Xint searchkw(table, s, sig, cs)
Xchar *table[];
Xchar *s;
Xint sig, cs;
X{
X    int count;
X
X    count = 0;
X    for ( ; *table != NULL; table++, count++) {
X        if (cs) {
X            if (strncmp(*table, s, sig) == 0) {
X                return count;
X            }
X        } else {
X            if (strnicmp(*table, s, sig) == 0) {
X                return count;
X            }
X        }
X    }
X    return(-1);
X}
X
XKeyRec *searchkey(table, str)
XKeyRec *table;
Xchar *str;
X{
X    for ( ;table->keystr != NULL; table++) {
X    	if (KeyCaseSensitive) {
X	    if (strncmp(table->keystr, str, SigKeyStrLen) == 0) {
X	        return(table);
X	    }
X	} else {
X	    if (strnicmp(table->keystr, str, SigKeyStrLen) == 0) {
X	        return(table);
X	    }
X	}
X    }
X    return(NULL);
X}
X
Xint PresetKey(key)
Xchar *key;
X{
X    KeyRec *idx;
X
X    if ((idx=searchkey(KeyTable, key)) != NULL) {
X        idx->val_set = cfgPreset;
X	return TRUE;
X    } else {
X	return FALSE;
X    }
X}
X
XCheckAllKeys()
X{
X    KeyRec *idx;
X    int flag = TRUE;
X
X    for(idx=KeyTable; idx->keystr != NULL; idx++) {
X        if (idx->val_set == FALSE) {
X            flag = FALSE;
X            fprintf(stderr, "WARNING: Key %s undefined\n", idx->keystr);
X        }
X    }
X    return flag;
X}
SHAR_EOF
fi # end of overwriting check
if test -f 'config.cfg'
then
	echo shar: will not over-write existing file "'config.cfg'"
else
sed 's/^X//' << \SHAR_EOF > 'config.cfg'
XSwitchChar      '-'
XTopMargin	10
XBottomMargin	20
XMargin		,2  3,4
XMagnification	0.8
XKeys		"true" false
XLine		"\"this is a long, line with double quote\""
XCASESENSITIVE	False
SHAR_EOF
fi # end of overwriting check
if test -f 'config.doc'
then
	echo shar: will not over-write existing file "'config.doc'"
else
sed 's/^X//' << \SHAR_EOF > 'config.doc'
X1  Introduction
X
X
X
X                        CONFIG Version 1.0
X
X                           Conrad Kwok
X
X                   Division of Computer Science
X                 University of California, Davis
X
X                         August 20, 1988
X
XCONFIG is  a set of  C routines for  reading configuration files.
XOne  of  the major objectives  of  CONFIG  is  to  allow existing
Xprograms which are not using configuration file to take advantage
Xof CONFIG with minimal changes.
X
XCONFIG can be used to read in integer numbers,  floating numbers,
Xcharacter,   string  and  it  can  also  recognize  user  defined
Xkeywords. Additional user type can be defined using build in user
Xhooks.
X
XYou may freely modify and distribute the source of  CONFIG on the
Xcondition  that  all  the  documentations  and  source  files are
Xincluded  and  unmodified.  The  author  welcomes  any  comments,
Xconstructive  or  otherwise,  suggestions  for  improvements, any
Xideas for possible future revisions and,  of course, bug reports.
XIt  is  also  requested that  he  is informed of  any significant
Xchanges or modifications made to the package.  Author's address:
X
Xinternet: kwok@iris.ucdavis.edu
Xcsnet   : kwok@ucd.csnet 
Xcsnet   : kwok%iris.ucdavis.edu@csnet.relay
Xuucp    : {ucbvax, uunet, ... }!ucdavis!iris!kwok
X
X
X2  Syntax of Configuration file
X
X2.1  A Configuration Key Entry
X
XA configuration key entry consists  of  a  key  and  one  or more
Xfields.
X
X	KEY	field1,field2, ...
X
XA key must begins at column 1  (left most column) and followed by
X1  or more white spaces. The number and type of the fields depend
Xon the definition of the key which will  be  described in details
Xin section 3.  Fields can be separated by a comma or one  or more
Xspaces.  In  order for config  to  accept  C like initialization,
Xcharacters "{"  and  "}"  are  treated  like  a  space in-between
Xfields.  For example,  the key "Margin"  requires four parameters
Xfor top,  bottom, left and right margins. It can be entered in on
Xof the following ways.
X
X1)	Margin	1, 1.5 , 1 ,1
X2)	Margin  1 1.5    1  1
X3) 	Margin	{ 1, 1.5, 1, 1 }
X
XAny  line  beginning  with  a  space  will  be  considered  as  a
Xcontinuation of the line above.  Therefore, the example above can
Xalso be entered in more than a line.
X
X4) 	Margin	1 1.5
X		1 1
X
XFurthermore, if the key has default values (for more information
Xabout default values, see 3.1 KeyTable), some of the fields may
Xbe skipped. For example, if only the left margin is needed to be
Xchanged, it may be entered in this way:
X
X5)	Margin	,,1
X
Xor
X
X6)	Margin	, , 1,
X
XIn this case, use of comma is mandatory; otherwise CONFIG has no
Xway to know the skipping of a field except the last field(s).
X
XAny line  beginning  with  the  characters  "#"  or  ";"  will be
Xignored. For example:
X
X	# All margin value are in term of inch(es).
X
XSyntax for each type of fields is specified in section 3.2.
X
XLastly,  only the first 20  characters  (compile  time changeable
Xparameter) of the key are used in comparison. In other words, all
Xkeys must be unique in the first 20 characters.
X
X
X2.2  A Configuration File
X
XA configuration file can  contain zero or more  configuration key
Xentry.  Each key entry must begin on a new line and the  key must
Xbegin at  column  1  (left  most  column).  Blank  lines  will be
Xignored. An simple example with two entries is given below:
X
X	# All margin values are in terms of inches
X	Margin	1, 1.5, 1, 1
X
X	# Valid options for PageNumbering are None, Top or Bottom
X	PageNumbering	Bottom
X
X
X
X3  Using Config
X
XIn order to use CONFIG,  information about the  keys expected and
Xthe name  of  the configuration file  must  be given. Information
Xabout the keys  are passed  from  user program to CONFIG  via the
Xglobal variable KeyTable.  The structure of KeyTable is described
Xin details in next subsection. The name of the configuration file
Xis passed to CONFIG at run time when CONFIG is asked to  read the
Xfile using the function "readconfig".  This is  also described in
Xdetail in later section.
X
XBesides  the mandatory information,  there are also  some options
Xthat can be  specified at  run time  such  as  verbose mode, scan
Xmode and ignoring case in comparison.
X
X
X3.1  KeyTable
X
XAll information regarding configuration keys are passed to CONFIG
Xvia the array KeyTable.  KeyTable is a array of structure KeyRec.
XThe description of the record is given below.
X
Xstruct {
X	char *keystr;	/* Specify the key */
X	void *addr;   	/* Address of the variable to receive the
X			 value */
X	int vtype;    	/* Type of value(s) expected */
X	int val_set;	/* A flag to indicate whether the key has
X			   been set or not. Possible values are
X			   FALSE, cfgDEFAULT, TRUE and PRESET. In
X			   initializing KeyTable, only FALSE or
X			   cfgDEFAULT should be specified */
X	int arraysize;	/* specify the size of the array if the
X			   address given in the field addr above
X			   is a array. Otherwise, it must be set
X			   to 0. */
X	double lower,upper; /* The interpretation is type
X			       dependent */
X	int userflag;	/* It is only used in V_intkw among all
X			   the predefined type. */
X	void *userdata; /* Same as above */
X	int (*hook)();  /* A user hook for this key. If it is not
X			   used, this field must be set to NULL */
X
X};
X
XThe keystr field of the last record  of keyTable must  be NULL to
Xindicate end of the list.
X
X
X3.2  Predefined Types
X
XThe predefined types can be divided into five major categories.
X	1. Integer numbers
X	2. Floating point numbers
X	3. Character or character strings
X	4. Keywords
X	5. User defined types
X
X
X
X3.2.1  Integer Numbers
X
XThere are a total of 7 predefined Integer types. They are:
X
X	  Name		Variable Type		Range
X	--------	-------------		-----
X	V_byte 		(signed) char		-128,127
X	V_ubyte		unsigned char		0,255
X	V_short		(signed) short		-32768,32767
X	V_ushort	unsigned short		0,65535
X	V_int		(signed) int		machine dependent
X	V_uint		unsigned int		machine dependent
X	V_long		(signed) long		-2147483648,
X						   2147483647
X
XIf int of the C compiler is 16-bit, the range of V_int and V_uint
Xwill be the same as V_short and V_ushort respectively. Otherwise,
Xthey are the same as V_long. If int is 32-bits, CONFIG may not be
Xable to correctly read large values for V_uint.
X
XThe  fields  lower and upper are used  to  specify the  lower and
Xupper  limit of  the acceptable value.  If upper is  less than or
Xequal to lower,  the defaults specified in the range column above
Xwill be used.
X
X
X3.2.2  Floating Numbers
X
XThere are 2 predefined types for floating numbers.
X
X	 Name		Variable Type		Range
X	------	 	-------------	 	-----
X	V_float		float		   -1e+30 -- +1e30
X	V_double	double		   -1e+30 -- +1e30
X
XThe lower and upper fields  are  used  to  specify  the allowable
Xinput range.  If upper is less  than or equal to  lower, defaults
Xfields specified in the range above will be used.
X
X
X3.2.3  Characters or Strings
X
XThere are 3 predefined types for character or strings.
X
X	 Name		Variable Type
X	------		-------------
X	V_char		char
X	V_string	char name[#]	# is a integer number
X	V_charptr	char *name
X
XFor V_char,  only one character  can be specified  in each field.
XTherefore,  if the array size  is  greater  than  one,  the input
Xcharacters must be separated by commas or white spaces.
X
XThe memory area for storing the string for type V_string  must be
Xdeclared  before  calling Config.  Usually,  the  string  will be
Xdeclared as a character array.  The upper field is used to stored
Xthe size of the character array.  Therefore the maximum length of
Xthe string is upper-1 excluding the termination NULL character.
X
XAnother  way to  read  character string  is  to  pass a character
Xpointer only to  Config.  Config will automatically malloc memory
Xfor the input string. The lower field specify the minimum size of
Xthe memory got from malloc and the upper field specify  the upper
Xlimit.
X
XThe  usually escape  sequence  in C may  be used in  the input to
Xspecify  the control characters  except \nnn is  not implemented.
XThe escape sequences include:
X
X	\b	backspace		\f	formfeed
X	\l	formfeed (^L)		\n	newline
X	\r	return			\t	tab
X
XSingle quotes in V_char and double quotes in V_string or
XV_charptr are optional but if they are used, both starting and
Xending quotes must be included.
X
X
X3.2.4  Keywords
X
XThe  type  V_intkw  allows  the  user  to  specify  the  list  of
Xacceptable keywords for the configurable key. The keyword is then
Xconverted to  a integer number  and stored  in  the variable. The
Xvariable must have the type int.
X
XThe  keyword number  is the order  of the list of  keywords given
X(described later)  starting from 0.  The field lower is  added to
Xthe number before storing in the variable. The field upper is not
Xused.   The  field  userflag  is  used  to  specify  whether  the
Xcomparison should  be  exact (TRUE)  or case insensitive (FALSE).
Xuserdata points  to  the array of  keywords.  The array should be
Xdeclared as  char  *name[].  The last entry of the array  must be
XNULL. See the example included for more information.
X
X
X3.2.5  User Types
X
XThere are predefined  user types --  V_usertype0 and V_usertype1.
XPointers   to  the   processing  function   should   be   put  in
XUTypeHook[#].  #  is either 0  or 1.  Default value is NULL which
Xmeans the type is not used.
X
X
X3.3  Config Interface routines
X
XThere are 3 routines in CONFIG that will normally be used.
X
X1. readconfig(char *filename)
X	The main function to read the configuration file  in filename.
X   If filename is not found, FALSE will be returned.
X
X2. PresetKey(char *key)
X	set the field val_set to Preset so  that the value  of the key
X   will not be changed. It will return false if key is not found.
X
X3. CheckAllKeys()
X	Check all keys  to  make  sure  that no val_set  field contain
X   FALSE.  It will  return  FALSE if  one or  more fields contain
X   FALSE.
X
X
X3.4  User Hooks
X
XThere are a number of user hooks  in CONFIG so that the  user may
Xextend CONFIG without changing the source codes.
X
X1. UTypeHook is described in "3.2.5 User Types".
X
X2.  UserDefHook is called when an unknown type is encountered. It
Xis a pointer to function(KeyRec *).
X
X3. Each key has its own hook in the KeyRec.hook. It is pointer to
Xfunction(int, KeyRec *). The function is called twice if defined.
XIt is called when the key is found in the configuration  file and
Xbefore any processing regarding the key.  It is called with first
Xargument equal to 0.  If it returns a FALSE, CONFIG will skip the
Xprocessing  and assume  the user  routine has done  the necessary
Xprocessing.  The  function  is  called the second time  after the
Xinput is  processed by CONFIG.  It is called  with first argument
Xequal to 1.
X
X
X3.5  Config-able Parameters
X
XThe following variables may be  set at  run  time  to  change the
Xdefaults in CONFIG.
X
X1. cfgVerbose  --  Default is FALSE.  When set to TRUE, debugging
Xinformation will be printed.
X
X2.  KeyCaseSensitive --  Default is FALSE.  When set to TRUE, the
Xcomparison will be case sensitive.
X
X3.  cfgScan  --  Default is FALSE.  When set to  TRUE, warning is
X*not*  issued even if a key in configuration file is not found in
Xthe KeyTable.  This is useful when a single configuration file is
Xshared by several programs.
X
X
X4  An Example
X
XCONFIG may sound much more complicated than it actually is. The
Xbest way to understand it is to look at the example included.
XBelow is two of the KeyRec's in the KeyTable.
X
X    (a)		(b)	  (c)	    (d)	   (e) (f)  (g)
X"TopMargin", &topMargin, V_int, cfgDefault, 0, -50, 50
X
X(a) is the key
X(b) is the address of the variable
X(c) is the type. TopMargin is an integer
X(d) means the default value is assigned. Therefore, this key is
X    optional
X(e) It is a simple variable, so 0 is used.
X(f) lower limit
X(g) upper limit
X
XAll remaining fields are automatically set to 0.
X
XAnother example is:
X
X     (a)	 (b)	 (c)	 (d)  (e)(f)(g)  (h)	(i)
X"CaseSenitive", &Case, V_intkw, FALSE, 0, 0, 0, FALSE, boolkw
X
X(a) is the key
X(b) is the address of the variable
X(c) is the type -- V_intkw
X(d) says no default value is assigned.
X(e) means it is a simple variable
X(f) will be added to the keyword number before storing in Case
X(g) not used
X(h) case insensitive comparison
X(i) the list of keywords. It is declared as
X	char *boolkw[] = {
X		"false", "true", NULL
X	};
X    "false" has the keyword number 0 and "true" has the keyword
Xnumber 1.
X
XRemember when a array is used, no '&' is required to get the
Xaddress. All the CONFIG constants are in the file "config.h".
X
SHAR_EOF
fi # end of overwriting check
if test -f 'config.h'
then
	echo shar: will not over-write existing file "'config.h'"
else
sed 's/^X//' << \SHAR_EOF > 'config.h'
X/*==================================================================*/
X/*	CONFIG Version 1.0  <August 21, 1988>			    */
X/*								    */
X/*	Written by Conrad Kwok, Division of Computer Science, UCD   */
X/*								    */
X/*	Permission is granted for freely distribution of this file  */
X/*		provided that this message is included.		    */
X/*==================================================================*/
X
X#define cfgDefault (-1)
X#define FALSE 0
X#define TRUE 1
X#define cfgPreset 2
X
X#define V_byte  1
X#define V_ubyte 2
X#define V_short 3
X#define V_ushort 4
X#define V_int   5
X#define V_uint  6
X#define V_long  7
X#define V_ulong 8
X#define V_float 9
X#define V_double 10
X#define V_string 11
X#define V_charptr 12
X#define V_char 13
X#define V_intkw 14
X#define V_usertype0 20
X#define V_usertype1 21
X
X#ifdef OldFashion
X#define void char
X#endif
X
Xstruct kr_struct {
X    char *keystr;
X    void *addr;
X    int vtype;
X    int val_set;
X    int arraysize;
X    double lower, upper;
X    int userflag;
X    void *userdata;
X    int (*hook)();
X};
X
Xtypedef struct kr_struct KeyRec;
X
Xextern int (*UTypeHook[])();
Xextern int (*UserDefHook)();
Xextern int cfgVerbose;
Xextern int KeyCaseSensitive;
Xextern int cfgScan;
X
Xextern KeyRec KeyTable[];
SHAR_EOF
fi # end of overwriting check
if test -f 'main.c'
then
	echo shar: will not over-write existing file "'main.c'"
else
sed 's/^X//' << \SHAR_EOF > 'main.c'
X#include <stdio.h>
X#include "config.h"
X
Xint TopMargin = 0, BotMargin = 0;
Xint Margin[4]= {10,20,30,40};
Xchar keys[2][6];
Xchar *line;
Xchar swch='/';
Xdouble Mag = 1;
Xint Case;
X
Xchar *boolkw[] = {
X    "false", "true", NULL
X};
X
XKeyRec KeyTable[] = {
X    { "TopMargin", &TopMargin, V_int, cfgDefault, 0, -50, 50 },
X    { "BottomMargin", &BotMargin, V_int, cfgDefault, 0, -50, 50 },
X    { "Magnification", &Mag, V_double, cfgDefault, 0, 0, 10 },
X    { "Margin", Margin, V_int, cfgDefault, 4 },
X    { "Keys", keys, V_string, FALSE, 2, 0, 6 },
X    { "Line", &line, V_charptr, FALSE, 0, 0, 0 },
X    { "SwitchChar", &swch, V_char, cfgDefault, 0 },
X    { "CaseSensitive", &Case, V_intkw, FALSE, 0, 0, 0, FALSE, boolkw },
X    { NULL } };
X
X
Xmain()
X{
X    cfgVerbose = TRUE;
X    KeyCaseSensitive = TRUE;
X    PresetKey("TopMargin");
X    if (readconfig("config.cfg") == 0) {
X        fprintf(stderr,"Configuration Not found\n");
X        exit(1);
X    }
X    CheckAllKeys();
X    printf("%d %d\n", TopMargin, BotMargin);
X    printf("%d %d %d %d\n", Margin[0], Margin[1],Margin[2], Margin[3]);
X    printf("%s %s\n", keys[0], keys[1]);
X    printf("%s\n", line);
X    printf("%lf\n", Mag);
X    printf("switch char = %c\n", swch);
X    printf("case %d\n", Case);
X}
X
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0