home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / sun / volume1 / calentool / part08 / utils.c < prev    next >
Encoding:
C/C++ Source or Header  |  1989-05-27  |  30.9 KB  |  1,244 lines

  1. /*
  2.  * $Header: utils.c,v 2.2 89/05/16 16:30:18 billr Exp $
  3.  */
  4. /*
  5.  * utils.c
  6.  *
  7.  * calentool - a year/month/week/day-at-a-glance calendar for Sun workstations.
  8.  *
  9.  * Author: Philip Heller, Sun Microsystems. Inc. <terrapin!heller@sun.com>
  10.  *
  11.  * Original source Copyright (C) 1987, Sun Microsystems, Inc.
  12.  *    All Rights Reserved
  13.  * Permission is hereby granted to use and modify this program in source
  14.  * or binary form as long as it is not sold for profit and this copyright
  15.  * notice remains intact.
  16.  *
  17.  *
  18.  * Changes/additions by: Bill Randle, Tektronix, Inc. <billr@saab.CNA.TEK.COM>
  19.  *
  20.  * Changes and additions Copyright (C) 1988, 1989 Tektronix, Inc.
  21.  *    All Rights Reserved
  22.  * Permission is hereby granted to use and modify the modifications in source
  23.  * or binary form as long as they are not sold for profit and this copyright
  24.  * notice remains intact.
  25.  */
  26. /********************************************
  27.  *                        *
  28.  *              Utility routines.        *
  29.  *                        *
  30.  ********************************************/
  31.  
  32.  
  33.  
  34. #include "ct.h"
  35. #include <stdio.h>
  36. #include <suntool/sunview.h>
  37. #include <suntool/canvas.h>
  38. #include <ctype.h>
  39. #include <sys/types.h>
  40. #include <sys/file.h>
  41. #include <sys/stat.h>
  42. #include <sys/errno.h>
  43.  
  44.  
  45. extern struct tm today, current;
  46. extern struct tm First;
  47. extern int day_is_open, read_only;
  48. extern struct dayslot slots[];
  49. extern char apts_pathname[], tmpapts_pathname[];
  50. extern int dayslot_width, nr_weekdays, n_tslots;
  51. extern int dayslot_height;
  52. extern char *monthnames[], *daynames[];
  53. extern char *mailto, *progname;
  54. extern int one_based, version2, new_entry;
  55. extern int findex;
  56. extern struct appt_entry future[];
  57. extern char todays_date[];
  58. extern char apts_dir[], lib_dir[];
  59. extern char printer[];
  60. extern int include_old, save_old;
  61. extern Frame frame;
  62. extern Canvas canvas;
  63. extern Pixwin *main_pixwin;
  64. extern int mainsw_state;
  65. extern Pixfont *font, *sfont;
  66. extern Frame prompt_frame;
  67. extern int update_interval;
  68. extern int errno;
  69.  
  70. char inbuf[512], strbuf[256], errbuf[64];
  71. char todays_date[32];
  72. static int including;
  73. static int log_to_console;
  74. char *daynames[] = {"Sunday","Monday","Tuesday","Wednesday",
  75.                "Thursday","Friday","Saturday"};
  76. char *monthnames[] = {"January","February","March","April",
  77.                  "May","June","July","August",
  78.                  "September","October","November","December"};
  79. char *dayname[7] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
  80. char rasfile[] = "/usr/tmp/calentool.ras";
  81. char psfile[] = "/usr/tmp/calentool.ps";
  82. char *fname = 0;
  83.  
  84. extern char *strcpy(), *strcat();
  85.  
  86. /*
  87.  * sets "today" and current time
  88.  */
  89. void
  90. get_today()
  91. {
  92.     struct tm *tm;
  93.     struct timeval tv;
  94.  
  95.     gettimeofday(&tv, 0);
  96.     tm = localtime(&tv.tv_sec);
  97.  
  98.     today = *tm;
  99.  
  100.     strcpy(todays_date, asctime(tm));
  101.     if (update_interval >= 60)
  102.         /* overwrite seconds field with year */
  103.         sprintf(&todays_date[16], " %d", today.tm_year+1900);
  104.     else
  105.         /* just delete trailing \n */
  106.         todays_date[strlen(todays_date)-1] = '\0';
  107. }
  108.  
  109. /*
  110.  *    Reset some values in current tm structure. Year, month and
  111.  *    day-of-month are valid but day and/or month may be < 0 or
  112.  *    greater than the maximum value, in which case they are adjusted
  113.  *    accordingly. Day-of-year and day-of-week are then recalculated.
  114.  */
  115. void
  116. fix_current_day()
  117. {
  118.     int month, totdays = 0;
  119.     struct tm from, to;
  120.  
  121.     if (current.tm_mon < JAN) {
  122.         current.tm_mon = DEC;
  123.         current.tm_year--;
  124.     } else if (current.tm_mon > DEC) {
  125.         current.tm_mon = JAN;
  126.         current.tm_year++;
  127.     }
  128.     if (current.tm_mday < 1) {
  129.         current.tm_mon--;
  130.         if (current.tm_mon < JAN) {
  131.             current.tm_mon = DEC;
  132.             current.tm_year--;
  133.         }
  134.         current.tm_mday += monthlength(current.tm_mon);
  135.     } else if (current.tm_mday > monthlength(current.tm_mon)) {
  136.         current.tm_mday -= monthlength(current.tm_mon);
  137.         current.tm_mon++;
  138.         if (current.tm_mon > DEC) {
  139.             current.tm_mon = JAN;
  140.             current.tm_year++;
  141.         }
  142.     }
  143.     current.tm_yday = current.tm_mday - 1;
  144.     for (month = 0; month < current.tm_mon; month++) {
  145.         current.tm_yday += monthlength(month);
  146.     }
  147.     if ((current.tm_year < today.tm_year)
  148.         || ((current.tm_year == today.tm_year)
  149.         && (current.tm_yday < today.tm_yday))) {
  150.         from = current;
  151.         to = today;
  152.     } else {
  153.         from = today;
  154.         to = current;
  155.     }
  156.     if (from.tm_year != to.tm_year) {
  157.         for (totdays = 0; from.tm_year < to.tm_year; from.tm_year++)
  158.             totdays += dysize(from.tm_year + 1900);
  159.     }
  160.     totdays += to.tm_yday - from.tm_yday;
  161.     if ((current.tm_year < today.tm_year)
  162.         || ((current.tm_year == today.tm_year)
  163.         && (current.tm_yday < today.tm_yday)))
  164.         totdays = -totdays;
  165.     current.tm_wday =
  166.         ((totdays % 7) + 7 + today.tm_wday) % 7;
  167. }
  168.  
  169. /*
  170.  * Compares two sets of year/month/day.  Returns -1 if the first is earlier than
  171.  * the second, +1 if later, 0 if they are the same.
  172.  */
  173. ymd_compare(day0, day1)
  174. struct tm day0, day1;
  175. {
  176.         if (day0.tm_year > day1.tm_year) return(1);
  177.         if (day0.tm_year < day1.tm_year) return(-1);
  178.         if (day0.tm_mon > day1.tm_mon) return(1);
  179.         if (day0.tm_mon < day1.tm_mon) return(-1);
  180.         if (day0.tm_mday > day1.tm_mday) return(1);
  181.         if (day0.tm_mday < day1.tm_mday) return(-1);
  182.         return(0);
  183. }
  184.  
  185. int
  186. monthlength(month)
  187. int    month;
  188. {
  189.     static int    monthlengths[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  190.  
  191.     if (month == FEB && (dysize(current.tm_year + 1900) == 366))
  192.         return(29);
  193.     else
  194.         return(monthlengths[month]);
  195. }
  196.  
  197. /*
  198.  *
  199.  * Append data from active timeslots to end of "tmp.appointments"
  200.  * file, then copy "tmp.appointments" to "appointments".  Note that
  201.  * when we opened the current day we filtered "appointments":
  202.  * all items that applied to the current day were displayed and
  203.  * stored in slots; all others were copied to "tmp.appointments".
  204.  * So by now "tmp.appointments" contains no entries for the
  205.  * current day.
  206.  * As an optimization, if nothing changed in the day then the
  207.  * original appointments file is left unchanged.
  208.  *
  209.  */
  210.  
  211. close_day()
  212. {
  213.         int i, j;
  214.         FILE *f;
  215.     struct stat sbuf;
  216.     struct appt_entry *aptr, *optr;
  217.  
  218.     if (read_only || !new_entry) {
  219.         new_entry = 0;
  220.         day_is_open = FALSE;
  221.         return(0);
  222.     }
  223.  
  224.     f = fopen(tmpapts_pathname, "a+");
  225.     if (f == NULL) {
  226.         err_rpt("can't open temp file for appending", NON_FATAL);
  227.         day_is_open = FALSE;
  228.         return(1);
  229.     }
  230.     
  231.     for (i=0; i<N_SLOTS; i++) {
  232.                 if (slots[i].first != NULL) {
  233.             aptr = slots[i].first;
  234.             if (put_aentry(f, aptr))
  235.                 /* write error */
  236.                 break;
  237.             optr = aptr;
  238.             while (aptr = aptr->next) {
  239.                 free(optr);
  240.                 if (put_aentry(f, aptr))
  241.                     /* write error */
  242.                     break;
  243.                 optr = aptr;
  244.             }
  245.             free(optr);
  246.         }
  247.         }
  248.     if (ferror(f))
  249.         err_rpt("write on temp file failed", FATAL);
  250.         fclose(f);
  251.     new_entry = 0;
  252.     day_is_open = FALSE;
  253.     /* don't rename zero length files */
  254.     stat(tmpapts_pathname, &sbuf);
  255.     if (sbuf.st_size == (off_t) 0)
  256.         return(1);
  257.     xrename(tmpapts_pathname, apts_pathname);
  258. }
  259.  
  260. /*
  261.  * get entry from appointments file
  262.  */
  263. get_aentry(apts_file, appt)
  264. FILE *apts_file;
  265. struct appt_entry *appt;
  266. {
  267.     char *ptr, *str;
  268.     char *fgets(), *index();
  269.     char *incl_ptr, incl_buf[128], wday[3];
  270.     int i, lib;
  271.     struct stat sbuf;
  272.     static FILE *include;
  273.  
  274.     appt->flags = appt->repeat = appt->lookahead = 0;
  275.     appt->sindex = 0;
  276.     appt->next = NULL;
  277.     if (including) {
  278.         if (fgets(inbuf, 512, include) == NULL) {
  279.             /* end of include file - get next entry
  280.              * from main file
  281.              */
  282.             including = 0;
  283.             fclose(include);
  284.             if (fgets(inbuf, 512, apts_file) == NULL)
  285.                 return(EOF);
  286.         } else {
  287.             including = 2;
  288.             /* don't modify stuff from include files */
  289.             appt->flags |= READONLY;
  290.         }
  291.     } else
  292.         if (fgets(inbuf, 512, apts_file) == NULL)
  293.             return(EOF);
  294.     ptr = inbuf;
  295.     if (*ptr == '#') {
  296.         if (!strcmp(inbuf, HEADER) && !including) {
  297.             /* first line in file read */
  298.             if (include_old && (First.tm_year < today.tm_year)) {
  299.                 /* read in old include file (if it exists) */
  300.                 /* prepend directory info */
  301.                 sprintf(incl_buf, "%s/.appointments.%02d",
  302.                     apts_dir, First.tm_year);
  303.                 if (!stat(incl_buf, &sbuf)) {
  304.                     if ((include = fopen(incl_buf, "r")) == NULL)
  305.                         err_rpt("can't open include file (ignored)", NON_FATAL);
  306.                     else
  307.                         including = 1;
  308.                 }
  309.             }
  310.         } else if (!strncmp(inbuf, "#include", 8)) {
  311.             /* include file */
  312.             if (including)
  313.                 err_rpt("include files may not be nested", FATAL);
  314.             incl_ptr = strbuf;
  315.             if ((ptr = index(inbuf, '"')) == NULL)
  316.  
  317.                 if ((ptr = index(inbuf, '<')) == NULL) {
  318.                     err_rpt("missing '\"' or '<' in include file spec", NON_FATAL);
  319.                     return(0);
  320.                 } else {
  321.                     lib = 1;
  322.                 }
  323.             else
  324.                 lib = 0;
  325.             ptr++;
  326.             while (*ptr && *ptr != '"' && *ptr != '>')
  327.                 *incl_ptr++ = *ptr++;
  328.             if (! *ptr) {
  329.                 err_rpt("missing '\"' or '>' in include file spec", NON_FATAL);
  330.                 return(0);
  331.             }
  332.             *incl_ptr = '\0';
  333.             if (strbuf[0] == '/')
  334.                 /* full pathname provided */
  335.                 strcpy(incl_buf, strbuf);
  336.             else
  337.                 /* prepend directory info */
  338.                 if (lib)
  339.                     sprintf(incl_buf, "%s/%s", lib_dir, strbuf);
  340.                 else
  341.                     sprintf(incl_buf, "%s/%s", apts_dir, strbuf);
  342.             if ((include = fopen(incl_buf, "r")) == NULL)
  343.                 err_rpt("can't open include file (ignored)", NON_FATAL);
  344.             else 
  345.                 including = 1;
  346.         }
  347.         appt->flags |= A_COMMENT;
  348.         return(0);
  349.     }
  350.     while (isspace(*ptr))
  351.         ++ptr;
  352.     if (!*ptr) {
  353.         /* empty line */
  354.         appt->flags |= A_COMMENT;
  355.         return(0);
  356.     }
  357.     if (*ptr == '*') {
  358.         appt->flags |= ALL_YEARS;
  359.         appt->year = START_YEAR;
  360.         ++ptr;    /* point to second '*' */
  361.         ++ptr;    /* point to space */
  362.     } else {
  363.         appt->year = 0;
  364.         while (isdigit(*ptr)) {
  365.             appt->year *= 10;
  366.             appt->year += *ptr++ - '0';
  367.         }
  368.         /* sanity check */
  369.         if (appt->year < 0) {
  370.             err_rpt("illegal year value (ignored)", NON_FATAL);
  371.             return(1);
  372.         }
  373.     }
  374.     while (isspace(*ptr))
  375.         ++ptr;
  376.     if (*ptr == '*') {
  377.         appt->flags |= ALL_MONTHS;
  378.         appt->month = 0;
  379.         ++ptr;
  380.     } else {
  381.         appt->month = (*ptr - '0') * 10;
  382.         appt->month += *++ptr - '0';
  383.         if (one_based) (appt->month)--;
  384.         /* sanity check */
  385.         if (appt->month < JAN || appt->month > DEC) {
  386.             /*
  387.             sprintf(errbuf, "illegal month value [%d] (ignored)", appt->month);
  388.             err_rpt(errbuf, NON_FATAL);
  389.             */
  390.             err_rpt("illegal month value (ignored)", NON_FATAL);
  391.             return(1);
  392.         }
  393.     }
  394.     ++ptr;
  395.     while (isspace(*ptr))
  396.         ++ptr;
  397.     if (*ptr == '*') {
  398.         appt->flags |= ALL_DAYS;
  399.         appt->day = 0;
  400.         appt->repeat = 1;
  401.         ++ptr;
  402.     } else if (isdigit(*ptr)) {
  403.         appt->day = (*ptr - '0') * 10;
  404.         appt->day += *++ptr - '0';
  405.         if (!one_based) (appt->day)++;
  406.         /* sanity check */
  407.         if (appt->day < 1 || appt->day > 31) {
  408.             err_rpt("illegal day value (ignored)", NON_FATAL);
  409.             return(1);
  410.         }
  411.     } else {
  412.         /* check for day names */
  413.         wday[0] = islower(*ptr) ? toupper(*ptr) : *ptr;
  414.         ++ptr;
  415.         wday[1] = islower(*ptr) ? toupper(*ptr) : *ptr;
  416.         wday[2] = '\0';
  417.         i = 0;
  418.         if (!strcmp(wday, dayname[i++]))
  419.             appt->flags |= EVERY_SUN;
  420.         else if (!strcmp(wday, dayname[i++]))
  421.             appt->flags |= EVERY_MON;
  422.         else if (!strcmp(wday, dayname[i++]))
  423.             appt->flags |= EVERY_TUE;
  424.         else if (!strcmp(wday, dayname[i++]))
  425.             appt->flags |= EVERY_WED;
  426.         else if (!strcmp(wday, dayname[i++]))
  427.             appt->flags |= EVERY_THU;
  428.         else if (!strcmp(wday, dayname[i++]))
  429.             appt->flags |= EVERY_FRI;
  430.         else if (!strcmp(wday, dayname[i]))
  431.             appt->flags |= EVERY_SAT;
  432.         else {
  433.             /* sanity check */
  434.             err_rpt("illegal day name (ignored)", NON_FATAL);
  435.             return(1);
  436.         }
  437.         appt->day = 0;
  438.         appt->flags |= REPEAT;
  439.         appt->repeat = ALL_WEEKS;    /* default to every week */
  440.     }
  441.     ++ptr;
  442.     while (isspace(*ptr))
  443.         ++ptr;
  444.     appt->hour = (*ptr - '0') * 10;
  445.     appt->hour += *++ptr - '0';
  446.     /* sanity check */
  447.     if (appt->hour < 0 || (appt->hour > 23 && appt->hour != 99)) {
  448.         err_rpt("illegal hour value (ignored)", NON_FATAL);
  449.         return(1);
  450.     }
  451.     if ((version2 && appt->hour == 99) || (!version2 && appt->hour == 0))
  452.         appt->flags |= A_NOTE;
  453.     ++ptr;
  454.     while (isspace(*ptr))
  455.         ++ptr;
  456.     appt->minute = (*ptr - '0') * 10;
  457.     appt->minute += *++ptr - '0';
  458.     /* sanity check */
  459.     if (appt->minute < 0 || (appt->minute > 59 && appt->minute != 99)) {
  460.         /* minutes currently can only be 00 or 30
  461.          * unless it's a note.
  462.          */
  463.         err_rpt("illegal minute value (ignored)", NON_FATAL);
  464.         return(1);
  465.     }
  466.     if ((appt->flags & A_NOTE) && version2 && appt->minute == 99)
  467.         appt->flags |= MARKED;  /* don't show in mon/yr display */
  468.     ++ptr;
  469.     while (isspace(*ptr))
  470.         ++ptr;
  471.     appt->arrows = (*ptr - '0') * 10;
  472.     appt->arrows += *++ptr - '0';
  473.     /* sanity check */
  474.     if (appt->arrows < 0 || appt->arrows > N_TSLOTS) {
  475.         err_rpt("illegal arrow value (ignored)", NON_FATAL);
  476.         return(1);
  477.     }
  478.     ++ptr;
  479.     while (isspace(*ptr))
  480.         ++ptr;
  481.     /* lookahead and repeat entries are free format, i.e. they */
  482.     /* can occur in either order */
  483.     if (*ptr == '[') {
  484.         appt->flags |= REPEAT;
  485.         if (appt->flags & EVERY_SOMEDAY) {
  486.             if ((appt->repeat = do_wk_repeat(&ptr)) < 0)
  487.                 return(1);
  488.         } else {
  489.             if ((appt->repeat = do_repeat(&ptr)) < 0)
  490.                 return(1);
  491.         }
  492.         if (*ptr == '<') {
  493.             appt->flags |= LOOKAHEAD;
  494.             if ((appt->lookahead = do_lookahead(&ptr)) < 0)
  495.                 return(1);
  496.         }
  497.         if (*ptr == '#') {
  498.             appt->flags |= DELETED;
  499.             ++ptr;
  500.             while (isspace(*ptr))
  501.                 ++ptr;
  502.         }
  503.     } else if (*ptr == '<') {
  504.         appt->flags |= LOOKAHEAD;
  505.         if ((appt->lookahead = do_lookahead(&ptr)) < 0)
  506.             return(1);
  507.         if (*ptr == '[') {
  508.             appt->flags |= REPEAT;
  509.             if (appt->flags & EVERY_SOMEDAY) {
  510.                 if ((appt->repeat = do_wk_repeat(&ptr)) < 0)
  511.                     return(1);
  512.             } else {
  513.                 if ((appt->repeat = do_repeat(&ptr)) < 0)
  514.                     return(1);
  515.             }
  516.         }
  517.         if (*ptr == '#') {
  518.             appt->flags |= DELETED;
  519.             ++ptr;
  520.             while (isspace(*ptr))
  521.                 ++ptr;
  522.         }
  523.     } else if (*ptr == '#') {
  524.         appt->flags |= DELETED;
  525.         ++ptr;
  526.         while (isspace(*ptr))
  527.             ++ptr;
  528.     }
  529.     str = strbuf;
  530.     while (*ptr && *ptr != '\n')
  531.         *str++ = *ptr++;
  532.     *str = '\0';
  533.     strcpy(appt->str, strbuf);
  534.  
  535.     return(0);
  536. }
  537.  
  538. /* parse normal repeated entry field */
  539. do_repeat(ptr)
  540. char **ptr;
  541. {
  542.     int repeat = 0;
  543.  
  544.     while (isdigit(*++*ptr))
  545.         repeat = repeat * 10 + (int)(**ptr - '0');
  546.     if (**ptr != ']') {
  547.         err_rpt("bad entry (ignored)", NON_FATAL);
  548.         return(-1);
  549.     }
  550.     /* sanity check */
  551.     if (repeat < 0) {
  552.         err_rpt("illegal repeat interval (ignored)", NON_FATAL);
  553.         return(-1);
  554.     }
  555.     ++*ptr;
  556.     while (isspace(**ptr))
  557.         ++*ptr;
  558.     return(repeat);
  559. }
  560.  
  561. /* parse weekly repeated entry field */
  562. do_wk_repeat(ptr)
  563. char **ptr;
  564. {
  565.     int repeat = 0;
  566.  
  567.     while (*++*ptr != ']') {
  568.         if (**ptr == ',')
  569.             continue;    /* get next week */
  570.         if (isdigit(**ptr)) {
  571.             repeat |= 0x1<<(**ptr - '1');
  572.         } else if (**ptr == 'L' || **ptr == 'l') {
  573.             /* last week in month */
  574.             repeat |= LAST_WEEK;
  575.         } else {
  576.             /* format error */
  577.             err_rpt("illegal repeat specification (ignored)", NON_FATAL);
  578.             return(-1);
  579.         }
  580.     }
  581.     /* sanity check */
  582.     if ((unsigned int)repeat > WEEK_LIMIT) {
  583.         err_rpt("illegal weekly repeat (ignored)", NON_FATAL);
  584.         return(-1);
  585.     }
  586.     ++*ptr;
  587.     while (isspace(**ptr))
  588.         ++*ptr;
  589.  
  590.     return(repeat);
  591. }
  592.  
  593. /* parse lookahead entry field */
  594. do_lookahead(ptr)
  595. char **ptr;
  596. {
  597.     int lookahead = 0;
  598.  
  599.     while (isdigit(*++*ptr))
  600.         lookahead = lookahead * 10 + (int)(**ptr - '0');
  601.     if (**ptr != '>') {
  602.         err_rpt("bad entry (ignored)", NON_FATAL);
  603.         return(-1);
  604.     }
  605.     /* sanity check */
  606.     if (lookahead < 0) {
  607.         err_rpt("illegal lookahead interval (ignored)", NON_FATAL);
  608.         return(-1);
  609.     }
  610.     ++*ptr;
  611.     while (isspace(**ptr))
  612.         ++*ptr;
  613.     return(lookahead);
  614. }
  615.  
  616. /*
  617.  * put entry into appointments file
  618.  */
  619. put_aentry(apts_file, appt)
  620. FILE *apts_file;
  621. struct appt_entry *appt;
  622. {
  623.     char *to_str();
  624.  
  625.     if (read_only)
  626.         return(0);
  627.  
  628.     if (appt->flags & READONLY)
  629.         /* don't copy include file entries */
  630.         /* (the include directive is copied as a comment) */
  631.         return(0);
  632.     if (appt->flags & A_COMMENT) {
  633.         fputs(inbuf, apts_file);
  634.         return(ferror(apts_file));
  635.     }
  636.     if (appt->flags & ALL_YEARS)
  637.         fputs("** ", apts_file);
  638.     else if (appt->year > 99)
  639.         fprintf(apts_file, "%03d ", appt->year);
  640.     else
  641.         fprintf(apts_file, "%02d ", appt->year);
  642.     if (appt->flags & ALL_MONTHS)
  643.         fputs("** ", apts_file);
  644.     else
  645.         fprintf(apts_file, "%02d ", one_based ? appt->month+1 : appt->month);
  646.     if (appt->flags & ALL_DAYS)
  647.         fputs("** ", apts_file);
  648.     else if (appt->flags & EVERY_SOMEDAY) {
  649.         switch (appt->flags & EVERY_SOMEDAY) {
  650.             case EVERY_SUN:
  651.                 fputs("Su ", apts_file);
  652.                 break;
  653.             case EVERY_MON:
  654.                 fputs("Mo ", apts_file);
  655.                 break;
  656.             case EVERY_TUE:
  657.                 fputs("Tu ", apts_file);
  658.                 break;
  659.             case EVERY_WED:
  660.                 fputs("We ", apts_file);
  661.                 break;
  662.             case EVERY_THU:
  663.                 fputs("Th ", apts_file);
  664.                 break;
  665.             case EVERY_FRI:
  666.                 fputs("Fr ", apts_file);
  667.                 break;
  668.             case EVERY_SAT:
  669.                 fputs("Sa ", apts_file);
  670.                 break;
  671.         }
  672.     } else
  673.         fprintf(apts_file, "%02d ", one_based ? appt->day : appt->day-1);
  674.     if (appt->flags & A_NOTE) {
  675.         appt->hour = 99;
  676.         appt->minute = 0;    /* assume unmarked note */
  677.     }
  678.     if ((appt->flags & MARKED_NOTE) == MARKED_NOTE)
  679.         appt->minute = 99;
  680.     if (!(appt->flags & (ALL_DAYS|DELETED)) && appt->flags & REPEAT) {
  681.         if (appt->flags & EVERY_SOMEDAY)
  682.             fprintf(apts_file, "%02d %02d %02d %s ", appt->hour, appt->minute, appt->arrows, to_str(appt->repeat));
  683.         else
  684.             fprintf(apts_file, "%02d %02d %02d [%d] ", appt->hour, appt->minute, appt->arrows, appt->repeat);
  685.     } else
  686.         fprintf(apts_file, "%02d %02d %02d ", appt->hour, appt->minute, appt->arrows);
  687.  
  688.     if (appt->flags & LOOKAHEAD)
  689.         fprintf(apts_file, "<%d> ", appt->lookahead);
  690.     if (appt->flags & DELETED)
  691.         fprintf(apts_file, "# %s\n", appt->str);
  692.     else
  693.         fprintf(apts_file, "%s\n", appt->str);
  694.     
  695.     /* check for failure (e.g. file system full) */
  696.     return(ferror(apts_file));
  697. }
  698.  
  699. char rptstr[10];
  700.  
  701. /* convert repeat bit map to printable string */
  702. char *
  703. to_str(repeat)
  704. int repeat;
  705. {
  706.     int i, j = 0;
  707.  
  708.     if (repeat == ALL_WEEKS)
  709.         /* if it's every week, then don't write [] spec */
  710.         rptstr[0] = '\0';
  711.     else {
  712.         rptstr[j++] = '[';
  713.         for (i=0; i<5; i++) {
  714.             if (repeat & (0x1<<i)) {
  715.                 rptstr[j++] = i+1 + '0';
  716.                 rptstr[j++] = ',';
  717.             }
  718.         }
  719.         if (repeat & LAST_WEEK) {
  720.             rptstr[j++] = 'L';
  721.             rptstr[j++] = ',';
  722.         }
  723.         rptstr[j] = '\0';
  724.         rptstr[--j] = ']';
  725.     }
  726.     return (rptstr);
  727. }
  728.  
  729. /*
  730.  * Print today's appointments to stdout or mail (useful if we only have an ASCII
  731.  * terminal connected to our Sun). Invoked by the "-p", "-P" or "-m" options.
  732.  */
  733. print_apts(level)
  734. int level;
  735. {
  736.     int i;
  737.     FILE *output, *popen();
  738.     char cmd[80], *name, *cuserid(), *format_appt();
  739.     struct appt_entry tmp_apt, *sptr;
  740.  
  741.     fix_current_day();
  742.     get_day_appts();
  743.     if (level == 2) {
  744.         if (mailto != NULL) {
  745.             name = mailto;
  746.         } else if ((name = cuserid(NULL)) == NULL) {
  747.             err_rpt("nobody to mail to", FATAL);
  748.         }
  749.         sprintf(cmd, "mail -s \"Appointments for today\" %s", name);
  750.         if ((output = popen(cmd, "w")) == NULL)
  751.             err_rpt("Couldn't pipe to 'mail'", FATAL);
  752.     } else {
  753.         output = stdout;
  754.     }
  755.     
  756.     fprintf(output,"\n\t*** Appointments for %s %s %d, 19%02d ***\n\n", 
  757.         daynames[current.tm_wday], monthnames[current.tm_mon],
  758.         current.tm_mday, current.tm_year);
  759.     
  760.     for (i=0; i<N_SLOTS; i++) {
  761.         if (i == n_tslots)
  762.             /* start of notes section */
  763.             fprintf(output,"\n\t\t     ===== Notes =====\n");
  764.         if (slots[i].first != NULL && slots[i].count > 0) {
  765.             /* at least one appt here */
  766.             slots[i].cur_appt = slots[i].first;
  767.             do {
  768.                 if (level == 3 && ((slots[i].cur_appt->flags & MARKED_NOTE) == MARKED_NOTE))
  769.                     continue;
  770.                 if (chk_deleted(i))
  771.                     continue;
  772.                 tmp_apt = *slots[i].cur_appt;
  773.                 tmp_apt.year = current.tm_year;
  774.                 tmp_apt.month = current.tm_mon;
  775.                 tmp_apt.day = current.tm_mday;
  776.                 fprintf(output, "%s\n", format_appt(&tmp_apt));
  777.             } while ((slots[i].cur_appt = slots[i].cur_appt->next) != NULL);
  778.         }
  779.     }
  780.     if (findex) {
  781.         /* print out future appointments */
  782.         fprintf(output, "\n\t\t===== Future Reminders =====\n");
  783.         for (i=0; i<findex; i++)
  784.             fprintf(output, "%s\n", format_appt(&future[i]));
  785.     }
  786.     fflush(output);
  787.     if (level == 2)
  788.         pclose(output);
  789. }
  790.  
  791. /*
  792.  * Convert from version 1 appts files to version 2 file format.
  793.  */
  794. ver1to2()
  795. {
  796.     FILE *oappts, *nappts, *fp;
  797.     struct appt_entry appt;
  798.     int err_flag, save_base;
  799.     char save_file[128];
  800.     struct stat stbuf;
  801.  
  802.     /*
  803.      * The main difference is that the ver 2 files are one-based, i.e
  804.      * days and months start with 1, rather than 0. Another difference
  805.      * is that a hour entry of 99 is used to flag a memo entry, rather
  806.      * than 00. This allows for 24-hour appointments.
  807.      * Version 2 appts files are marked with a special header line
  808.      * defined by the HEADER string (in ct.h).  Version 2 files
  809.      * also support a "lookahead" reminder service to remind one
  810.      * in advance of a future appointment.
  811.      * If "save_old" is set, then any appointments for years prior
  812.      * to this one are save in a special file of the form
  813.      * ".appointments.YY", where YY is the year.
  814.      */
  815.  
  816.     if (read_only != 0) {
  817.         err_rpt("appts file is read-only, no conversion done", NON_FATAL);
  818.         return;
  819.     }
  820.  
  821.      /* open files, etc */
  822.     if ((oappts = fopen(apts_pathname, "r")) == NULL) {
  823.         err_rpt("can't open appts file for reading", FATAL);
  824.         /* NOT REACHED */
  825.      }
  826.     if ((nappts = fopen(tmpapts_pathname, "w")) == NULL) {
  827.         err_rpt("can't open temp file for writing", NON_FATAL);
  828.         return;
  829.      }
  830.      /* write new header line */
  831.      fputs(HEADER, nappts);
  832.  
  833.      /* copy existing entries to the new file */
  834.      save_base = one_based;
  835.      while ((err_flag = get_aentry(oappts, &appt)) != EOF) {
  836.         if (err_flag)
  837.             continue;    /* ignore badly formatted input */
  838.         if (appt.hour == 0)
  839.             appt.flags |= A_NOTE;
  840.         one_based = 1;        /* force new format output */
  841.         if (save_old && !(appt.flags & ALL_YEARS) && (appt.year < today.tm_year)
  842.            && !((appt.flags & REPEAT) && !(appt.flags & EVERY_SOMEDAY))
  843.            && !(appt.flags & A_COMMENT)) {
  844.             /* prepend directory info */
  845.             sprintf(save_file, "%s/.appointments.%02d",
  846.                 apts_dir, appt.year);
  847.             if (stat(save_file, &stbuf) && errno == ENOENT) {
  848.                 /* new file*/
  849.                 if ((fp = fopen(save_file, "w")) == NULL)
  850.                     err_rpt("can't open save file, bailing out", FATAL);
  851.                 fputs(HEADER, fp);
  852.                 fclose(fp);
  853.             }
  854.             if ((fp = fopen(save_file, "a+")) == NULL)
  855.                 err_rpt("can't open save file, bailing out", FATAL);
  856.             else {
  857.                 if (put_aentry(fp, &appt))
  858.                     err_rpt("write to save appt file failed, bailing out", FATAL);
  859.                 fclose(fp);
  860.             }
  861.         } else {
  862.             if (put_aentry(nappts, &appt))
  863.                 err_rpt("write to new appt file failed, bailing out", FATAL);
  864.         }
  865.         one_based = save_base;    /* (maybe) force old format input */
  866.     }
  867.     fclose(oappts);
  868.     fclose(nappts);
  869.     xrename(tmpapts_pathname, apts_pathname);
  870.     one_based = 1;
  871.     version2 = 1;
  872. }
  873.  
  874. /*
  875.  * Scan appointments file for outdated appointments and save them to a
  876.  * special file of the form ".appointments.YY", where YY is the year
  877.  * of that appointment.
  878.  */
  879. create_old_files()
  880. {
  881.     FILE *oappts, *nappts, *fp;
  882.     struct appt_entry appt;
  883.     int err_flag;
  884.     char save_file[128];
  885.     struct stat stbuf;
  886.  
  887.     if (read_only != 0) {
  888.         err_rpt("appts file is read-only, no conversion done", NON_FATAL);
  889.         return;
  890.     }
  891.  
  892.      /* open files, etc */
  893.     if ((oappts = fopen(apts_pathname, "r")) == NULL) {
  894.         err_rpt("can't open appts file for reading", FATAL);
  895.         /* NOT REACHED */
  896.      }
  897.     if ((nappts = fopen(tmpapts_pathname, "w")) == NULL) {
  898.         err_rpt("can't open temp file for writing", NON_FATAL);
  899.         return;
  900.      }
  901.  
  902.      /* copy existing entries to the tmp file, checking dates */
  903.      while ((err_flag = get_aentry(oappts, &appt)) != EOF) {
  904.         if (err_flag)
  905.             continue;    /* ignore badly formatted input */
  906.         if (!(appt.flags & A_COMMENT)
  907.            && !(appt.flags & ALL_YEARS) && (appt.year < today.tm_year)
  908.            && !((appt.flags & REPEAT) && !(appt.flags & EVERY_SOMEDAY))) {
  909.             /* prepend directory info */
  910.             sprintf(save_file, "%s/.appointments.%02d",
  911.                 apts_dir, appt.year);
  912.             if (stat(save_file, &stbuf) && errno == ENOENT) {
  913.                 /* new file*/
  914.                 if ((fp = fopen(save_file, "w")) == NULL)
  915.                     err_rpt("can't open save file, bailing out", FATAL);
  916.                 fputs(HEADER, fp);
  917.                 fclose(fp);
  918.             }
  919.             if ((fp = fopen(save_file, "a+")) == NULL)
  920.                 err_rpt("can't open save file, bailing out", FATAL);
  921.             else {
  922.                 if (put_aentry(fp, &appt))
  923.                     err_rpt("write to save appt file failed, bailing out", FATAL);
  924.                 fclose(fp);
  925.             }
  926.         } else {
  927.             if (put_aentry(nappts, &appt))
  928.                 err_rpt("write to new appt file failed, bailing out", FATAL);
  929.         }
  930.     }
  931.     fclose(oappts);
  932.     fclose(nappts);
  933.     xrename(tmpapts_pathname, apts_pathname);
  934. }
  935.  
  936. /*
  937.  * convert appt entry to ASCII string for display with date, time and msg
  938.  */
  939. char *
  940. format_appt(appt)
  941. struct appt_entry *appt;
  942. {
  943.     int e_hour, e_minutes, duration;
  944.     struct tm Save;
  945.  
  946.     if (appt->arrows > 0) {
  947.         duration = appt->arrows + 1;
  948.         e_hour = appt->hour + duration/2;
  949.         e_minutes = appt->minute + ((duration%2) * 30);
  950.     } else {
  951.         e_hour = appt->hour;
  952.         e_minutes = appt->minute + 30;
  953.     }
  954.     if (e_minutes == 60) {
  955.         e_minutes = 0;
  956.         ++e_hour;
  957.     }
  958.     /* get day of week */
  959.     Save = current;
  960.     current.tm_year = appt->year;
  961.     current.tm_mon = appt->month;
  962.     current.tm_mday = appt->day;
  963.     fix_current_day();
  964.  
  965.     if (appt->flags & A_NOTE) {
  966.         /* note */
  967.         if (appt->flags & ALL_YEARS)
  968.             sprintf(strbuf,"%3.3s %2d/%02d    --  %s",
  969.                 daynames[current.tm_wday], appt->month+1,
  970.                 appt->day, appt->str);
  971.         else if (appt->year > 99)
  972.             sprintf(strbuf,"%3.3s %2d/%02d/%02d --  %s",
  973.                 daynames[current.tm_wday], appt->month+1,
  974.                 appt->day, appt->year-100, appt->str);
  975.         else
  976.             sprintf(strbuf,"%3.3s %2d/%02d/%02d --  %s",
  977.                 daynames[current.tm_wday], appt->month+1,
  978.                 appt->day, appt->year, appt->str);
  979.     } else
  980.         /* standard appointment */
  981.         sprintf(strbuf,"%3.3s %2d/%02d/%02d -- %2d:%02d to %2d:%02d   %s",
  982.             daynames[current.tm_wday], appt->month+1,
  983.             appt->day, appt->year, appt->hour, appt->minute,
  984.             e_hour, e_minutes, appt->str);
  985.  
  986.     current = Save;
  987.     return(strbuf);
  988. }
  989.  
  990. /*
  991.  * convert appt entry to ASCII string for display with time and msg
  992.  */
  993. char *
  994. format_appt_nd(appt)
  995. struct appt_entry *appt;
  996. {
  997.     int e_hour, e_minutes, duration;
  998.     struct tm Save;
  999.  
  1000.     if (appt->arrows > 0) {
  1001.         duration = appt->arrows + 1;
  1002.         e_hour = appt->hour + duration/2;
  1003.         e_minutes = appt->minute + ((duration%2) * 30);
  1004.     } else {
  1005.         e_hour = appt->hour;
  1006.         e_minutes = appt->minute + 30;
  1007.     }
  1008.     if (e_minutes == 60) {
  1009.         e_minutes = 0;
  1010.         ++e_hour;
  1011.     }
  1012.  
  1013.     if (appt->flags & A_NOTE) {
  1014.         /* note */
  1015.         sprintf(strbuf,"%s", appt->str);
  1016.     } else
  1017.         /* standard appointment */
  1018.         sprintf(strbuf,"%2d:%02d to %2d:%02d   %s",
  1019.             appt->hour, appt->minute, e_hour, e_minutes, appt->str);
  1020.  
  1021.     return(strbuf);
  1022. }
  1023.  
  1024. /*
  1025.  * parse the date on the given tring and reset the "current"
  1026.  * date to reflect that date. The date may take the form of a
  1027.  * day name (e.g. Tu, Tue, Tuesday) or a date in m/d/y format
  1028.  * where the month and/or year may be missing (e.g. 27 = 27th
  1029.  * of this month, 8/27 = August 27 of this year, 8/27/89 =
  1030.  * August 27 of 1989. If 'cmdline' is true, then the string
  1031.  * came from the command line '-d' option.
  1032.  */
  1033. int
  1034. parse_date(str, cmdline)
  1035. char *str;
  1036. int cmdline;
  1037. {
  1038.     char c[4];
  1039.     int i, dow = -1, m = -1, d = -1, y = -1;
  1040.  
  1041.     if (isdigit(*str)) {
  1042.         /* must be a m/d/y date */
  1043.         /* assume it's a month first */
  1044.         m = *str++ - '0';
  1045.         if (isdigit(*str))
  1046.             m = m*10 + *str++ - '0';
  1047.         if (!*str) {
  1048.             /* no more chars => day only */
  1049.             d = m;
  1050.             m = -1;
  1051.         } else if (*str++ != '/') {
  1052.             if (cmdline)
  1053.                 err_rpt("badly formed date for -d option (ignored)", NON_FATAL);
  1054.             else
  1055.                 err_rpt("badly formed date - please reenter", NON_FATAL);
  1056.             return(1);
  1057.         } else {
  1058.             d = *str++ - '0';
  1059.             if (isdigit(*str))
  1060.                 d = d*10 + *str++ - '0';
  1061.             if (*str++ == '/') {
  1062.                 /* year also specified */
  1063.                 y = *str++ - '0';
  1064.                 if (isdigit(*str)) {
  1065.                     y = y*10 + *str++ - '0';
  1066.                     if (*str && isdigit(*str))
  1067.                         y = y*10 + *str - '0';
  1068.                 }
  1069.             }
  1070.         }
  1071.         if (y > 0)
  1072.             current.tm_year = y;
  1073.         if (m > 0)
  1074.             current.tm_mon = m - 1;
  1075.         if (d > 0)
  1076.             current.tm_mday = d;
  1077.         fix_current_day();
  1078.     } else {
  1079.         /* day of week */
  1080.         /* check for day names */
  1081.         c[0] = islower(*str) ? toupper(*str) : *str;
  1082.         ++str;
  1083.         c[1] = islower(*str) ? toupper(*str) : *str;
  1084.         c[2] = '\0';
  1085.         for (i=0; i<7; i++) {
  1086.             if (!strcmp(c, dayname[i])) {
  1087.                 dow = i;
  1088.                 break;
  1089.             }
  1090.         }
  1091.         if (dow >= 0) {
  1092.             /* match found */
  1093.             current.tm_mday += dow - current.tm_wday;
  1094.             fix_current_day();
  1095.         } else {
  1096.             if (cmdline)
  1097.                 err_rpt("badly formed date for -d option (ignored)", NON_FATAL);
  1098.             else
  1099.                 err_rpt("badly formed date - please reenter", NON_FATAL);
  1100.             return(1);
  1101.         }
  1102.     }
  1103.     return(0);
  1104. }
  1105.  
  1106. #ifndef NO_PRINTER
  1107. /*
  1108.  * Print to Postscript compatable printer.  If we are displaying
  1109.  * the day or week page, then create a raster file of the canvas and
  1110.  * feed it to a raster->ps filter (sun2ps).  If on a month page, then
  1111.  * print a pretty month calendar with appts written in (as best
  1112.  * can be).
  1113.  */
  1114. print_calendar(file_type)
  1115. int file_type;
  1116. {
  1117.     Pixrect *save_pr;
  1118.     char prntcmd[64];
  1119.     int type = RT_STANDARD;
  1120.     int copy_flag = TRUE;
  1121.     Rect *rect;
  1122.     struct pr_prpos where;
  1123.     char buf[128], *cuserid();
  1124.     FILE *fp, *pfp;
  1125.  
  1126.     lock_cursors();
  1127.     working(TRUE);
  1128.     sprintf(buf, "Appointments file \"%s\" printed for %s on %s", apts_pathname, cuserid(NULL), todays_date);
  1129.     if (mainsw_state != DISPLAYING_MONTH) {
  1130.         if ((fp = fopen(rasfile, "w")) != NULL) {
  1131.             rect = (Rect *) window_get(canvas, WIN_RECT);
  1132.             save_pr = mem_create(rect->r_width, rect->r_height+3*sfont->pf_defaultsize.y, 1);
  1133.             pr_rop(save_pr,0,0,rect->r_width-3,rect->r_height,PIX_SRC,main_pixwin->pw_prretained,0,0);
  1134.             where.pr = save_pr;
  1135.             where.pos.x = 6 * font->pf_defaultsize.x;
  1136.             where.pos.y = rect->r_height + 2*sfont->pf_defaultsize.y;
  1137.             pf_text(where, PIX_SRC, sfont, buf);
  1138.             pr_dump(save_pr, fp, NULL, type, copy_flag);
  1139.             fclose(fp);
  1140.             if (file_type == PR_POSTSCRIPT) {
  1141.                 if ((pfp = fopen(psfile, "w")) != NULL) {
  1142.                     fp = fopen(rasfile, "r");
  1143.                     /* ras2ps closes the files that
  1144.                      * we opened here.
  1145.                      */
  1146.                     ras2ps(fp, pfp);
  1147.                     sprintf(prntcmd, "%s %s", printer, psfile);
  1148.                     system(prntcmd);
  1149.                     unlink(psfile);
  1150.                 } else
  1151.                     err_rpt("can't open tmp ps file", NON_FATAL);
  1152.             } else {
  1153.                 sprintf(prntcmd, "%s %s", printer, rasfile);
  1154.                 system(prntcmd);
  1155.             }
  1156.             pr_destroy(save_pr);
  1157.             unlink(rasfile);
  1158.         } else
  1159.             err_rpt("can't open tmp raster file", NON_FATAL);
  1160.     } else if (mainsw_state == DISPLAYING_MONTH) {
  1161.         if (file_type != PR_POSTSCRIPT) {
  1162.             err_rpt("only PostScript output available for month printout", NON_FATAL);
  1163.         } else {
  1164.             if ((pfp = fopen(psfile, "w")) != NULL) {
  1165.                 print_month(pfp);
  1166.                 fclose(pfp);
  1167.                 sprintf(prntcmd, "%s %s", printer, psfile);
  1168.                 system(prntcmd);
  1169.                 unlink(psfile);
  1170.             } else
  1171.                 err_rpt("can't open tmp ps file", NON_FATAL);
  1172.         }
  1173.     }
  1174.     working(FALSE);
  1175.     unlock_cursors();
  1176. }
  1177. #endif    /* NO_PRINTER */
  1178.  
  1179. /* set error logging flag */
  1180. err2console(state)
  1181. int state;
  1182. {
  1183.     /*
  1184.      * if TRUE, forces error messages to the console, even
  1185.      * if the base frame is running
  1186.      */
  1187.     log_to_console = state;
  1188. }
  1189.  
  1190. /*
  1191.  * Error reporting. Try first to put message in a popup frame, then
  1192.  * the console, then stderr as a last resort.
  1193.  */
  1194. err_rpt(errstr, fatal_flag)
  1195. char *errstr;
  1196. int fatal_flag;
  1197. {
  1198.     FILE    *f;
  1199.     int    closed;
  1200.  
  1201.     closed = (int) window_get(frame, FRAME_CLOSED);
  1202.     if (frame && !log_to_console && !closed) {
  1203.         /* base frame exists */
  1204.         create_prompt_frame(errstr, FALSE);
  1205.         (void) window_loop(prompt_frame);
  1206.         window_set(prompt_frame, WIN_SHOW, FALSE, 0);
  1207.     } else if ((f=fopen("/dev/console", "w")) != NULL) {
  1208.         fprintf(f, "%s: %s\n", progname, errstr);
  1209.         fclose(f);
  1210.     } else
  1211.         fprintf(stderr, "%s: %s\n", progname, errstr);
  1212.     if (fatal_flag)
  1213.         exit(1);
  1214. }
  1215.  
  1216. /* Clean-up */
  1217. cleanup()
  1218. {
  1219.     if (day_is_open)
  1220.         close_day();
  1221.  
  1222.     /* create outdated include files (if necessary) */
  1223.     if (save_old)
  1224.         create_old_files();
  1225.             
  1226.     /* delete tmp file */
  1227.     if (access(tmpapts_pathname, R_OK) == 0 && unlink(tmpapts_pathname) < 0)
  1228.         perror(tmpapts_pathname);
  1229. }
  1230.  
  1231. char sysbuf[512];
  1232.  
  1233. /* Rename files, copying if necessary */
  1234. xrename(from, to)
  1235. char *from, *to;
  1236. {
  1237.     if (rename(from, to) == -1) {
  1238.         /* rename sys call fialed, try doing a copy */
  1239.         sprintf(sysbuf, "cp %s %s", from, to);
  1240.         if (system(sysbuf) != 0)
  1241.             err_rpt("couldn't rename/copy tmp file", NON_FATAL);
  1242.     }
  1243. }
  1244.