BSD 4_3_Reno release
[unix-history] / usr / src / usr.bin / cal / cal.c
index 5ad52c9..88c47ab 100644 (file)
-char   *sccsid = "@(#)cal.c    4.2 82/02/28";
-char   dayw[] = {
-       " S  M Tu  W Th  F  S"
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Kim Letkeman.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that: (1) source distributions retain this entire copyright
+ * notice and comment, and (2) distributions including binaries display
+ * the following acknowledgement:  ``This product includes software
+ * developed by the University of California, Berkeley and its contributors''
+ * in the documentation or other materials provided with the distribution
+ * and in all advertising materials mentioning features or use of this
+ * software. Neither the name of the University nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1989 The Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cal.c      4.8 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define        THURSDAY                4               /* for reformation */
+#define        SATURDAY                6               /* 1 Jan 1 was a Saturday */
+#define        FIRST_MISSING_DAY       639787          /* 3 Sep 1752 */
+#define        NUMBER_MISSING_DAYS     11              /* 11 day correction */
+#define        SPACE                   -1              /* used in day array */
+
+static int days_in_month[2][13] = {
+       {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
+       {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
 };
 };
-char   *smon[]= {
-       "January", "February", "March", "April",
-       "May", "June", "July", "August",
-       "September", "October", "November", "December",
+
+static int sep1752[42] = {
+       SPACE,  SPACE,  1,      2,      14,     15,     16,
+       17,     18,     19,     20,     21,     22,     23,
+       24,     25,     26,     27,     28,     29,     30,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+}, j_sep1752[42] = {
+       SPACE,  SPACE,  245,    246,    258,    259,    260,
+       261,    262,    263,    264,    265,    266,    267,
+       268,    269,    270,    271,    272,    273,    274,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
+       SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,  SPACE,
 };
 };
-char   string[432];
-main(argc, argv)
-char *argv[];
-{
-       register y, i, j;
-       int m;
 
 
-       if(argc < 2) {
-               printf("usage: cal [month] year\n");
-               exit(0);
-       }
-       if(argc == 2)
-               goto xlong;
+static char *month_names[12] = {
+       "January", "February", "March", "April", "May", "June",
+       "July", "August", "September", "October", "November", "December",
+};
 
 
-/*
- *     print out just month
- */
+static char *day_headings = " S  M  Tu W  Th F  S";
+static char *j_day_headings = " S   M   Tu  W   Th  F   S";
 
 
-       m = number(argv[1]);
-       if(m<1 || m>12)
-               goto badarg;
-       y = number(argv[2]);
-       if(y<1 || y>9999)
-               goto badarg;
-       printf("   %s %u\n", smon[m-1], y);
-       printf("%s\n", dayw);
-       cal(m, y, string, 24);
-       for(i=0; i<6*24; i+=24)
-               pstr(string+i, 24);
-       exit(0);
+/* leap year -- account for gregorian reformation in 1752 */
+#define        leap_year(yr) \
+       ((yr) <= 1752 ? !((yr) % 4) : \
+       !((yr) % 4) && ((yr) % 100) || !((yr) % 400))
 
 
-/*
- *     print out complete year
- */
+/* number of centuries since 1700, not inclusive */
+#define        centuries_since_1700(yr) \
+       ((yr) > 1700 ? (yr) / 100 - 17 : 0)
 
 
-xlong:
-       y = number(argv[1]);
-       if(y<1 || y>9999)
-               goto badarg;
-       printf("\n\n\n");
-       printf("                                %u\n", y);
-       printf("\n");
-       for(i=0; i<12; i+=3) {
-               for(j=0; j<6*72; j++)
-                       string[j] = '\0';
-               printf("         %.3s", smon[i]);
-               printf("                        %.3s", smon[i+1]);
-               printf("                       %.3s\n", smon[i+2]);
-               printf("%s   %s   %s\n", dayw, dayw, dayw);
-               cal(i+1, y, string, 72);
-               cal(i+2, y, string+23, 72);
-               cal(i+3, y, string+46, 72);
-               for(j=0; j<6*72; j+=72)
-                       pstr(string+j, 72);
-       }
-       printf("\n\n\n");
-       exit(0);
+/* number of centuries since 1700 whose modulo of 400 is 0 */
+#define        quad_centuries_since_1700(yr) \
+       ((yr) > 1600 ? ((yr) - 1600) / 400 : 0)
 
 
-badarg:
-       printf("Bad argument\n");
-}
+/* number of leap years between year 1 and this year, not inclusive */
+#define        leap_years_since_year_1(yr) \
+       ((yr) / 4 - centuries_since_1700(yr) + quad_centuries_since_1700(yr))
 
 
-number(str)
-char *str;
-{
-       register n, c;
-       register char *s;
-
-       n = 0;
-       s = str;
-       while(c = *s++) {
-               if(c<'0' || c>'9')
-                       return(0);
-               n = n*10 + c-'0';
-       }
-       return(n);
-}
+int julian;
 
 
-pstr(str, n)
-char *str;
+main(argc, argv)
+       int argc;
+       char **argv;
 {
 {
-       register i;
-       register char *s;
+       extern char *optarg;
+       extern int optind;
+       struct tm *local_time;
+       time_t now, time();
+       int ch, month, year, yflag;
 
 
-       s = str;
-       i = n;
-       while(i--)
-               if(*s++ == '\0')
-                       s[-1] = ' ';
-       i = n+1;
-       while(i--)
-               if(*--s != ' ')
+       yflag = 0;
+       while ((ch = getopt(argc, argv, "jy")) != EOF)
+               switch(ch) {
+               case 'j':
+                       julian = 1;
+                       break;
+               case 'y':
+                       yflag = 1;
                        break;
                        break;
-       s[1] = '\0';
-       printf("%s\n", str);
+               case '?':
+               default:
+                       usage();
+               }
+       argc -= optind;
+       argv += optind;
+
+       switch(argc) {
+       case 2:
+               if ((month = atoi(*argv++)) <= 0 || month > 12) {
+                       (void)fprintf(stderr, "cal: illegal month value.\n");
+                       exit(1);
+               }
+               /* FALLTHROUGH */
+       case 1:
+               if ((year = atoi(*argv)) <= 0 || year > 9999) {
+                       (void)fprintf(stderr, "cal: illegal year value.\n");
+                       exit(1);
+               }
+               break;
+       case 0:
+               (void)time(&now);
+               local_time = localtime(&now);
+               year = local_time->tm_year + 1900;
+               if (!yflag)
+                       month = local_time->tm_mon + 1;
+               break;
+       default:
+               usage();
+       }
+       if (month)
+               monthly(month, year);
+       else if (julian)
+               j_yearly(year);
+       else
+               yearly(year);
+       exit(0);
 }
 
 }
 
-char   mon[] = {
-       0,
-       31, 29, 31, 30,
-       31, 30, 31, 31,
-       30, 31, 30, 31,
-};
+#define        DAY_LEN         3               /* 3 spaces per day */
+#define        J_DAY_LEN       4               /* 4 spaces per day */
+#define        WEEK_LEN        20              /* 7 * 3 - one space at the end */
+#define        J_WEEK_LEN      27              /* 7 * 4 - one space at the end */
+#define        HEAD_SEP        2               /* spaces between day headings */
+#define        J_HEAD_SEP      3
 
 
-cal(m, y, p, w)
-char *p;
+monthly(month, year)
+       int month, year;
 {
 {
-       register d, i;
-       register char *s;
+       register int col, row;
+       register char *p;
+       int len, days[42];
+       char lineout[30];
 
 
-       s = p;
-       d = jan1(y);
-       mon[2] = 29;
-       mon[9] = 30;
+       day_array(month, year, days);
+       len = sprintf(lineout, "%s %d", month_names[month - 1], year);
+       (void)printf("%*s%s\n%s\n",
+           ((julian ? J_WEEK_LEN : WEEK_LEN) - len) / 2, "",
+           lineout, julian ? j_day_headings : day_headings);
+       for (row = 0; row < 6; row++) {
+               for (col = 0, p = lineout; col < 7; col++,
+                   p += julian ? J_DAY_LEN : DAY_LEN)
+                       ascii_day(p, days[row * 7 + col]);
+               trim_trailing_spaces(lineout);
+               (void)printf("%s\n", lineout);
+       }
+}
 
 
-       switch((jan1(y+1)+7-d)%7) {
+j_yearly(year)
+       int year;
+{
+       register int col, *dp, i, month, row, which_cal;
+       register char *p;
+       int days[12][42];
+       char lineout[80];
 
 
-       /*
-        *      non-leap year
-        */
-       case 1:
-               mon[2] = 28;
-               break;
+       (void)sprintf(lineout, "%d", year);
+       center(lineout, J_WEEK_LEN * 2 + J_HEAD_SEP, 0);
+       (void)printf("\n\n");
+       for (i = 0; i < 12; i++)
+               day_array(i + 1, year, &days[i][0]);
+       (void)memset(lineout, ' ', sizeof(lineout) - 1);
+       lineout[sizeof(lineout) - 1] = '\0';
+       for (month = 0; month < 12; month += 2) {
+               center(month_names[month], J_WEEK_LEN, J_HEAD_SEP);
+               center(month_names[month + 1], J_WEEK_LEN, 0);
+               (void)printf("\n%s%*s%s\n", j_day_headings, J_HEAD_SEP, "",
+                   j_day_headings);
+               for (row = 0; row < 6; row++) {
+                       for (which_cal = 0; which_cal < 2; which_cal++) {
+                               p = lineout + which_cal * (J_WEEK_LEN + 2);
+                               dp = &days[month + which_cal][row * 7];
+                               for (col = 0; col < 7; col++, p += J_DAY_LEN)
+                                       ascii_day(p, *dp++);
+                       }
+                       trim_trailing_spaces(lineout);
+                       (void)printf("%s\n", lineout);
+               }
+       }
+       (void)printf("\n");
+}
 
 
-       /*
-        *      1752
-        */
-       default:
-               mon[9] = 19;
-               break;
+yearly(year)
+       int year;
+{
+       register int col, *dp, i, month, row, which_cal;
+       register char *p;
+       int days[12][42];
+       char lineout[80];
 
 
-       /*
-        *      leap year
-        */
-       case 2:
-               ;
-       }
-       for(i=1; i<m; i++)
-               d += mon[i];
-       d %= 7;
-       s += 3*d;
-       for(i=1; i<=mon[m]; i++) {
-               if(i==3 && mon[m]==19) {
-                       i += 11;
-                       mon[m] += 11;
-               }
-               if(i > 9)
-                       *s = i/10+'0';
-               s++;
-               *s++ = i%10+'0';
-               s++;
-               if(++d == 7) {
-                       d = 0;
-                       s = p+w;
-                       p = s;
+       (void)sprintf(lineout, "%d", year);
+       center(lineout, WEEK_LEN * 3 + HEAD_SEP * 2, 0);
+       (void)printf("\n\n");
+       for (i = 0; i < 12; i++)
+               day_array(i + 1, year, &days[i][0]);
+       (void)memset(lineout, ' ', sizeof(lineout) - 1);
+       lineout[sizeof(lineout) - 1] = '\0';
+       for (month = 0; month < 12; month += 3) {
+               center(month_names[month], WEEK_LEN, HEAD_SEP);
+               center(month_names[month + 1], WEEK_LEN, HEAD_SEP);
+               center(month_names[month + 2], WEEK_LEN, 0);
+               (void)printf("\n%s%*s%s%*s%s\n", day_headings, HEAD_SEP,
+                   "", day_headings, HEAD_SEP, "", day_headings);
+               for (row = 0; row < 6; row++) {
+                       for (which_cal = 0; which_cal < 3; which_cal++) {
+                               p = lineout + which_cal * (WEEK_LEN + 2);
+                               dp = &days[month + which_cal][row * 7];
+                               for (col = 0; col < 7; col++, p += DAY_LEN)
+                                       ascii_day(p, *dp++);
+                       }
+                       trim_trailing_spaces(lineout);
+                       (void)printf("%s\n", lineout);
                }
        }
                }
        }
+       (void)printf("\n");
 }
 
 /*
 }
 
 /*
- *     return day of the week
- *     of jan 1 of given year
+ * day_array --
+ *     Fill in an array of 42 integers with a calendar.  Assume for a moment
+ *     that you took the (maximum) 6 rows in a calendar and stretched them
+ *     out end to end.  You would have 42 numbers or spaces.  This routine
+ *     builds that array for any month from Jan. 1 through Dec. 9999.
  */
  */
-
-jan1(yr)
+day_array(month, year, days)
+       register int *days;
+       int month, year;
 {
 {
-       register y, d;
+       register int i, day, dw, dm, *p;
+
+       dm = days_in_month[leap_year(year)][month];
+       dw = day_in_week(1, month, year);
+       if (month == 9 && year == 1752) {
+               p = julian ? j_sep1752 : sep1752;
+               for (i = 0; i < 42; i++)
+                       *days++ = *p++;
+               return;
+       }
+       for (i = 42, p = days; i--;)
+               *p++ = SPACE;
+       day = julian ? day_in_year(1, month, year) : 1;
+       while (dm--)
+               days[dw++] = day++;
+}
 
 /*
 
 /*
- *     normal gregorian calendar
- *     one extra day per four years
+ * day_in_year --
+ *     return the 1 based day number within the year
  */
  */
+day_in_year(day, month, year)
+       register int day, month;
+       int year;
+{
+       register int i, leap;
 
 
-       y = yr;
-       d = 4+y+(y+3)/4;
+       leap = leap_year(year);
+       for (i = 1; i < month; i++)
+               day += days_in_month[leap][i];
+       return(day);
+}
 
 /*
 
 /*
- *     julian calendar
- *     regular gregorian
- *     less three days per 400
+ * day_in_week
+ *     return the 0 based day number for any date from 1 Jan. 1 to
+ *     31 Dec. 9999.  Assumes the Gregorian reformation eliminates
+ *     3 Sep. 1752 through 13 Sep. 1752.  Returns Thursday for all
+ *     missing days.
  */
  */
+day_in_week(day, month, year)
+       int day, month, year;
+{
+       long temp;
+
+       temp = (long)(year - 1) * 365 + leap_years_since_year_1(year - 1)
+           + day_in_year(day, month, year);
+       if (temp < FIRST_MISSING_DAY)
+               return((temp - 1 + SATURDAY) % 7);
+       if (temp >= (FIRST_MISSING_DAY + NUMBER_MISSING_DAYS))
+               return(((temp - 1 + SATURDAY) - NUMBER_MISSING_DAYS) % 7);
+       return(THURSDAY);
+}
+
+ascii_day(p, day)
+       register char *p;
+       register int day;
+{
+       register int display, val;
 
 
-       if(y > 1800) {
-               d -= (y-1701)/100;
-               d += (y-1601)/400;
+       if (day == SPACE) {
+               memset(p, ' ', julian ? 4 : 3);
+               return;
        }
        }
+       display = 0;
+       if (julian) {
+               if (val = day / 100) {
+                       day %= 100;
+                       *p++ = val + '0';
+                       display = 1;
+               } else
+                       *p++ = ' ';
+       }
+       val = day / 10;
+       if (val || display)
+               *p++ = val + '0';
+       else
+               *p++ = ' ';
+       *p++ = day % 10 + '0';
+       *p = ' ';
+}
 
 
-/*
- *     great calendar changeover instant
- */
+trim_trailing_spaces(s)
+       register char *s;
+{
+       register char *p;
 
 
-       if(y > 1752)
-               d += 3;
+       for (p = s; *p; ++p);
+       while (p > s && isspace(*--p));
+       if (p > s)
+               ++p;
+       *p = '\0';
+}
 
 
-       return(d%7);
+center(str, len, separate)
+       char *str;
+       register int len;
+       int separate;
+{
+       len -= strlen(str);
+       (void)printf("%*s%s%*s", len / 2, "", str, len / 2 + len % 2, "");
+       if (separate)
+               (void)printf("%*s", separate, "");
+}
+
+usage()
+{
+       (void)fprintf(stderr, "usage: cal [-jy] [[month] year]\n");
+       exit(1);
 }
 }