v04i034: Config 1.0
Conrad Kwok
kwok at iris.ucdavis.edu
Wed Aug 24 14:32:47 AEST 1988
Posting-number: Volume 4, Issue 34
Submitted-by: "Conrad Kwok" <kwok at 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 at iris.ucdavis.edu
Xcsnet : kwok at ucd.csnet
Xcsnet : kwok%iris.ucdavis.edu at 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 at iris.ucdavis.edu
Xcsnet : kwok at ucd.csnet
Xcsnet : kwok%iris.ucdavis.edu at 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
More information about the Comp.sources.misc
mailing list