xref: /freebsd/contrib/tzcode/date.c (revision f374ba41)
1 /* Display or set the current time and date.  */
2 
3 /* Copyright 1985, 1987, 1988 The Regents of the University of California.
4    All rights reserved.
5 
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9    1. Redistributions of source code must retain the above copyright
10       notice, this list of conditions and the following disclaimer.
11    2. Redistributions in binary form must reproduce the above copyright
12       notice, this list of conditions and the following disclaimer in the
13       documentation and/or other materials provided with the distribution.
14    3. Neither the name of the University nor the names of its contributors
15       may be used to endorse or promote products derived from this software
16       without specific prior written permission.
17 
18    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
19    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28    SUCH DAMAGE.  */
29 
30 #include "private.h"
31 #include <locale.h>
32 #include <stdio.h>
33 
34 #if !HAVE_POSIX_DECLS
35 extern char *		optarg;
36 extern int		optind;
37 #endif
38 
39 static int		retval = EXIT_SUCCESS;
40 
41 static void		display(const char *, time_t);
42 static void		dogmt(void);
43 static void		errensure(void);
44 static void		timeout(FILE *, const char *, const struct tm *);
45 static ATTRIBUTE_NORETURN void usage(void);
46 
47 int
48 main(const int argc, char *argv[])
49 {
50 	register const char *	format = "+%+";
51 	register int		ch;
52 	register bool		rflag = false;
53 	time_t			t;
54 	intmax_t		secs;
55 	char *			endarg;
56 
57 #ifdef LC_ALL
58 	setlocale(LC_ALL, "");
59 #endif /* defined(LC_ALL) */
60 #if HAVE_GETTEXT
61 # ifdef TZ_DOMAINDIR
62 	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
63 # endif /* defined(TEXTDOMAINDIR) */
64 	textdomain(TZ_DOMAIN);
65 #endif /* HAVE_GETTEXT */
66 	t = time(NULL);
67 	while ((ch = getopt(argc, argv, "ucr:")) != EOF && ch != -1) {
68 		switch (ch) {
69 		default:
70 			usage();
71 		case 'u':		/* do it in UT */
72 		case 'c':
73 			dogmt();
74 			break;
75 		case 'r':		/* seconds since 1970 */
76 			if (rflag) {
77 				fprintf(stderr,
78 					_("date: error: multiple -r's used"));
79 				usage();
80 			}
81 			rflag = true;
82 			errno = 0;
83 			secs = strtoimax(optarg, &endarg, 0);
84 			if (*endarg || optarg == endarg)
85 				errno = EINVAL;
86 			else if (! (TIME_T_MIN <= secs && secs <= TIME_T_MAX))
87 				errno = ERANGE;
88 			if (errno) {
89 				perror(optarg);
90 				errensure();
91 				exit(retval);
92 			}
93 			t = secs;
94 			break;
95 		}
96 	}
97 	if (optind < argc) {
98 	  if (argc - optind != 1) {
99 	    fprintf(stderr,
100 		    _("date: error: multiple operands in command line\n"));
101 	    usage();
102 	  }
103 	  format = argv[optind];
104 	  if (*format != '+') {
105 	    fprintf(stderr, _("date: unknown operand: %s\n"), format);
106 	    usage();
107 	  }
108 	}
109 
110 	display(format, t);
111 	return retval;
112 }
113 
114 static void
115 dogmt(void)
116 {
117 	static char **	fakeenv;
118 
119 	if (fakeenv == NULL) {
120 		static char	tzeutc0[] = "TZ=UTC0";
121 		ptrdiff_t from, to, n;
122 
123 		for (n = 0;  environ[n] != NULL;  ++n)
124 			continue;
125 #if defined ckd_add && defined ckd_mul
126 		if (!ckd_add(&n, n, 2) && !ckd_mul(&n, n, sizeof *fakeenv)
127 		    && n <= SIZE_MAX)
128 		  fakeenv = malloc(n);
129 #else
130 		if (n <= min(PTRDIFF_MAX, SIZE_MAX) / sizeof *fakeenv - 2)
131 		  fakeenv = malloc((n + 2) * sizeof *fakeenv);
132 #endif
133 		if (fakeenv == NULL) {
134 			fprintf(stderr, _("date: Memory exhausted\n"));
135 			errensure();
136 			exit(retval);
137 		}
138 		to = 0;
139 		fakeenv[to++] = tzeutc0;
140 		for (from = 1; environ[from] != NULL; ++from)
141 			if (strncmp(environ[from], "TZ=", 3) != 0)
142 				fakeenv[to++] = environ[from];
143 		fakeenv[to] = NULL;
144 		environ = fakeenv;
145 	}
146 }
147 
148 static void
149 errensure(void)
150 {
151 	if (retval == EXIT_SUCCESS)
152 		retval = EXIT_FAILURE;
153 }
154 
155 static void
156 usage(void)
157 {
158 	fprintf(stderr,
159 		       _("date: usage: date [-u] [-c] [-r seconds]"
160 			 " [+format]\n"));
161 	errensure();
162 	exit(retval);
163 }
164 
165 static void
166 display(char const *format, time_t now)
167 {
168 	struct tm *tmp;
169 
170 	tmp = localtime(&now);
171 	if (!tmp) {
172 		fprintf(stderr,
173 			_("date: error: time out of range\n"));
174 		errensure();
175 		return;
176 	}
177 	timeout(stdout, format, tmp);
178 	putchar('\n');
179 	fflush(stdout);
180 	fflush(stderr);
181 	if (ferror(stdout) || ferror(stderr)) {
182 		fprintf(stderr,
183 			_("date: error: couldn't write results\n"));
184 		errensure();
185 	}
186 }
187 
188 static void
189 timeout(FILE *fp, char const *format, struct tm const *tmp)
190 {
191 	char *cp = NULL;
192 	ptrdiff_t result;
193 	ptrdiff_t size = 1024 / 2;
194 
195 	for ( ; ; ) {
196 #ifdef ckd_mul
197 		bool bigger = !ckd_mul(&size, size, 2) && size <= SIZE_MAX;
198 #else
199 		bool bigger = (size <= min(PTRDIFF_MAX, SIZE_MAX) / 2
200 			       && (size *= 2, true));
201 #endif
202 		char *newcp = bigger ? realloc(cp, size) : NULL;
203 		if (!newcp) {
204 			fprintf(stderr,
205 				_("date: error: can't get memory\n"));
206 			errensure();
207 			exit(retval);
208 		}
209 		cp = newcp;
210 		result = strftime(cp, size, format, tmp);
211 		if (result != 0)
212 			break;
213 	}
214 	fwrite(cp + 1, 1, result - 1, fp);
215 	free(cp);
216 }
217