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
35extern char *		optarg;
36extern int		optind;
37#endif
38
39static int		retval = EXIT_SUCCESS;
40
41static void		display(const char *, time_t);
42static void		dogmt(void);
43static void		errensure(void);
44static void		timeout(FILE *, const char *, const struct tm *);
45ATTRIBUTE_NORETURN static void usage(void);
46
47int
48main(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
116static void
117dogmt(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
150static void
151errensure(void)
152{
153	if (retval == EXIT_SUCCESS)
154		retval = EXIT_FAILURE;
155}
156
157static void
158usage(void)
159{
160	fprintf(stderr,
161		       _("date: usage: date [-u] [-c] [-r seconds]"
162			 " [+format]\n"));
163	errensure();
164	exit(retval);
165}
166
167static void
168display(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
190static void
191timeout(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