Oops. Forgot the usleep.c file.
[oweals/busybox.git] / date.c
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 "internal.h"
24 #define BB_DECLARE_EXTERN
25 #define bb_need_invalid_date
26 #define bb_need_memory_exhausted
27 #include "messages.c"
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <stdio.h>
34
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
42 static const char date_usage[] = "date [OPTION]... [+FORMAT]\n"
43         "  or:  date [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n\n"
44         "Display the current time in the given FORMAT, or set the system date.\n"
45         "\nOptions:\n\t-R\t\toutput RFC-822 compliant date string\n"
46         "\t-s\t\tset time described by STRING\n"
47
48         "\t-u\t\tprint or set Coordinated Universal Time\n";
49
50
51 /* Input parsing code is always bulky - used heavy duty libc stuff as
52    much as possible, missed out a lot of bounds checking */
53
54 /* Default input handling to save suprising some people */
55
56 struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
57 {
58         int nr;
59
60         nr = sscanf(t_string, "%2d%2d%2d%2d%d",
61                                 &(tm_time->tm_mon),
62                                 &(tm_time->tm_mday),
63                                 &(tm_time->tm_hour),
64                                 &(tm_time->tm_min), &(tm_time->tm_year));
65
66         if (nr < 4 || nr > 5) {
67                 fatalError(invalid_date, "date", t_string); 
68         }
69
70         /* correct for century  - minor Y2K problem here? */
71         if (tm_time->tm_year >= 1900)
72                 tm_time->tm_year -= 1900;
73         /* adjust date */
74         tm_time->tm_mon -= 1;
75
76         return (tm_time);
77
78 }
79
80
81 /* The new stuff for LRP */
82
83 struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
84 {
85         struct tm itm_time, jtm_time, ktm_time, ltm_time, mtm_time, ntm_time;
86
87         itm_time = *tm_time;
88         jtm_time = *tm_time;
89         ktm_time = *tm_time;
90         ltm_time = *tm_time;
91         mtm_time = *tm_time;
92         ntm_time = *tm_time;
93
94         /* Parse input and assign appropriately to tm_time */
95
96         if (sscanf(t_string, "%d:%d:%d",
97                            &itm_time.tm_hour, &itm_time.tm_min, &itm_time.tm_sec) == 3) {
98
99                 *tm_time = itm_time;
100                 return (tm_time);
101
102         } else if (sscanf(t_string, "%d:%d",
103                                           &jtm_time.tm_hour, &jtm_time.tm_min) == 2) {
104
105                 *tm_time = jtm_time;
106                 return (tm_time);
107
108         } else if (sscanf(t_string, "%d.%d-%d:%d:%d",
109                                           &ktm_time.tm_mon,
110                                           &ktm_time.tm_mday,
111                                           &ktm_time.tm_hour,
112                                           &ktm_time.tm_min, &ktm_time.tm_sec) == 5) {
113
114                 ktm_time.tm_mon -= 1;   /* Adjust dates from 1-12 to 0-11 */
115                 *tm_time = ktm_time;
116                 return (tm_time);
117
118         } else if (sscanf(t_string, "%d.%d-%d:%d",
119                                           &ltm_time.tm_mon,
120                                           &ltm_time.tm_mday,
121                                           &ltm_time.tm_hour, &ltm_time.tm_min) == 4) {
122
123                 ltm_time.tm_mon -= 1;   /* Adjust dates from 1-12 to 0-11 */
124                 *tm_time = ltm_time;
125                 return (tm_time);
126
127         } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d",
128                                           &mtm_time.tm_year,
129                                           &mtm_time.tm_mon,
130                                           &mtm_time.tm_mday,
131                                           &mtm_time.tm_hour,
132                                           &mtm_time.tm_min, &mtm_time.tm_sec) == 6) {
133
134                 mtm_time.tm_year -= 1900;       /* Adjust years */
135                 mtm_time.tm_mon -= 1;   /* Adjust dates from 1-12 to 0-11 */
136                 *tm_time = mtm_time;
137                 return (tm_time);
138
139         } else if (sscanf(t_string, "%d.%d.%d-%d:%d",
140                                           &ntm_time.tm_year,
141                                           &ntm_time.tm_mon,
142                                           &ntm_time.tm_mday,
143                                           &ntm_time.tm_hour, &ntm_time.tm_min) == 5) {
144                 ntm_time.tm_year -= 1900;       /* Adjust years */
145                 ntm_time.tm_mon -= 1;   /* Adjust dates from 1-12 to 0-11 */
146                 *tm_time = ntm_time;
147                 return (tm_time);
148
149         }
150
151         fatalError(invalid_date, "date", t_string); 
152 }
153
154
155 int date_main(int argc, char **argv)
156 {
157         char *date_str = NULL;
158         char *date_fmt = NULL;
159         char *t_buff;
160         int i;
161         int set_time = 0;
162         int rfc822 = 0;
163         int utc = 0;
164         int use_arg = 0;
165         time_t tm;
166         struct tm tm_time;
167
168         /* Interpret command line args */
169         i = --argc;
170         argv++;
171         while (i > 0 && **argv) {
172                 if (**argv == '-') {
173                         while (i > 0 && *++(*argv))
174                                 switch (**argv) {
175                                 case 'R':
176                                         rfc822 = 1;
177                                         break;
178                                 case 's':
179                                         set_time = 1;
180                                         if (date_str != NULL)
181                                                 usage(date_usage);
182                                         date_str = optarg;
183                                         break;
184                                 case 'u':
185                                         utc = 1;
186                                         if (putenv("TZ=UTC0") != 0) 
187                                                 fatalError(memory_exhausted, "date");
188                                         /* Look ma, no break.  Don't fix it either. */
189                                 case 'd':
190                                         use_arg = 1;
191                                         if (date_str != NULL)
192                                                 usage(date_usage);
193                                         date_str = optarg;
194                                         break;
195                                 case '-':
196                                         usage(date_usage);
197                                 }
198                 } else {
199                         if ((date_fmt == NULL) && (**argv == '+'))
200                                 date_fmt = *argv + 1;   /* Skip over the '+' */
201                         else if (date_str == NULL) {
202                                 set_time = 1;
203                                 date_str = *argv;
204                         } else {
205                                 usage(date_usage);
206                         }
207                 }
208                 i--;
209                 argv++;
210         }
211
212
213         /* Now we have parsed all the information except the date format
214            which depends on whether the clock is being set or read */
215
216         time(&tm);
217         memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
218         /* Zero out fields - take her back to midnight! */
219         if (date_str != NULL) {
220                 tm_time.tm_sec = 0;
221                 tm_time.tm_min = 0;
222                 tm_time.tm_hour = 0;
223         }
224
225         /* Process any date input to UNIX time since 1 Jan 1970 */
226         if (date_str != NULL) {
227
228                 if (strchr(date_str, ':') != NULL) {
229                         date_conv_ftime(&tm_time, date_str);
230                 } else {
231                         date_conv_time(&tm_time, date_str);
232                 }
233
234                 /* Correct any day of week and day of year etc fields */
235                 tm = mktime(&tm_time);
236                 if (tm < 0)
237                         fatalError(invalid_date, "date", date_str); 
238
239                 /* if setting time, set it */
240                 if (set_time) {
241                         if (stime(&tm) < 0) {
242                                 fatalError("date: can't set date.\n");
243                         }
244                 }
245         }
246
247         /* Display output */
248
249         /* Deal with format string */
250         if (date_fmt == NULL) {
251                 date_fmt = (rfc822
252                                         ? (utc
253                                            ? "%a, %_d %b %Y %H:%M:%S GMT"
254                                            : "%a, %_d %b %Y %H:%M:%S %z")
255                                         : "%a %b %e %H:%M:%S %Z %Y");
256
257         } else if (*date_fmt == '\0') {
258                 /* Imitate what GNU 'date' does with NO format string! */
259                 printf("\n");
260                 exit(TRUE);
261         }
262
263         /* Handle special conversions */
264
265         if (strncmp(date_fmt, "%f", 2) == 0) {
266                 date_fmt = "%Y.%m.%d-%H:%M:%S";
267         }
268
269         /* Print OUTPUT (after ALL that!) */
270         t_buff = xmalloc(201);
271         strftime(t_buff, 200, date_fmt, &tm_time);
272         printf("%s\n", t_buff);
273
274         exit(TRUE);
275
276 }