home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2303 / diary.c next >
Encoding:
C/C++ Source or Header  |  1990-12-28  |  15.1 KB  |  548 lines

  1. /*
  2.  * Author: Jason Baietto, jason@ssd.csd.harris.com
  3.  * xdiary Copyright 1990 Harris Corporation
  4.  *
  5.  * Permission to use, copy, modify, and distribute, this software and its
  6.  * documentation for any purpose is hereby granted without fee, provided that
  7.  * the above copyright notice appear in all copies and that both that
  8.  * copyright notice and this permission notice appear in supporting
  9.  * documentation, and that the name of the copyright holder be used in
  10.  * advertising or publicity pertaining to distribution of the software with
  11.  * specific, written prior permission, and that no fee is charged for further
  12.  * distribution of this software, or any modifications thereof.  The copyright
  13.  * holder makes no representations about the suitability of this software for
  14.  * any purpose.  It is provided "as is" without express or implied warranty.
  15.  *
  16.  * THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17.  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, AND IN NO
  18.  * EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19.  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM ITS USE,
  20.  * LOSS OF DATA, PROFITS, QPA OR GPA, WHETHER IN AN ACTION OF CONTRACT,
  21.  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
  22.  * THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23.  */
  24.  
  25. /*
  26.    diary.c -- routines to read, manage, and save the diary file.
  27.  
  28.    Diary format:  newline terminated date string followed by a
  29.                   newline terminated quote enclosed string.
  30.                   The string may contain newlines and quotes, but
  31.                   quotes must be doubled (i.e. "" becomes ").
  32.  
  33.    The diary format is very simple so that it can easily be updated
  34.    in a text editor for people who want to add text to a date but
  35.    are too impatient to bring up the xdiary application.
  36.  
  37.    Author: Jason Baietto
  38.      Date: October 19, 1990
  39. */
  40.  
  41.  
  42. /*---------------------------------------------------------------------------*/
  43. /*                               Header Files                                */
  44. /*---------------------------------------------------------------------------*/
  45. #include <stdio.h>
  46. #include <ctype.h>
  47. #include <values.h>
  48. #include <X11/Intrinsic.h>
  49. #include "Date.h"
  50.  
  51.  
  52. /*---------------------------------------------------------------------------*/
  53. /*                          Global Types And Macros                          */
  54. /*---------------------------------------------------------------------------*/
  55. #define START_LENGTH 20000
  56. #define GROW_LENGTH 10000
  57. #define INIT_YEARS 10
  58. #define NEWLINE '\n'
  59. #define QUOTE   '\"'
  60. #define STRING 0
  61. #define DATE 1
  62. #define EMPTYSTRING ""
  63.  
  64. typedef struct day_entry_type {
  65.    char * text;
  66.    int  freeable;
  67. } day_entry;
  68.  
  69. typedef struct month_entry_type {
  70.    struct day_entry_type day[31];
  71. } month_entry;
  72.  
  73. typedef struct year_entry_type {
  74.    int year;
  75.    struct month_entry_type * month[12];
  76.    struct year_entry_type * next;
  77.    struct year_entry_type * prev;
  78. } year_entry;
  79.  
  80. static year_entry head;
  81. static year_entry tail;
  82.  
  83. /*---------------------------------------------------------------------------*/
  84. /*                              Static Routines                              */
  85. /*---------------------------------------------------------------------------*/
  86. static char * read_diary(file_name)
  87. char * file_name;
  88. {
  89.    int i = 0;
  90.    int c = 0;
  91.    char * char_ptr;
  92.    long current_length = START_LENGTH;
  93.    FILE * file;
  94.  
  95.    char_ptr = (char *) XtMalloc(current_length);
  96.  
  97.    file = fopen(file_name, "r");
  98.    if (!file) {
  99.       return ((char *) NULL);
  100.    }
  101.  
  102.    /* Just read the whole thing into memory to minimize file I/O time */
  103.    while (c != EOF) {
  104.       c = getc(file);
  105.       char_ptr[i++] = c;
  106.  
  107.       if (i >= current_length) {
  108.          current_length += GROW_LENGTH;
  109.          char_ptr = (char *) XtRealloc(char_ptr, current_length);
  110.       }
  111.    }
  112.  
  113.    fclose(file);
  114.    return(char_ptr);
  115. }
  116.  
  117.  
  118.  
  119.  
  120. /*---------------------------------------------------------------------------*/
  121. /*                              Global Routines                              */
  122. /*---------------------------------------------------------------------------*/
  123. void install(date, string, freeable)
  124. Date date;
  125. char * string;
  126. int freeable;
  127. {
  128.    year_entry  * year_ptr = &head;
  129.    year_entry  * save_ptr;
  130.    year_entry  * last_ptr;
  131.    month_entry * month_ptr;
  132.    day_entry   * day_ptr;
  133.  
  134.    /* Find or create the year entry. */
  135.    while (year_ptr) {
  136.       if (year_ptr->year < date.year) {
  137.          /* Not yet, try the next year entry. */
  138.          last_ptr = year_ptr;
  139.          year_ptr = year_ptr->next;
  140.       } else if (year_ptr->year > date.year) {
  141.          /* No year entry exists, create one. */
  142.          save_ptr = year_ptr;
  143.          year_ptr = (year_entry *) XtCalloc(1, sizeof(year_entry));
  144.          last_ptr->next = year_ptr;
  145.          year_ptr->next = save_ptr;
  146.          year_ptr->prev = last_ptr;
  147.          save_ptr->prev = year_ptr;
  148.          year_ptr->year = date.year;
  149.          break;
  150.       } else {
  151.          /* Found it. */
  152.          break;
  153.       }
  154.    }/*while*/
  155.  
  156.    /* Find or create the corresponding month array. */
  157.    month_ptr = year_ptr->month[date.month-1];
  158.    if (!month_ptr) {
  159.       month_ptr = (month_entry *) XtCalloc(1, sizeof(month_entry));
  160.       year_ptr->month[date.month-1] = month_ptr;
  161.    }
  162.  
  163.    day_ptr = &month_ptr->day[date.day-1];
  164.    if (!day_ptr->text) {
  165.       /* No old text exists. */
  166.       day_ptr->text = string;
  167.       day_ptr->freeable = freeable;
  168.    } else {
  169.       if (day_ptr->freeable) {
  170.          XtFree(day_ptr->text);
  171.       }
  172.       day_ptr->text = string;
  173.       day_ptr->freeable = freeable;
  174.    }
  175. }
  176.  
  177.  
  178.  
  179.  
  180.  
  181. char * retrieve(date)
  182. Date date;
  183. {
  184.    year_entry * year_ptr = &head;
  185.    month_entry * month_ptr;
  186.    char * text_ptr;
  187.  
  188.    while (year_ptr) {
  189.       if (year_ptr->year == date.year) {
  190.          month_ptr = year_ptr->month[date.month-1];
  191.          if (month_ptr) {
  192.             text_ptr = month_ptr->day[date.day-1].text;
  193.             if (text_ptr) {
  194.                return (text_ptr);
  195.             }
  196.             break;
  197.          }
  198.          break;
  199.       } else if (year_ptr->year > date.year) {
  200.          break;
  201.       }
  202.       year_ptr = year_ptr->next;
  203.    }
  204.    return EMPTYSTRING;
  205. }
  206.  
  207.  
  208.  
  209.  
  210. int not_empty(string)
  211. char * string;
  212. {
  213.    /* Catch the null string. */
  214.    if (*string == NULL) {
  215.       return FALSE;
  216.    }
  217.  
  218.    while (*string) {
  219.       if (!isspace(*string)) {
  220.          return TRUE;
  221.       }
  222.       string++;
  223.    }
  224.    return FALSE;
  225. }
  226.  
  227.  
  228.  
  229.  
  230. /* This is ugly.  Oh well. */
  231. Date next_entry(date)
  232. Date date;
  233. {
  234.    year_entry * year_ptr = &head;
  235.    month_entry * month_ptr;
  236.    char * text_ptr;
  237.    Date return_date;
  238.    int month;
  239.    int day;
  240.  
  241.    while (year_ptr) {
  242.       if (year_ptr->year == date.year) {
  243.          /* Same year as date specified. */
  244.          for (month=date.month-1; month < 12; month++) {
  245.             month_ptr = year_ptr->month[month];
  246.             if (month_ptr) {
  247.                if (month == date.month-1) {
  248.                   /* Same month as date specified. */
  249.                   day = date.day;
  250.                } else {
  251.                   day = 0;
  252.                }
  253.                for (; day < 31; day++) {
  254.                   text_ptr = month_ptr->day[day].text;
  255.                   if (text_ptr && not_empty(text_ptr)) {
  256.                      return_date.year  = year_ptr->year;
  257.                      return_date.month = month+1;
  258.                      return_date.day   = day+1;
  259.                      return(return_date);
  260.                   }
  261.                }
  262.             }
  263.          }
  264.       } else if (year_ptr->year > date.year) {
  265.          for (month=0; month < 12; month++) {
  266.             month_ptr = year_ptr->month[month];
  267.             if (month_ptr) {
  268.                for (day=0; day < 31; day++) {
  269.                   text_ptr = month_ptr->day[day].text;
  270.                   if (text_ptr && not_empty(text_ptr)) {
  271.                      return_date.year  = year_ptr->year;
  272.                      return_date.month = month+1;
  273.                      return_date.day   = day+1;
  274.                      return(return_date);
  275.                   }
  276.                }
  277.             }
  278.          }
  279.       }
  280.       year_ptr = year_ptr->next;
  281.    }/*while*/
  282.  
  283.    /* No entry following the specified date was found. */
  284.    return_date.year  = 0;
  285.    return_date.month = 0;
  286.    return_date.day   = 0;
  287.    return return_date;
  288. }
  289.  
  290.  
  291.  
  292. /* This is ugly.  Oh well. */
  293. Date prev_entry(date)
  294. Date date;
  295. {
  296.    year_entry * year_ptr = &tail;
  297.    month_entry * month_ptr;
  298.    char * text_ptr;
  299.    Date return_date;
  300.    int month;
  301.    int day;
  302.  
  303.    while (year_ptr) {
  304.       if (year_ptr->year == date.year) {
  305.          /* Same year as date specified. */
  306.          for (month=date.month-1; month >= 0; month--) {
  307.             month_ptr = year_ptr->month[month];
  308.             if (month_ptr) {
  309.                if (month == date.month-1) {
  310.                   /* Same month as date specified. */
  311.                   day = date.day-2;
  312.                } else {
  313.                   day = 30;
  314.                }
  315.                for (; day >= 0; day--) {
  316.                   text_ptr = month_ptr->day[day].text;
  317.                   if (text_ptr && not_empty(text_ptr)) {
  318.                      return_date.year  = year_ptr->year;
  319.                      return_date.month = month+1;
  320.                      return_date.day   = day+1;
  321.                      return(return_date);
  322.                   }
  323.                }
  324.             }
  325.          }
  326.       } else if (year_ptr->year < date.year) {
  327.          for (month=11; month >= 0; month--) {
  328.             month_ptr = year_ptr->month[month];
  329.             if (month_ptr) {
  330.                for (day=30; day >= 0; day--) {
  331.                   text_ptr = month_ptr->day[day].text;
  332.                   if (text_ptr && not_empty(text_ptr)) {
  333.                      return_date.year  = year_ptr->year;
  334.                      return_date.month = month+1;
  335.                      return_date.day   = day+1;
  336.                      return(return_date);
  337.                   }
  338.                }
  339.             }
  340.          }
  341.       }
  342.       year_ptr = year_ptr->prev;
  343.    }/*while*/
  344.  
  345.    /* No entry following the specified date was found. */
  346.    return_date.year  = 0;
  347.    return_date.month = 0;
  348.    return_date.day   = 0;
  349.    return return_date;
  350. }
  351.  
  352.  
  353.  
  354.  
  355. void save_diary(file_name)
  356. char * file_name;
  357. {
  358.    year_entry * year_ptr = &head;
  359.    month_entry * month_ptr;
  360.    char * text_ptr;
  361.    FILE * file;
  362.    int month;
  363.    int day;
  364.  
  365.    file = fopen(file_name, "w");
  366.    if (!file) {
  367.       /*VARARGS*/
  368.       fprintf(stderr, "xdiary: fopen of file \"%s\" failed\n", file_name);
  369.       return;
  370.    }
  371.  
  372.    while(year_ptr) {
  373.       for (month=0; month < 12; month++) {
  374.          month_ptr = year_ptr->month[month];
  375.          if (month_ptr) {
  376.             for (day=0; day < 31; day++) {
  377.                text_ptr = month_ptr->day[day].text;
  378.                if (text_ptr && not_empty(text_ptr)) {
  379.                   /*VARARGS*/
  380.                   fprintf(
  381.                      file,
  382.                      "%d/%d/%d\n",
  383.                      month+1, day+1, year_ptr->year
  384.                   );
  385.                   putc(QUOTE, file);
  386.                   while (*text_ptr) {
  387.                      putc(*text_ptr, file);
  388.                      if (*text_ptr == QUOTE) {
  389.                         putc(QUOTE, file);
  390.                      }
  391.                      text_ptr++;
  392.                   }
  393.                   putc(QUOTE, file);
  394.                   putc(NEWLINE, file);
  395.                   putc(NEWLINE, file);
  396.                }
  397.             }
  398.          }
  399.       }
  400.       year_ptr = year_ptr->next;
  401.    }/*while*/
  402.  
  403.    fclose(file);
  404. }
  405.  
  406.  
  407.  
  408.  
  409.  
  410. #define parse_error(str) \
  411.    (/*VARARGS*/fprintf( \
  412.       stderr, \
  413.       "xdiary: file \"%s\", line %d: %s\n", \
  414.       file_name, \
  415.       current_line, \
  416.       (str) \
  417.    ))
  418.  
  419. int init_diary(file_name)
  420. char * file_name;
  421. {
  422.    char * file = read_diary(file_name);
  423.    char * fileptr = &file[0];
  424.    char * start_of_string = fileptr;
  425.    char * end_of_string = fileptr;
  426.    int parsing_date = FALSE;
  427.    int parsing_string = FALSE;
  428.    int current_line = 1;
  429.    int token_last_seen = STRING;
  430.    Date date;
  431.  
  432.    /* Initialize the head and tail to simplify everything. */
  433.    tail.year = MAXINT;
  434.    tail.next = NULL;
  435.    tail.prev = &head;
  436.  
  437.    head.year = 0;
  438.    head.next = &tail;
  439.    head.prev = NULL;
  440.    
  441.    if (!file) {
  442.       /*VARARGS*/
  443.       fprintf(stderr, "xdiary: file \"%s\" will be created\n", file_name);
  444.       return FALSE;
  445.    }
  446.  
  447.    while ((int)*fileptr != EOF) {
  448.       switch (*fileptr) {
  449.          case QUOTE:
  450.             if (parsing_string) {
  451.                if (fileptr[1] == QUOTE) {
  452.                   /* Change two quotes to one. */
  453.                   *end_of_string = QUOTE;
  454.                   end_of_string++;
  455.                   fileptr++;
  456.                } else {
  457.                   /* End of string. */
  458.                   *end_of_string = 0;
  459.                   parsing_string = FALSE;
  460.                   if (token_last_seen == STRING) {
  461.                      parse_error("date missing");
  462.                   } else {
  463.                      install(date, start_of_string, FALSE);
  464.                   }
  465.                   token_last_seen = STRING;
  466.                   start_of_string = end_of_string = fileptr + 1;
  467.                }
  468.             } else {
  469.                /* Must be the start of a new string. */
  470.                parsing_string = TRUE;
  471.                start_of_string = end_of_string = fileptr + 1;
  472.             }
  473.             break;
  474.          case NEWLINE:
  475.             if (parsing_date) {
  476.                /* Finished parsing a date. */
  477.                parsing_date = FALSE;
  478.                *end_of_string = 0;
  479.                if (token_last_seen == DATE) {
  480.                   parse_error("text missing");
  481.                }
  482.                token_last_seen = DATE;
  483.                date = convert_string_to_date(
  484.                          default_month_names,
  485.                          start_of_string
  486.                       );
  487.                if (date.year == 0) {
  488.                   parse_error("bad date");
  489.                   token_last_seen = STRING;
  490.                }
  491.             } else if (parsing_string) {
  492.                /* Newline is part of a string. */
  493.                *end_of_string = *fileptr;
  494.                end_of_string++;
  495.             }
  496.             current_line++;
  497.             break;
  498.          default:
  499.             if (parsing_date || parsing_string) {
  500.                /* Part of string. */
  501.                *end_of_string = *fileptr;
  502.                end_of_string++;
  503.             } else {
  504.                /* If character is signigicant, start parsing date. */
  505.                if (!isspace(*fileptr)) {
  506.                   parsing_date = TRUE;
  507.                   end_of_string = start_of_string = fileptr;
  508.                   end_of_string++;
  509.                }
  510.             }
  511.       }/*switch*/
  512.       fileptr++;
  513.    }
  514.    return TRUE;
  515. }
  516.  
  517.  
  518.  
  519.  
  520.  
  521. #ifdef DEBUG
  522. void dump_dairy()
  523. {
  524.    year_entry * year_ptr = &head;
  525.    int month;
  526.    int day;
  527.  
  528.    while(year_ptr) {
  529.       for (month=0; month < 12; month++) {
  530.          if (year_ptr->month[month]) {
  531.             for (day=0; day < 31; day++) {
  532.                if (year_ptr->month[month]->day[day].text) {
  533.                   /*VARARGS*/
  534.                   printf(
  535.                      "date=%d/%d/%d\nfreeable=%d\ntext=\"%s\"\n",
  536.                      year_ptr->year, month, day,
  537.                      year_ptr->month[month]->day[day].freeable,
  538.                      year_ptr->month[month]->day[day].text
  539.                   );
  540.                }
  541.             }
  542.          }
  543.       }
  544.       year_ptr = year_ptr->next;
  545.    }/*while*/
  546. }
  547. #endif
  548.