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