home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume17 / calentool / part11 / cal2ct.c next >
Encoding:
C/C++ Source or Header  |  1991-04-06  |  11.1 KB  |  480 lines

  1. /*
  2.  * $Header: cal2ct.c,v 2.4 91/03/27 16:44:54 billr Exp $
  3.  */
  4. /*
  5.  * cal2ct - convert calendar reminder files to calentool style files
  6.  *
  7.  * Author: Bill Randle, Tektronix, Inc. <billr@saab.CNA.TEK.COM>
  8.  *
  9.  * Copyright (C) 1989, 1991 Tektronix, Inc.  All Rights Reserved
  10.  *
  11.  * Permission is hereby granted to use and modify this code in source
  12.  * or binary form as long as it is not sold for profit and this copyright
  13.  * notice remains intact.
  14.  */
  15.  
  16. #include "ct.h"
  17. #include <stdio.h>
  18. #include <ctype.h>
  19. #include <sys/time.h>
  20.  
  21. struct appt_entry appts, *aptr;
  22. char filename[128], *file, *ofile;
  23. FILE *fp;
  24. struct tm current, start, today, *localtime();
  25. struct timeval tp;
  26. int run_days = -1, use_date = 0;
  27. int day_first = FALSE;
  28. void err_rpt();
  29.  
  30. extern char *getenv();
  31. extern int optind;
  32. extern char *optarg;
  33.  
  34. main(argc, argv)
  35. int argc;
  36. char *argv[];
  37. {
  38.     int flag;
  39.  
  40.     gettimeofday(&tp, NULL);
  41.     current = today = *localtime(&tp.tv_sec);
  42.     ofile = NULL;
  43.     while ((flag = getopt(argc, argv, "d:f:r:eE")) != EOF)
  44.         switch (flag) {
  45.             case 'd':  /* starting date */
  46.                 /* updates "current" */
  47.                 (void)parse_date(optarg, TRUE);
  48.                 use_date++;
  49.                 break;
  50.             case 'e':
  51.             case 'E':  /* European style dates */
  52.                 day_first = TRUE;
  53.                 break;
  54.             case 'f':  /* output file */
  55.                 ofile = optarg;
  56.                 break;
  57.             case 'r':  /* number of days to process */
  58.                 run_days = atoi(optarg);
  59.                 break;
  60.             default:  /* unknown option */
  61.                 fprintf(stderr, "usage: cal2ct [-d date] [-r days] [-e] [-f outfile] [file]\n");
  62.                 break;
  63.         }
  64.  
  65.     start = current;
  66.     if (optind < argc)
  67.         file = argv[optind];
  68.     else {
  69.         strcpy(filename, getenv("HOME"));
  70.         strcat(filename, "/calendar");
  71.         file = filename;
  72.     }
  73.  
  74.     if ((fp = fopen(file, "r")) == NULL) {
  75.         fprintf(stderr, "can't open calendar file for reading\n");
  76.         exit(1);
  77.     }
  78.     if (!read_cal_file()) {
  79.         fprintf(stderr, "no reminders read from %s\n", file);
  80.         exit(1);
  81.     }
  82.     fclose(fp);
  83.     if (ofile)
  84.         strcpy(filename, ofile);
  85.     else {
  86.         strcpy(filename, getenv("HOME"));
  87.         strcat(filename, "/.appointments");
  88.     }
  89.     if ((fp = fopen(filename, "w")) == NULL) {
  90.         fprintf(stderr, "can't open appointments file for writing\n");
  91.         exit(1);
  92.     }
  93.     write_ct_file();
  94. }
  95.  
  96. /*
  97.  * read dates from calendar file and stuff into appts struct
  98.  */
  99. read_cal_file()
  100. {
  101.     char *fgets();
  102.     char buf[512];
  103.     struct appt_entry *optr;
  104.  
  105.     aptr = &appts;
  106.     while (fgets(buf, 512, fp) != NULL) {
  107.         aptr->repeat = aptr->lookahead = 0;
  108.         aptr->warn = 10;
  109.         aptr->flags = A_NOTE;
  110.         aptr->next = NULL;
  111.         if (parse_cal_date(buf))
  112.             continue;
  113.         aptr->next = (struct appt_entry *)malloc(sizeof(struct appt_entry));
  114.         if (aptr->next == NULL) {
  115.             fprintf(stderr, "out of memory\n");
  116.             return;
  117.         }
  118.         optr = aptr;
  119.         aptr = aptr->next;
  120.     }
  121.     if (aptr == &appts)
  122.         return(0);    /* nothing read */
  123.     /* don't need the last one */
  124.     free(aptr);
  125.     optr->next = NULL;
  126.     return(1);
  127. }
  128.  
  129. /*
  130.  * write out the new .appointments file
  131.  */
  132. write_ct_file()
  133. {
  134.     aptr = &appts;
  135.     fputs(HEADER, fp);
  136.     while (aptr) {
  137.         if (put_aentry(fp, aptr)) {
  138.             fprintf(stderr, "error writing appointments file\n");
  139.             return;
  140.         }
  141.         aptr = aptr->next;
  142.     }
  143. }
  144.  
  145. char *dayname[7] = {"SU", "MO", "TU", "WE", "TH", "FR", "SA"};
  146. char *monthname[12] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  147.     "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
  148.  
  149. /*
  150.  * parse the date in the buffer and reset the "current"
  151.  * date to reflect that date. The date may take the form of a
  152.  * month and day where the month may be spelled or numeric, e.g.:
  153.  * Feb 27, feb 27, 2/27. The month may be a `*' to refer to any
  154.  * month, e.g. "* 27" or "* /27".
  155.  * Limitation: the date is expected to start at the begining of
  156.  * the line. (calendar allows it anywhere in the line.)
  157.  */
  158. parse_cal_date(str)
  159. char *str;
  160. {
  161.     char c[4];
  162.     int i, m = -1, d = -1;
  163.     void fix_current_day();
  164.  
  165.     current = start;    /* start with this month, day, year */
  166.     while (isspace(*str))
  167.         ++str;
  168.  
  169.     if (isdigit(*str)) {
  170.         /* must be a m/d date */
  171.         /* assume it's a month first */
  172.         m = *str++ - '0';
  173.         if (isdigit(*str))
  174.             m = m*10 + *str++ - '0';
  175.         --m; /* make it zero based */
  176.         if (*str != '/') {
  177.             /* no more chars => bad format */
  178.             fprintf(stderr, "badly formed date: %s\n", str-2);
  179.             return(1);
  180.         } else {
  181.             ++str;
  182.             if (isdigit(*str)) {
  183.                 d = *str++ - '0';
  184.                 while (isdigit(*str))
  185.                     d = d*10 + *str++ - '0';
  186.             } else {
  187.                 fprintf(stderr, "badly formed date: %s\n", str-2);
  188.                 return(1);
  189.             }
  190.         }
  191.     } else if (*str == '*') {
  192.         aptr->flags |= ALL_MONTHS;
  193.         ++str;
  194.         while (isspace(*str) || *str == '/')
  195.             ++str;
  196.         d = *str++ - '0';
  197.         while (isdigit(*str))
  198.             d = d*10 + *str++ - '0';
  199.     } else {
  200.         /* month name */
  201.         c[0] = islower(*str) ? toupper(*str) : *str;
  202.         ++str;
  203.         c[1] = islower(*str) ? toupper(*str) : *str;
  204.         if (*++str) {
  205.             c[2] = islower(*str) ? toupper(*str) : *str;
  206.             c[3] = '\0';
  207.         } else
  208.             c[2] = '\0';
  209.         while (!isspace(*str))
  210.             ++str;
  211.         /* check month names */
  212.         for (i=0; i<12; i++) {
  213.             if (!strcmp(c, monthname[i])) {
  214.                 m = i;
  215.                 break;;
  216.             }
  217.         }
  218.         if (m >= 0) {
  219.             /* match found */
  220.             while (!isspace(*str))
  221.                 ++str;
  222.             d = *++str - '0';
  223.             ++str;
  224.             while (isdigit(*str))
  225.                 d = d*10 + *str++ - '0';
  226.         } else {
  227.             fprintf(stderr, "badly formed date: %s\n", str-2);
  228.             return(1);
  229.         }
  230.     }
  231.     current.tm_mon = m;
  232.     current.tm_mday = d;
  233.     if (use_date || run_days >= 0) {
  234.         if (!run_days) {
  235.             if (ymd_compare(current, start) != 0)
  236.                 return(1);
  237.         } else if (run_days > 0) {
  238.             if (ymd_compare(current, start) >= 0) {
  239.                 struct tm Save;
  240.  
  241.                 Save = current;
  242.                 current = start;
  243.                 current.tm_mday += run_days;
  244.                 fix_current_day();
  245.                 if (ymd_compare(Save, current) > 0)
  246.                     return(1);
  247.                 current = Save;
  248.             } else
  249.                 return(1);
  250.         } else if (ymd_compare(current, start) < 0)
  251.             return(1);
  252.     }
  253.     while (isspace(*str))
  254.         ++str;
  255.     strcpy(aptr->str, str);
  256.     aptr->year = current.tm_year;
  257.     aptr->month = current.tm_mon;
  258.     aptr->day = current.tm_mday;
  259.     return(0);
  260. }
  261.  
  262. /*
  263.  *    Reset some values in current tm structure. Year, month and
  264.  *    day-of-month are valid but day and/or month may be < 0 or
  265.  *    greater than the maximum value, in which case they are adjusted
  266.  *    accordingly. Day-of-year and day-of-week are then recalculated.
  267.  */
  268. void
  269. fix_current_day()
  270. {
  271.     int month, totdays = 0;
  272.     struct tm from, to;
  273.  
  274.     if (current.tm_mon < JAN) {
  275.         current.tm_mon = DEC;
  276.         current.tm_year--;
  277.     } else if (current.tm_mon > DEC) {
  278.         current.tm_mon = JAN;
  279.         current.tm_year++;
  280.     }
  281.     if (current.tm_mday < 1) {
  282.         current.tm_mon--;
  283.         if (current.tm_mon < JAN) {
  284.             current.tm_mon = DEC;
  285.             current.tm_year--;
  286.         }
  287.         current.tm_mday += monthlength(current.tm_mon);
  288.     } else if (current.tm_mday > monthlength(current.tm_mon)) {
  289.         current.tm_mday -= monthlength(current.tm_mon);
  290.         current.tm_mon++;
  291.         if (current.tm_mon > DEC) {
  292.             current.tm_mon = JAN;
  293.             current.tm_year++;
  294.         }
  295.     }
  296.     current.tm_yday = current.tm_mday - 1;
  297.     for (month = 0; month < current.tm_mon; month++) {
  298.         current.tm_yday += monthlength(month);
  299.     }
  300.     if ((current.tm_year < today.tm_year)
  301.         || ((current.tm_year == today.tm_year)
  302.         && (current.tm_yday < today.tm_yday))) {
  303.         from = current;
  304.         to = today;
  305.     } else {
  306.         from = today;
  307.         to = current;
  308.     }
  309.     if (from.tm_year != to.tm_year) {
  310.         for (totdays = 0; from.tm_year < to.tm_year; from.tm_year++)
  311.             totdays += dysize(from.tm_year + 1900);
  312.     }
  313.     totdays += to.tm_yday - from.tm_yday;
  314.     if ((current.tm_year < today.tm_year)
  315.         || ((current.tm_year == today.tm_year)
  316.         && (current.tm_yday < today.tm_yday)))
  317.         totdays = -totdays;
  318.     current.tm_wday =
  319.         ((totdays % 7) + 7 + today.tm_wday) % 7;
  320. }
  321.  
  322. int
  323. monthlength(month)
  324. int    month;
  325. {
  326.     static int    monthlengths[] = {31,28,31,30,31,30,31,31,30,31,30,31};
  327.  
  328.     if (month == FEB && (dysize(current.tm_year + 1900) == 366))
  329.         return(29);
  330.     else
  331.         return(monthlengths[month]);
  332. }
  333.  
  334. /*
  335.  * Compares two sets of year/month/day.  Returns -1 if the first is earlier than
  336.  * the second, +1 if later, 0 if they are the same.
  337.  */
  338. ymd_compare(day0, day1)
  339. struct tm day0, day1;
  340. {
  341.         if (day0.tm_year > day1.tm_year) return(1);
  342.         if (day0.tm_year < day1.tm_year) return(-1);
  343.         if (day0.tm_mon > day1.tm_mon) return(1);
  344.         if (day0.tm_mon < day1.tm_mon) return(-1);
  345.         if (day0.tm_mday > day1.tm_mday) return(1);
  346.         if (day0.tm_mday < day1.tm_mday) return(-1);
  347.         return(0);
  348. }
  349.  
  350. /*
  351.  * parse the date on the given string and reset the "current"
  352.  * date to reflect that date. The date may take the form of a
  353.  * day name (e.g. Tu, Tue, Tuesday) or a date in m/d/y format
  354.  * where the month and/or year may be missing (e.g. 27 = 27th
  355.  * of this month, 8/27 = August 27 of this year, 8/27/89 =
  356.  * August 27 of 1989. If 'cmdline' is true, then the string
  357.  * came from the command line '-d' option.
  358.  * If the first character of the date is + or - scan the number and
  359.  * use it as an offset in days from the current date.  Thus -1 becomes
  360.  * yesterday and +1 becomes tomorrow. pbm.
  361.  */
  362. int
  363. parse_date(str, cmdline)
  364. char *str;
  365. int cmdline;
  366. {
  367.     char c[4];
  368.     int i, dow = -1, m = -1, d = -1, y = -1;
  369.  
  370.     if (isdigit(*str)) {
  371.         /* must be a m/d/y date */
  372.         /* assume it's a month first */
  373.         m = *str++ - '0';
  374.         if (isdigit(*str))
  375.             m = m*10 + *str++ - '0';
  376.         if (!*str) {
  377.             /* no more chars => day only */
  378.             d = m;
  379.             m = -1;
  380.         } else if (*str++ != '/') {
  381.             if (cmdline)
  382.                 err_rpt("badly formed date for -d option (ignored)", NON_FATAL);
  383.             else
  384.                 err_rpt("badly formed date - please reenter", NON_FATAL);
  385.             return(1);
  386.         } else {
  387.             d = *str++ - '0';
  388.             if (isdigit(*str))
  389.                 d = d*10 + *str++ - '0';
  390.             if (*str++ == '/') {
  391.                 /* year also specified */
  392.                 y = *str++ - '0';
  393.                 if (isdigit(*str)) {
  394.                     y = y*10 + *str++ - '0';
  395.                     if (*str && isdigit(*str))
  396.                         y = y*10 + *str++ - '0';
  397.                     if (*str && isdigit(*str))
  398.                         y = y*10 + *str++ - '0';
  399.                 }
  400.             }
  401.         }
  402.         if (y > 0) {
  403.             if (y > 1900)
  404.                 y -= 1900;
  405.             current.tm_year = y;
  406.         }
  407.         if (day_first) {
  408.             if (m > 0) {
  409.                 current.tm_mon = d - 1;
  410.                 current.tm_mday = m;
  411.             } else if (d > 0)
  412.                 current.tm_mday = d;
  413.         } else {
  414.             if (m > 0) {
  415.                 current.tm_mon = m - 1;
  416.                 current.tm_mday = d;
  417.             } else if (d > 0)
  418.                 current.tm_mday = d;
  419.         }
  420.         fix_current_day();
  421.     } else if (*str == '-' || *str == '+') {
  422.         /*
  423.          * If the argument begins with a + or - assume that it is an
  424.          * offset in days from the current date. Use current date if the
  425.          * number doesn't scan after the - or +. pbm
  426.          */
  427.         if (sscanf(str, "%d", &i) == 1) {
  428.             current.tm_mday += i;
  429.             fix_current_day();
  430.         }
  431.     } else {
  432.         /* day of week */
  433.         /* check for day names */
  434.         c[0] = islower(*str) ? toupper(*str) : *str;
  435.         ++str;
  436.         c[1] = islower(*str) ? toupper(*str) : *str;
  437.         if (*++str) {
  438.             c[2] = islower(*str) ? toupper(*str) : *str;
  439.             c[3] = '\0';
  440.         } else
  441.             c[2] = '\0';
  442.         for (i=0; i<7; i++) {
  443.             if (!strncmp(c, dayname[i], 2)) {
  444.                 dow = i;
  445.                 break;
  446.             }
  447.         }
  448.         if (dow >= 0) {
  449.             /* match found */
  450.             current.tm_mday += dow - current.tm_wday;
  451.             fix_current_day();
  452.         } else if (!strcmp(c, "TOM")) {
  453.             /* tommorrow */
  454.             current.tm_mday++;
  455.             fix_current_day();
  456.         } else if (!strcmp(c, "YES")) {
  457.             /* yesterday */
  458.             current.tm_mday--;
  459.             fix_current_day();
  460.         } else if (strcmp(c, "TOD")) {
  461.             if (cmdline)
  462.                 err_rpt("badly formed date for -d option (ignored)", NON_FATAL);
  463.             else
  464.                 err_rpt("badly formed date - please reenter", NON_FATAL);
  465.             return(1);
  466.         }
  467.     }
  468.     return(0);
  469. }
  470.  
  471. void
  472. err_rpt(str, flag)
  473. char *str;
  474. int flag;
  475. {
  476.     fprintf(stderr, "%s\n", str);
  477.     if (flag == FATAL)
  478.         exit(1);
  479. }
  480.