xref: /src/contrib/tzcode/date.c (revision ff2c98b30b57b9763e2a6575f729bab676e6c025)
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 static int		retval = EXIT_SUCCESS;
35 
36 static void		display(const char *, time_t);
37 static void		dogmt(void);
38 static void		errensure(void);
39 static void		timeout(FILE *, const char *, const struct tm *);
40 ATTRIBUTE_NORETURN static void usage(void);
41 
42 int
main(const int argc,char * argv[])43 main(const int argc, char *argv[])
44 {
45 	register const char *	format = "+%+";
46 	register int		ch;
47 	register bool		rflag = false;
48 	time_t			t;
49 	intmax_t		secs;
50 	char *			endarg;
51 
52 #ifdef LC_ALL
53 	setlocale(LC_ALL, "");
54 #endif /* defined(LC_ALL) */
55 #if HAVE_GETTEXT
56 # ifdef TZ_DOMAINDIR
57 	bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
58 # endif /* defined(TEXTDOMAINDIR) */
59 	textdomain(TZ_DOMAIN);
60 #endif /* HAVE_GETTEXT */
61 	t = time(NULL);
62 	while ((ch = getopt(argc, argv, "ucr:")) != -1) {
63 		switch (ch) {
64 		default:
65 			usage();
66 		case 'u':		/* do it in UT */
67 		case 'c':
68 			dogmt();
69 			break;
70 		case 'r':		/* seconds since 1970 */
71 			if (rflag) {
72 				fprintf(stderr,
73 					_("date: error: multiple -r's used"));
74 				usage();
75 			}
76 			rflag = true;
77 			errno = 0;
78 			secs = strtoimax(optarg, &endarg, 0);
79 			if (*endarg || optarg == endarg)
80 				errno = EINVAL;
81 			else if (! (TIME_T_MIN <= secs && secs <= TIME_T_MAX))
82 				errno = ERANGE;
83 			if (errno) {
84 				char const *e = strerror(errno);
85 				fprintf(stderr, _("date: %s: %s\n"),
86 					optarg, e);
87 				errensure();
88 				exit(retval);
89 			}
90 			t = secs;
91 			break;
92 		}
93 	}
94 	if (optind < argc) {
95 	  if (argc - optind != 1) {
96 	    fprintf(stderr,
97 		    _("date: error: multiple operands in command line\n"));
98 	    usage();
99 	  }
100 	  format = argv[optind];
101 	  if (*format != '+') {
102 	    fprintf(stderr, _("date: unknown operand: %s\n"), format);
103 	    usage();
104 	  }
105 	}
106 
107 	display(format, t);
108 	return retval;
109 }
110 
111 static void
dogmt(void)112 dogmt(void)
113 {
114 	static char **	fakeenv;
115 
116 	if (fakeenv == NULL) {
117 		static char	tzeutc0[] = "TZ=UTC0";
118 		ptrdiff_t from, to, n;
119 
120 		for (n = 0;  environ[n] != NULL;  ++n)
121 			continue;
122 #if defined ckd_add && defined ckd_mul
123 		if (!ckd_add(&n, n, 2) && !ckd_mul(&n, n, sizeof *fakeenv)
124 		    && n <= INDEX_MAX)
125 		  fakeenv = malloc(n);
126 #else
127 		if (n <= INDEX_MAX / sizeof *fakeenv - 2)
128 		  fakeenv = malloc((n + 2) * sizeof *fakeenv);
129 #endif
130 		if (fakeenv == NULL) {
131 			fprintf(stderr, _("date: Memory exhausted\n"));
132 			errensure();
133 			exit(retval);
134 		}
135 		to = 0;
136 		fakeenv[to++] = tzeutc0;
137 		for (from = 1; environ[from] != NULL; ++from)
138 			if (strncmp(environ[from], "TZ=", 3) != 0)
139 				fakeenv[to++] = environ[from];
140 		fakeenv[to] = NULL;
141 		environ = fakeenv;
142 	}
143 }
144 
145 static void
errensure(void)146 errensure(void)
147 {
148 	if (retval == EXIT_SUCCESS)
149 		retval = EXIT_FAILURE;
150 }
151 
152 static void
usage(void)153 usage(void)
154 {
155 	fprintf(stderr,
156 		       _("date: usage: date [-u] [-c] [-r seconds]"
157 			 " [+format]\n"));
158 	errensure();
159 	exit(retval);
160 }
161 
162 static void
display(char const * format,time_t now)163 display(char const *format, time_t now)
164 {
165 	struct tm *tmp;
166 
167 	tmp = localtime(&now);
168 	if (!tmp) {
169 		fprintf(stderr,
170 			_("date: error: time out of range\n"));
171 		errensure();
172 		return;
173 	}
174 	timeout(stdout, format, tmp);
175 	putchar('\n');
176 	fflush(stdout);
177 	fflush(stderr);
178 	if (ferror(stdout) || ferror(stderr)) {
179 		fprintf(stderr,
180 			_("date: error: couldn't write results\n"));
181 		errensure();
182 	}
183 }
184 
185 static void
timeout(FILE * fp,char const * format,struct tm const * tmp)186 timeout(FILE *fp, char const *format, struct tm const *tmp)
187 {
188 	char *cp = NULL;
189 	ptrdiff_t result;
190 	ptrdiff_t size = 1024 / 2;
191 
192 	for ( ; ; ) {
193 #ifdef ckd_mul
194 		bool bigger = !ckd_mul(&size, size, 2) && size <= INDEX_MAX;
195 #else
196 		bool bigger = size <= INDEX_MAX / 2 && (size *= 2, true);
197 #endif
198 		char *newcp = bigger ? realloc(cp, size) : NULL;
199 		if (!newcp) {
200 			fprintf(stderr,
201 				_("date: error: can't get memory\n"));
202 			errensure();
203 			exit(retval);
204 		}
205 		cp = newcp;
206 		result = strftime(cp, size, format, tmp);
207 		if (result != 0)
208 			break;
209 	}
210 	fwrite(cp + 1, 1, result - 1, fp);
211 	free(cp);
212 }
213