[9] | 1 | /* cal - print a calendar Author: Maritn Minow */
|
---|
| 2 |
|
---|
| 3 | #include <stdlib.h>
|
---|
| 4 | #include <string.h>
|
---|
| 5 | #include <stdio.h>
|
---|
| 6 |
|
---|
| 7 | #define do3months domonth
|
---|
| 8 | #define IO_SUCCESS 0 /* Unix definitions */
|
---|
| 9 | #define IO_ERROR 1
|
---|
| 10 | #define EOS 0
|
---|
| 11 |
|
---|
| 12 | #define ENTRY_SIZE 3 /* 3 bytes per value */
|
---|
| 13 | #define DAYS_PER_WEEK 7 /* Sunday, etc. */
|
---|
| 14 | #define WEEKS_PER_MONTH 6 /* Max. weeks in a month */
|
---|
| 15 | #define MONTHS_PER_LINE 3 /* Three months across */
|
---|
| 16 | #define MONTH_SPACE 3 /* Between each month */
|
---|
| 17 |
|
---|
| 18 | char *badarg = {"Bad argument\n"};
|
---|
| 19 | char *how = {"Usage: cal [month] year\n"};
|
---|
| 20 |
|
---|
| 21 | /* Calendar() stuffs data into layout[],
|
---|
| 22 | * output() copies from layout[] to outline[], (then trims blanks).
|
---|
| 23 | */
|
---|
| 24 | char layout[MONTHS_PER_LINE][WEEKS_PER_MONTH][DAYS_PER_WEEK][ENTRY_SIZE];
|
---|
| 25 | char outline[(MONTHS_PER_LINE * DAYS_PER_WEEK * ENTRY_SIZE)
|
---|
| 26 | + (MONTHS_PER_LINE * MONTH_SPACE)
|
---|
| 27 | + 1];
|
---|
| 28 |
|
---|
| 29 | char *weekday = " S M Tu W Th F S";
|
---|
| 30 | char *monthname[] = {
|
---|
| 31 | "???", /* No month 0 */
|
---|
| 32 | "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
---|
| 33 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
---|
| 34 | };
|
---|
| 35 |
|
---|
| 36 | _PROTOTYPE(int main, (int argc, char **argv));
|
---|
| 37 | _PROTOTYPE(void doyear, (int year));
|
---|
| 38 | _PROTOTYPE(void domonth, (int year, int month));
|
---|
| 39 | _PROTOTYPE(void output, (int nmonths));
|
---|
| 40 | _PROTOTYPE(void calendar, (int year, int month, int indx));
|
---|
| 41 | _PROTOTYPE(void usage, (char *s));
|
---|
| 42 | _PROTOTYPE(int date, (int year, int month, int week, int wday));
|
---|
| 43 | _PROTOTYPE(void setmonth, (int year, int month));
|
---|
| 44 | _PROTOTYPE(int getdate, (int week, int wday));
|
---|
| 45 | _PROTOTYPE(static int Jan1, (int year));
|
---|
| 46 |
|
---|
| 47 | int main(argc, argv)
|
---|
| 48 | int argc;
|
---|
| 49 | char *argv[];
|
---|
| 50 | {
|
---|
| 51 | register int year;
|
---|
| 52 |
|
---|
| 53 | register int arg1val;
|
---|
| 54 | int arg1len;
|
---|
| 55 | int arg2val;
|
---|
| 56 |
|
---|
| 57 | if (argc <= 1) {
|
---|
| 58 | usage(how);
|
---|
| 59 | } else {
|
---|
| 60 | arg1val = atoi(argv[1]);
|
---|
| 61 | arg1len = strlen(argv[1]);
|
---|
| 62 | if (argc == 2) {
|
---|
| 63 | /* Only one argument, if small, it's a month. If
|
---|
| 64 | * large, it's a year. Note: cal 0082 Year
|
---|
| 65 | * 0082 cal 82 Year 0082 */
|
---|
| 66 | if (arg1len <= 2 && arg1val <= 12)
|
---|
| 67 | do3months(year, arg1val);
|
---|
| 68 | else
|
---|
| 69 | doyear(arg1val);
|
---|
| 70 | } else {
|
---|
| 71 | /* Two arguments, allow 1980 12 or 12 1980 */
|
---|
| 72 | arg2val = atoi(argv[2]);
|
---|
| 73 | if (arg1len > 2)
|
---|
| 74 | do3months(arg1val, arg2val);
|
---|
| 75 | else
|
---|
| 76 | do3months(arg2val, arg1val);
|
---|
| 77 | }
|
---|
| 78 | }
|
---|
| 79 | return(IO_SUCCESS);
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | void doyear(year)
|
---|
| 83 | int year;
|
---|
| 84 | /* Print the calendar for an entire year. */
|
---|
| 85 | {
|
---|
| 86 | register int month;
|
---|
| 87 |
|
---|
| 88 | if (year < 1 || year > 9999) usage(badarg);
|
---|
| 89 | if (year < 100)
|
---|
| 90 | printf("\n\n\n 00%2d\n\n", year);
|
---|
| 91 | else
|
---|
| 92 | printf("\n\n\n%35d\n\n", year);
|
---|
| 93 | for (month = 1; month <= 12; month += MONTHS_PER_LINE) {
|
---|
| 94 | printf("%12s%23s%23s\n",
|
---|
| 95 | monthname[month],
|
---|
| 96 | monthname[month + 1],
|
---|
| 97 | monthname[month + 2]);
|
---|
| 98 | printf("%s %s %s\n", weekday, weekday, weekday);
|
---|
| 99 | calendar(year, month + 0, 0);
|
---|
| 100 | calendar(year, month + 1, 1);
|
---|
| 101 | calendar(year, month + 2, 2);
|
---|
| 102 | output(3);
|
---|
| 103 | #if MONTHS_PER_LINE != 3
|
---|
| 104 | #error "the above will not work"
|
---|
| 105 | #endif
|
---|
| 106 | }
|
---|
| 107 | printf("\n\n\n");
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | void domonth(year, month)
|
---|
| 111 | int year;
|
---|
| 112 | int month;
|
---|
| 113 | /* Do one specific month -- note: no longer used */
|
---|
| 114 | {
|
---|
| 115 | if (year < 1 || year > 9999) usage(badarg);
|
---|
| 116 | if (month <= 0 || month > 12) usage(badarg);
|
---|
| 117 | printf("%9s%5d\n\n%s\n", monthname[month], year, weekday);
|
---|
| 118 | calendar(year, month, 0);
|
---|
| 119 | output(1);
|
---|
| 120 | printf("\n\n");
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | void output(nmonths)
|
---|
| 124 | int nmonths; /* Number of months to do */
|
---|
| 125 | /* Clean up and output the text. */
|
---|
| 126 | {
|
---|
| 127 | register int week;
|
---|
| 128 | register int month;
|
---|
| 129 | register char *outp;
|
---|
| 130 | int i;
|
---|
| 131 | char tmpbuf[21], *p;
|
---|
| 132 |
|
---|
| 133 | for (week = 0; week < WEEKS_PER_MONTH; week++) {
|
---|
| 134 | outp = outline;
|
---|
| 135 | for (month = 0; month < nmonths; month++) {
|
---|
| 136 | /* The -1 in the following removes the unwanted
|
---|
| 137 | * leading blank from the entry for Sunday. */
|
---|
| 138 | p = &layout[month][week][0][1];
|
---|
| 139 | for (i = 0; i < 20; i++) tmpbuf[i] = *p++;
|
---|
| 140 | tmpbuf[20] = 0;
|
---|
| 141 | sprintf(outp, "%s ", tmpbuf);
|
---|
| 142 | outp += (DAYS_PER_WEEK * ENTRY_SIZE) + MONTH_SPACE - 1;
|
---|
| 143 | }
|
---|
| 144 | while (outp > outline && outp[-1] == ' ') outp--;
|
---|
| 145 | *outp = EOS;
|
---|
| 146 | printf("%s\n", outline);
|
---|
| 147 | }
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 | void calendar(year, month, indx)
|
---|
| 151 | int year;
|
---|
| 152 | int month;
|
---|
| 153 | int indx; /* Which of the three months */
|
---|
| 154 | /* Actually build the calendar for this month. */
|
---|
| 155 | {
|
---|
| 156 | register char *tp;
|
---|
| 157 | int week;
|
---|
| 158 | register int wday;
|
---|
| 159 | register int today;
|
---|
| 160 |
|
---|
| 161 | setmonth(year, month);
|
---|
| 162 | for (week = 0; week < WEEKS_PER_MONTH; week++) {
|
---|
| 163 | for (wday = 0; wday < DAYS_PER_WEEK; wday++) {
|
---|
| 164 | tp = &layout[indx][week][wday][0];
|
---|
| 165 | *tp++ = ' ';
|
---|
| 166 | today = getdate(week, wday);
|
---|
| 167 | if (today <= 0) {
|
---|
| 168 | *tp++ = ' ';
|
---|
| 169 | *tp++ = ' ';
|
---|
| 170 | } else if (today < 10) {
|
---|
| 171 | *tp++ = ' ';
|
---|
| 172 | *tp = (today + '0');
|
---|
| 173 | } else {
|
---|
| 174 | *tp++ = (today / 10) + '0';
|
---|
| 175 | *tp = (today % 10) + '0';
|
---|
| 176 | }
|
---|
| 177 | }
|
---|
| 178 | }
|
---|
| 179 | }
|
---|
| 180 |
|
---|
| 181 | void usage(s)
|
---|
| 182 | char *s;
|
---|
| 183 | {
|
---|
| 184 | /* Fatal parameter error. */
|
---|
| 185 |
|
---|
| 186 | fprintf(stderr, "%s", s);
|
---|
| 187 | exit(IO_ERROR);
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | /* Calendar routines, intended for eventual porting to TeX
|
---|
| 191 | *
|
---|
| 192 | * date(year, month, week, wday)
|
---|
| 193 | * Returns the date on this week (0 is first, 5 last possible)
|
---|
| 194 | * and day of the week (Sunday == 0)
|
---|
| 195 | * Note: January is month 1.
|
---|
| 196 | *
|
---|
| 197 | * setmonth(year, month)
|
---|
| 198 | * Parameters are as above, sets getdate() for this month.
|
---|
| 199 | *
|
---|
| 200 | * int
|
---|
| 201 | * getdate(week, wday)
|
---|
| 202 | * Parameters are as above, uses the data set by setmonth()
|
---|
| 203 | */
|
---|
| 204 |
|
---|
| 205 | /* This structure is used to pass data between setmonth() and getdate().
|
---|
| 206 | * It needs considerable expansion if the Julian->Gregorian change is
|
---|
| 207 | * to be extended to other countries.
|
---|
| 208 | */
|
---|
| 209 |
|
---|
| 210 | static struct {
|
---|
| 211 | int this_month; /* month number used in 1752 checking */
|
---|
| 212 | int feb; /* Days in February for this month */
|
---|
| 213 | int sept; /* Days in September for this month */
|
---|
| 214 | int days_in_month; /* Number of days in this month */
|
---|
| 215 | int dow_first; /* Day of week of the 1st day in month */
|
---|
| 216 | } info;
|
---|
| 217 |
|
---|
| 218 | static int day_month[] = { /* 30 days hath September... */
|
---|
| 219 | 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
|
---|
| 220 | };
|
---|
| 221 |
|
---|
| 222 | int date(year, month, week, wday)
|
---|
| 223 | int year; /* Calendar date being computed */
|
---|
| 224 | int month; /* January == 1 */
|
---|
| 225 | int week; /* Week in the month 0..5 inclusive */
|
---|
| 226 | int wday; /* Weekday, Sunday == 0 */
|
---|
| 227 | /* Return the date of the month that fell on this week and weekday.
|
---|
| 228 | * Return zero if it's out of range.
|
---|
| 229 | */
|
---|
| 230 | {
|
---|
| 231 | setmonth(year, month);
|
---|
| 232 | return(getdate(week, wday));
|
---|
| 233 | }
|
---|
| 234 |
|
---|
| 235 | void setmonth(year, month)
|
---|
| 236 | int year; /* Year to compute */
|
---|
| 237 | int month; /* Month, January is month 1 */
|
---|
| 238 | /* Setup the parameters needed to compute this month
|
---|
| 239 | * (stored in the info structure).
|
---|
| 240 | */
|
---|
| 241 | {
|
---|
| 242 | register int i;
|
---|
| 243 |
|
---|
| 244 | if (month < 1 || month > 12) {/* Verify caller's parameters */
|
---|
| 245 | info.days_in_month = 0; /* Garbage flag */
|
---|
| 246 | return;
|
---|
| 247 | }
|
---|
| 248 | info.this_month = month; /* used in 1752 checking */
|
---|
| 249 | info.dow_first = Jan1(year); /* Day of January 1st for now */
|
---|
| 250 | info.feb = 29; /* Assume leap year */
|
---|
| 251 | info.sept = 30; /* Assume normal year */
|
---|
| 252 | /* Determine whether it's an ordinary year, a leap year or the
|
---|
| 253 | * magical calendar switch year of 1752. */
|
---|
| 254 | switch ((Jan1(year + 1) + 7 - info.dow_first) % 7) {
|
---|
| 255 | case 1: /* Not a leap year */
|
---|
| 256 | info.feb = 28;
|
---|
| 257 | case 2: /* Ordinary leap year */
|
---|
| 258 | break;
|
---|
| 259 |
|
---|
| 260 | default: /* The magical moment arrives */
|
---|
| 261 | info.sept = 19; /* 19 days hath September */
|
---|
| 262 | break;
|
---|
| 263 | }
|
---|
| 264 | info.days_in_month =
|
---|
| 265 | (month == 2) ? info.feb
|
---|
| 266 | : (month == 9) ? info.sept
|
---|
| 267 | : day_month[month];
|
---|
| 268 | for (i = 1; i < month; i++) {
|
---|
| 269 | switch (i) { /* Special months? */
|
---|
| 270 | case 2: /* February */
|
---|
| 271 | info.dow_first += info.feb;
|
---|
| 272 | break;
|
---|
| 273 |
|
---|
| 274 | case 9: info.dow_first += info.sept; break;
|
---|
| 275 |
|
---|
| 276 | default:
|
---|
| 277 | info.dow_first += day_month[i];
|
---|
| 278 | break;
|
---|
| 279 | }
|
---|
| 280 | }
|
---|
| 281 | info.dow_first %= 7; /* Now it's Sunday to Saturday */
|
---|
| 282 | }
|
---|
| 283 |
|
---|
| 284 | int getdate(week, wday)
|
---|
| 285 | int week;
|
---|
| 286 | int wday;
|
---|
| 287 | {
|
---|
| 288 | register int today;
|
---|
| 289 |
|
---|
| 290 | /* Get a first guess at today's date and make sure it's in range. */
|
---|
| 291 | today = (week * 7) + wday - info.dow_first + 1;
|
---|
| 292 | if (today <= 0 || today > info.days_in_month)
|
---|
| 293 | return(0);
|
---|
| 294 | else if (info.sept == 19 && info.this_month == 9
|
---|
| 295 | && today >= 3) /* The magical month? */
|
---|
| 296 | return(today + 11); /* If so, some dates changed */
|
---|
| 297 | else /* Otherwise, */
|
---|
| 298 | return(today); /* Return the date */
|
---|
| 299 | }
|
---|
| 300 |
|
---|
| 301 | static int Jan1(year)
|
---|
| 302 | int year;
|
---|
| 303 | /* Return day of the week for Jan 1 of the specified year. */
|
---|
| 304 | {
|
---|
| 305 | register int day;
|
---|
| 306 |
|
---|
| 307 | day = year + 4 + ((year + 3) / 4); /* Julian Calendar */
|
---|
| 308 | if (year > 1800) { /* If it's recent, do */
|
---|
| 309 | day -= ((year - 1701) / 100); /* Clavian correction */
|
---|
| 310 | day += ((year - 1601) / 400); /* Gregorian correction */
|
---|
| 311 | }
|
---|
| 312 | if (year > 1752) /* Adjust for Gregorian */
|
---|
| 313 | day += 3; /* calendar */
|
---|
| 314 | return(day % 7);
|
---|
| 315 | }
|
---|