Consolidate #include <sys/time.h> so libbb.h does it.
[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  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
10 */
11
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <unistd.h>
15 #include <time.h>
16 #include <stdio.h>
17 #include <string.h>
18 #include "busybox.h"
19
20 /* This 'date' command supports only 2 time setting formats,
21    all the GNU strftime stuff (its in libc, lets use it),
22    setting time using UTC and displaying int, as well as
23    an RFC 822 complient date output for shell scripting
24    mail commands */
25
26 /* Input parsing code is always bulky - used heavy duty libc stuff as
27    much as possible, missed out a lot of bounds checking */
28
29 /* Default input handling to save surprising some people */
30
31 static struct tm *date_conv_time(struct tm *tm_time, const char *t_string)
32 {
33         int nr;
34         char *cp;
35
36         nr = sscanf(t_string, "%2d%2d%2d%2d%d", &(tm_time->tm_mon),
37                                 &(tm_time->tm_mday), &(tm_time->tm_hour), &(tm_time->tm_min),
38                                 &(tm_time->tm_year));
39
40         if (nr < 4 || nr > 5) {
41                 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
42         }
43
44         cp = strchr(t_string, '.');
45         if (cp) {
46                 nr = sscanf(cp + 1, "%2d", &(tm_time->tm_sec));
47                 if (nr != 1) {
48                         bb_error_msg_and_die(bb_msg_invalid_date, t_string);
49                 }
50         }
51
52         /* correct for century  - minor Y2K problem here? */
53         if (tm_time->tm_year >= 1900) {
54                 tm_time->tm_year -= 1900;
55         }
56         /* adjust date */
57         tm_time->tm_mon -= 1;
58
59         return (tm_time);
60
61 }
62
63
64 /* The new stuff for LRP */
65
66 static struct tm *date_conv_ftime(struct tm *tm_time, const char *t_string)
67 {
68         struct tm t;
69
70         /* Parse input and assign appropriately to tm_time */
71
72         if (t = *tm_time, sscanf(t_string, "%d:%d:%d", &t.tm_hour, &t.tm_min,
73                                                  &t.tm_sec) == 3) {
74                 /* no adjustments needed */
75         } else if (t = *tm_time, sscanf(t_string, "%d:%d", &t.tm_hour,
76                                                                 &t.tm_min) == 2) {
77                 /* no adjustments needed */
78         } else if (t = *tm_time, sscanf(t_string, "%d.%d-%d:%d:%d", &t.tm_mon,
79                                                 &t.tm_mday, &t.tm_hour,
80                                                 &t.tm_min, &t.tm_sec) == 5) {
81                 /* Adjust dates from 1-12 to 0-11 */
82                 t.tm_mon -= 1;
83         } else if (t = *tm_time, sscanf(t_string, "%d.%d-%d:%d", &t.tm_mon,
84                                                 &t.tm_mday,
85                                                 &t.tm_hour, &t.tm_min) == 4) {
86                 /* Adjust dates from 1-12 to 0-11 */
87                 t.tm_mon -= 1;
88         } else if (t = *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d:%d", &t.tm_year,
89                                                 &t.tm_mon, &t.tm_mday,
90                                                 &t.tm_hour, &t.tm_min,
91                                                         &t.tm_sec) == 6) {
92                 t.tm_year -= 1900;      /* Adjust years */
93                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
94         } else if (t = *tm_time, sscanf(t_string, "%d.%d.%d-%d:%d", &t.tm_year,
95                                                 &t.tm_mon, &t.tm_mday,
96                                                 &t.tm_hour, &t.tm_min) == 5) {
97                 t.tm_year -= 1900;      /* Adjust years */
98                 t.tm_mon -= 1;  /* Adjust dates from 1-12 to 0-11 */
99         } else {
100                 bb_error_msg_and_die(bb_msg_invalid_date, t_string);
101         }
102         *tm_time = t;
103         return (tm_time);
104 }
105
106 #define DATE_OPT_RFC2822        0x01
107 #define DATE_OPT_SET            0x02
108 #define DATE_OPT_UTC            0x04
109 #define DATE_OPT_DATE           0x08
110 #define DATE_OPT_REFERENCE      0x10
111 #define DATE_OPT_TIMESPEC       0x20
112 #define DATE_OPT_HINT           0x40
113
114 int date_main(int argc, char **argv)
115 {
116         char *date_str = NULL;
117         char *date_fmt = NULL;
118         int set_time;
119         int utc;
120         time_t tm;
121         unsigned long opt;
122         struct tm tm_time;
123         char *filename = NULL;
124
125         int ifmt = 0;
126         char *isofmt_arg;
127         char *hintfmt_arg;
128
129         bb_opt_complementally = "?:d--s:s--d";
130         opt = bb_getopt_ulflags(argc, argv, "Rs:ud:r:"
131                                         USE_FEATURE_DATE_ISOFMT("I::D:"),
132                                         &date_str, &date_str, &filename
133                                         USE_FEATURE_DATE_ISOFMT(, &isofmt_arg, &hintfmt_arg));
134         set_time = opt & DATE_OPT_SET;
135         utc = opt & DATE_OPT_UTC;
136         if (utc && putenv("TZ=UTC0") != 0) {
137                 bb_error_msg_and_die(bb_msg_memory_exhausted);
138         }
139
140         if(ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_TIMESPEC)) {
141                 if (!isofmt_arg) {
142                         ifmt = 1;
143                 } else {
144                         char *isoformats[]={"date","hours","minutes","seconds"};
145                         for(ifmt = 4; ifmt;)
146                                 if(!strcmp(isofmt_arg,isoformats[--ifmt]))
147                                         break;
148                 }
149                 if (!ifmt) {
150                         bb_show_usage();
151                 }
152         }
153
154         /* XXX, date_fmt == NULL from this always */
155         if ((date_fmt == NULL) && (optind < argc) && (argv[optind][0] == '+')) {
156                 date_fmt = &argv[optind][1];    /* Skip over the '+' */
157         } else if (date_str == NULL) {
158                 set_time = 1;
159                 date_str = argv[optind];
160         }
161
162         /* Now we have parsed all the information except the date format
163            which depends on whether the clock is being set or read */
164
165         if(filename) {
166                 struct stat statbuf;
167                 xstat(filename,&statbuf);
168                 tm=statbuf.st_mtime;
169         } else time(&tm);
170         memcpy(&tm_time, localtime(&tm), sizeof(tm_time));
171         /* Zero out fields - take her back to midnight! */
172         if (date_str != NULL) {
173                 tm_time.tm_sec = 0;
174                 tm_time.tm_min = 0;
175                 tm_time.tm_hour = 0;
176
177                 /* Process any date input to UNIX time since 1 Jan 1970 */
178                 if (ENABLE_FEATURE_DATE_ISOFMT && (opt & DATE_OPT_HINT)) {
179                         strptime(date_str, hintfmt_arg, &tm_time);
180                 } else if (strchr(date_str, ':') != NULL) {
181                         date_conv_ftime(&tm_time, date_str);
182                 } else {
183                         date_conv_time(&tm_time, date_str);
184                 }
185
186                 /* Correct any day of week and day of year etc. fields */
187                 tm_time.tm_isdst = -1;  /* Be sure to recheck dst. */
188                 tm = mktime(&tm_time);
189                 if (tm < 0) {
190                         bb_error_msg_and_die(bb_msg_invalid_date, date_str);
191                 }
192                 if (utc && putenv("TZ=UTC0") != 0) {
193                         bb_error_msg_and_die(bb_msg_memory_exhausted);
194                 }
195
196                 /* if setting time, set it */
197                 if (set_time && stime(&tm) < 0) {
198                         bb_perror_msg("cannot set date");
199                 }
200         }
201
202         /* Display output */
203
204         /* Deal with format string */
205         if (date_fmt == NULL) {
206                 /* Start with the default case */
207                 
208                 date_fmt = (opt & DATE_OPT_RFC2822 ?
209                                         (utc ? "%a, %d %b %Y %H:%M:%S GMT" :
210                                         "%a, %d %b %Y %H:%M:%S %z") :
211                                         "%a %b %e %H:%M:%S %Z %Y");
212
213                 if (ENABLE_FEATURE_DATE_ISOFMT) {
214                         if (ifmt == 4)
215                                 date_fmt = utc ? "%Y-%m-%dT%H:%M:%SZ" : "%Y-%m-%dT%H:%M:%S%z";
216                         else if (ifmt == 3)
217                                 date_fmt = utc ? "%Y-%m-%dT%H:%MZ" : "%Y-%m-%dT%H:%M%z";
218                         else if (ifmt == 2) 
219                                 date_fmt = utc ? "%Y-%m-%dT%HZ" : "%Y-%m-%dT%H%z";
220                         else if (ifmt == 1)
221                                 date_fmt = "%Y-%m-%d";
222                 }
223         }
224         
225         if (*date_fmt == '\0') {
226
227                 /* With no format string, just print a blank line */
228                 
229                 *bb_common_bufsiz1=0;
230         } else {
231
232                 /* Handle special conversions */
233
234                 if (strncmp(date_fmt, "%f", 2) == 0) {
235                         date_fmt = "%Y.%m.%d-%H:%M:%S";
236                 }
237
238                 /* Generate output string */
239                 strftime(bb_common_bufsiz1, 200, date_fmt, &tm_time);
240         }
241         puts(bb_common_bufsiz1);
242
243         return EXIT_SUCCESS;
244 }