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