Some corrections, some additions, some embellishments.
[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 "busybox.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 #include <getopt.h>
35
36
37 /* This 'date' command supports only 2 time setting formats, 
38    all the GNU strftime stuff (its in libc, lets use it),
39    setting time using UTC and displaying int, as well as
40    an RFC 822 complient date output for shell scripting
41    mail commands */
42
43 /* Input parsing code is always bulky - used heavy duty libc stuff as
44    much as possible, missed out a lot of bounds checking */
45
46 /* Default input handling to save suprising some people */
47
48 struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
49 {
50         int nr;
51
52         nr = sscanf(t_string, "%2d%2d%2d%2d%d",
53                                 &(tm_time->tm_mon),
54                                 &(tm_time->tm_mday),
55                                 &(tm_time->tm_hour),
56                                 &(tm_time->tm_min), &(tm_time->tm_year));
57
58         if (nr < 4 || nr > 5) {
59                 error_msg_and_die(invalid_date, t_string); 
60         }
61
62         /* correct for century  - minor Y2K problem here? */
63         if (tm_time->tm_year >= 1900)
64                 tm_time->tm_year -= 1900;
65         /* adjust date */
66         tm_time->tm_mon -= 1;
67
68         return (tm_time);
69
70 }
71
72
73 /* The new stuff for LRP */
74
75 struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
76 {
77         struct tm t;
78
79         /* Parse input and assign appropriately to tm_time */
80
81         if (t=*tm_time,sscanf(t_string, "%d:%d:%d",
82                            &t.tm_hour, &t.tm_min, &t.tm_sec) == 3) {
83                                         /* no adjustments needed */
84
85         } else if (t=*tm_time,sscanf(t_string, "%d:%d",
86                                           &t.tm_hour, &t.tm_min) == 2) {
87                                         /* no adjustments needed */
88
89
90         } else if (t=*tm_time,sscanf(t_string, "%d.%d-%d:%d:%d",
91                                           &t.tm_mon,
92                                           &t.tm_mday,
93                                           &t.tm_hour,
94                                           &t.tm_min, &t.tm_sec) == 5) {
95
96                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
97
98         } else if (t=*tm_time,sscanf(t_string, "%d.%d-%d:%d",
99                                           &t.tm_mon,
100                                           &t.tm_mday,
101                                           &t.tm_hour, &t.tm_min) == 4) {
102
103                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
104
105         } else if (t=*tm_time,sscanf(t_string, "%d.%d.%d-%d:%d:%d",
106                                           &t.tm_year,
107                                           &t.tm_mon,
108                                           &t.tm_mday,
109                                           &t.tm_hour,
110                                           &t.tm_min, &t.tm_sec) == 6) {
111
112                 t.tm_year -= 1900;      /* Adjust years */
113                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
114
115         } else if (t=*tm_time,sscanf(t_string, "%d.%d.%d-%d:%d",
116                                           &t.tm_year,
117                                           &t.tm_mon,
118                                           &t.tm_mday,
119                                           &t.tm_hour, &t.tm_min) == 5) {
120                 t.tm_year -= 1900;      /* Adjust years */
121                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
122
123         } else {
124                 error_msg_and_die(invalid_date, t_string); 
125         }
126         *tm_time = t;
127         return (tm_time);
128 }
129
130
131 int date_main(int argc, char **argv)
132 {
133         char *date_str = NULL;
134         char *date_fmt = NULL;
135         char *t_buff;
136         int c;
137         int set_time = 0;
138         int rfc822 = 0;
139         int utc = 0;
140         int use_arg = 0;
141         time_t tm;
142         struct tm tm_time;
143
144         /* Interpret command line args */
145         while ((c = getopt(argc, argv, "Rs:ud:")) != EOF) {
146                 switch (c) {
147                         case 'R':
148                                 rfc822 = 1;
149                                 break;
150                         case 's':
151                                 set_time = 1;
152                                 if ((date_str != NULL) || ((date_str = optarg) == NULL)) {
153                                         usage(date_usage);
154                                 }
155                                 break;
156                         case 'u':
157                                 utc = 1;
158                                 if (putenv("TZ=UTC0") != 0)
159                                         error_msg_and_die(memory_exhausted);
160                                 break;
161                         case 'd':
162                                 use_arg = 1;
163                                 if ((date_str != NULL) || ((date_str = optarg) == NULL))
164                                         usage(date_usage);
165                                 break;
166                         default:
167                                 usage(date_usage);
168                 }
169         }
170
171         if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+'))
172                 date_fmt = &argv[optind][1];   /* Skip over the '+' */
173         else if (date_str == NULL) {
174                 set_time = 1;
175                 date_str = argv[optind];
176         } 
177 #if 0
178         else {
179                 error_msg("date_str='%s'  date_fmt='%s'\n", date_str, date_fmt);
180                 usage(date_usage);
181         }
182 #endif
183
184         /* Now we have parsed all the information except the date format
185            which depends on whether the clock is being set or read */
186
187         time(&tm);
188         memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
189         /* Zero out fields - take her back to midnight! */
190         if (date_str != NULL) {
191                 tm_time.tm_sec = 0;
192                 tm_time.tm_min = 0;
193                 tm_time.tm_hour = 0;
194         }
195
196         /* Process any date input to UNIX time since 1 Jan 1970 */
197         if (date_str != NULL) {
198
199                 if (strchr(date_str, ':') != NULL) {
200                         date_conv_ftime(&tm_time, date_str);
201                 } else {
202                         date_conv_time(&tm_time, date_str);
203                 }
204
205                 /* Correct any day of week and day of year etc fields */
206                 tm = mktime(&tm_time);
207                 if (tm < 0)
208                         error_msg_and_die(invalid_date, date_str); 
209                 if ( utc ) {
210                         if (putenv("TZ=UTC0") != 0)
211                                 error_msg_and_die(memory_exhausted);
212                 }
213
214                 /* if setting time, set it */
215                 if (set_time) {
216                         if (stime(&tm) < 0) {
217                                 perror_msg("cannot set date");
218                         }
219                 }
220         }
221
222         /* Display output */
223
224         /* Deal with format string */
225         if (date_fmt == NULL) {
226                 date_fmt = (rfc822
227                                         ? (utc
228                                            ? "%a, %_d %b %Y %H:%M:%S GMT"
229                                            : "%a, %_d %b %Y %H:%M:%S %z")
230                                         : "%a %b %e %H:%M:%S %Z %Y");
231
232         } else if (*date_fmt == '\0') {
233                 /* Imitate what GNU 'date' does with NO format string! */
234                 printf("\n");
235                 return EXIT_SUCCESS;
236         }
237
238         /* Handle special conversions */
239
240         if (strncmp(date_fmt, "%f", 2) == 0) {
241                 date_fmt = "%Y.%m.%d-%H:%M:%S";
242         }
243
244         /* Print OUTPUT (after ALL that!) */
245         t_buff = xmalloc(201);
246         strftime(t_buff, 200, date_fmt, &tm_time);
247         printf("%s\n", t_buff);
248
249         return EXIT_SUCCESS;
250 }