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 | }
|
---|