home *** CD-ROM | disk | FTP | other *** search
- /* dataman - simple data manager for reading data from text files
- * Written by Jim McBeath (jimmc) at SCI
- *
- * Revision history:
- * 24-Jan-85 Jim McBeath Put structure description into include file
- * 14-Mar-86 J. McBeath Add getRecord()
- * 9-May-86 J. McBeath Add unnumbered index mode
- * 13-May-68 J. McBeath Bug fix for unnumbered record at start of file;
- * add check for duplicate record numbers;
- * modify error message scheme; add multi-line
- * data value capability.
- * 18-Sep-86 J. McBeath make strsave and strsave2 not static
- * v1.5 jimmc 8.jan.87 Add checks for trailing spaces
- * v1.6 jimmc 19.jan.87 Output error messages to error file also,
- * add closeDataFile
- * 8.Jan.88 jimmc Lint cleanup
- */
-
- /* This module is a very simple data manager which allows a simple
- interface to a text file. The text file is the database. This
- module has routines to read that database (it is assumed that
- the writing is done by another program, e.g. a text editor).
- The format of the data file is as follows:
-
- A file consists of a sequence of records. All records are ascii
- text. Records are separated by blank lines. Lines within each
- record contain the data for that record. The first line of a
- record must begin with an integer. This integer is the index
- number of that record, and is used to reference the record. All
- other text on the line with the index number is ignored, so
- can be used as a comment line.
-
- Alternatively, the records can all be unnumbered. In this case,
- index numbers are automatically generated. The only allowable
- record number in this mode is 0, which specifies that that record
- is a comment record and is to be ignored. The record numbers in
- this mode start at 1 and increase sequentially.
-
- The remaining lines in a record are data items for that record.
- A data item consists of a key and a payload. The key is any
- string of alphanumeric characters or '_' or '.' followed immediately
- by a colon. All remaining text on the line,
- after the colon, is the payload for that data item.
-
- Both index numbers and key strings must be unique; non-unique
- items will not be referenceable. Index numbers need not be in
- any order. Key strings should be short to increase speed.
- (Note that this is not designed to be a particularly fast
- system anyway!)
- */
-
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <ctype.h>
- #include <strings.h>
- #include "index.h"
- #include "dataman.h"
-
- struct dline {
- int ltype; /* what kind of data line we found */
- #define LBLANK 0 /* nothing at all on the line */
- #define LCOMMENT 1 /* line starts with a colon */
- #define LKEY 2 /* normal key:value line */
- #define LNOKEY 3 /* line with text but no key, no leading colon */
- char *key; /* malloc'ed key string */
- char *value; /* malloc'ed value string, including \n */
- };
-
- extern char *malloc(), *realloc();
- extern int errno;
- extern int sys_nerr;
- extern char *sys_errlist[];
- extern long getIndex();
-
- struct toplevel *initIndex();
- FILE *indexfp; /* used by writeoneindex */
- FILE *errorfp; /* used to write out errors when making index */
-
- #ifdef vms
- #define index strchr
- #endif
-
- int dataDebug=0; /* set this flag to give debug output */
- int dataStatus=0; /* status after most recent operation */
- char *dataErrMsg=""; /* the error message */
- char dataErrBuf[1000]; /* where we put our error messages */
- char *dataErrStrs[] = {
- "successful data operation", /* 0 */
- "can't open data file", /* 1 */
- "no more memory", /* 2 */
- "can't start an index table", /* 3 */
- "invalid pointer to getData", /* 4 */
- "no file open in getData", /* 5 */
- "no index table in getData", /* 6 */
- "no such index number", /* 7 */
- "key not found", /* 8 */
- "mixed records with and without indexes", /* 9 */
- "unnumbered records must not start on first line of file", /* 10 */
- "can't open index file", /* 11 */
- "error creating new index file", /* 12 */
- };
- #define ERET(n) { dataStatus = n; dataErrMsg = dataErrStrs[n]; return 0; }
- #define EMSGRET { dataStatus = -1; dataErrMsg = dataErrBuf; return 0; }
- #define EPRET(n) { sprintf(dataErrBuf, "%s: %s", dataErrStrs[n], \
- (errno<sys_nerr?sys_errlist[errno]:"Unknown unix error")); \
- dataStatus = n; dataErrMsg = dataErrBuf; return 0; }
-
- /*..........*/
-
- long /* return the last-modification date of a file, or 0 */
- fdate(fn)
- char *fn; /* name of the file to get the date for */
- {
- struct stat sbuf;
- int t;
-
- t = stat(fn,&sbuf);
- if (t) return 0;
- return sbuf.st_mtime;
- }
-
- /*..........*/
-
- long /* return the last-modification date of a file, or 0 */
- fddate(fp)
- FILE *fp; /* the file to get the date for */
- {
- struct stat sbuf;
- int t;
-
- t = fstat(fileno(fp),&sbuf);
- if (t) return 0;
- return sbuf.st_mtime;
- }
-
- /*..........*/
-
- char *strsave(ss)
- char *ss;
- {
- char *dd;
- dd = malloc((unsigned)(strlen(ss)+1));
- if (dd==0) fferror("no more memory");
- strcpy(dd,ss);
- return dd;
- }
-
- /*..........*/
-
- char *strsave2(s1,s2)
- char *s1,*s2;
- {
- char *dd;
- int l1,l2;
- l1 = strlen(s1);
- l2 = strlen(s2);
- dd = malloc((unsigned)(l1+l2+1));
- if (dd==0) fferror("no more memory");
- strcpy(dd,s1);
- strcpy(dd+l1,s2);
- return dd;
- }
-
- /*..........*/
-
- int
- writeoneindex(n,l)
- int n; /* index number */
- int l; /* seek offset */
- {
- if (n<=0) return 0;
- putw(n,indexfp); /* write out the index number */
- putw(l,indexfp); /* write out the seek offset */
- return 0;
- }
-
- /*..........*/
-
- char *dlinebuf=0;
- int dlinebufsize=0;
- int dlineno;
- char *dfilename;
-
- getdline(fp)
- FILE *fp;
- {
- int t;
-
- if (dlinebufsize==0) {
- dlinebufsize = 100; /* a starting point */
- dlinebuf = malloc((unsigned)dlinebufsize);
- if (dlinebuf==0) fferror("no more memory");
- }
- dlinebuf[dlinebufsize-1]=0; /* set to null so we can check it */
- dlinebuf[dlinebufsize-2]=0;
- dlinebuf[0]=0;
- fgets(dlinebuf,dlinebufsize,fp);
- while (dlinebuf[dlinebufsize-2]!=0 && dlinebuf[dlinebufsize-2]!='\n') {
- t = dlinebufsize-1; /* this is where new piece should start */
- dlinebufsize *=2; /* try twice the size */
- dlinebuf = realloc(dlinebuf,(unsigned)dlinebufsize);
- if (dlinebuf==0) fferror("no more memory");
- dlinebuf[t]=0;
- dlinebuf[dlinebufsize-1]=0;
- dlinebuf[dlinebufsize-2]=0;
- fgets(dlinebuf+t,dlinebufsize-t,fp);
- /* pick up next part of line */
- }
- /* We now have the complete line in dlinebuf, no matter how long it was! */
- dlineno++;
- }
-
- /*..........*/
-
- struct dline * /* reads and parses one line */
- readdline(fp,dlp)
- FILE *fp; /* File to read from */
- struct dline *dlp; /* structure to fill in; if nul, allocates one */
- {
- int c;
- char *cp;
-
- if (dlp==0) {
- dlp = (struct dline *)malloc(sizeof(struct dline));
- if (dlp==0) fferror("no more memory");
- dlp->key=0;
- dlp->value=0;
- }
- getdline(fp); /* read data line into dlinebuf */
- for (cp=dlinebuf; (c= *cp)!=0; cp++) {
- if (!(isalnum(c)||c=='_'||c=='.')) break;
- }
- if (c=='\n' || c==0) {
- if (cp==dlinebuf) dlp->ltype=LBLANK;
- else dlp->ltype=LNOKEY;
- dlp->key = 0;
- dlp->value = strsave(dlinebuf);
- }
- else if (c==':') {
- if (cp==dlinebuf) {
- dlp->ltype=LCOMMENT;
- dlp->key=0;
- }
- else {
- dlp->ltype=LKEY;
- *cp = 0; /* null terminate the key */
- dlp->key = strsave(dlinebuf);
- }
- dlp->value = strsave(cp+1);
- }
- else {
- dlp->ltype=LNOKEY;
- dlp->key = 0;
- dlp->value = strsave(dlinebuf);
- }
- return dlp;
- }
-
- /*..........*/
-
- struct dpoint * /* a pointer to our internal struct */
- /* returns 0 on error */
- initDataFile(fn) /* init the data file to be used */
- char *fn; /* the filename to look up */
- {
- FILE *fp, *ifp;
- struct dpoint *pp;
- struct toplevel *qq;
- int iflag,n;
- int lastindex=0;
- int indexedmode=0; /* 0 means mode is not yet set */
- #define INDEXMODEYES 1
- #define INDEXMODENO 2
- long lastftell=0;
- char *indexfn;
- char *errorfn;
- #define INDEXSUFF ".index"
- #define ERRORSUFF ".error"
- int l,c;
- int t;
- long lt;
- char *msg;
-
- dfilename = fn; /* for error messages during init */
- dlineno = 0;
- fp = fopen(fn,"r"); /* get his data file */
- if (!fp) EPRET(1) /* can't do anything if no file */
- pp = (struct dpoint *)malloc(sizeof(struct dpoint));
- if (!pp) { /* if no memory for us */
- fclose(fp); /* dump the file */
- EPRET(2) /* error return */
- }
- pp->ff = fp; /* put file pointer into our data block */
- qq = initIndex(); /* start up an index table */
- if (qq==0) { /* if can't start up an index table */
- fclose(fp);
- free((char *)pp);
- ERET(3)
- }
- pp->xx = qq; /* save pointer to index table */
- if (dataDebug) printf("initDataFile: index table is at %X\n", qq);
-
- l = strlen(fn)+sizeof(INDEXSUFF)+1;
- indexfn = malloc((unsigned)l);
- if (indexfn==0) ERET(2) /* no more memory */
- sprintf(indexfn,"%s%s", fn, INDEXSUFF);
- if (fdate(indexfn)>fddate(fp)) {
- /* if we have an up-to-date index */
- ifp = fopen(indexfn,"r");
- if (ifp==0) EPRET(11)
- while (!feof(ifp)) {
- n = getw(ifp); /* read the index number */
- lastftell = getw(ifp);
- /* read the seek offset */
- if (n>0) setIndex(qq,n,lastftell);
- /* set the table */
- }
- dataStatus=0;
- return pp; /* done */
- }
-
- l = strlen(fn)+sizeof(ERRORSUFF)+1;
- errorfn = malloc((unsigned)l);
- if (errorfn==0) ERET(2) /* no more memory */
- sprintf(errorfn,"%s%s", fn, ERRORSUFF);
- errorfp = fopen(errorfn,"w"); /* the error file */
- iflag = 1; /* note we are looking for start of record */
- while (!feof(fp)) { /* scan through the file */
- getdline(fp); /* read line into dlinebuf */
- l = strlen(dlinebuf);
- if (l>0 && dlinebuf[l-1]=='\n') --l;
- if (l>0 && ((c=dlinebuf[l-1])==' ' || c=='\t')) {
- msg="trailing spaces or tabs";
- fprintf(stderr,
- "warning: %s in data file %s on line %d\n",
- msg, dfilename, dlineno);
- if (errorfp) fprintf(errorfp,
- "warning: %s in data file %s on line %d\n",
- msg, dfilename, dlineno);
- }
- if (iflag) { /* looking for start of next record */
- if (dlinebuf[0]==0) break; /* EOF */
- if (dlinebuf[0]=='\n') { /* another blank line */
- if (indexedmode!=INDEXMODEYES)
- lastftell = ftell(fp);
- }
- else if (sscanf(dlinebuf,"%d",&n)==1) {
- /* if index number */
- if (n>0) { /* ignore records numbered 0 */
- switch(indexedmode) {
- case 0:
- indexedmode=INDEXMODEYES;
- break;
- case INDEXMODENO:
- ERET(9) /* NOTREACHED */
- }
- if (dataDebug)
- printf("initDataFile: index %d at loc %d\n", n,ftell(fp));
- lastftell = ftell(fp);
- lt = getIndex(qq,n);
- if (lt!=0) {
- sprintf(dataErrBuf,
- "duplicate record index %d (seek offsets %d and %d)",
- n, lt, lastftell);
- EMSGRET
- }
- setIndex(qq,n,lastftell);
- /* remember start of next line */
- }
- iflag=0;
- /* no longer looking for start of record */
- }
- else { /* start of a record without an index */
- switch (indexedmode) {
- case 0:
- indexedmode=INDEXMODENO;
- break;
- case INDEXMODEYES:
- ERET(9) /* NOTREACHED */
- }
- if (dataDebug)
- printf("initDataFile: index %d at loc %d\n", lastindex+1,lastftell);
- if (lastftell==0)
- ERET(10) /* can't start right at 0! */
- setIndex(qq,++lastindex,lastftell);
- /* remember start of this line */
- iflag=0;
- }
- }
- else { /* in the middle of a record */
- if (dlinebuf[0]=='\n') {
- /* see if this is a blank line */
- if (indexedmode!=INDEXMODEYES)
- lastftell = ftell(fp);
- iflag++; /* note blank line - */
- /* start looking for next record */
- }
- }
- }
- if (errorfp) fclose(errorfp);
- /* Now create an index file to use for future runs */
- indexfp = fopen(indexfn,"w");
- if (indexfp) { /* if we got it, make the file */
- enumIndex(pp->xx,writeoneindex); /* write out the index info */
- t = fclose(indexfp);
- if (t) { EPRET(12) }
- }
- dataStatus=0;
- return pp; /* return pointer to our structure */
- }
-
- /*..........*/
-
- closeDataFile(pp)
- struct dpoint *pp;
- {
- if (!pp) ERET(4) /* check for valid pointer */
- if (!(pp->ff)) ERET(5) /* error return if no file open */
- if (!(pp->xx)) ERET(6) /* error if no index table pointer */
- freeIndex(pp->xx); /* dump the index */
- fclose(pp->ff); /* free up the file pointer */
- free((char *)pp);
- dataStatus=0;
- return 0;
- }
-
- /*..........*/
-
- char *getdatalastp=0;
-
- char * /* returns a pointer to the data string */
- /* return NULL if no data */
- getData(pp,indexn,key) /* get a data item */
- struct dpoint *pp; /* pointer to our structure */
- int indexn; /* the index of the record of interest */
- char *key; /* the key string */
- {
- long l;
- int len;
- char *index();
- struct dline dl, dl2;
- struct dline *dlp, *dlp2;
- int unnamedok;
- char *oldv, *addv;
-
- if (!pp) ERET(4) /* check for valid pointer */
- if (!(pp->ff)) ERET(5) /* error return if no file open */
- if (!(pp->xx)) ERET(6) /* error if no index table pointer */
- l = getIndex(pp->xx,indexn); /* get the lseek value for that index */
- if (l==0) ERET(7) /* error if no lseek value for that index */
- fseek(pp->ff,l,0); /* seek to the start of that line */
- if (strcmp(key,"unnamed")==0) unnamedok=1;
- else unnamedok=0;
- while (!feof(pp->ff)) { /* read lines until eof (or blank) */
- dlp = readdline(pp->ff,&dl);
- if (dlp->ltype==LBLANK) break; /* blank line */
- else if (dlp->ltype==LCOMMENT) continue;
- else if (dlp->ltype==LKEY) unnamedok=0;
- if ((dlp->ltype==LNOKEY && unnamedok) ||
- (dlp->ltype==LKEY && strcmp(key,dlp->key)==0)) {
- /* we've got a hot one */
- dlp2 = readdline(pp->ff,&dl2);
- while (dlp2->ltype==LNOKEY) {
- oldv = dlp->value;
- addv = dlp2->value;
- if (*addv == '+')
- addv++;
- /* skip over leading plus mark */
- dlp->value = strsave2(dlp->value,addv);
- free(oldv);
- free(dlp2->value);
- dlp2 = readdline(pp->ff,&dl2);
- }
- len = strlen(dlp->value);
- if (dlp->value[len-1]=='\n') dlp->value[len-1]=0;
- free(dlp2->key);
- free(dlp2->value);
- free(dlp->key);
- if (getdatalastp) free(getdatalastp);
- getdatalastp = dlp->value;
- dataStatus=0;
- return dlp->value;
- }
- } /* continue - read next line and compare */
- ERET(8) /* eof or blank line, key not found */
- }
-
- /*..........*/
-
- struct drecord * /* returns a pointer to a record struct */
- /* return NULL if no data or error */
- getRecord(pp,indexn,rp) /* get a complete record */
- struct dpoint *pp; /* pointer to our structure */
- int indexn; /* the index of the record of interest */
- struct drecord *rp; /* structure to fill in; if NULL, allocs one */
- {
- int i;
- int len;
- long l;
- char *index();
- int numfields;
- #define MAXRECNUM 1000
- char *names[MAXRECNUM]; /* make number of fields */
- char *values[MAXRECNUM];
- struct dline dl, dl2;
- struct dline *dlp, *dlp2;
- char *oldv, *addv;
-
- if (!pp) ERET(4) /* check for valid pointer */
- if (!(pp->ff)) ERET(5) /* error return if no file open */
- if (!(pp->xx)) ERET(6) /* error if no index table pointer */
- if (rp==0) {
- rp = (struct drecord *)malloc(sizeof(struct drecord));
- if (rp==0) return 0;
- rp->numfields = 0;
- rp->name = 0;
- rp->value = 0;
- }
- for (i=0; i<rp->numfields; i++) {
- free(rp->name[i]);
- free(rp->value[i]);
- };
- if (rp->name) free((char *)rp->name);
- if (rp->value) free((char *)rp->value);
- l = getIndex(pp->xx,indexn); /* get the lseek value for that index */
- if (l==0) ERET(7) /* error if no lseek value for that index */
- fseek(pp->ff,l,0); /* seek to the start of that line */
- numfields = 0;
- dlp = readdline(pp->ff,&dl);
- while (dlp->ltype!=LBLANK) {
- while (dlp->ltype==LCOMMENT) {
- free(dlp->value);
- dlp = readdline(pp->ff,&dl);
- }
- if (dlp->ltype==LBLANK) break;
- dlp2 = readdline(pp->ff,&dl2);
- while (dlp2->ltype==LNOKEY) {
- oldv = dlp->value;
- addv = dlp2->value;
- if (*addv == '+')
- addv++; /* skip over leading plus mark */
- dlp->value = strsave2(dlp->value,addv);
- free(oldv);
- free(dlp2->value);
- dlp2 = readdline(pp->ff,&dl2);
- }
- len = strlen(dlp->value);
- if (dlp->value[len-1]=='\n') dlp->value[len-1]=0;
- if (dlp->ltype==LNOKEY)
- names[numfields] = strsave("unnamed");
- else names[numfields] = dlp->key;
- values[numfields] = dlp->value;
- numfields++;
- dl = dl2;
- }
- if (dlp->ltype==LBLANK) free(dlp->value);
- dataStatus=0;
- rp->numfields = numfields;
- rp->name = (char **)malloc((unsigned)(numfields*sizeof(char *)));
- if (rp->name==0) return 0;
- rp->value = (char **)malloc((unsigned)(numfields*sizeof(char *)));
- if (rp->value==0) return 0;
- for (i=0; i<numfields; i++) {
- rp->name[i] = names[i];
- rp->value[i] = values[i];
- }
- return rp;
- }
-
- /* end */
-