1/* vi: set sw=4 ts=4: */
2/*
3 * Mini date implementation for busybox
4 *
5 * by Matthew Grant <grantma@anathoth.gen.nz>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21*/
22
23#include <stdlib.h>
24#include <errno.h>
25#include <sys/time.h>
26#include <unistd.h>
27#include <time.h>
28#include <stdio.h>
29#include <string.h>
30#include <getopt.h>
31#include "busybox.h"
32
33
34/* This 'date' command supports only 2 time setting formats,
35   all the GNU strftime stuff (its in libc, lets use it),
36   setting time using UTC and displaying int, as well as
37   an RFC 822 complient date output for shell scripting
38   mail commands */
39
40/* Input parsing code is always bulky - used heavy duty libc stuff as
41   much as possible, missed out a lot of bounds checking */
42
43/* Default input handling to save suprising some people */
44
45static struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
46{
47	int nr;
48
49	nr = sscanf(t_string, "%2d%2d%2d%2d%d",
50				&(tm_time->tm_mon),
51				&(tm_time->tm_mday),
52				&(tm_time->tm_hour),
53				&(tm_time->tm_min), &(tm_time->tm_year));
54
55	if (nr < 4 || nr > 5) {
56		error_msg_and_die(invalid_date, t_string);
57	}
58
59	/* correct for century  - minor Y2K problem here? */
60	if (tm_time->tm_year >= 1900)
61		tm_time->tm_year -= 1900;
62	/* adjust date */
63	tm_time->tm_mon -= 1;
64
65	return (tm_time);
66
67}
68
69
70/* The new stuff for LRP */
71
72static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
73{
74	struct tm t;
75
76	/* Parse input and assign appropriately to tm_time */
77
78	if (t=*tm_time,sscanf(t_string, "%d:%d:%d",
79			   &t.tm_hour, &t.tm_min, &t.tm_sec) == 3) {
80					/* no adjustments needed */
81
82	} else if (t=*tm_time,sscanf(t_string, "%d:%d",
83					  &t.tm_hour, &t.tm_min) == 2) {
84					/* no adjustments needed */
85
86
87	} else if (t=*tm_time,sscanf(t_string, "%d.%d-%d:%d:%d",
88					  &t.tm_mon,
89					  &t.tm_mday,
90					  &t.tm_hour,
91					  &t.tm_min, &t.tm_sec) == 5) {
92
93		t.tm_mon -= 1;	/* Adjust dates from 1-12 to 0-11 */
94
95	} else if (t=*tm_time,sscanf(t_string, "%d.%d-%d:%d",
96					  &t.tm_mon,
97					  &t.tm_mday,
98					  &t.tm_hour, &t.tm_min) == 4) {
99
100		t.tm_mon -= 1;	/* Adjust dates from 1-12 to 0-11 */
101
102	} else if (t=*tm_time,sscanf(t_string, "%d.%d.%d-%d:%d:%d",
103					  &t.tm_year,
104					  &t.tm_mon,
105					  &t.tm_mday,
106					  &t.tm_hour,
107					  &t.tm_min, &t.tm_sec) == 6) {
108
109		t.tm_year -= 1900;	/* Adjust years */
110		t.tm_mon -= 1;	/* Adjust dates from 1-12 to 0-11 */
111
112	} else if (t=*tm_time,sscanf(t_string, "%d.%d.%d-%d:%d",
113					  &t.tm_year,
114					  &t.tm_mon,
115					  &t.tm_mday,
116					  &t.tm_hour, &t.tm_min) == 5) {
117		t.tm_year -= 1900;	/* Adjust years */
118		t.tm_mon -= 1;	/* Adjust dates from 1-12 to 0-11 */
119
120	} else {
121		error_msg_and_die(invalid_date, t_string);
122	}
123	*tm_time = t;
124	return (tm_time);
125}
126
127
128int date_main(int argc, char **argv)
129{
130	char *date_str = NULL;
131	char *date_fmt = NULL;
132	char *t_buff;
133	int c;
134	int set_time = 0;
135	int rfc822 = 0;
136	int utc = 0;
137	int use_arg = 0;
138	time_t tm;
139	struct tm tm_time;
140
141	/* Interpret command line args */
142	while ((c = getopt(argc, argv, "Rs:ud:")) != EOF) {
143		switch (c) {
144			case 'R':
145				rfc822 = 1;
146				break;
147			case 's':
148				set_time = 1;
149				if ((date_str != NULL) || ((date_str = optarg) == NULL)) {
150					show_usage();
151				}
152				break;
153			case 'u':
154				utc = 1;
155				if (putenv("TZ=UTC0") != 0)
156					error_msg_and_die(memory_exhausted);
157				break;
158			case 'd':
159				use_arg = 1;
160				if ((date_str != NULL) || ((date_str = optarg) == NULL))
161					show_usage();
162				break;
163			default:
164				show_usage();
165		}
166	}
167
168	if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+'))
169		date_fmt = &argv[optind][1];   /* Skip over the '+' */
170	else if (date_str == NULL) {
171		set_time = 1;
172		date_str = argv[optind];
173	}
174
175	/* Now we have parsed all the information except the date format
176	   which depends on whether the clock is being set or read */
177
178	time(&tm);
179	memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
180	/* Zero out fields - take her back to midnight! */
181	if (date_str != NULL) {
182		tm_time.tm_sec = 0;
183		tm_time.tm_min = 0;
184		tm_time.tm_hour = 0;
185	}
186
187	/* Process any date input to UNIX time since 1 Jan 1970 */
188	if (date_str != NULL) {
189
190		if (strchr(date_str, ':') != NULL) {
191			date_conv_ftime(&tm_time, date_str);
192		} else {
193			date_conv_time(&tm_time, date_str);
194		}
195
196		/* Correct any day of week and day of year etc. fields */
197		tm = mktime(&tm_time);
198		if (tm < 0)
199			error_msg_and_die(invalid_date, date_str);
200		if ( utc ) {
201			if (putenv("TZ=UTC0") != 0)
202				error_msg_and_die(memory_exhausted);
203		}
204
205		/* if setting time, set it */
206		if (set_time) {
207			if (stime(&tm) < 0) {
208				perror_msg("cannot set date");
209			}
210		}
211	}
212
213	/* Display output */
214
215	/* Deal with format string */
216	if (date_fmt == NULL) {
217		date_fmt = (rfc822
218					? (utc
219					   ? "%a, %_d %b %Y %H:%M:%S GMT"
220					   : "%a, %_d %b %Y %H:%M:%S %z")
221					: "%a %b %e %H:%M:%S %Z %Y");
222
223	} else if (*date_fmt == '\0') {
224		/* Imitate what GNU 'date' does with NO format string! */
225		printf("\n");
226		return EXIT_SUCCESS;
227	}
228
229	/* Handle special conversions */
230
231	if (strncmp(date_fmt, "%f", 2) == 0) {
232		date_fmt = "%Y.%m.%d-%H:%M:%S";
233	}
234
235	/* Print OUTPUT (after ALL that!) */
236	t_buff = xmalloc(201);
237	strftime(t_buff, 200, date_fmt, &tm_time);
238	puts(t_buff);
239
240	return EXIT_SUCCESS;
241}
242