home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3065 / db.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-03-15  |  15.0 KB  |  590 lines

  1. /*
  2.  *    db.c : Database routines for xkal.
  3.  *           Handles searching and updating the appointment graph.
  4.  *           See db.h for a description of the data structure.
  5.  *
  6.  *    George Ferguson, ferguson@cs.rochester.edu, 27 Oct 1990.
  7.  *    Version 1.1 - 27 Feb 1991.
  8.  *
  9.  *    $Id: db.c,v 2.2 91/03/13 13:31:16 ferguson Exp $
  10.  *
  11.  * The following can be used in place of the ones from X11/Intrinsic.h:
  12.  *    #define XtNew(T)    (T *)malloc(sizeof(T))
  13.  *    #define XtFree(X)    if ((X) != NULL) free(X)
  14.  *    #define XtNewString(S)    strcpy(malloc(strlen(S)+1),S)
  15.  *
  16.  */
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20. #include <errno.h>
  21. #include <sys/types.h>            /* for writeDb() backup only */
  22. #include <sys/stat.h>            /*  "      "        "      "  */
  23. #include <X11/Intrinsic.h>
  24. #include "db.h"
  25. #include "util.h"
  26. #include "date-strings.h"
  27. #include "app-resources.h"        /* for levelDelim and daySlashMonth */
  28. #ifdef USE_ALERT
  29. #include "alert.h"
  30. #endif
  31. extern char *program;
  32.  
  33. /*
  34.  * Functions defined here:
  35.  */
  36. void initDb();
  37. Msgrec *addEntry();
  38. Boolean deleteEntry();
  39. int lookupEntry();
  40. void readDb(), writeDb(), writeAppoint();
  41.  
  42. /*
  43.  * Data defined here (could be static but we might hack on the DB later,
  44.  * like we do with xkal2xremind and the like).
  45.  */
  46. Yearrec *Db[8];
  47.  
  48. /*    -    -    -    -    -    -    -    -    */
  49.  
  50. /*VARARGS1*/
  51. static void
  52. dprintf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9)
  53. char *fmt,*a1,*a2,*a3,*a4,*a5,*a6,*a7,*a8,*a9;
  54. {
  55. #ifdef debug
  56.     printf(fmt,a1,a2,a3,a4,a5,a6,a7,a8,a9);
  57. #endif
  58. }
  59.  
  60. /*    -    -    -    -    -    -    -    -    */
  61. /*
  62.  * initDb() : Pretty lame, and unneeded for most UNIX boxes, but I put
  63.  *    it here anyway. We never erase the db, so we don't have to
  64.  *    worry about this.
  65.  */
  66. void
  67. initDb()
  68. {
  69.     int i;
  70.  
  71.     for (i=0; i < 8; i++)
  72.     Db[i] = NULL;        /* should free them all */
  73. }
  74.  
  75. /*    -    -    -    -    -    -    -    -    */
  76. /*
  77.  * addEntry() : Add an entry to the appropriate place in the database
  78.  *    by traversing it and possibly adding nodes. Not much to it.
  79.  */
  80. Msgrec *
  81. addEntry(dow,year,mon,day,hour,mins,text,flag,level)
  82. int dow,year,mon,day,hour,mins;
  83. char *text;
  84. int flag,level;
  85. {
  86.     Yearrec *yp;
  87.     Monrec *mp;
  88.     Dayrec *dp;
  89.     Msgrec *xp;
  90.  
  91.     if (Db[dow] == NULL) {
  92.     Db[dow] = XtNew(Yearrec);
  93.     Db[dow]->year = year;
  94.     Db[dow]->mons = NULL;
  95.     Db[dow]->next = NULL;
  96.     }
  97.     for (yp = Db[dow]; yp->year != year && yp->next != NULL; yp = yp->next) ;
  98.     if (yp->year != year) {
  99.     yp->next = XtNew(Yearrec);
  100.     yp->next->year = year;
  101.     yp->next->mons = NULL;
  102.     yp->next->next = NULL;
  103.     yp = yp->next;
  104.     }
  105.     if (yp->mons == NULL) {
  106.     yp->mons = XtNew(Monrec);
  107.     yp->mons->mon = mon;
  108.     yp->mons->days = NULL;
  109.     yp->mons->next = NULL;
  110.     }
  111.     for (mp = yp->mons; mp->mon != mon && mp->next != NULL; mp = mp->next) ;
  112.     if (mp->mon != mon) {
  113.     mp->next = XtNew(Monrec);
  114.     mp->next->mon = mon;
  115.     mp->next->days = NULL;
  116.     mp->next->next=NULL;
  117.     mp = mp->next;
  118.     }
  119.  
  120.     if (mp->days == NULL) {
  121.     mp->days = XtNew(Dayrec);
  122.     mp->days->day = day;
  123.     mp->days->msgs = NULL;
  124.     mp->days->next = NULL;
  125.     }
  126.     for (dp = mp->days; dp->day != day && dp->next != NULL; dp = dp->next) ;
  127.     if (dp->day != day) {
  128.     dp->next = XtNew(Dayrec);
  129.     dp->next->day = day;
  130.     dp->next->msgs = NULL;
  131.     dp->next->next=NULL;
  132.     dp = dp->next;
  133.     }
  134.  
  135.     if (dp->msgs == NULL) {
  136.     dp->msgs = XtNew(Msgrec);
  137.     dp->msgs->next = NULL;
  138.     } else {
  139.     xp = dp->msgs;
  140.     dp->msgs = XtNew(Msgrec);
  141.     dp->msgs->next = xp;
  142.     }
  143.     dp->msgs->dow = dow;
  144.     dp->msgs->year = year;
  145.     dp->msgs->month = mon;
  146.     dp->msgs->day = day;
  147.     dp->msgs->hour = hour;
  148.     dp->msgs->mins = mins;
  149.     dp->msgs->text = XtNewString(text);
  150.     dp->msgs->system = flag;
  151.     dp->msgs->level = level;
  152.     return(dp->msgs);
  153. }
  154.  
  155. /*
  156.  * deleteEntry() : Removes an entry matching the given parameters from
  157.  *    the database, and tidies up afterwards. Note that everything must
  158.  *    match, the text as well.
  159.  */
  160. Boolean
  161. deleteEntry(dow,year,mon,day,hour,mins,text)
  162. int dow,year,mon,day,hour,mins;
  163. char *text;
  164. {
  165.     Yearrec *yp,*yp2;
  166.     Monrec *mp,*mp2;
  167.     Dayrec *dp,*dp2;
  168.     Msgrec *xp,*xp2;
  169.  
  170.     for (yp = Db[dow]; yp != NULL && yp->year != year; yp = yp->next) ;
  171.     if (yp == NULL)
  172.     return(False);
  173.     for (mp = yp->mons; mp != NULL && mp->mon != mon; mp = mp->next) ;
  174.     if (mp == NULL)
  175.     return(False);
  176.     for (dp = mp->days; dp != NULL && dp->day != day; dp = dp->next) ;
  177.     if (dp == NULL)
  178.     return(False);
  179.     for (xp = dp->msgs; xp != NULL; xp = xp2) {
  180.     xp2 = xp->next;
  181.     if (xp->hour == hour && xp->mins == mins &&
  182.                         strcmp(xp->text, text) == 0) {
  183.         XtFree(xp->text);        /* free the text */
  184.         if (dp->msgs == xp)        /* and the msgrec */
  185.         dp->msgs = xp->next;
  186.         else {
  187.         for (xp2 = dp->msgs; xp2->next != xp; xp2 = xp2->next);
  188.         xp2->next = xp->next;
  189.         }
  190.         XtFree(xp);
  191.         if (dp->msgs == NULL) {    /* if no more entries, free the day */
  192.         if (mp->days == dp)
  193.             mp->days = dp->next;
  194.         else {
  195.             for (dp2 = mp->days; dp2->next != dp; dp2 = dp2->next);
  196.             dp2->next = dp->next;
  197.         }
  198.         XtFree(dp);
  199.         }
  200.         if (mp->days == NULL) {    /* if no more days, free the month */
  201.         if (yp->mons == mp)
  202.             yp->mons = mp->next;
  203.         else {
  204.             for (mp2 = yp->mons; mp2->next != mp; mp2 = mp2->next);
  205.             mp2->next = mp->next;
  206.         }
  207.         XtFree(mp);
  208.         }
  209.         if (yp->mons == NULL) {    /* if no more months, free the year */
  210.         if (Db[dow] == yp)
  211.             Db[dow] = Db[dow]->next;
  212.         else {
  213.             for (yp2 = Db[dow]; yp2->next != yp; yp2 = yp2->next);
  214.             yp2->next = yp->next;
  215.         }
  216.         XtFree(yp);
  217.         }
  218.         return(True);
  219.     }
  220.     }
  221.     return(False);
  222. }
  223.  
  224. /*    -    -    -    -    -    -    -    -    */
  225. /*
  226.  * lookupEntry() : Fills in at most max entries in result[] with pointers
  227.  *    to messages for the given date/time. Returns the number of
  228.  *    entries filled or -1 if there were more than max entries (but the
  229.  *    ones that were filled, ie max of them, are valid). As a special
  230.  *    case, if max is -1, then no entries are filled (result can be NULL)
  231.  *    and the number of entries is returned. (If useLevel is True, then
  232.  *    the total of the levels of all applicable appointments is returned
  233.  *    rather than the number of appointments.)
  234.  *
  235.  *    The definition of a match is complicated by the fact that items in
  236.  *    the database may be only partially specified. The approach is as
  237.  *    follows:
  238.  *    (a) First consider all db records which do not specify a dow (ie.
  239.  *        from Db[0]). Within these, the day, month, and year must match
  240.  *        if given, otherwise they are assumed to match. The hour and
  241.  *        minutes must match explicitly.
  242.  *    (b) Then consider all db records specifying the dow of the given date.
  243.  *        If the record does not also specify a day, then the month and
  244.  *        year must match if given, as above, and the time must match
  245.  *        exactly. If the record specifies both dow and day, then the
  246.  *        interpretation is "the first dow after date", so we have to
  247.  *        check if the current date is within a week of the date in the
  248.  *        record.
  249.  *
  250.  *    Note that the strings are not copied.
  251.  */
  252. int
  253. lookupEntry(day,mon,year,hour,mins,max,result,useLevel)
  254. int day,mon,year,hour,mins,max;
  255. Msgrec *result[];
  256. {
  257.     Yearrec *yp;
  258.     Monrec *mp;
  259.     Dayrec *dp;
  260.     int num,n,i;
  261.  
  262.     dprintf("lookup for %d:%02d, %d %s %d...\n",hour,mins,day,
  263.                         shortMonthStr[mon-1],year);
  264.     num = 0;
  265.     dprintf("  checking DB with dow = 0...\n");
  266.     for (yp = Db[0]; yp != NULL; yp = yp->next) {
  267.     dprintf("    year = %d\n", yp->year);
  268.     if (yp->year == year || yp->year == 0)
  269.         for (mp = yp->mons; mp != NULL; mp = mp->next) {
  270.         dprintf("      mon = %d\n", mp->mon);
  271.         if (mp->mon == mon || mp->mon == 0)
  272.             for (dp = mp->days; dp != NULL; dp = dp->next) {
  273.             dprintf("\tday = %d\n", dp->day);
  274.             if (dp->day == day || dp->day == 0) {
  275.                 n = lookupEntryDay(dp,hour,mins,max-num,
  276.                             result+num,useLevel);
  277.                 if (n < 0)
  278.                 return(-1);
  279.                 else
  280.                 num += n;
  281.             }
  282.         }
  283.     }
  284.     }
  285.     dprintf("  checking DB with real dow...\n");
  286.     for (yp = Db[computeDOW(day,mon,year)]; yp != NULL; yp = yp->next) {
  287.     dprintf("    year = %d\n", yp->year);
  288.     for (mp = yp->mons; mp != NULL; mp = mp->next) {
  289.         dprintf("      mon = %d\n", mp->mon);
  290.         for (dp = mp->days; dp != NULL; dp = dp->next) {
  291.         dprintf("\tday = %d\n", dp->day);
  292.         if (dp->day == 0) {
  293.             if ((yp->year == 0 || yp->year == year) &&
  294.             (mp->mon == 0 || mp->mon == mon)) {
  295.             n = lookupEntryDay(dp,hour,mins,max-num,
  296.                             result+num,useLevel);
  297.             if (n < 0)
  298.                 return(-1);
  299.             else
  300.                 num += n;
  301.             }
  302.         } else {    /* have dow and day */
  303.             int d,m,y;
  304.  
  305.             d = day;
  306.             m = mon;
  307.             y = year;
  308.             dprintf("\tscanning back from %d %d %d\n",d,m,y);
  309.             for (i=0; i < 7; i++)
  310.             if (dp->day == d && (yp->year == 0 || yp->year == y) &&
  311.                         (mp->mon == 0 || mp->mon == m))
  312.                 break;
  313.             else
  314.                 prevDay(&d,&m,&y);
  315.             if (i < 7) {
  316.             n = lookupEntryDay(dp,hour,mins,max-num,
  317.                             result+num,useLevel);
  318.             if (n < 0)
  319.                 return(-1);
  320.             else
  321.                 num += n;
  322.             }
  323.         }
  324.         }
  325.     }
  326.     }
  327.     dprintf("returning %d\n", num);
  328.     return(num);
  329. }
  330.  
  331. /*
  332.  * lookupEntryDay() : Scans the list of messages associated with the
  333.  *    given Dayrec and fills in at most max entries of result[] with
  334.  *    pointers to the messages. Returns the number of entries filled in,
  335.  *    or -1 if there were more entries than max (but all that were filled
  336.  *    in, ie. max of them, are valid).
  337.  *
  338.  *    If the given hour or mins is -1, then it's assumed to match, making
  339.  *    it easy to get all the entries for a day.
  340.  */
  341. static int
  342. lookupEntryDay(dp,hour,mins,max,result,useLevel)
  343. Dayrec *dp;
  344. int hour,mins,max;
  345. Msgrec *result[];
  346. Boolean useLevel;
  347. {
  348.     Msgrec *xp;
  349.     int num;
  350.  
  351.     num = 0;
  352.     for (xp = dp->msgs; xp != NULL; xp = xp->next) {
  353.     dprintf("\t  h:m = %d:%02d\n",xp->hour,xp->mins);
  354.     if ((hour == -1 || xp->hour == -1 || xp->hour == hour) &&
  355.         (mins == -1 || xp->mins == -1 || xp->mins == mins)) {
  356.         dprintf("\t    YES\n");
  357.         if (max >= 0 && num >= max)
  358.         return (-1);
  359.         else if (max > 0)
  360.         result[num] = xp;
  361.         if (useLevel)
  362.         num += xp->level;
  363.         else
  364.         num += 1;
  365.     }
  366.     }
  367.     return(num);
  368. }
  369.  
  370. /*    -    -    -    -    -    -    -    -    */
  371. /*
  372.  * readDb() : Reads the given file into the database, setting the flag
  373.  *    bit on new entries appropriately. This function understands
  374.  *    comments starting with #, and the #include capability. It uses
  375.  *    parseLine to actually decode the date spec.
  376.  */
  377. void
  378. readDb(name,systemFlag)
  379. char *name;
  380. int systemFlag;
  381. {
  382.     FILE *fp;
  383.     char *fname,buf[256];
  384.     int line,i,n,c;
  385.     int dow,y,m,d,h,mins,lev;
  386.     char *t;
  387.  
  388.     fname = expandFilename(name);
  389.     if ((fp=fopen(fname,"r")) == NULL) {
  390.     if (errno != ENOENT)    /* appointment file may not exist */
  391.         perror(fname);
  392.     return;
  393.     }
  394.     c = line = 0;
  395.     while (c != EOF) {
  396.     n = 0;
  397.     line += 1;
  398.     while (n < 255 && (c=getc(fp)) != EOF && c != '\n')
  399.         buf[n++] = c;
  400.     buf[n] = '\0';
  401.     if (n == 0) {
  402.         continue;
  403.     } else if (c != EOF && c != '\n') {
  404.         fprintf(stderr,"%s: line %d too long, file %s\n",program,
  405.                                 line,fname);
  406.         while ((c=getc(fp)) != EOF && c != '\n') ;
  407.     }
  408.     i = 0;
  409.     while (isspace(buf[i]))
  410.         i += 1;
  411.     if (buf[i] == '#') {
  412.         if (strncmp(buf+i+1,"include",7) == 0) {
  413.         i += 8;
  414.         while (isspace(buf[i]))
  415.             i += 1;
  416.         readDb(buf+i,systemFlag);
  417.         }
  418.         continue;
  419.     }
  420.     parseLine(buf+i,&dow,&y,&m,&d,&h,&mins,&t,&lev);
  421. #ifdef debug
  422.     if (dow != 0)
  423.         printf("%s ",shortDowStr[dow-1]);
  424.     if (d != 0)
  425.         printf("%2d ",d);
  426.     if (m != 0)
  427.         printf("%s ",shortMonthStr[m-1]);
  428.     if (y != 0)
  429.         printf("%4d ",y);
  430.     if (h != -1)
  431.         printf("%2d:%02d ",h,mins);
  432.     if (lev != 0)
  433.         printf("@%d@ ",lev);
  434.     printf("%s\n",t);
  435. #endif
  436.     addEntry(dow,y,m,d,h,mins,t,systemFlag,lev);
  437.     }
  438.     fclose(fp);
  439. }
  440.  
  441. /*
  442.  * writeDb() : Writes any non-system database entries to the given file
  443.  *    in the canonical format. If writeAll is True, then system entries are
  444.  *    also written.
  445.  */
  446. void
  447. writeDb(name,writeAll)
  448. char *name;
  449. Boolean writeAll;
  450. {
  451.     FILE *fp;
  452.     char *fname,*backup;
  453.     struct stat stbuf;
  454.     int dow;
  455.     Yearrec *yp;
  456.     Monrec *mp;
  457.     Dayrec *dp;
  458.     Msgrec *xp;
  459.  
  460.     fname = expandFilename(name);
  461.     if (*appResources.backupExtension && stat(fname,&stbuf) == 0) {
  462.     backup = XtMalloc(strlen(fname)+strlen(appResources.backupExtension)+1);
  463.     strcpy(backup,fname);
  464.     strcat(backup,appResources.backupExtension);
  465.     (void)unlink(backup);
  466.     if (link(fname,backup) < 0) {
  467. #ifdef USE_ALERT
  468.         alert("Error: Can't backup \"%s\"; save aborted.",fname);
  469. #else
  470.         fprintf(stderr,"\007%s: can't backup \"%s\"; save aborted\n",program,fname);
  471. #endif
  472.         XtFree(backup);
  473.         return;
  474.     }
  475.     (void)unlink(fname);
  476.     XtFree(backup);
  477.     }
  478.     if ((fp=fopen(fname,"w")) == NULL) {
  479.     perror(fname);
  480.     return;
  481.     }
  482.     for (dow = 0; dow < 8; dow++)
  483.       for (yp = Db[dow]; yp != NULL; yp = yp->next)
  484.     for (mp = yp->mons; mp != NULL; mp = mp->next)
  485.         for (dp = mp->days; dp != NULL; dp = dp->next)
  486.         for (xp = dp->msgs; xp != NULL; xp = xp->next)
  487.             if (!xp->system || writeAll)
  488.             writeAppoint(fp,xp);
  489.  
  490.     fclose(fp);
  491. }
  492.  
  493. /*
  494.  * writeAppoint(fp,msg) : Formats the msg onto the given stream.
  495.  *
  496.  * The following escapes are possible in appResources.outputFormat:
  497.  *    %w    short form of day of the week
  498.  *    %W    long form of day of the week
  499.  *    %d    day
  500.  *    %m    short form of month
  501.  *    %M    long form of month
  502.  *    %n    numeric form of month
  503.  *    %y    year
  504.  *    %Y    year modulo 100
  505.  *    %t    time
  506.  *    %l    criticality level
  507.  *    %~    space iff previous pattern was printed
  508.  *    %/    slash iff previous pattern was printed
  509.  * Any other character is simply printed. The text of the appointment follows,
  510.  * then a newline.
  511.  */
  512. void
  513. writeAppoint(fp,msg)
  514. FILE *fp;
  515. Msgrec *msg;
  516. {
  517.     char *s;
  518.     Boolean flag;
  519.  
  520.     s = appResources.outputFormat;
  521.     flag = False;
  522.     while (*s) {
  523.     if (*s == '%') {
  524.         s += 1;
  525.         switch (*s) {
  526.         case '\0': fprintf(fp,"%");
  527.                break;
  528.         case '~': if (flag)
  529.                   fprintf(fp," ");
  530.                break;
  531.         case '/': if (flag)
  532.                   fprintf(fp,"/");
  533.                break;
  534.         case 'w': if (msg->dow > 0)
  535.                   fprintf(fp,"%s",shortDowStr[msg->dow-1]);
  536.               flag = (msg->dow > 0);
  537.               break;
  538.         case 'W': if (msg->dow > 0)
  539.                   fprintf(fp,"%s",longDowStr[msg->dow-1]);
  540.               flag = (msg->dow > 0);
  541.               break;
  542.         case 'd': if (msg->day > 0)
  543.                   fprintf(fp,"%d",msg->day);
  544.               flag = (msg->day > 0);
  545.               break;
  546.         case 'm': if (msg->month > 0)
  547.                   fprintf(fp,"%s",shortMonthStr[msg->month-1]);
  548.               flag = (msg->month > 0);
  549.               break;
  550.         case 'M': if (msg->month > 0)
  551.                   fprintf(fp,"%s",longMonthStr[msg->month-1]);
  552.               flag = (msg->month > 0);
  553.               break;
  554.         case 'n': if (msg->month > 0)
  555.                   fprintf(fp,"%d",msg->month);
  556.               flag = (msg->month > 0);
  557.               break;
  558.         case 'y': if (msg->year > 0)
  559.                   fprintf(fp,"%d",msg->year);
  560.               flag = (msg->year > 0);
  561.               break;
  562.         case 'Y': if (msg->year > 0)
  563.                   fprintf(fp,"%d",msg->year%100);
  564.               flag = (msg->year > 0);
  565.               break;
  566.         case 't': if (msg->hour != -1)
  567.                  fprintf(fp,"%s",timetostr(msg->hour*60+msg->mins));
  568.               flag = (msg->hour != -1);
  569.               break;
  570.         case 'l': if (msg->level > 0) {
  571.                   fprintf(fp,"%c",*appResources.levelDelim);
  572.                   fprintf(fp,"%d",msg->level);
  573.                   if (*(appResources.levelDelim+1))
  574.                   fprintf(fp,"%c",*(appResources.levelDelim+1));
  575.                   else
  576.                   fprintf(fp,"%c",*appResources.levelDelim);
  577.               }
  578.               flag = (msg->level > 0);
  579.               break;
  580.         default: fprintf(fp,"%c",*s);
  581.         } /* switch */
  582.     } else {
  583.        fprintf(fp,"%c",*s);
  584.     }
  585.     if (*s)
  586.         s += 1;
  587.     } /* while */
  588.     fprintf(fp,"%s\n",msg->text);
  589. }
  590.