source: trunk/minix/commands/simple/date.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 9.5 KB
Line 
1/* date - Display (or set) the date and time Author: V. Archer */
2
3#include <sys/types.h>
4#include <ctype.h>
5#include <stddef.h>
6#include <stdlib.h>
7#include <time.h>
8#include <string.h>
9#include <unistd.h>
10
11#define MIN 60L /* # seconds in a minute */
12#define HOUR (60 * MIN) /* # seconds in an hour */
13#define DAY (24 * HOUR) /* # seconds in a day */
14#define YEAR (365 * DAY) /* # seconds in a (non-leap) year */
15
16int qflag, uflag, sflag, Sflag;
17
18/* Default output file descriptor.
19 */
20int outfd = 1;
21
22_PROTOTYPE(int main, (int argc, char **argv));
23_PROTOTYPE(void putchar, (int c));
24_PROTOTYPE(void pstring, (char *s, int len));
25_PROTOTYPE(void pldecimal, (unsigned long d, int digits));
26_PROTOTYPE(void pdecimal, (int d, int digits));
27_PROTOTYPE(void fmtdate, (char *format, time_t t, struct tm *p));
28_PROTOTYPE(time_t make_time, (char *t));
29_PROTOTYPE(struct tm *september, (time_t *tp));
30_PROTOTYPE(void usage, (void));
31
32/* Main module. Handles P1003.2 date and system administrator's date. The
33 * date entered should be given GMT, regardless of the system's TZ!
34 */
35int main(argc, argv)
36int argc;
37char **argv;
38{
39 time_t t;
40 struct tm *tm;
41 char *format;
42 char time_buf[40];
43 int n;
44 int i;
45
46 time(&t);
47
48 i = 1;
49 while (i < argc && argv[i][0] == '-') {
50 char *opt = argv[i++] + 1, *end;
51
52 if (opt[0] == '-' && opt[1] == 0) break;
53
54 while (*opt != 0) switch (*opt++) {
55 case 'q':
56 qflag = 1;
57 break;
58 case 's':
59 sflag = 1;
60 break;
61 case 'u':
62 uflag = 1;
63 break;
64 case 'S':
65 Sflag = 1;
66 break;
67 case 't':
68 /* (obsolete, now -r) */
69 case 'r':
70 if (*opt == 0) {
71 if (i == argc) usage();
72 opt = argv[i++];
73 }
74 t = strtoul(opt, &end, 10);
75 if (*end != 0) usage();
76 opt = "";
77 break;
78 default:
79 usage();
80 }
81 }
82
83 if (!qflag && i < argc && ('0' <= argv[i][0] && argv[i][0] <= '9')) {
84 t = make_time(argv[i++]);
85 sflag = 1;
86 }
87
88 format = "%c";
89 if (i < argc && argv[i][0] == '+') format = argv[i++] + 1;
90
91 if (i != argc) usage();
92
93 if (qflag) {
94 pstring("\nPlease enter date: MMDDYYhhmmss. Then hit the RETURN key.\n", -1);
95 n = read(0, time_buf, sizeof(time_buf));
96 if (n > 0 && time_buf[n-1] == '\n') n--;
97 if (n >= 0) time_buf[n] = 0;
98 t = make_time(time_buf);
99 sflag = 1;
100 }
101
102 if (sflag && stime(&t) != 0) {
103 outfd = 2;
104 pstring("No permission to set time\n", -1);
105 return(1);
106 }
107
108 tm = Sflag ? september(&t) : uflag ? gmtime(&t) : localtime(&t);
109
110 fmtdate(format, t, tm);
111 putchar('\n');
112 return(0);
113}
114
115/* Replacement for stdio putchar().
116 */
117void putchar(c)
118int c;
119{
120 static char buf[1024];
121 static char *bp = buf;
122
123 if (c != 0) *bp++ = c;
124 if (c == 0 || c == '\n' || bp == buf + sizeof(buf)) {
125 write(outfd, buf, bp - buf);
126 bp = buf;
127 }
128}
129
130/* Internal function that prints a n-digits number. Replaces stdio in our
131 * specific case.
132 */
133void pldecimal(d, digits)
134unsigned long d;
135int digits;
136{
137 digits--;
138 if (d > 9 || digits > 0) pldecimal(d / 10, digits);
139 putchar('0' + (d % 10));
140}
141
142void pdecimal(d, digits)
143int d, digits;
144{
145 pldecimal((unsigned long) d, digits);
146}
147
148/* Internal function that prints a fixed-size string. Replaces stdio in our
149 * specific case.
150 */
151void pstring(s, len)
152char *s;
153int len;
154{
155 while (*s)
156 if (len--)
157 putchar(*s++);
158 else
159 break;
160}
161
162/* Format the date, using the given locale string. A special case is the
163 * TZ which might be a sign followed by four digits (New format time zone).
164 */
165void fmtdate(format, t, p)
166char *format;
167time_t t;
168struct tm *p;
169{
170 int i;
171 char *s;
172 static char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday",
173 "Thursday", "Friday", "Saturday"};
174 static char *month[] = {"January", "February", "March", "April",
175 "May", "June", "July", "August",
176 "September", "October", "November", "December"};
177
178 while (*format)
179 if (*format == '%') {
180 switch (*++format) {
181 case 'A':
182 pstring(wday[p->tm_wday], -1);
183 break;
184 case 'B':
185 pstring(month[p->tm_mon], -1);
186 break;
187 case 'D':
188 pdecimal(p->tm_mon + 1, 2);
189 putchar('/');
190 pdecimal(p->tm_mday, 2);
191 putchar('/');
192 case 'y':
193 pdecimal(p->tm_year % 100, 2);
194 break;
195 case 'H':
196 pdecimal(p->tm_hour, 2);
197 break;
198 case 'I':
199 i = p->tm_hour % 12;
200 pdecimal(i ? i : 12, 2);
201 break;
202 case 'M':
203 pdecimal(p->tm_min, 2);
204 break;
205 case 'X':
206 case 'T':
207 pdecimal(p->tm_hour, 2);
208 putchar(':');
209 pdecimal(p->tm_min, 2);
210 putchar(':');
211 case 'S':
212 pdecimal(p->tm_sec, 2);
213 break;
214 case 'U':
215 pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2);
216 break;
217 case 'W':
218 if (--(p->tm_wday) < 0) p->tm_wday = 6;
219 pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2);
220 if (++(p->tm_wday) > 6) p->tm_wday = 0;
221 break;
222 case 'Y':
223 pdecimal(p->tm_year + 1900, 4);
224 break;
225 case 'Z':
226 if (uflag) {
227 s = "GMT";
228 } else {
229 s = (p->tm_isdst == 1) ? tzname[1] : tzname[0];
230 }
231 pstring(s, strlen(s));
232 break;
233 case 'a':
234 pstring(wday[p->tm_wday], 3);
235 break;
236 case 'b':
237 case 'h':
238 pstring(month[p->tm_mon], 3);
239 break;
240 case 'c':
241 if (!(s = getenv("LC_TIME")))
242 s = "%a %b %e %T %Z %Y";
243 fmtdate(s, t, p);
244 break;
245 case 'd':
246 pdecimal(p->tm_mday, 2);
247 break;
248 case 'e':
249 if (p->tm_mday < 10) putchar(' ');
250 pdecimal(p->tm_mday, 1);
251 break;
252 case 'j':
253 pdecimal(p->tm_yday + 1, 3);
254 break;
255 case 'm':
256 pdecimal(p->tm_mon + 1, 2);
257 break;
258 case 'n': putchar('\n'); break;
259 case 'p':
260 if (p->tm_hour < 12)
261 putchar('A');
262 else
263 putchar('P');
264 putchar('M');
265 break;
266 case 'r':
267 fmtdate("%I:%M:%S %p", t, p);
268 break;
269 case 's':
270 pldecimal((unsigned long) t, 0);
271 break;
272 case 't': putchar('\t'); break;
273 case 'w':
274 putchar('0' + p->tm_wday);
275 break;
276 case 'x':
277 fmtdate("%B %e %Y", t, p);
278 break;
279 case '%': putchar('%'); break;
280 case '\0': format--;
281 }
282 format++;
283 } else
284 putchar(*format++);
285}
286
287/* Convert a local date string into GMT time in seconds. */
288time_t make_time(t)
289char *t;
290{
291 struct tm tm; /* user specified time */
292 time_t now; /* current time */
293 int leap; /* current year is leap year */
294 int i; /* general index */
295 int fld; /* number of fields */
296 int f[6]; /* time fields */
297 static int days_per_month[2][12] = {
298 { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
299 { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }};
300
301/* Get current time just in case */
302 now = time((time_t *) 0);
303 tm = *localtime(&now);
304 tm.tm_sec = 0;
305 tm.tm_mon++;
306 tm.tm_year %= 100;
307
308/* Parse the time */
309#if '0'+1 != '1' || '1'+1 != '2' || '2'+1 != '3' || '3'+1 != '4' || \
310 '4'+1 != '5' || '5'+1 != '6' || '6'+1 != '7' || '7'+1 != '8' || '8'+1 != '9'
311 << Code unsuitable for character collating sequence >>
312#endif
313
314 for (fld = 0; fld < sizeof(f)/sizeof(f[0]); fld++) {
315 if (*t == 0) break;
316 f[fld] = 0;
317 for (i = 0; i < 2; i++, t++) {
318 if (*t < '0' || *t > '9') usage();
319 f[fld] = f[fld] * 10 + *t - '0';
320 }
321 }
322
323 switch (fld) {
324 case 2:
325 tm.tm_hour = f[0]; tm.tm_min = f[1]; break;
326
327 case 3:
328 tm.tm_hour = f[0]; tm.tm_min = f[1]; tm.tm_sec = f[2];
329 break;
330
331 case 5:
332 tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2];
333 tm.tm_hour = f[3]; tm.tm_min = f[4];
334 break;
335
336 case 6:
337 tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2];
338 tm.tm_hour = f[3]; tm.tm_min = f[4]; tm.tm_sec = f[5];
339 break;
340
341 default:
342 usage();
343 }
344
345/* Convert the time into seconds since 1 January 1970 */
346 if (tm.tm_year < 70)
347 tm.tm_year += 100;
348 leap = (tm.tm_year % 4 == 0 && tm.tm_year % 400 != 0);
349 if (tm.tm_mon < 1 || tm.tm_mon > 12 ||
350 tm.tm_mday < 1 || tm.tm_mday > days_per_month[leap][tm.tm_mon-1] ||
351 tm.tm_hour > 23 || tm.tm_min > 59) {
352 outfd = 2;
353 pstring("Illegal date format\n", -1);
354 exit(1);
355 }
356
357/* Convert the time into Minix time - zone independent code */
358 {
359 time_t utctime; /* guess at unix time */
360 time_t nextbit; /* next bit to try */
361 int rv; /* result of try */
362 struct tm *tmp; /* local time conversion */
363
364#define COMPARE(a,b) ((a) != (b)) ? ((a) - (b)) :
365
366 utctime = 1;
367 do {
368 nextbit = utctime;
369 utctime = nextbit << 1;
370 } while (utctime >= 1);
371
372 for (utctime = 0; ; nextbit >>= 1) {
373
374 utctime |= nextbit;
375 tmp = localtime(&utctime);
376 if (tmp == 0) continue;
377
378 rv = COMPARE(tmp->tm_year, tm.tm_year)
379 COMPARE(tmp->tm_mon + 1, tm.tm_mon)
380 COMPARE(tmp->tm_mday, tm.tm_mday)
381 COMPARE(tmp->tm_hour, tm.tm_hour)
382 COMPARE(tmp->tm_min, tm.tm_min)
383 COMPARE(tmp->tm_sec, tm.tm_sec)
384 0;
385
386 if (rv > 0)
387 utctime &= ~nextbit;
388 else if (rv == 0)
389 break;
390
391 if (nextbit == 0) {
392 uflag = 1;
393 outfd = 2;
394 pstring("Inexact conversion to UTC from ", -1);
395 fmtdate("%c\n", utctime, localtime(&utctime) );
396 exit(1);
397 }
398 }
399 return utctime;
400 }
401}
402
403/* Correct the time to the reckoning of Eternal September. */
404struct tm *september(tp)
405time_t *tp;
406{
407 time_t t;
408 int days;
409 struct tm *tm;
410
411 tm = localtime(tp);
412
413 t = *tp - (tm->tm_hour - 12) * 3600L; /* No zone troubles around noon. */
414 days = 0;
415
416 while (tm->tm_year > 93 || (tm->tm_year == 93 && tm->tm_mon >= 8)) {
417 /* Step back a year or a month. */
418 days += tm->tm_year > 93 ? tm->tm_yday+1 : tm->tm_mday;
419 t = *tp - days * (24 * 3600L);
420
421 tm = localtime(&t);
422 }
423
424 if (days > 0) {
425 tm = localtime(tp);
426 tm->tm_mday = days;
427 tm->tm_year = 93;
428 tm->tm_mon = 8;
429#if SANITY
430 t = mktime(tm);
431 tm = localtime(&t);
432#endif
433 }
434 return tm;
435}
436
437/* (Extended) Posix prototype of date. */
438void usage()
439{
440 outfd = 2;
441 pstring("Usage: date [-qsuS] [-r seconds] [[MMDDYY]hhmm[ss]] [+format]\n", -1);
442 exit(1);
443}
Note: See TracBrowser for help on using the repository browser.