This incorporates Posix math support into ash. The Posix math support
[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 <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         /* Interpret command line args */
142         while ((c = getopt(argc, argv, "Rs:ud:")) != EOF) {
143                 switch (c) {
144                         case 'R':
145                                 rfc822 = 1;
146                                 break;
147                         case 's':
148                                 set_time = 1;
149                                 if ((date_str != NULL) || ((date_str = optarg) == NULL)) {
150                                         show_usage();
151                                 }
152                                 break;
153                         case 'u':
154                                 utc = 1;
155                                 if (putenv("TZ=UTC0") != 0)
156                                         error_msg_and_die(memory_exhausted);
157                                 break;
158                         case 'd':
159                                 use_arg = 1;
160                                 if ((date_str != NULL) || ((date_str = optarg) == NULL))
161                                         show_usage();
162                                 break;
163                         default:
164                                 show_usage();
165                 }
166         }
167
168         if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+'))
169                 date_fmt = &argv[optind][1];   /* Skip over the '+' */
170         else if (date_str == NULL) {
171                 set_time = 1;
172                 date_str = argv[optind];
173         } 
174 #if 0
175         else {
176                 error_msg("date_str='%s'  date_fmt='%s'\n", date_str, date_fmt);
177                 show_usage();
178         }
179 #endif
180
181         /* Now we have parsed all the information except the date format
182            which depends on whether the clock is being set or read */
183
184         time(&tm);
185         memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
186         /* Zero out fields - take her back to midnight! */
187         if (date_str != NULL) {
188                 tm_time.tm_sec = 0;
189                 tm_time.tm_min = 0;
190                 tm_time.tm_hour = 0;
191         }
192
193         /* Process any date input to UNIX time since 1 Jan 1970 */
194         if (date_str != NULL) {
195
196                 if (strchr(date_str, ':') != NULL) {
197                         date_conv_ftime(&tm_time, date_str);
198                 } else {
199                         date_conv_time(&tm_time, date_str);
200                 }
201
202                 /* Correct any day of week and day of year etc. fields */
203                 tm = mktime(&tm_time);
204                 if (tm < 0)
205                         error_msg_and_die(invalid_date, date_str); 
206                 if ( utc ) {
207                         if (putenv("TZ=UTC0") != 0)
208                                 error_msg_and_die(memory_exhausted);
209                 }
210
211                 /* if setting time, set it */
212                 if (set_time) {
213                         if (stime(&tm) < 0) {
214                                 perror_msg("cannot set date");
215                         }
216                 }
217         }
218
219         /* Display output */
220
221         /* Deal with format string */
222         if (date_fmt == NULL) {
223                 date_fmt = (rfc822
224                                         ? (utc
225                                            ? "%a, %_d %b %Y %H:%M:%S GMT"
226                                            : "%a, %_d %b %Y %H:%M:%S %z")
227                                         : "%a %b %e %H:%M:%S %Z %Y");
228
229         } else if (*date_fmt == '\0') {
230                 /* Imitate what GNU 'date' does with NO format string! */
231                 printf("\n");
232                 return EXIT_SUCCESS;
233         }
234
235         /* Handle special conversions */
236
237         if (strncmp(date_fmt, "%f", 2) == 0) {
238                 date_fmt = "%Y.%m.%d-%H:%M:%S";
239         }
240
241         /* Print OUTPUT (after ALL that!) */
242         t_buff = xmalloc(201);
243         strftime(t_buff, 200, date_fmt, &tm_time);
244         puts(t_buff);
245
246         return EXIT_SUCCESS;
247 }