original netbsd's tr(1)
[minix.git] / commands / zoneinfo / date.c
blob5f5c8b6adce0d6ffd8077011607c569f43a3034b
1 #ifndef lint
2 #ifndef NOID
3 static char elsieid[] = "@(#)date.c 7.45";
4 /*
5 ** Modified from the UCB version with the SCCS ID appearing below.
6 */
7 #endif /* !defined NOID */
8 #endif /* !defined lint */
11 * Copyright (c) 1985, 1987, 1988 The Regents of the University of California.
12 * All rights reserved.
14 * Redistribution and use in source and binary forms are permitted
15 * provided that the above copyright notice and this paragraph are
16 * duplicated in all such forms and that any documentation,
17 * advertising materials, and other materials related to such
18 * distribution and use acknowledge that the software was developed
19 * by the University of California, Berkeley. The name of the
20 * University may not be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
24 * WARRANTIES OF MERCHANT[A]BILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27 #include <fcntl.h> /* for O_WRONLY, O_APPEND */
29 #ifndef lint
30 char copyright[] =
31 "@(#) Copyright (c) 1985, 1987, 1988 The Regents of the University of California.\n\
32 All rights reserved.\n";
33 #endif /* not lint */
35 #ifndef lint
36 static char sccsid[] = "@(#)date.c 4.23 (Berkeley) 9/20/88";
37 #endif /* not lint */
39 #include "private.h"
40 #if HAVE_ADJTIME || HAVE_SETTIMEOFDAY
41 #include "sys/time.h" /* for struct timeval, struct timezone */
42 #endif /* HAVE_ADJTIME || HAVE_SETTIMEOFDAY */
43 #include "locale.h"
44 #include "utmp.h" /* for OLD_TIME (or its absence) */
45 #if HAVE_UTMPX_H
46 #include "utmpx.h"
47 #endif
49 #ifndef OTIME_MSG
50 #define OTIME_MSG "old time"
51 #endif
52 #ifndef NTIME_MSG
53 #define NTIME_MSG "new time"
54 #endif
57 ** The two things date knows about time are. . .
60 #ifndef TM_YEAR_BASE
61 #define TM_YEAR_BASE 1900
62 #endif /* !defined TM_YEAR_BASE */
64 #ifndef SECSPERMIN
65 #define SECSPERMIN 60
66 #endif /* !defined SECSPERMIN */
68 extern char ** environ;
69 #if 0
70 extern double atof();
71 extern char * getlogin();
72 extern time_t mktime();
73 extern char * optarg;
74 extern int optind;
75 extern char * strchr();
76 extern time_t time();
77 extern char * tzname[2];
78 #endif
80 static int retval = EXIT_SUCCESS;
82 static void checkfinal P((const char *, int, time_t, time_t));
83 static int comptm P((const struct tm *, const struct tm *));
84 static time_t convert P((const char *, int, time_t));
85 static void display P((const char *));
86 static void dogmt P((void));
87 static void errensure P((void));
88 static void iffy P((time_t, time_t, const char *, const char *));
89 int main P((int, char**));
90 static const char * nondigit P((const char *));
91 static void oops P((const char *));
92 static void reset P((time_t, int));
93 static void timeout P((FILE *, const char *, const struct tm *));
94 static void usage P((void));
95 static void wildinput P((const char *, const char *,
96 const char *));
98 int
99 main(argc, argv)
100 const int argc;
101 char * argv[];
103 register const char * format;
104 register const char * value;
105 register const char * cp;
106 register int ch;
107 register int dousg;
108 register int aflag = 0;
109 register int dflag = 0;
110 register int nflag = 0;
111 register int tflag = 0;
112 register int minuteswest;
113 register int dsttime;
114 register double adjust;
115 time_t now;
116 time_t t;
118 INITIALIZE(dousg);
119 INITIALIZE(minuteswest);
120 INITIALIZE(dsttime);
121 INITIALIZE(adjust);
122 INITIALIZE(t);
123 #ifdef LC_ALL
124 (void) setlocale(LC_ALL, "");
125 #endif /* defined(LC_ALL) */
126 #if HAVE_GETTEXT
127 #ifdef TZ_DOMAINDIR
128 (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
129 #endif /* defined(TEXTDOMAINDIR) */
130 (void) textdomain(TZ_DOMAIN);
131 #endif /* HAVE_GETTEXT */
132 (void) time(&now);
133 format = value = NULL;
134 while ((ch = getopt(argc, argv, "ucnd:t:a:")) != EOF && ch != -1) {
135 switch (ch) {
136 default:
137 usage();
138 case 'u': /* do it in UTC */
139 case 'c':
140 dogmt();
141 break;
142 case 'n': /* don't set network */
143 nflag = 1;
144 break;
145 case 'd': /* daylight saving time */
146 if (dflag) {
147 (void) fprintf(stderr,
148 _("date: error: multiple -d's used"));
149 usage();
151 dflag = 1;
152 cp = optarg;
153 dsttime = atoi(cp);
154 if (*cp == '\0' || *nondigit(cp) != '\0')
155 wildinput(_("-t value"), optarg,
156 _("must be a non-negative number"));
157 break;
158 case 't': /* minutes west of UTC */
159 if (tflag) {
160 (void) fprintf(stderr,
161 _("date: error: multiple -t's used"));
162 usage();
164 tflag = 1;
165 cp = optarg;
166 minuteswest = atoi(cp);
167 if (*cp == '+' || *cp == '-')
168 ++cp;
169 if (*cp == '\0' || *nondigit(cp) != '\0')
170 wildinput(_("-d value"), optarg,
171 _("must be a number"));
172 break;
173 case 'a': /* adjustment */
174 if (aflag) {
175 (void) fprintf(stderr,
176 _("date: error: multiple -a's used"));
177 usage();
179 aflag = 1;
180 cp = optarg;
181 adjust = atof(cp);
182 if (*cp == '+' || *cp == '-')
183 ++cp;
184 if (*cp == '\0' || strcmp(cp, ".") == 0)
185 wildinput(_("-a value"), optarg,
186 _("must be a number"));
187 cp = nondigit(cp);
188 if (*cp == '.')
189 ++cp;
190 if (*nondigit(cp) != '\0')
191 wildinput(_("-a value"), optarg,
192 _("must be a number"));
193 break;
196 while (optind < argc) {
197 cp = argv[optind++];
198 if (*cp == '+')
199 if (format == NULL)
200 format = cp + 1;
201 else {
202 (void) fprintf(stderr,
203 _("date: error: multiple formats in command line\n"));
204 usage();
206 else if (value == NULL)
207 value = cp;
208 else {
209 (void) fprintf(stderr,
210 _("date: error: multiple values in command line\n"));
211 usage();
214 if (value != NULL) {
216 ** This order ensures that "reasonable" twelve-digit inputs
217 ** (such as 120203042006) won't be misinterpreted
218 ** even if time_t's range all the way back to the thirteenth
219 ** century. Do not change the order.
221 t = convert(value, (dousg = TRUE), now);
222 if (t == -1)
223 t = convert(value, (dousg = FALSE), now);
224 if (t == -1) {
226 ** Out of range values,
227 ** or time that falls in a DST transition hole?
229 if ((cp = strchr(value, '.')) != NULL) {
231 ** Ensure that the failure of
232 ** TZ=America/New_York date 8712312359.60
233 ** doesn't get misdiagnosed. (It was
234 ** TZ=America/New_York date 8712311859.60
235 ** when the leap second was inserted.)
236 ** The normal check won't work since
237 ** the given time is valid in UTC.
239 if (atoi(cp + 1) >= SECSPERMIN)
240 wildinput(_("time"), value,
241 _("out of range seconds given"));
243 dogmt();
244 t = convert(value, FALSE, now);
245 if (t == -1)
246 t = convert(value, TRUE, now);
247 wildinput(_("time"), value,
248 (t == -1) ?
249 _("out of range value given") :
250 _("time skipped when clock springs forward"));
254 ** Entire command line has now been checked.
256 if (aflag) {
257 #if HAVE_ADJTIME
258 struct timeval tv;
260 tv.tv_sec = (int) adjust;
261 tv.tv_usec = (int) ((adjust - tv.tv_sec) * 1000000L);
262 if (adjtime(&tv, (struct timeval *) NULL) != 0)
263 oops("adjtime");
264 #endif /* HAVE_ADJTIME */
265 #if !HAVE_ADJTIME
266 reset((time_t) (now + adjust), nflag);
267 #endif /* !HAVE_ADJTIME */
269 ** Sun silently ignores everything else; we follow suit.
271 exit(retval);
273 if (dflag || tflag) {
274 #if HAVE_SETTIMEOFDAY == 2
275 struct timezone tz;
277 if (!dflag || !tflag)
278 if (gettimeofday((struct timeval *) NULL, &tz) != 0)
279 oops("gettimeofday");
280 if (dflag)
281 tz.tz_dsttime = dsttime;
282 if (tflag)
283 tz.tz_minuteswest = minuteswest;
284 if (settimeofday((struct timeval *) NULL, &tz) != 0)
285 oops("settimeofday");
286 #endif /* HAVE_SETTIMEOFDAY == 2 */
287 #if HAVE_SETTIMEOFDAY != 2
288 (void) fprintf(stderr,
289 _("date: warning: kernel doesn't keep -d/-t information, option ignored\n"));
290 #endif /* HAVE_SETTIMEOFDAY != 2 */
293 if (value == NULL)
294 display(format);
296 reset(t, nflag);
298 checkfinal(value, dousg, t, now);
300 #ifdef EBUG
302 struct tm tm;
304 tm = *localtime(&t);
305 timeout(stdout, "%c\n", &tm);
306 exit(retval);
308 #endif /* defined EBUG */
310 display(format);
312 /* gcc -Wall pacifier */
313 for ( ; ; )
314 continue;
317 static void
318 dogmt()
320 static char ** fakeenv;
322 if (fakeenv == NULL) {
323 register int from;
324 register int to;
325 register int n;
326 static char tzegmt0[] = "TZ=GMT0";
328 for (n = 0; environ[n] != NULL; ++n)
329 continue;
330 fakeenv = (char **) malloc((size_t) (n + 2) * sizeof *fakeenv);
331 if (fakeenv == NULL) {
332 (void) perror(_("Memory exhausted"));
333 errensure();
334 exit(retval);
336 to = 0;
337 fakeenv[to++] = tzegmt0;
338 for (from = 1; environ[from] != NULL; ++from)
339 if (strncmp(environ[from], "TZ=", 3) != 0)
340 fakeenv[to++] = environ[from];
341 fakeenv[to] = NULL;
342 environ = fakeenv;
346 #ifdef OLD_TIME
349 ** We assume we're on a System-V-based system,
350 ** should use stime,
351 ** should write System-V-format utmp entries,
352 ** and don't have network notification to worry about.
355 /*ARGSUSED*/
356 static void
357 #if __STDC__
358 reset(const time_t newt, const int nflag)
359 #else /* !__STDC__ */
360 reset(newt, nflag)
361 const time_t newt;
362 const int nflag;
363 #endif /* !__STDC__ */
365 register int fid;
366 time_t oldt;
367 static struct {
368 struct utmp before;
369 struct utmp after;
370 } s;
371 #if HAVE_UTMPX_H
372 static struct {
373 struct utmpx before;
374 struct utmpx after;
375 } sx;
376 #endif
379 ** Wouldn't it be great if stime returned the old time?
381 (void) time(&oldt);
382 if (stime(&newt) != 0)
383 oops("stime");
384 s.before.ut_type = OLD_TIME;
385 s.before.ut_time = oldt;
386 (void) strcpy(s.before.ut_line, OTIME_MSG);
387 s.after.ut_type = NEW_TIME;
388 s.after.ut_time = newt;
389 (void) strcpy(s.after.ut_line, NTIME_MSG);
390 fid = open(WTMP_FILE, O_WRONLY | O_APPEND);
391 if (fid < 0)
392 oops(_("log file open"));
393 if (write(fid, (char *) &s, sizeof s) != sizeof s)
394 oops(_("log file write"));
395 if (close(fid) != 0)
396 oops(_("log file close"));
397 #if !HAVE_UTMPX_H
398 pututline(&s.before);
399 pututline(&s.after);
400 #endif /* !HAVE_UTMPX_H */
401 #if HAVE_UTMPX_H
402 sx.before.ut_type = OLD_TIME;
403 sx.before.ut_tv.tv_sec = oldt;
404 (void) strcpy(sx.before.ut_line, OTIME_MSG);
405 sx.after.ut_type = NEW_TIME;
406 sx.after.ut_tv.tv_sec = newt;
407 (void) strcpy(sx.after.ut_line, NTIME_MSG);
408 #if !SUPPRESS_WTMPX_FILE_UPDATE
409 /* In Solaris 2.5 (and presumably other systems),
410 `date' does not update /var/adm/wtmpx.
411 This must be a bug. If you'd like to reproduce the bug,
412 define SUPPRESS_WTMPX_FILE_UPDATE to be nonzero. */
413 fid = open(WTMPX_FILE, O_WRONLY | O_APPEND);
414 if (fid < 0)
415 oops(_("log file open"));
416 if (write(fid, (char *) &sx, sizeof sx) != sizeof sx)
417 oops(_("log file write"));
418 if (close(fid) != 0)
419 oops(_("log file close"));
420 #endif /* !SUPPRESS_WTMPX_FILE_UPDATE */
421 pututxline(&sx.before);
422 pututxline(&sx.after);
423 #endif /* HAVE_UTMPX_H */
426 #endif /* defined OLD_TIME */
427 #ifndef OLD_TIME
430 ** We assume we're on a BSD-based system,
431 ** should use settimeofday,
432 ** should write BSD-format utmp entries (using logwtmp),
433 ** and may get to worry about network notification.
434 ** The "time name" changes between 4.3-tahoe and 4.4;
435 ** we include sys/param.h to determine which we should use.
438 #ifndef TIME_NAME
439 #include "sys/param.h"
440 #ifdef BSD4_4
441 #define TIME_NAME "date"
442 #endif /* defined BSD4_4 */
443 #ifndef BSD4_4
444 #define TIME_NAME ""
445 #endif /* !defined BSD4_4 */
446 #endif /* !defined TIME_NAME */
448 #include "syslog.h"
449 #include "sys/socket.h"
450 #include "netinet/in.h"
451 #include "netdb.h"
452 #define TSPTYPES
454 #if 0
455 extern int logwtmp();
456 #endif
458 #if HAVE_SETTIMEOFDAY == 1
459 #define settimeofday(t, tz) (settimeofday)(t)
460 #endif /* HAVE_SETTIMEOFDAY == 1 */
462 #ifndef TSP_SETDATE
463 /*ARGSUSED*/
464 #endif /* !defined TSP_SETDATE */
465 static void
466 reset(newt, nflag)
467 const time_t newt;
468 const int nflag;
470 register const char * username;
471 static struct timeval tv; /* static so tv_usec is 0 */
473 #ifdef EBUG
474 return;
475 #endif /* defined EBUG */
476 username = getlogin();
477 if (username == NULL || *username == '\0') /* single-user or no tty */
478 username = "root";
479 tv.tv_sec = newt;
480 #ifdef TSP_SETDATE
481 if (nflag || !netsettime(tv))
482 #endif /* defined TSP_SETDATE */
485 ** "old" entry is always written, for compatibility.
487 logwtmp("|", TIME_NAME, "");
488 if (settimeofday(&tv, (struct timezone *) NULL) == 0) {
489 logwtmp("{", TIME_NAME, ""); /* } */
490 syslog(LOG_AUTH | LOG_NOTICE, _("date set by %s"),
491 username);
492 } else oops("settimeofday");
496 #endif /* !defined OLD_TIME */
498 static void
499 wildinput(item, value, reason)
500 const char * const item;
501 const char * const value;
502 const char * const reason;
504 (void) fprintf(stderr,
505 _("date: error: bad command line %s \"%s\", %s\n"),
506 item, value, reason);
507 usage();
510 static void
511 errensure P((void))
513 if (retval == EXIT_SUCCESS)
514 retval = EXIT_FAILURE;
517 static const char *
518 nondigit(cp)
519 register const char * cp;
521 while (is_digit(*cp))
522 ++cp;
523 return cp;
526 static void
527 usage P((void))
529 (void) fprintf(stderr, _("date: usage is date [-u] [-c] [-n] [-d dst] \
530 [-t min-west] [-a sss.fff] [[yyyy]mmddhhmm[yyyy][.ss]] [+format]\n"));
531 errensure();
532 exit(retval);
535 static void
536 oops(string)
537 const char * const string;
539 int e = errno;
541 (void) fprintf(stderr, _("date: error: "));
542 errno = e;
543 (void) perror(string);
544 errensure();
545 display((char *) NULL);
548 static void
549 display(format)
550 const char * const format;
552 struct tm tm;
553 time_t now;
555 (void) time(&now);
556 tm = *localtime(&now);
557 timeout(stdout, format ? format : "%+", &tm);
558 (void) putchar('\n');
559 (void) fflush(stdout);
560 (void) fflush(stderr);
561 if (ferror(stdout) || ferror(stderr)) {
562 (void) fprintf(stderr,
563 _("date: error: couldn't write results\n"));
564 errensure();
566 exit(retval);
569 #if 0
570 extern size_t strftime();
571 #endif
573 #define INCR 1024
575 static void
576 timeout(fp, format, tmp)
577 FILE * const fp;
578 const char * const format;
579 const struct tm * const tmp;
581 char * cp;
582 size_t result;
583 size_t size;
585 if (*format == '\0')
586 return;
587 size = INCR;
588 cp = malloc((size_t) size);
589 for ( ; ; ) {
590 if (cp == NULL) {
591 (void) fprintf(stderr,
592 _("date: error: can't get memory\n"));
593 errensure();
594 exit(retval);
596 cp[0] = '\1';
597 result = strftime(cp, size, format, tmp);
598 if (result != 0 || cp[0] == '\0')
599 break;
600 size += INCR;
601 cp = realloc(cp, (size_t) size);
603 (void) fwrite(cp, 1, result, fp);
604 free(cp);
607 static int
608 comptm(atmp, btmp)
609 register const struct tm * const atmp;
610 register const struct tm * const btmp;
612 register int result;
614 if ((result = (atmp->tm_year - btmp->tm_year)) == 0 &&
615 (result = (atmp->tm_mon - btmp->tm_mon)) == 0 &&
616 (result = (atmp->tm_mday - btmp->tm_mday)) == 0 &&
617 (result = (atmp->tm_hour - btmp->tm_hour)) == 0 &&
618 (result = (atmp->tm_min - btmp->tm_min)) == 0)
619 result = atmp->tm_sec - btmp->tm_sec;
620 return result;
624 ** convert --
625 ** convert user's input into a time_t.
628 #define ATOI2(ar) (ar[0] - '0') * 10 + (ar[1] - '0'); ar += 2;
630 static time_t
631 #if __STDC__
632 convert(register const char * const value, const int dousg, const time_t t)
633 #else /* !__STDC__ */
634 convert(value, dousg, t)
635 register const char * const value;
636 const int dousg;
637 const time_t t;
638 #endif /* !__STDC__ */
640 register const char * cp;
641 register const char * dotp;
642 register int cent, year_in_cent, month, hour, day, mins, secs;
643 struct tm tm, outtm;
644 time_t outt;
646 tm = *localtime(&t);
647 #define DIVISOR 100
648 year_in_cent = tm.tm_year % DIVISOR + TM_YEAR_BASE % DIVISOR;
649 cent = tm.tm_year / DIVISOR + TM_YEAR_BASE / DIVISOR +
650 year_in_cent / DIVISOR;
651 year_in_cent %= DIVISOR;
652 if (year_in_cent < 0) {
653 year_in_cent += DIVISOR;
654 --cent;
656 month = tm.tm_mon + 1;
657 day = tm.tm_mday;
658 hour = tm.tm_hour;
659 mins = tm.tm_min;
660 secs = 0;
662 dotp = strchr(value, '.');
663 for (cp = value; *cp != '\0'; ++cp)
664 if (!is_digit(*cp) && cp != dotp)
665 wildinput(_("time"), value, _("contains a nondigit"));
667 if (dotp == NULL)
668 dotp = strchr(value, '\0');
669 else {
670 cp = dotp + 1;
671 if (strlen(cp) != 2)
672 wildinput(_("time"), value,
673 _("seconds part is not two digits"));
674 secs = ATOI2(cp);
677 cp = value;
678 switch (dotp - cp) {
679 default:
680 wildinput(_("time"), value,
681 _("main part is wrong length"));
682 case 12:
683 if (!dousg) {
684 cent = ATOI2(cp);
685 year_in_cent = ATOI2(cp);
687 month = ATOI2(cp);
688 day = ATOI2(cp);
689 hour = ATOI2(cp);
690 mins = ATOI2(cp);
691 if (dousg) {
692 cent = ATOI2(cp);
693 year_in_cent = ATOI2(cp);
695 break;
696 case 8: /* mmddhhmm */
697 month = ATOI2(cp);
698 /* fall through to. . . */
699 case 6: /* ddhhmm */
700 day = ATOI2(cp);
701 /* fall through to. . . */
702 case 4: /* hhmm */
703 hour = ATOI2(cp);
704 mins = ATOI2(cp);
705 break;
706 case 10:
707 if (!dousg) {
708 year_in_cent = ATOI2(cp);
710 month = ATOI2(cp);
711 day = ATOI2(cp);
712 hour = ATOI2(cp);
713 mins = ATOI2(cp);
714 if (dousg) {
715 year_in_cent = ATOI2(cp);
717 break;
720 tm.tm_year = cent * 100 + year_in_cent - TM_YEAR_BASE;
721 tm.tm_mon = month - 1;
722 tm.tm_mday = day;
723 tm.tm_hour = hour;
724 tm.tm_min = mins;
725 tm.tm_sec = secs;
726 tm.tm_isdst = -1;
727 outtm = tm;
728 outt = mktime(&outtm);
729 return (comptm(&tm, &outtm) == 0) ? outt : -1;
733 ** Code from here on out is either based on code provided by UCB
734 ** or is only called just before the program exits.
738 ** Check for iffy input.
741 static void
742 #if __STDC__
743 checkfinal(const char * const value,
744 const int didusg,
745 const time_t t,
746 const time_t oldnow)
747 #else /* !__STDC__ */
748 checkfinal(value, didusg, t, oldnow)
749 const char * const value;
750 const int didusg;
751 const time_t t;
752 const time_t oldnow;
753 #endif /* !__STDC__ */
755 time_t othert;
756 struct tm tm;
757 struct tm othertm;
758 register int pass;
759 register long offset;
762 ** See if there's both a USG and a BSD interpretation.
764 othert = convert(value, !didusg, oldnow);
765 if (othert != -1 && othert != t)
766 iffy(t, othert, value, _("year could be at start or end"));
768 ** See if there's both a DST and a STD version.
770 tm = *localtime(&t);
771 othertm = tm;
772 othertm.tm_isdst = !tm.tm_isdst;
773 othert = mktime(&othertm);
774 if (othert != -1 && othertm.tm_isdst != tm.tm_isdst &&
775 comptm(&tm, &othertm) == 0)
776 iffy(t, othert, value,
777 _("both standard and summer time versions exist"));
779 ** Final check.
781 ** If a jurisdiction shifts time *without* shifting whether time is
782 ** summer or standard (as Hawaii, the United Kingdom, and Saudi Arabia
783 ** have done), routine checks for iffy times may not work.
784 ** So we perform this final check, deferring it until after the time has
785 ** been set--it may take a while, and we don't want to introduce an unnecessary
786 ** lag between the time the user enters their command and the time that
787 ** stime/settimeofday is called.
789 ** We just check nearby times to see if any have the same representation
790 ** as the time that convert returned. We work our way out from the center
791 ** for quick response in solar time situations. We only handle common cases--
792 ** offsets of at most a minute, and offsets of exact numbers of minutes
793 ** and at most an hour.
795 for (offset = 1; offset <= 60; ++offset)
796 for (pass = 1; pass <= 4; ++pass) {
797 if (pass == 1)
798 othert = t + offset;
799 else if (pass == 2)
800 othert = t - offset;
801 else if (pass == 3)
802 othert = t + 60 * offset;
803 else othert = t - 60 * offset;
804 othertm = *localtime(&othert);
805 if (comptm(&tm, &othertm) == 0)
806 iffy(t, othert, value,
807 _("multiple matching times exist"));
811 static void
812 #if __STDC__
813 iffy(const time_t thist, const time_t thatt,
814 const char * const value, const char * const reason)
815 #else /* !__STDC__ */
816 iffy(thist, thatt, value, reason)
817 const time_t thist;
818 const time_t thatt;
819 const char * const value;
820 const char * const reason;
821 #endif /* !__STDC__ */
823 struct tm tm;
825 (void) fprintf(stderr, _("date: warning: ambiguous time \"%s\", %s.\n"),
826 value, reason);
827 tm = *gmtime(&thist);
829 ** Avoid running afoul of SCCS!
831 timeout(stderr, _("Time was set as if you used\n\tdate -u %m%d%H\
833 %Y.%S\n"), &tm);
834 tm = *localtime(&thist);
835 timeout(stderr, _("to get %c"), &tm);
836 (void) fprintf(stderr, _(" (%s). Use\n"),
837 tm.tm_isdst ? _("summer time") : _("standard time"));
838 tm = *gmtime(&thatt);
839 timeout(stderr, _("\tdate -u %m%d%H\
841 %Y.%S\n"), &tm);
842 tm = *localtime(&thatt);
843 timeout(stderr, _("to get %c"), &tm);
844 (void) fprintf(stderr, _(" (%s).\n"),
845 tm.tm_isdst ? _("summer time") : _("standard time"));
846 errensure();
847 exit(retval);
850 #ifdef TSP_SETDATE
851 #define WAITACK 2 /* seconds */
852 #define WAITDATEACK 5 /* seconds */
855 * Set the date in the machines controlled by timedaemons
856 * by communicating the new date to the local timedaemon.
857 * If the timedaemon is in the master state, it performs the
858 * correction on all slaves. If it is in the slave state, it
859 * notifies the master that a correction is needed.
860 * Returns 1 on success, 0 on failure.
862 netsettime(ntv)
863 struct timeval ntv;
865 int s, length, port, timed_ack, found, err;
866 long waittime;
867 fd_set ready;
868 char hostname[MAXHOSTNAMELEN];
869 struct timeval tout;
870 struct servent *sp;
871 struct tsp msg;
872 struct sockaddr_in sin, dest, from;
874 sp = getservbyname("timed", "udp");
875 if (sp == 0) {
876 fputs(_("udp/timed: unknown service\n"), stderr);
877 retval = 2;
878 return (0);
880 dest.sin_port = sp->s_port;
881 dest.sin_family = AF_INET;
882 dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY);
883 s = socket(AF_INET, SOCK_DGRAM, 0);
884 if (s < 0) {
885 if (errno != EPROTONOSUPPORT)
886 perror("date: socket");
887 goto bad;
889 bzero((char *)&sin, sizeof (sin));
890 sin.sin_family = AF_INET;
891 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
892 sin.sin_port = htons((u_short)port);
893 if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
894 break;
895 if (errno != EADDRINUSE) {
896 if (errno != EADDRNOTAVAIL)
897 perror("date: bind");
898 goto bad;
901 if (port == IPPORT_RESERVED / 2) {
902 fputs(_("date: All ports in use\n"), stderr);
903 goto bad;
905 msg.tsp_type = TSP_SETDATE;
906 msg.tsp_vers = TSPVERSION;
907 if (gethostname(hostname, sizeof (hostname))) {
908 perror("gethostname");
909 goto bad;
911 (void) strncpy(msg.tsp_name, hostname, sizeof (hostname));
912 msg.tsp_seq = htons((u_short)0);
913 msg.tsp_time.tv_sec = htonl((u_long)ntv.tv_sec);
914 msg.tsp_time.tv_usec = htonl((u_long)ntv.tv_usec);
915 length = sizeof (struct sockaddr_in);
916 if (connect(s, &dest, length) < 0) {
917 perror("date: connect");
918 goto bad;
920 if (send(s, (char *)&msg, sizeof (struct tsp), 0) < 0) {
921 if (errno != ECONNREFUSED)
922 perror("date: send");
923 goto bad;
925 timed_ack = -1;
926 waittime = WAITACK;
927 loop:
928 tout.tv_sec = waittime;
929 tout.tv_usec = 0;
930 FD_ZERO(&ready);
931 FD_SET(s, &ready);
932 found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout);
933 length = sizeof err;
934 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *)&err, &length) == 0
935 && err) {
936 errno = err;
937 if (errno != ECONNREFUSED)
938 perror(_("date: send (delayed error)"));
939 goto bad;
941 if (found > 0 && FD_ISSET(s, &ready)) {
942 length = sizeof (struct sockaddr_in);
943 if (recvfrom(s, (char *)&msg, sizeof (struct tsp), 0, &from,
944 &length) < 0) {
945 if (errno != ECONNREFUSED)
946 perror("date: recvfrom");
947 goto bad;
949 msg.tsp_seq = ntohs(msg.tsp_seq);
950 msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec);
951 msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec);
952 switch (msg.tsp_type) {
954 case TSP_ACK:
955 timed_ack = TSP_ACK;
956 waittime = WAITDATEACK;
957 goto loop;
959 case TSP_DATEACK:
960 (void)close(s);
961 return (1);
963 default:
964 fprintf(stderr,
965 _("date: Wrong ack received from timed: %s\n"),
966 tsptype[msg.tsp_type]);
967 timed_ack = -1;
968 break;
971 if (timed_ack == -1)
972 fputs(_("date: Can't reach time daemon, time set locally.\n"),
973 stderr);
974 bad:
975 (void)close(s);
976 retval = 2;
977 return (0);
979 #endif /* defined TSP_SETDATE */