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