blob: dd054be6634c650066bbd95155339f7f036f1868 [file] [log] [blame]
Erik Andersene49d5ec2000-02-08 19:58:47 +00001/* vi: set sw=4 ts=4: */
Eric Andersen2ce1edc1999-10-12 15:42:48 +00002/*
3 * Mini date implementation for busybox
4 *
Eric Andersenc4996011999-10-20 22:08:37 +00005 * by Matthew Grant <grantma@anathoth.gen.nz>
Eric Andersen2ce1edc1999-10-12 15:42:48 +00006 *
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
Eric Andersencc8ed391999-10-05 16:24:54 +000023#include "internal.h"
Erik Andersenfac10d72000-02-07 05:29:42 +000024#define BB_DECLARE_EXTERN
25#define bb_need_invalid_date
26#define bb_need_memory_exhausted
27#include "messages.c"
Eric Andersencc8ed391999-10-05 16:24:54 +000028#include <stdlib.h>
29#include <errno.h>
30#include <sys/time.h>
31#include <unistd.h>
32#include <time.h>
33#include <stdio.h>
Eric Andersencc8ed391999-10-05 16:24:54 +000034
35
36/* This 'date' command supports only 2 time setting formats,
37 all the GNU strftime stuff (its in libc, lets use it),
38 setting time using UTC and displaying int, as well as
39 an RFC 822 complient date output for shell scripting
40 mail commands */
41
Erik Andersene49d5ec2000-02-08 19:58:47 +000042static const char date_usage[] = "date [OPTION]... [+FORMAT]\n"
Erik Andersen1d1d9502000-04-21 01:26:49 +000043 " or: date [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n"
44#ifndef BB_FEATURE_TRIVIAL_HELP
45 "\nDisplays the current time in the given FORMAT, or sets the system date.\n"
46 "\nOptions:\n\t-R\tOutputs RFC-822 compliant date string\n"
47 "\t-s\tSets time described by STRING\n"
48 "\t-u\tPrints or sets Coordinated Universal Time\n"
49#endif
50 ;
Eric Andersencc8ed391999-10-05 16:24:54 +000051
52
53/* Input parsing code is always bulky - used heavy duty libc stuff as
54 much as possible, missed out a lot of bounds checking */
55
56/* Default input handling to save suprising some people */
57
Erik Andersene49d5ec2000-02-08 19:58:47 +000058struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
59{
60 int nr;
Eric Andersencc8ed391999-10-05 16:24:54 +000061
Erik Andersene49d5ec2000-02-08 19:58:47 +000062 nr = sscanf(t_string, "%2d%2d%2d%2d%d",
63 &(tm_time->tm_mon),
64 &(tm_time->tm_mday),
65 &(tm_time->tm_hour),
66 &(tm_time->tm_min), &(tm_time->tm_year));
Eric Andersencc8ed391999-10-05 16:24:54 +000067
Erik Andersene49d5ec2000-02-08 19:58:47 +000068 if (nr < 4 || nr > 5) {
Erik Andersen1ad302a2000-03-24 00:54:46 +000069 fatalError(invalid_date, "date", t_string);
Erik Andersene49d5ec2000-02-08 19:58:47 +000070 }
Eric Andersencc8ed391999-10-05 16:24:54 +000071
Erik Andersene49d5ec2000-02-08 19:58:47 +000072 /* correct for century - minor Y2K problem here? */
73 if (tm_time->tm_year >= 1900)
74 tm_time->tm_year -= 1900;
75 /* adjust date */
76 tm_time->tm_mon -= 1;
77
78 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +000079
80}
81
82
83/* The new stuff for LRP */
84
Erik Andersene49d5ec2000-02-08 19:58:47 +000085struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
86{
87 struct tm itm_time, jtm_time, ktm_time, ltm_time, mtm_time, ntm_time;
Eric Andersencc8ed391999-10-05 16:24:54 +000088
Erik Andersene49d5ec2000-02-08 19:58:47 +000089 itm_time = *tm_time;
90 jtm_time = *tm_time;
91 ktm_time = *tm_time;
92 ltm_time = *tm_time;
93 mtm_time = *tm_time;
94 ntm_time = *tm_time;
Eric Andersencc8ed391999-10-05 16:24:54 +000095
Erik Andersene49d5ec2000-02-08 19:58:47 +000096 /* Parse input and assign appropriately to tm_time */
Eric Andersencc8ed391999-10-05 16:24:54 +000097
Erik Andersene49d5ec2000-02-08 19:58:47 +000098 if (sscanf(t_string, "%d:%d:%d",
99 &itm_time.tm_hour, &itm_time.tm_min, &itm_time.tm_sec) == 3) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000100
Erik Andersene49d5ec2000-02-08 19:58:47 +0000101 *tm_time = itm_time;
102 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000103
Erik Andersene49d5ec2000-02-08 19:58:47 +0000104 } else if (sscanf(t_string, "%d:%d",
105 &jtm_time.tm_hour, &jtm_time.tm_min) == 2) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000106
Erik Andersene49d5ec2000-02-08 19:58:47 +0000107 *tm_time = jtm_time;
108 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000109
Erik Andersene49d5ec2000-02-08 19:58:47 +0000110 } else if (sscanf(t_string, "%d.%d-%d:%d:%d",
111 &ktm_time.tm_mon,
112 &ktm_time.tm_mday,
113 &ktm_time.tm_hour,
114 &ktm_time.tm_min, &ktm_time.tm_sec) == 5) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000115
Erik Andersene49d5ec2000-02-08 19:58:47 +0000116 ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
117 *tm_time = ktm_time;
118 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000119
Erik Andersene49d5ec2000-02-08 19:58:47 +0000120 } else if (sscanf(t_string, "%d.%d-%d:%d",
121 &ltm_time.tm_mon,
122 &ltm_time.tm_mday,
123 &ltm_time.tm_hour, &ltm_time.tm_min) == 4) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000124
Erik Andersene49d5ec2000-02-08 19:58:47 +0000125 ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
126 *tm_time = ltm_time;
127 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000128
Erik Andersene49d5ec2000-02-08 19:58:47 +0000129 } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d",
130 &mtm_time.tm_year,
131 &mtm_time.tm_mon,
132 &mtm_time.tm_mday,
133 &mtm_time.tm_hour,
134 &mtm_time.tm_min, &mtm_time.tm_sec) == 6) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000135
Erik Andersene49d5ec2000-02-08 19:58:47 +0000136 mtm_time.tm_year -= 1900; /* Adjust years */
137 mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
138 *tm_time = mtm_time;
139 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000140
Erik Andersene49d5ec2000-02-08 19:58:47 +0000141 } else if (sscanf(t_string, "%d.%d.%d-%d:%d",
142 &ntm_time.tm_year,
143 &ntm_time.tm_mon,
144 &ntm_time.tm_mday,
145 &ntm_time.tm_hour, &ntm_time.tm_min) == 5) {
146 ntm_time.tm_year -= 1900; /* Adjust years */
147 ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
148 *tm_time = ntm_time;
149 return (tm_time);
Eric Andersencc8ed391999-10-05 16:24:54 +0000150
Erik Andersene49d5ec2000-02-08 19:58:47 +0000151 }
152
Erik Andersen1ad302a2000-03-24 00:54:46 +0000153 fatalError(invalid_date, "date", t_string);
Eric Andersencc8ed391999-10-05 16:24:54 +0000154}
155
156
Erik Andersene49d5ec2000-02-08 19:58:47 +0000157int date_main(int argc, char **argv)
Eric Andersencc8ed391999-10-05 16:24:54 +0000158{
Erik Andersene49d5ec2000-02-08 19:58:47 +0000159 char *date_str = NULL;
160 char *date_fmt = NULL;
161 char *t_buff;
162 int i;
163 int set_time = 0;
164 int rfc822 = 0;
165 int utc = 0;
166 int use_arg = 0;
167 time_t tm;
168 struct tm tm_time;
169
170 /* Interpret command line args */
171 i = --argc;
Eric Andersen2ce1edc1999-10-12 15:42:48 +0000172 argv++;
Erik Andersene49d5ec2000-02-08 19:58:47 +0000173 while (i > 0 && **argv) {
174 if (**argv == '-') {
175 while (i > 0 && *++(*argv))
176 switch (**argv) {
177 case 'R':
178 rfc822 = 1;
179 break;
180 case 's':
181 set_time = 1;
182 if (date_str != NULL)
183 usage(date_usage);
184 date_str = optarg;
185 break;
186 case 'u':
187 utc = 1;
Erik Andersen1ad302a2000-03-24 00:54:46 +0000188 if (putenv("TZ=UTC0") != 0)
189 fatalError(memory_exhausted, "date");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000190 /* Look ma, no break. Don't fix it either. */
191 case 'd':
192 use_arg = 1;
193 if (date_str != NULL)
194 usage(date_usage);
195 date_str = optarg;
196 break;
197 case '-':
198 usage(date_usage);
199 }
200 } else {
Erik Andersen298854f2000-03-23 01:09:18 +0000201 if ((date_fmt == NULL) && (**argv == '+'))
202 date_fmt = *argv + 1; /* Skip over the '+' */
Erik Andersene49d5ec2000-02-08 19:58:47 +0000203 else if (date_str == NULL) {
204 set_time = 1;
205 date_str = *argv;
206 } else {
207 usage(date_usage);
208 }
209 }
210 i--;
211 argv++;
212 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000213
214
Erik Andersene49d5ec2000-02-08 19:58:47 +0000215 /* Now we have parsed all the information except the date format
216 which depends on whether the clock is being set or read */
Eric Andersencc8ed391999-10-05 16:24:54 +0000217
Erik Andersene49d5ec2000-02-08 19:58:47 +0000218 time(&tm);
219 memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
220 /* Zero out fields - take her back to midnight! */
221 if (date_str != NULL) {
222 tm_time.tm_sec = 0;
223 tm_time.tm_min = 0;
224 tm_time.tm_hour = 0;
225 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000226
Erik Andersene49d5ec2000-02-08 19:58:47 +0000227 /* Process any date input to UNIX time since 1 Jan 1970 */
228 if (date_str != NULL) {
Eric Andersencc8ed391999-10-05 16:24:54 +0000229
Erik Andersene49d5ec2000-02-08 19:58:47 +0000230 if (strchr(date_str, ':') != NULL) {
231 date_conv_ftime(&tm_time, date_str);
232 } else {
233 date_conv_time(&tm_time, date_str);
234 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000235
Erik Andersene49d5ec2000-02-08 19:58:47 +0000236 /* Correct any day of week and day of year etc fields */
237 tm = mktime(&tm_time);
Erik Andersen1ad302a2000-03-24 00:54:46 +0000238 if (tm < 0)
239 fatalError(invalid_date, "date", date_str);
Eric Andersencc8ed391999-10-05 16:24:54 +0000240
Erik Andersene49d5ec2000-02-08 19:58:47 +0000241 /* if setting time, set it */
242 if (set_time) {
243 if (stime(&tm) < 0) {
Erik Andersen1ad302a2000-03-24 00:54:46 +0000244 fatalError("date: can't set date.\n");
Erik Andersene49d5ec2000-02-08 19:58:47 +0000245 }
246 }
247 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000248
Erik Andersene49d5ec2000-02-08 19:58:47 +0000249 /* Display output */
Eric Andersencc8ed391999-10-05 16:24:54 +0000250
Erik Andersene49d5ec2000-02-08 19:58:47 +0000251 /* Deal with format string */
252 if (date_fmt == NULL) {
253 date_fmt = (rfc822
254 ? (utc
255 ? "%a, %_d %b %Y %H:%M:%S GMT"
256 : "%a, %_d %b %Y %H:%M:%S %z")
257 : "%a %b %e %H:%M:%S %Z %Y");
Eric Andersencc8ed391999-10-05 16:24:54 +0000258
Erik Andersene49d5ec2000-02-08 19:58:47 +0000259 } else if (*date_fmt == '\0') {
260 /* Imitate what GNU 'date' does with NO format string! */
261 printf("\n");
262 exit(TRUE);
263 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000264
Erik Andersene49d5ec2000-02-08 19:58:47 +0000265 /* Handle special conversions */
Eric Andersencc8ed391999-10-05 16:24:54 +0000266
Erik Andersene49d5ec2000-02-08 19:58:47 +0000267 if (strncmp(date_fmt, "%f", 2) == 0) {
268 date_fmt = "%Y.%m.%d-%H:%M:%S";
269 }
Eric Andersencc8ed391999-10-05 16:24:54 +0000270
Erik Andersene49d5ec2000-02-08 19:58:47 +0000271 /* Print OUTPUT (after ALL that!) */
Erik Andersen0d068a22000-03-21 22:32:57 +0000272 t_buff = xmalloc(201);
Erik Andersene49d5ec2000-02-08 19:58:47 +0000273 strftime(t_buff, 200, date_fmt, &tm_time);
274 printf("%s\n", t_buff);
275
276 exit(TRUE);
Eric Andersencc8ed391999-10-05 16:24:54 +0000277
278}