xref: /freebsd/contrib/tzcode/date.c (revision e0c4386e)
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 ATTRIBUTE_NORETURN static 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 				char const *e = strerror(errno);
90 				fprintf(stderr, _("date: %s: %s\n"),
91 					optarg, e);
92 				errensure();
93 				exit(retval);
94 			}
95 			t = secs;
96 			break;
97 		}
98 	}
99 	if (optind < argc) {
100 	  if (argc - optind != 1) {
101 	    fprintf(stderr,
102 		    _("date: error: multiple operands in command line\n"));
103 	    usage();
104 	  }
105 	  format = argv[optind];
106 	  if (*format != '+') {
107 	    fprintf(stderr, _("date: unknown operand: %s\n"), format);
108 	    usage();
109 	  }
110 	}
111 
112 	display(format, t);
113 	return retval;
114 }
115 
116 static void
117 dogmt(void)
118 {
119 	static char **	fakeenv;
120 
121 	if (fakeenv == NULL) {
122 		static char	tzeutc0[] = "TZ=UTC0";
123 		ptrdiff_t from, to, n;
124 
125 		for (n = 0;  environ[n] != NULL;  ++n)
126 			continue;
127 #if defined ckd_add && defined ckd_mul
128 		if (!ckd_add(&n, n, 2) && !ckd_mul(&n, n, sizeof *fakeenv)
129 		    && n <= INDEX_MAX)
130 		  fakeenv = malloc(n);
131 #else
132 		if (n <= INDEX_MAX / sizeof *fakeenv - 2)
133 		  fakeenv = malloc((n + 2) * sizeof *fakeenv);
134 #endif
135 		if (fakeenv == NULL) {
136 			fprintf(stderr, _("date: Memory exhausted\n"));
137 			errensure();
138 			exit(retval);
139 		}
140 		to = 0;
141 		fakeenv[to++] = tzeutc0;
142 		for (from = 1; environ[from] != NULL; ++from)
143 			if (strncmp(environ[from], "TZ=", 3) != 0)
144 				fakeenv[to++] = environ[from];
145 		fakeenv[to] = NULL;
146 		environ = fakeenv;
147 	}
148 }
149 
150 static void
151 errensure(void)
152 {
153 	if (retval == EXIT_SUCCESS)
154 		retval = EXIT_FAILURE;
155 }
156 
157 static void
158 usage(void)
159 {
160 	fprintf(stderr,
161 		       _("date: usage: date [-u] [-c] [-r seconds]"
162 			 " [+format]\n"));
163 	errensure();
164 	exit(retval);
165 }
166 
167 static void
168 display(char const *format, time_t now)
169 {
170 	struct tm *tmp;
171 
172 	tmp = localtime(&now);
173 	if (!tmp) {
174 		fprintf(stderr,
175 			_("date: error: time out of range\n"));
176 		errensure();
177 		return;
178 	}
179 	timeout(stdout, format, tmp);
180 	putchar('\n');
181 	fflush(stdout);
182 	fflush(stderr);
183 	if (ferror(stdout) || ferror(stderr)) {
184 		fprintf(stderr,
185 			_("date: error: couldn't write results\n"));
186 		errensure();
187 	}
188 }
189 
190 static void
191 timeout(FILE *fp, char const *format, struct tm const *tmp)
192 {
193 	char *cp = NULL;
194 	ptrdiff_t result;
195 	ptrdiff_t size = 1024 / 2;
196 
197 	for ( ; ; ) {
198 #ifdef ckd_mul
199 		bool bigger = !ckd_mul(&size, size, 2) && size <= INDEX_MAX;
200 #else
201 		bool bigger = size <= INDEX_MAX / 2 && (size *= 2, true);
202 #endif
203 		char *newcp = bigger ? realloc(cp, size) : NULL;
204 		if (!newcp) {
205 			fprintf(stderr,
206 				_("date: error: can't get memory\n"));
207 			errensure();
208 			exit(retval);
209 		}
210 		cp = newcp;
211 		result = strftime(cp, size, format, tmp);
212 		if (result != 0)
213 			break;
214 	}
215 	fwrite(cp + 1, 1, result - 1, fp);
216 	free(cp);
217 }
218