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