home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume18 / geneal / part03 / dataman.c next >
C/C++ Source or Header  |  1989-03-08  |  16KB  |  572 lines

  1. /* dataman - simple data manager for reading data from text files
  2.  * Written by Jim McBeath (jimmc) at SCI
  3.  *
  4.  * Revision history:
  5.  * 24-Jan-85    Jim McBeath    Put structure description into include file
  6.  * 14-Mar-86  J. McBeath    Add getRecord()
  7.  *  9-May-86  J. McBeath    Add unnumbered index mode
  8.  * 13-May-68  J. McBeath    Bug fix for unnumbered record at start of file;
  9.  *                add check for duplicate record numbers;
  10.  *                modify error message scheme; add multi-line
  11.  *                data value capability.
  12.  * 18-Sep-86  J. McBeath    make strsave and strsave2 not static
  13.  * v1.5  jimmc     8.jan.87  Add checks for trailing spaces
  14.  * v1.6  jimmc  19.jan.87  Output error messages to error file also,
  15.  *                add closeDataFile
  16.  *  8.Jan.88  jimmc  Lint cleanup
  17.  */
  18.  
  19. /* This module is a very simple data manager which allows a simple
  20.    interface to a text file.  The text file is the database.  This
  21.    module has routines to read that database (it is assumed that
  22.    the writing is done by another program, e.g. a text editor).
  23.    The format of the data file is as follows:
  24.  
  25.    A file consists of a sequence of records.  All records are ascii
  26.    text.  Records are separated by blank lines.  Lines within each
  27.    record contain the data for that record.  The first line of a
  28.    record must begin with an integer.  This integer is the index
  29.    number of that record, and is used to reference the record.  All
  30.    other text on the line with the index number is ignored, so
  31.    can be used as a comment line.
  32.  
  33.    Alternatively, the records can all be unnumbered.  In this case,
  34.    index numbers are automatically generated.  The only allowable
  35.    record number in this mode is 0, which specifies that that record
  36.    is a comment record and is to be ignored.  The record numbers in
  37.    this mode start at 1 and increase sequentially.
  38.  
  39.    The remaining lines in a record are data items for that record.
  40.    A data item consists of a key and a payload.  The key is any
  41.    string of alphanumeric characters or '_' or '.' followed immediately
  42.    by a colon.  All remaining text on the line,
  43.    after the colon, is the payload for that data item.
  44.  
  45.    Both index numbers and key strings must be unique; non-unique
  46.    items will not be referenceable.  Index numbers need not be in
  47.    any order.  Key strings should be short to increase speed.
  48.    (Note that this is not designed to be a particularly fast
  49.    system anyway!)
  50. */
  51.  
  52. #include <stdio.h>
  53. #include <sys/types.h>
  54. #include <sys/stat.h>
  55. #include <ctype.h>
  56. #include <strings.h>
  57. #include "index.h"
  58. #include "dataman.h"
  59.  
  60. struct dline {
  61.     int ltype;    /* what kind of data line we found */
  62. #define LBLANK 0    /* nothing at all on the line */
  63. #define LCOMMENT 1    /* line starts with a colon */
  64. #define LKEY 2        /* normal key:value line */
  65. #define LNOKEY 3    /* line with text but no key, no leading colon */
  66.     char *key;    /* malloc'ed key string */
  67.     char *value;    /* malloc'ed value string, including \n */
  68. };
  69.  
  70. extern char *malloc(), *realloc();
  71. extern int errno;
  72. extern int sys_nerr;
  73. extern char *sys_errlist[];
  74. extern long getIndex();
  75.  
  76. struct toplevel *initIndex();
  77. FILE *indexfp;        /* used by writeoneindex */
  78. FILE *errorfp;        /* used to write out errors when making index */
  79.  
  80. #ifdef vms
  81. #define index strchr
  82. #endif
  83.  
  84. int dataDebug=0;        /* set this flag to give debug output */
  85. int dataStatus=0;        /* status after most recent operation */
  86. char *dataErrMsg="";        /* the error message */
  87. char dataErrBuf[1000];        /* where we put our error messages */
  88. char *dataErrStrs[] = {
  89.     "successful data operation",    /* 0 */
  90.     "can't open data file",        /* 1 */
  91.     "no more memory",            /* 2 */
  92.     "can't start an index table",    /* 3 */
  93.     "invalid pointer to getData",    /* 4 */
  94.     "no file open in getData",        /* 5 */
  95.     "no index table in getData",    /* 6 */
  96.     "no such index number",        /* 7 */
  97.     "key not found",            /* 8 */
  98.     "mixed records with and without indexes",    /* 9 */
  99.     "unnumbered records must not start on first line of file", /* 10 */
  100.     "can't open index file",        /* 11 */
  101.     "error creating new index file",    /* 12 */
  102. };
  103. #define ERET(n) { dataStatus = n;  dataErrMsg = dataErrStrs[n]; return 0; }
  104. #define EMSGRET { dataStatus = -1; dataErrMsg = dataErrBuf;     return 0; }
  105. #define EPRET(n) { sprintf(dataErrBuf, "%s: %s", dataErrStrs[n], \
  106.            (errno<sys_nerr?sys_errlist[errno]:"Unknown unix error")); \
  107.           dataStatus = n; dataErrMsg = dataErrBuf; return 0; }
  108.  
  109. /*..........*/
  110.  
  111. long        /* return the last-modification date of a file, or 0 */
  112. fdate(fn)
  113. char *fn;    /* name of the file to get the date for */
  114. {
  115.     struct stat sbuf;
  116.     int t;
  117.  
  118.     t = stat(fn,&sbuf);
  119.     if (t) return 0;
  120.     return sbuf.st_mtime;
  121. }
  122.  
  123. /*..........*/
  124.  
  125. long        /* return the last-modification date of a file, or 0 */
  126. fddate(fp)
  127. FILE *fp;    /*  the file to get the date for */
  128. {
  129.     struct stat sbuf;
  130.     int t;
  131.  
  132.     t = fstat(fileno(fp),&sbuf);
  133.     if (t) return 0;
  134.     return sbuf.st_mtime;
  135. }
  136.  
  137. /*..........*/
  138.  
  139. char *strsave(ss)
  140. char *ss;
  141. {
  142.     char *dd;
  143.     dd = malloc((unsigned)(strlen(ss)+1));
  144.     if (dd==0) fferror("no more memory");
  145.     strcpy(dd,ss);
  146.     return dd;
  147. }
  148.  
  149. /*..........*/
  150.  
  151. char *strsave2(s1,s2)
  152. char *s1,*s2;
  153. {
  154.     char *dd;
  155.     int l1,l2;
  156.     l1 = strlen(s1);
  157.     l2 = strlen(s2);
  158.     dd = malloc((unsigned)(l1+l2+1));
  159.     if (dd==0) fferror("no more memory");
  160.     strcpy(dd,s1);
  161.     strcpy(dd+l1,s2);
  162.     return dd;
  163. }
  164.  
  165. /*..........*/
  166.  
  167. int
  168. writeoneindex(n,l)
  169. int n;            /* index number */
  170. int l;            /* seek offset */
  171. {
  172.     if (n<=0) return 0;
  173.     putw(n,indexfp);    /* write out the index number */
  174.     putw(l,indexfp);    /* write out the seek offset */
  175.     return 0;
  176. }
  177.  
  178. /*..........*/
  179.  
  180. char *dlinebuf=0;
  181. int dlinebufsize=0;
  182. int dlineno;
  183. char *dfilename;
  184.  
  185. getdline(fp)
  186. FILE *fp;
  187. {
  188.     int t;
  189.  
  190.     if (dlinebufsize==0) {
  191.         dlinebufsize = 100;    /* a starting point */
  192.         dlinebuf = malloc((unsigned)dlinebufsize);
  193.         if (dlinebuf==0) fferror("no more memory");
  194.     }
  195.     dlinebuf[dlinebufsize-1]=0;    /* set to null so we can check it */
  196.     dlinebuf[dlinebufsize-2]=0;
  197.     dlinebuf[0]=0;
  198.     fgets(dlinebuf,dlinebufsize,fp);
  199.     while (dlinebuf[dlinebufsize-2]!=0 && dlinebuf[dlinebufsize-2]!='\n') {
  200.         t = dlinebufsize-1; /* this is where new piece should start */
  201.         dlinebufsize *=2;    /* try twice the size */
  202.         dlinebuf = realloc(dlinebuf,(unsigned)dlinebufsize);
  203.         if (dlinebuf==0) fferror("no more memory");
  204.         dlinebuf[t]=0;
  205.         dlinebuf[dlinebufsize-1]=0;
  206.         dlinebuf[dlinebufsize-2]=0;
  207.         fgets(dlinebuf+t,dlinebufsize-t,fp);
  208.             /* pick up next part of line */
  209.     }
  210. /* We now have the complete line in dlinebuf, no matter how long it was! */
  211.     dlineno++;
  212. }
  213.  
  214. /*..........*/
  215.  
  216. struct dline *        /* reads and parses one line */
  217. readdline(fp,dlp)
  218. FILE *fp;        /* File to read from */
  219. struct dline *dlp;    /* structure to fill in; if nul, allocates one */
  220. {
  221.     int c;
  222.     char *cp;
  223.  
  224.     if (dlp==0) {
  225.         dlp = (struct dline *)malloc(sizeof(struct dline));
  226.         if (dlp==0) fferror("no more memory");
  227.         dlp->key=0;
  228.         dlp->value=0;
  229.     }
  230.     getdline(fp);    /* read data line into dlinebuf */
  231.     for (cp=dlinebuf; (c= *cp)!=0; cp++) {
  232.         if (!(isalnum(c)||c=='_'||c=='.')) break;
  233.     }
  234.     if (c=='\n' || c==0) {
  235.         if (cp==dlinebuf) dlp->ltype=LBLANK;
  236.         else dlp->ltype=LNOKEY;
  237.         dlp->key = 0;
  238.         dlp->value = strsave(dlinebuf);
  239.     }
  240.     else if (c==':') {
  241.         if (cp==dlinebuf) { 
  242.             dlp->ltype=LCOMMENT; 
  243.             dlp->key=0; 
  244.         }
  245.         else {
  246.             dlp->ltype=LKEY;
  247.             *cp = 0;    /* null terminate the key */
  248.             dlp->key = strsave(dlinebuf);
  249.         }
  250.         dlp->value = strsave(cp+1);
  251.     }
  252.     else {
  253.         dlp->ltype=LNOKEY;
  254.         dlp->key = 0;
  255.         dlp->value = strsave(dlinebuf);
  256.     }
  257.     return dlp;
  258. }
  259.  
  260. /*..........*/
  261.  
  262. struct dpoint *            /* a pointer to our internal struct */
  263. /* returns 0 on error */
  264. initDataFile(fn)        /* init the data file to be used */
  265. char *fn;            /* the filename to look up */
  266. {
  267.     FILE *fp, *ifp;
  268.     struct dpoint *pp;
  269.     struct toplevel *qq;
  270.     int iflag,n;
  271.     int lastindex=0;
  272.     int indexedmode=0;    /* 0 means mode is not yet set */
  273. #define INDEXMODEYES 1
  274. #define INDEXMODENO 2
  275.     long lastftell=0;
  276.     char *indexfn;
  277.     char *errorfn;
  278. #define INDEXSUFF ".index"
  279. #define ERRORSUFF ".error"
  280.     int l,c;
  281.     int t;
  282.     long lt;
  283.     char *msg;
  284.  
  285.     dfilename = fn;        /* for error messages during init */
  286.     dlineno = 0;
  287.     fp = fopen(fn,"r");        /* get his data file */
  288.     if (!fp) EPRET(1)        /* can't do anything if no file */
  289.     pp = (struct dpoint *)malloc(sizeof(struct dpoint));
  290.     if (!pp) {            /* if no memory for us */
  291.         fclose(fp);        /* dump the file */
  292.         EPRET(2)        /* error return */
  293.     }
  294.     pp->ff = fp;        /* put file pointer into our data block */
  295.     qq = initIndex();        /* start up an index table */
  296.     if (qq==0) {        /* if can't start up an index table */
  297.         fclose(fp);
  298.         free((char *)pp);
  299.         ERET(3)
  300.     }
  301.     pp->xx = qq;            /* save pointer to index table */
  302.     if (dataDebug) printf("initDataFile: index table is at %X\n", qq);
  303.  
  304.     l = strlen(fn)+sizeof(INDEXSUFF)+1;
  305.     indexfn = malloc((unsigned)l);
  306.     if (indexfn==0) ERET(2)        /* no more memory */
  307.     sprintf(indexfn,"%s%s", fn, INDEXSUFF);
  308.     if (fdate(indexfn)>fddate(fp)) {
  309.         /* if we have an up-to-date index */
  310.         ifp = fopen(indexfn,"r");
  311.         if (ifp==0) EPRET(11)
  312.             while (!feof(ifp)) {
  313.                 n = getw(ifp);    /* read the index number */
  314.                 lastftell = getw(ifp);
  315.                     /* read the seek offset */
  316.                 if (n>0) setIndex(qq,n,lastftell);
  317.                     /* set the table */
  318.             }
  319.         dataStatus=0;
  320.         return pp;        /* done */
  321.     }
  322.  
  323.     l = strlen(fn)+sizeof(ERRORSUFF)+1;
  324.     errorfn = malloc((unsigned)l);
  325.     if (errorfn==0) ERET(2)        /* no more memory */
  326.     sprintf(errorfn,"%s%s", fn, ERRORSUFF);
  327.     errorfp = fopen(errorfn,"w");    /* the error file */
  328.     iflag = 1;        /* note we are looking for start of record */
  329.     while (!feof(fp)) {        /* scan through the file */
  330.         getdline(fp);        /* read line into dlinebuf */
  331.         l = strlen(dlinebuf);
  332.         if (l>0 && dlinebuf[l-1]=='\n') --l;
  333.         if (l>0 && ((c=dlinebuf[l-1])==' ' || c=='\t')) {
  334.             msg="trailing spaces or tabs";
  335.             fprintf(stderr,
  336.                 "warning: %s in data file %s on line %d\n",
  337.                 msg, dfilename, dlineno);
  338.             if (errorfp) fprintf(errorfp,
  339.                 "warning: %s in data file %s on line %d\n",
  340.                 msg, dfilename, dlineno);
  341.         }
  342.         if (iflag) {        /* looking for start of next record */
  343.             if (dlinebuf[0]==0) break;    /* EOF */
  344.             if (dlinebuf[0]=='\n') {    /* another blank line */
  345.                 if (indexedmode!=INDEXMODEYES)
  346.                     lastftell = ftell(fp);
  347.             }
  348.             else if (sscanf(dlinebuf,"%d",&n)==1) {
  349.                 /* if index number */
  350.                 if (n>0) {    /* ignore records numbered 0 */
  351.                     switch(indexedmode) {
  352.                     case 0: 
  353.                         indexedmode=INDEXMODEYES; 
  354.                         break;
  355.                     case INDEXMODENO: 
  356.                         ERET(9) /* NOTREACHED */
  357.                     }
  358.                     if (dataDebug)
  359. printf("initDataFile: index %d at loc %d\n", n,ftell(fp));
  360.                     lastftell = ftell(fp);
  361.                     lt = getIndex(qq,n);
  362.                     if (lt!=0) {
  363.                         sprintf(dataErrBuf,
  364. "duplicate record index %d (seek offsets %d and %d)",
  365.                             n, lt, lastftell);
  366.                         EMSGRET
  367.                     }
  368.                     setIndex(qq,n,lastftell);
  369.                         /* remember start of next line */
  370.                 }
  371.                 iflag=0;
  372.                   /* no longer looking for start of record */
  373.             }
  374.             else {    /* start of a record without an index */
  375.                 switch (indexedmode) {
  376.                 case 0: 
  377.                     indexedmode=INDEXMODENO; 
  378.                     break;
  379.                 case INDEXMODEYES: 
  380.                     ERET(9)    /* NOTREACHED */
  381.                 }
  382.                 if (dataDebug)
  383. printf("initDataFile: index %d at loc %d\n", lastindex+1,lastftell);
  384.                 if (lastftell==0)
  385.                     ERET(10) /* can't start right at 0! */
  386.                 setIndex(qq,++lastindex,lastftell);
  387.                     /* remember start of this line */
  388.                 iflag=0;
  389.             }
  390.         }
  391.         else {            /* in the middle of a record */
  392.             if (dlinebuf[0]=='\n') {
  393.                 /* see if this is a blank line */
  394.                 if (indexedmode!=INDEXMODEYES)
  395.                     lastftell = ftell(fp);
  396.                 iflag++; /* note blank line - */
  397.                     /*  start looking for next record */
  398.             }
  399.         }
  400.     }
  401.     if (errorfp) fclose(errorfp);
  402.     /* Now create an index file to use for future runs */
  403.     indexfp = fopen(indexfn,"w");
  404.     if (indexfp) {        /* if we got it, make the file */
  405.         enumIndex(pp->xx,writeoneindex); /* write out the index info */
  406.         t = fclose(indexfp);
  407.         if (t) { EPRET(12) }
  408.     }
  409.     dataStatus=0;
  410.     return pp;            /* return pointer to our structure */
  411. }
  412.  
  413. /*..........*/
  414.  
  415. closeDataFile(pp)
  416. struct dpoint *pp;
  417. {
  418.     if (!pp) ERET(4)        /* check for valid pointer */
  419.     if (!(pp->ff)) ERET(5)        /* error return if no file open */
  420.     if (!(pp->xx)) ERET(6)        /* error if no index table pointer */
  421.     freeIndex(pp->xx);        /* dump the index */
  422.     fclose(pp->ff);        /* free up the file pointer */
  423.     free((char *)pp);
  424.     dataStatus=0;
  425.     return 0;
  426. }
  427.  
  428. /*..........*/
  429.  
  430. char *getdatalastp=0;
  431.  
  432. char *                /* returns a pointer to the data string */
  433. /*  return NULL if no data */
  434. getData(pp,indexn,key)        /* get a data item */
  435. struct dpoint *pp;        /* pointer to our structure */
  436. int indexn;            /* the index of the record of interest */
  437. char *key;            /* the key string */
  438. {
  439.     long l;
  440.     int len;
  441.     char *index();
  442.     struct dline dl, dl2;
  443.     struct dline *dlp, *dlp2;
  444.     int unnamedok;
  445.     char *oldv, *addv;
  446.  
  447.     if (!pp) ERET(4)        /* check for valid pointer */
  448.     if (!(pp->ff)) ERET(5)        /* error return if no file open */
  449.     if (!(pp->xx)) ERET(6)        /* error if no index table pointer */
  450.     l = getIndex(pp->xx,indexn);  /* get the lseek value for that index */
  451.     if (l==0) ERET(7)    /* error if no lseek value for that index */
  452.     fseek(pp->ff,l,0);    /* seek to the start of that line */
  453.     if (strcmp(key,"unnamed")==0) unnamedok=1;
  454.     else unnamedok=0;
  455.     while (!feof(pp->ff)) {        /* read lines until eof (or blank) */
  456.         dlp = readdline(pp->ff,&dl);
  457.         if (dlp->ltype==LBLANK) break;    /* blank line */
  458.         else if (dlp->ltype==LCOMMENT) continue;
  459.         else if (dlp->ltype==LKEY) unnamedok=0;
  460.         if ((dlp->ltype==LNOKEY && unnamedok) ||
  461.             (dlp->ltype==LKEY && strcmp(key,dlp->key)==0)) {
  462.             /* we've got a hot one */
  463.             dlp2 = readdline(pp->ff,&dl2);
  464.             while (dlp2->ltype==LNOKEY) {
  465.                 oldv = dlp->value;
  466.                 addv = dlp2->value;
  467.                 if (*addv == '+')
  468.                     addv++;
  469.                     /* skip over leading plus mark */
  470.                 dlp->value = strsave2(dlp->value,addv);
  471.                 free(oldv);
  472.                 free(dlp2->value);
  473.                 dlp2 = readdline(pp->ff,&dl2);
  474.             }
  475.             len = strlen(dlp->value);
  476.             if (dlp->value[len-1]=='\n') dlp->value[len-1]=0;
  477.             free(dlp2->key);
  478.             free(dlp2->value);
  479.             free(dlp->key);
  480.             if (getdatalastp) free(getdatalastp);
  481.             getdatalastp = dlp->value;
  482.             dataStatus=0;
  483.             return dlp->value;
  484.         }
  485.     }            /* continue - read next line and compare */
  486.     ERET(8)            /* eof or blank line, key not found */
  487. }
  488.  
  489. /*..........*/
  490.  
  491. struct drecord *        /* returns a pointer to a record struct */
  492. /*  return NULL if no data or error */
  493. getRecord(pp,indexn,rp)        /* get a complete record */
  494. struct dpoint *pp;        /* pointer to our structure */
  495. int indexn;            /* the index of the record of interest */
  496. struct drecord *rp;        /* structure to fill in; if NULL, allocs one */
  497. {
  498.     int i;
  499.     int len;
  500.     long l;
  501.     char *index();
  502.     int numfields;
  503. #define MAXRECNUM 1000
  504.     char *names[MAXRECNUM];    /* make number of fields */
  505.     char *values[MAXRECNUM];
  506.     struct dline dl, dl2;
  507.     struct dline *dlp, *dlp2;
  508.     char *oldv, *addv;
  509.  
  510.     if (!pp) ERET(4)        /* check for valid pointer */
  511.     if (!(pp->ff)) ERET(5)        /* error return if no file open */
  512.     if (!(pp->xx)) ERET(6)        /* error if no index table pointer */
  513.     if (rp==0) {
  514.         rp = (struct drecord *)malloc(sizeof(struct drecord));
  515.         if (rp==0) return 0;
  516.         rp->numfields = 0;
  517.         rp->name = 0;
  518.         rp->value = 0;
  519.     }
  520.     for (i=0; i<rp->numfields; i++) {
  521.         free(rp->name[i]);
  522.         free(rp->value[i]);
  523.     };
  524.     if (rp->name) free((char *)rp->name);
  525.     if (rp->value) free((char *)rp->value);
  526.     l = getIndex(pp->xx,indexn);  /* get the lseek value for that index */
  527.     if (l==0) ERET(7)    /* error if no lseek value for that index */
  528.     fseek(pp->ff,l,0);    /* seek to the start of that line */
  529.     numfields = 0;
  530.     dlp = readdline(pp->ff,&dl);
  531.     while (dlp->ltype!=LBLANK) {
  532.         while (dlp->ltype==LCOMMENT) {
  533.             free(dlp->value);
  534.             dlp = readdline(pp->ff,&dl);
  535.         }
  536.         if (dlp->ltype==LBLANK) break;
  537.         dlp2 = readdline(pp->ff,&dl2);
  538.         while (dlp2->ltype==LNOKEY) {
  539.             oldv = dlp->value;
  540.             addv = dlp2->value;
  541.             if (*addv == '+')
  542.                 addv++;    /* skip over leading plus mark */
  543.             dlp->value = strsave2(dlp->value,addv);
  544.             free(oldv);
  545.             free(dlp2->value);
  546.             dlp2 = readdline(pp->ff,&dl2);
  547.         }
  548.         len = strlen(dlp->value);
  549.         if (dlp->value[len-1]=='\n') dlp->value[len-1]=0;
  550.         if (dlp->ltype==LNOKEY)
  551.             names[numfields] = strsave("unnamed");
  552.         else    names[numfields] = dlp->key;
  553.         values[numfields] = dlp->value;
  554.         numfields++;
  555.         dl = dl2;
  556.     }
  557.     if (dlp->ltype==LBLANK) free(dlp->value);
  558.     dataStatus=0;
  559.     rp->numfields = numfields;
  560.     rp->name = (char **)malloc((unsigned)(numfields*sizeof(char *)));
  561.     if (rp->name==0) return 0;
  562.     rp->value = (char **)malloc((unsigned)(numfields*sizeof(char *)));
  563.     if (rp->value==0) return 0;
  564.     for (i=0; i<numfields; i++) {
  565.         rp->name[i] = names[i];
  566.         rp->value[i] = values[i];
  567.     }
  568.     return rp;
  569. }
  570.  
  571. /* end */
  572.