home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume3 / calendar < prev    next >
Internet Message Format  |  1986-11-30  |  12KB

  1. From: genrad!decvax!minow (Martin Minow)
  2. Subject: A calendar program
  3. Newsgroups: mod.sources
  4. Approved: jpn@panda.UUCP
  5.  
  6. Mod.sources:  Volume 3, Issue 75
  7. Submitted by: genrad!decvax!minow (Martin Minow)
  8.  
  9.  
  10.  
  11. # This is a shell archive.  Remove anything before this line, then
  12. # unpack it by saving it in a file and typing "sh file".  (Files
  13. # unpacked will be owned by you and have default permissions.)
  14. #
  15. # This archive contains:
  16. # readme.txt calend.c
  17.  
  18. echo x - readme.txt
  19. cat > "readme.txt" << '//E*O*F readme.txt//'
  20. This is a public-domain reimplementation of the Unix "cal" calendar program.
  21. The inner-loop -- that puts a date into the calendar -- was designed
  22. for eventual rewriting in LaTeX (but I haven't gotten that working yet).
  23.  
  24. If you aren't interested in Julian/Gregorian hacks, the code can
  25. be considerably simplified.
  26.  
  27. Happy New Year.
  28.  
  29. Martin Minow
  30. decvax!minow
  31. //E*O*F readme.txt//
  32.  
  33. echo x - calend.c
  34. cat > "calend.c" << '//E*O*F calend.c//'
  35. /*
  36.  *             C A L E N D A R
  37.  *
  38.  * Usage:
  39.  *    calend    MM        If small, it's a month, if large, a year.
  40.  * or
  41.  *    calend    YYYY MM        year/month
  42.  * or
  43.  *    calend    MM YYYY    
  44.  */
  45.  
  46. /*)BUILD
  47. */
  48.  
  49. #ifdef DOCUMENTATION
  50.  
  51. title    calend    Print a Calendar
  52. index    calend    Print a Calendar
  53.  
  54. usage
  55.  
  56.     calend [year] [month]
  57.  
  58. description
  59.  
  60.     When invoked without arguments, calend prints a
  61.     calendar for the preceeding, current, and next
  62.     months of the current year.
  63.  
  64.     If a month is given (a value from 1 through 12),
  65.     it prints the three months centered on the requested
  66.     month.  For example,
  67.  
  68.         calend 12
  69.  
  70.     Prints November and December for this year, and
  71.     January for next year.
  72.  
  73.     If a year is given, it prints a calendar for the
  74.     entire year:
  75.  
  76.         calend 1985
  77.  
  78.     If both values are given, it prints the three months
  79.     centered on the indicated date:
  80.  
  81.         calend 1752 9
  82.         calend 9 1752
  83.  
  84.     Note that a three or four digit number will always be taken as
  85.     a year.  A one or two digit number will be either a month
  86.     or a year in the 20th century:
  87.  
  88.         calend 78        (equals calend 1978)
  89.         calend 0078        (early common era)
  90.  
  91. bugs
  92.  
  93.     The change from the Julian to Gregorian calendars follows
  94.     usage in England and her colonies.  Options should be provided
  95.     to process the change for other countries (and localities).
  96.     This is, however, a fairly complex task with little payback.
  97.  
  98.     The year didn't always start in January.
  99.  
  100. references
  101.  
  102.     Enclycopaedia Brittanica, 13th edition, Vol. 4, p. 987 et. seq.
  103.  
  104. author
  105.  
  106.     Martin Minow
  107.  
  108. #endif
  109.  
  110. #include <stdio.h>
  111. #include <time.h>
  112. #ifdef    decus
  113. int    $$narg = 1;            /* Don't prompt            */
  114. #endif
  115. #ifdef vms
  116. #include        ssdef
  117. #define    IO_ERROR    SS$_ABORT
  118. #define    IO_SUCCESS    SS$_NORMAL
  119. #endif
  120. #ifndef    IO_ERROR
  121. #define    IO_SUCCESS    0        /* Unix definitions        */
  122. #define    IO_ERROR    1
  123. #endif
  124. #define    EOS    0
  125.  
  126. #define    ENTRY_SIZE    3        /* 3 bytes per value        */
  127. #define DAYS_PER_WEEK    7        /* Sunday, etc.            */
  128. #define    WEEKS_PER_MONTH    6        /* Max. weeks in a month    */
  129. #define    MONTHS_PER_LINE    3        /* Three months across        */
  130. #define    MONTH_SPACE    3        /* Between each month        */
  131.  
  132. /*
  133.  * calendar() stuffs data into layout[],
  134.  * output() copies from layout[] to outline[], (then trims blanks).
  135.  */
  136. char    layout[MONTHS_PER_LINE][WEEKS_PER_MONTH][DAYS_PER_WEEK][ENTRY_SIZE];
  137. char    outline[(MONTHS_PER_LINE * DAYS_PER_WEEK * ENTRY_SIZE)
  138.         + (MONTHS_PER_LINE * MONTH_SPACE)
  139.         + 1];
  140.  
  141. char    *weekday = " S  M Tu  W Th  F  S";
  142. char    *monthname[] = {
  143.     "???",                        /* No month 0    */
  144.     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  145.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  146. };
  147.  
  148. main(argc, argv)
  149. int        argc;
  150. char        *argv[];
  151. {
  152.     register int    month;
  153.     register int    year;
  154.  
  155.     register int    arg1val;
  156.     int        arg1len;
  157.     int        arg2val;
  158.     int        tvec[2];
  159.     struct tm    *tm;
  160.  
  161.     time(&tvec[0]);
  162.     tm = localtime(&tvec[0]);
  163.     year = tm->tm_year + 1900;
  164.     month = tm->tm_mon + 1;
  165.     if (argc <= 1) {
  166.         /*
  167.          * No arguments mean do last, this, and next month
  168.          */
  169.         do3months(year, month);
  170.     }
  171.     else {
  172.         arg1val = atoi(argv[1]);
  173.         arg1len = strlen(argv[1]);
  174.         if (argc == 2) {
  175.         /*
  176.          * Only one argument, if small, it's a month.  If
  177.          * large, it's a year.  Note:
  178.          *    calend    0082    Year '82
  179.          *    calend    82    Year 1982
  180.          */
  181.         if (arg1len <= 2 && arg1val <= 12)
  182.             do3months(year, arg1val);
  183.         else {
  184.             if (arg1len <= 2 && arg1val > 0 && arg1val <= 99)
  185.             arg1val += 1900;
  186.             doyear(arg1val);
  187.         }
  188.         }
  189.         else {
  190.         /*
  191.          * Two arguments, allow 1980 12 or 12 1980
  192.          */
  193.         arg2val = atoi(argv[2]);
  194.         if (arg1len > 2)
  195.             do3months(arg1val, arg2val);
  196.         else
  197.             do3months(arg2val, arg1val);
  198.         }
  199.     }
  200.     exit(IO_SUCCESS);
  201. }
  202.  
  203. doyear(year)
  204. int        year;
  205. /*
  206.  * Print the calendar for an entire year.
  207.  */
  208. {
  209.     register int    month;
  210.  
  211.     if (year < 1 || year > 9999)
  212.         usage("year", year);
  213.     printf("\n\n\n%35d\n\n", year);
  214.     for (month = 1; month <= 12; month += MONTHS_PER_LINE) {
  215.         printf("%12s%23s%23s\n",
  216.             monthname[month],
  217.             monthname[month+1],
  218.             monthname[month+2]);
  219.         printf("%s   %s   %s\n", weekday, weekday, weekday);
  220.         calendar(year, month+0, 0);
  221.         calendar(year, month+1, 1);
  222.         calendar(year, month+2, 2);
  223.         output(3);
  224. #if MONTHS_PER_LINE != 3
  225.         << error, the above won't work >>
  226. #endif
  227.     }
  228.     printf("\n\n\n");
  229. }
  230.  
  231. domonth(year, month)
  232. int        year;
  233. int        month;
  234. /*
  235.  * Do one specific month -- note: no longer used
  236.  */
  237. {
  238.     if (year < 1 || year > 9999)
  239.         usage("year", year);
  240.     if (month <= 0 || month > 12)
  241.         usage("month", month);
  242.     printf("%9s%5d\n\n%s\n", monthname[month], year, weekday);
  243.     calendar(year, month, 0);
  244.     output(1);
  245.     printf("\n\n");
  246. }
  247.  
  248. do3months(thisyear, thismonth)
  249. int        thisyear;
  250. register int    thismonth;
  251. /*
  252.  * Do last month, this month, and next month.  The parameters
  253.  * are guaranteed accurate. (and year will not be less than 2 nor
  254.  * greater than 9998).
  255.  */
  256. {
  257.     int        lastmonth;
  258.     int        lastyear;
  259.     int        nextmonth;
  260.     int        nextyear;
  261.  
  262.     lastyear = nextyear = thisyear;
  263.     if ((lastmonth = thismonth - 1) == 0) {
  264.         lastmonth = 12;
  265.         lastyear--;
  266.     }
  267.     if ((nextmonth = thismonth + 1) == 13) {
  268.         nextmonth = 1;
  269.         nextyear++;
  270.     }
  271.     printf("%9s%5d%18s%5d%18s%5d\n",
  272.         monthname[lastmonth], lastyear,
  273.         monthname[thismonth], thisyear,
  274.         monthname[nextmonth], nextyear);
  275.     printf("%s   %s   %s\n", weekday, weekday, weekday);
  276.     calendar(lastyear, lastmonth, 0);
  277.     calendar(thisyear, thismonth, 1);
  278.     calendar(nextyear, nextmonth, 2);
  279.     output(3);
  280. #if MONTHS_PER_LINE != 3
  281.     << error, the above won't work >>
  282. #endif
  283.     printf("\n\n\n");
  284. }
  285.     
  286. output(nmonths)
  287. int        nmonths;        /* Number of months to do    */
  288. /*
  289.  * Clean up and output the text.
  290.  */
  291. {
  292.     register int    week;
  293.     register int    month;
  294.     register char    *outp;
  295.  
  296.     for (week = 0; week < WEEKS_PER_MONTH; week++) {
  297.         outp = outline;
  298.         for (month = 0; month < nmonths; month++) {
  299.         /*
  300.          * The -1 in the following removes
  301.          * the unwanted leading blank from
  302.          * the entry for Sunday.
  303.          */
  304.         sprintf(outp, "%.*s%*s",
  305.             DAYS_PER_WEEK * ENTRY_SIZE - 1,
  306.             &layout[month][week][0][1],
  307.             MONTH_SPACE, "");
  308.         outp += (DAYS_PER_WEEK * ENTRY_SIZE) + MONTH_SPACE - 1;
  309.         }
  310.         while (outp > outline && outp[-1] == ' ')
  311.         outp--;
  312.         *outp = EOS;
  313.         puts(outline);
  314.     }
  315. }
  316.  
  317. calendar(year, month, index)
  318. int        year;
  319. int        month;
  320. int        index;        /* Which of the three months        */
  321. /*
  322.  * Actually build the calendar for this month.
  323.  */
  324. {
  325.     register char    *tp;
  326.     int        week;
  327.     register int    wday;
  328.     register int    today;
  329.  
  330.     setmonth(year, month);
  331.     for (week = 0; week < WEEKS_PER_MONTH; week++) {
  332.         for (wday = 0; wday < DAYS_PER_WEEK; wday++) {
  333.         tp = &layout[index][week][wday][0];
  334.         *tp++ = ' ';
  335.         today = getdate(week, wday);
  336.         if (today <= 0) {
  337.             *tp++ = ' ';
  338.             *tp++ = ' ';
  339.         }
  340.         else if (today < 10) {
  341.             *tp++ = ' ';
  342.             *tp   = (today + '0');
  343.         }
  344.         else {
  345.             *tp++ = (today / 10) + '0';
  346.             *tp   = (today % 10) + '0';
  347.         }
  348.         }
  349.     }
  350. }
  351.  
  352. usage(what, value)
  353. char        *what;
  354. int        value;
  355. /*
  356.  * Fatal parameter error
  357.  */
  358. {
  359.     fprintf(stderr, "Calendar parameter error: bad %s: %d\n",
  360.         what, value);
  361.     fprintf(stderr, "Usage: \"calend month\" or \"calend year month\"\n");
  362.     fprintf(stderr, "Year and month are integers.\n");
  363.     exit(IO_ERROR);
  364. }
  365.  
  366. /*
  367.  * Calendar routines, intended for eventual porting to TeX
  368.  *
  369.  * date(year, month, week, wday)
  370.  *    Returns the date on this week (0 is first, 5 last possible)
  371.  *    and day of the week (Sunday == 0)
  372.  *    Note: January is month 1.
  373.  *
  374.  * setmonth(year, month)
  375.  *    Parameters are as above, sets getdate() for this month.
  376.  *
  377.  * int
  378.  * getdate(week, wday)
  379.  *    Parameters are as above, uses the data set by setmonth()
  380.  */
  381.  
  382. /*
  383.  * This structure is used to pass data between setmonth() and getdate().
  384.  * It needs considerable expansion if the Julian->Gregorian change is
  385.  * to be extended to other countries.
  386.  */
  387.  
  388. static struct {
  389.     int        feb;        /* Days in February for this month    */
  390.     int        sept;        /* Days in September for this month    */
  391.     int        days_in_month;    /* Number of days in this month        */
  392.     int        dow_first;    /* Day of week of the 1st day in month    */
  393. } info;
  394.  
  395. static int day_month[] = {    /* 30 days hath September...        */
  396.     0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
  397. };
  398.  
  399. int
  400. date(year, month, week, wday)
  401. int        year;        /* Calendar date being computed        */
  402. int        month;        /* January == 1                */
  403. int        week;        /* Week in the month 0..5 inclusive    */
  404. int        wday;        /* Weekday, Sunday == 0            */
  405. /*
  406.  * Return the date of the month that fell on this week and weekday.
  407.  * Return zero if it's out of range.
  408.  */
  409. {
  410.     setmonth(year, month);
  411.     return (getdate(week, wday));
  412. }
  413.  
  414. setmonth(year, month)
  415. int        year;            /* Year to compute        */
  416. int        month;            /* Month, January is month 1    */
  417. /*
  418.  * Setup the parameters needed to compute this month
  419.  * (stored in the info structure).
  420.  */
  421. {
  422.     register int        i;
  423.  
  424.     if (month < 1 || month > 12) {    /* Verify caller's parameters    */
  425.         info.days_in_month = 0;    /* Garbage flag            */
  426.         return;
  427.     }
  428.     info.dow_first = Jan1(year);    /* Day of January 1st for now    */
  429.     info.feb = 29;            /* Assume leap year        */
  430.     info.sept = 30;            /* Assume normal year        */
  431.     /*
  432.      * Determine whether it's an ordinary year, a leap year
  433.      * or the magical calendar switch year of 1752.
  434.      */
  435.     switch ((Jan1(year + 1) + 7 - info.dow_first) % 7) {
  436.     case 1:                /* Not a leap year        */
  437.         info.feb = 28;
  438.     case 2:                /* Ordinary leap year        */
  439.         break;
  440.  
  441.     default:            /* The magical moment arrives    */
  442.         info.sept = 19;        /* 19 days hath September    */
  443.         break;
  444.     }
  445.     info.days_in_month =
  446.           (month == 2) ? info.feb
  447.         : (month == 9) ? info.sept
  448.         : day_month[month];
  449.     for (i = 1; i < month; i++) {
  450.         switch (i) {        /* Special months?        */
  451.         case 2:            /* February            */
  452.         info.dow_first += info.feb;
  453.         break;
  454.  
  455.         case 9:
  456.         info.dow_first += info.sept;
  457.         break;
  458.  
  459.         default:
  460.         info.dow_first += day_month[i];
  461.         break;
  462.         }
  463.     }
  464.     info.dow_first %= 7;        /* Now it's Sunday to Saturday    */
  465. }
  466.  
  467. int
  468. getdate(week, wday)
  469. int        week;
  470. int        wday;
  471. {
  472.     register int    today;
  473.  
  474.     /*
  475.      * Get a first guess at today's date and make sure it's in range.
  476.      */
  477.     today = (week * 7) + wday - info.dow_first + 1;
  478.     if (today <= 0 || today > info.days_in_month)
  479.         return (0);
  480.     else if (info.sept == 19 && today >= 3)    /* The magical month?    */
  481.         return (today + 11);    /* If so, some dates changed    */
  482.     else                /* Otherwise,            */
  483.         return (today);        /* Return the date        */
  484. }
  485.  
  486. static int
  487. Jan1(year)
  488. int        year;
  489. /*
  490.  * Return day of the week for Jan 1 of the specified year.
  491.  */
  492. {
  493.     register int    day;
  494.  
  495.     day = year + 4 + ((year + 3) / 4);    /* Julian Calendar    */
  496.     if (year > 1800) {            /* If it's recent, do    */
  497.         day -= ((year - 1701) / 100);    /* Clavian correction    */
  498.         day += ((year - 1601) / 400);    /* Gregorian correction    */
  499.     }
  500.     if (year > 1752)            /* Adjust for Gregorian    */
  501.         day += 3;                /* calendar        */
  502.     return (day % 7);
  503. }
  504.  
  505. //E*O*F calend.c//
  506.  
  507. exit 0
  508.  
  509.