latest and greatest.
[oweals/busybox.git] / date.c
1 #include "internal.h"
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <sys/time.h>
5 #include <unistd.h>
6 #include <time.h>
7 #include <stdio.h>
8 #include <getopt.h>
9
10
11 /* This 'date' command supports only 2 time setting formats, 
12    all the GNU strftime stuff (its in libc, lets use it),
13    setting time using UTC and displaying int, as well as
14    an RFC 822 complient date output for shell scripting
15    mail commands */
16
17 const char      date_usage[] = "date [-uR] [+FORMAT|+%f] [ [-s|-d] MMDDhhmm[[CC]YY]\n"
18 "| [[[[CCYY.]MM.DD-]hh:mm[:ss]]]] ]";
19
20 //static const char date_usage[] = "Usage: date [OPTION]... [+FORMAT]\n"
21 //"or:  date [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n"
22 //"Display the current time in the given FORMAT, or set the system date.\n";
23
24
25 static struct option const long_options[] =
26 {
27   {"date", required_argument, NULL, 'd'},
28   /*  {"rfc-822", no_argument, NULL, 'R'},
29   {"set", required_argument, NULL, 's'},
30   {"uct", no_argument, NULL, 'u'},
31   {"utc", no_argument, NULL, 'u'},
32   {"universal", no_argument, NULL, 'u'}, */
33   {NULL, 0, NULL, 0}
34 };
35   
36
37
38 /* Input parsing code is always bulky - used heavy duty libc stuff as
39    much as possible, missed out a lot of bounds checking */
40
41 /* Default input handling to save suprising some people */
42
43 struct tm *
44 date_conv_time(struct tm *tm_time, const char *t_string) {
45   int nr;
46
47   nr = sscanf(t_string, "%2d%2d%2d%2d%d", 
48              &(tm_time->tm_mon),
49              &(tm_time->tm_mday),
50              &(tm_time->tm_hour),
51              &(tm_time->tm_min),
52              &(tm_time->tm_year));
53
54   if(nr < 4 || nr > 5) {
55     fprintf(stderr, "date: invalid date `%s'\n", t_string);
56     exit(1);
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
72 struct tm *
73 date_conv_ftime(struct tm *tm_time, const char *t_string) {
74   struct tm itm_time, jtm_time, ktm_time, \
75     ltm_time, mtm_time, ntm_time;
76   
77   itm_time = *tm_time;
78   jtm_time = *tm_time;
79   ktm_time = *tm_time;
80   ltm_time = *tm_time;
81   mtm_time = *tm_time;
82   ntm_time = *tm_time;
83
84   /* Parse input and assign appropriately to tm_time */
85   
86   if(sscanf(t_string, "%d:%d:%d",
87             &itm_time.tm_hour, 
88             &itm_time.tm_min, 
89             &itm_time.tm_sec) == 3 ) {
90
91     *tm_time = itm_time;
92     return(tm_time);
93
94   } else if (sscanf(t_string, "%d:%d", 
95                     &jtm_time.tm_hour, 
96                     &jtm_time.tm_min) == 2) {
97
98     *tm_time = jtm_time;
99     return(tm_time);
100
101   } else if (sscanf(t_string, "%d.%d-%d:%d:%d",
102                     &ktm_time.tm_mon, 
103                     &ktm_time.tm_mday, 
104                     &ktm_time.tm_hour,
105                     &ktm_time.tm_min,
106                     &ktm_time.tm_sec) == 5) {
107
108     ktm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
109     *tm_time = ktm_time;
110     return(tm_time);
111
112   } else if (sscanf(t_string, "%d.%d-%d:%d",
113                     &ltm_time.tm_mon, 
114                     &ltm_time.tm_mday, 
115                     &ltm_time.tm_hour,
116                     &ltm_time.tm_min) == 4) {
117
118     ltm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
119     *tm_time = ltm_time;
120     return(tm_time);
121
122   } else if (sscanf(t_string, "%d.%d.%d-%d:%d:%d",
123                     &mtm_time.tm_year,
124                     &mtm_time.tm_mon, 
125                     &mtm_time.tm_mday, 
126                     &mtm_time.tm_hour,
127                     &mtm_time.tm_min,
128                     &mtm_time.tm_sec) == 6) {
129
130     mtm_time.tm_year -= 1900; /* Adjust years */
131     mtm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
132     *tm_time = mtm_time;
133     return(tm_time);
134
135   } else if (sscanf(t_string, "%d.%d.%d-%d:%d",
136                     &ntm_time.tm_year,
137                     &ntm_time.tm_mon, 
138                     &ntm_time.tm_mday, 
139                     &ntm_time.tm_hour,
140                     &ntm_time.tm_min) == 5) { 
141     ntm_time.tm_year -= 1900; /* Adjust years */
142     ntm_time.tm_mon -= 1; /* Adjust dates from 1-12 to 0-11 */
143     *tm_time = ntm_time;
144     return(tm_time);
145
146   }
147
148   fprintf(stderr, "date: invalid date `%s'\n", t_string);
149
150   exit(1);
151
152 }
153
154
155 void
156 date_err(void) {
157   fprintf(stderr, "date: only one date argument can be given at a time.\n");
158   exit(1);
159 }
160
161 int
162 date_main(int argc, char * * argv)
163 {
164   char *date_str = NULL;
165   char *date_fmt = NULL;
166   char *t_buff;
167   int set_time = 0;
168   int rfc822 = 0;
169   int utc = 0;
170   int use_arg = 0;
171   int n_args;
172   time_t tm; 
173   struct tm tm_time;
174   char optc;
175   
176   /* Interpret command line args */
177         
178
179   while ((optc = getopt_long (argc, argv, "d:Rs:u", long_options, NULL))
180          != EOF) {
181     switch (optc) {
182     case 0:
183       break;    
184
185     case 'R':
186       rfc822 = 1;
187       break;
188
189     case 's':
190       set_time = 1;
191       if(date_str != NULL) date_err();
192       date_str = optarg;
193       break;
194
195     case 'u':
196       utc = 1;
197       if (putenv ("TZ=UTC0") != 0) {
198         fprintf(stderr,"date: memory exhausted\n");
199         return(1);
200       }
201 #if LOCALTIME_CACHE
202       tzset ();
203 #endif                                                                                break;
204
205     case 'd':
206       use_arg = 1;
207       if(date_str != NULL) date_err();
208       date_str = optarg;
209       break;
210
211     default:
212       fprintf(stderr, "Usage: %s", date_usage);
213       break;
214     }
215   }
216
217
218   n_args = argc - optind; 
219
220   while (n_args--){
221     switch(argv[optind][0]) {
222       case '+':
223       /* Date format strings */
224       if(date_fmt != NULL) {
225         fprintf(stderr, "date: only one date format can be given.\n");
226         return(1);
227       }
228       date_fmt = &argv[optind][1];
229       break;
230
231       case '\0':
232       break;
233
234       default:
235       /* Anything left over must be a date string to set the time */
236       set_time = 1;
237       if(date_str != NULL) date_err();
238       date_str = argv[optind];
239       break;
240     }
241     optind++;
242   }
243
244
245   /* Now we have parsed all the information except the date format
246    which depends on whether the clock is being set or read */
247
248   time(&tm);
249   memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
250   /* Zero out fields - take her back to midnight!*/
251   if(date_str != NULL) {
252     tm_time.tm_sec = 0;
253     tm_time.tm_min = 0;
254     tm_time.tm_hour = 0;
255   }
256
257   /* Process any date input to UNIX time since 1 Jan 1970 */
258   if(date_str != NULL) {
259
260     if(strchr(date_str, ':') != NULL) {
261       date_conv_ftime(&tm_time, date_str);
262     } else {
263       date_conv_time(&tm_time, date_str);
264     }
265
266     /* Correct any day of week and day of year etc fields */
267     tm = mktime(&tm_time);
268     if (tm < 0 ) {
269       fprintf(stderr, "date: invalid date `%s'\n", date_str);
270       exit(1);
271     }
272
273     /* if setting time, set it */
274     if(set_time) {
275       if( stime(&tm) < 0) {
276         fprintf(stderr, "date: can't set date.\n");
277         exit(1);
278       }
279     }
280   }
281   
282   /* Display output */
283
284   /* Deal with format string */
285   if(date_fmt == NULL) {
286     date_fmt = (rfc822
287                 ? (utc
288                    ? "%a, %_d %b %Y %H:%M:%S GMT"
289                    : "%a, %_d %b %Y %H:%M:%S %z")
290                 : "%a %b %e %H:%M:%S %Z %Y");
291
292   } else if ( *date_fmt == '\0' ) {
293     /* Imitate what GNU 'date' does with NO format string! */
294     printf ("\n");
295     return(0);
296   }
297
298   /* Handle special conversions */
299
300   if( strncmp( date_fmt, "%f", 2) == 0 ) {
301     date_fmt = "%Y.%m.%d-%H:%M:%S";
302   }
303
304   /* Print OUTPUT (after ALL that!) */
305   t_buff = malloc(201);
306   strftime(t_buff, 200, date_fmt, &tm_time);
307   printf("%s\n", t_buff);
308
309   return(0);
310
311 }