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