Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/coreutils/date.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (hide annotations) (download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 9554 byte(s)
-updated to busybox-1.17.1
1 niro 532 /* 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 niro 816 * bugfixes and cleanup by Bernhard Reutner-Fischer
9 niro 532 *
10     * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
11     */
12    
13     /* This 'date' command supports only 2 time setting formats,
14     all the GNU strftime stuff (its in libc, lets use it),
15     setting time using UTC and displaying it, as well as
16     an RFC 2822 compliant date output for shell scripting
17     mail commands */
18    
19     /* Input parsing code is always bulky - used heavy duty libc stuff as
20     much as possible, missed out a lot of bounds checking */
21    
22     /* Default input handling to save surprising some people */
23    
24 niro 984 /* GNU coreutils 6.9 man page:
25     * date [OPTION]... [+FORMAT]
26     * date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
27     * -d, --date=STRING
28     * display time described by STRING, not `now'
29     * -f, --file=DATEFILE
30     * like --date once for each line of DATEFILE
31     * -r, --reference=FILE
32     * display the last modification time of FILE
33     * -R, --rfc-2822
34     * output date and time in RFC 2822 format.
35     * Example: Mon, 07 Aug 2006 12:34:56 -0600
36     * --rfc-3339=TIMESPEC
37     * output date and time in RFC 3339 format.
38     * TIMESPEC='date', 'seconds', or 'ns'
39     * Date and time components are separated by a single space:
40     * 2006-08-07 12:34:56-06:00
41     * -s, --set=STRING
42     * set time described by STRING
43     * -u, --utc, --universal
44     * print or set Coordinated Universal Time
45     *
46     * Busybox:
47     * long options are not supported
48     * -f is not supported
49     * -I seems to roughly match --rfc-3339, but -I has _optional_ param
50     * (thus "-I seconds" doesn't work, only "-Iseconds"),
51     * and does not support -Ins
52     * -D FMT is a bbox extension for _input_ conversion of -d DATE
53     */
54 niro 1123
55     //kbuild:lib-$(CONFIG_DATE) += date.o
56    
57     //config:config DATE
58     //config: bool "date"
59     //config: default y
60     //config: help
61     //config: date is used to set the system date or display the
62     //config: current time in the given format.
63     //config:
64     //config:config FEATURE_DATE_ISOFMT
65     //config: bool "Enable ISO date format output (-I)"
66     //config: default y
67     //config: depends on DATE
68     //config: help
69     //config: Enable option (-I) to output an ISO-8601 compliant
70     //config: date/time string.
71     //config:
72     //config:config FEATURE_DATE_NANO
73     //config: bool "Support %[num]N nanosecond format specifier"
74     //config: default n
75     //config: depends on DATE
76     //config: help
77     //config: Support %[num]N format specifier. Adds ~250 bytes of code.
78     //config:
79     //config:config FEATURE_DATE_COMPAT
80     //config: bool "Support weird 'date MMDDhhmm[[YY]YY][.ss]' format"
81     //config: default y
82     //config: depends on DATE
83     //config: help
84     //config: System time can be set by 'date -s DATE' and simply 'date DATE',
85     //config: but formats of DATE string are different. 'date DATE' accepts
86     //config: a rather weird MMDDhhmm[[YY]YY][.ss] format with completely
87     //config: unnatural placement of year between minutes and seconds.
88     //config: date -s (and other commands like touch -d) use more sensible
89     //config: formats (for one, ISO format YYYY-MM-DD hh:mm:ss.ssssss).
90     //config:
91     //config: With this option off, 'date DATE' is 'date -s DATE' support
92     //config: the same format. With it on, 'date DATE' additionally supports
93     //config: MMDDhhmm[[YY]YY][.ss] format.
94    
95 niro 984 #include "libbb.h"
96 niro 1123 #if ENABLE_FEATURE_DATE_NANO
97     # include <sys/syscall.h>
98     #endif
99 niro 532
100 niro 984 enum {
101     OPT_RFC2822 = (1 << 0), /* R */
102     OPT_SET = (1 << 1), /* s */
103     OPT_UTC = (1 << 2), /* u */
104     OPT_DATE = (1 << 3), /* d */
105     OPT_REFERENCE = (1 << 4), /* r */
106     OPT_TIMESPEC = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
107     OPT_HINT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
108     };
109 niro 532
110     static void maybe_set_utc(int opt)
111     {
112 niro 984 if (opt & OPT_UTC)
113 niro 816 putenv((char*)"TZ=UTC0");
114 niro 532 }
115    
116 niro 984 #if ENABLE_LONG_OPTS
117     static const char date_longopts[] ALIGN1 =
118     "rfc-822\0" No_argument "R"
119     "rfc-2822\0" No_argument "R"
120     "set\0" Required_argument "s"
121     "utc\0" No_argument "u"
122     /* "universal\0" No_argument "u" */
123     "date\0" Required_argument "d"
124     "reference\0" Required_argument "r"
125     ;
126     #endif
127    
128 niro 816 int date_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
129     int date_main(int argc UNUSED_PARAM, char **argv)
130 niro 532 {
131 niro 1123 struct timespec ts;
132 niro 816 struct tm tm_time;
133 niro 984 char buf_fmt_dt2str[64];
134 niro 532 unsigned opt;
135     int ifmt = -1;
136 niro 816 char *date_str;
137     char *fmt_dt2str;
138     char *fmt_str2dt;
139     char *filename;
140     char *isofmt_arg = NULL;
141 niro 532
142 niro 816 opt_complementary = "d--s:s--d"
143 niro 984 IF_FEATURE_DATE_ISOFMT(":R--I:I--R");
144     IF_LONG_OPTS(applet_long_options = date_longopts;)
145 niro 816 opt = getopt32(argv, "Rs:ud:r:"
146 niro 984 IF_FEATURE_DATE_ISOFMT("I::D:"),
147 niro 532 &date_str, &date_str, &filename
148 niro 984 IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt));
149 niro 816 argv += optind;
150 niro 532 maybe_set_utc(opt);
151    
152 niro 984 if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_TIMESPEC)) {
153 niro 816 ifmt = 0; /* default is date */
154     if (isofmt_arg) {
155     static const char isoformats[] ALIGN1 =
156 niro 984 "date\0""hours\0""minutes\0""seconds\0"; /* ns? */
157     ifmt = index_in_substrings(isoformats, isofmt_arg);
158 niro 816 if (ifmt < 0)
159 niro 532 bb_show_usage();
160     }
161     }
162    
163 niro 816 fmt_dt2str = NULL;
164     if (argv[0] && argv[0][0] == '+') {
165 niro 984 fmt_dt2str = &argv[0][1]; /* skip over the '+' */
166 niro 816 argv++;
167     }
168 niro 984 if (!(opt & (OPT_SET | OPT_DATE))) {
169     opt |= OPT_SET;
170 niro 816 date_str = argv[0]; /* can be NULL */
171 niro 984 if (date_str) {
172     #if ENABLE_FEATURE_DATE_COMPAT
173     int len = strspn(date_str, "0123456789");
174     if (date_str[len] == '\0'
175     || (date_str[len] == '.'
176     && isdigit(date_str[len+1])
177     && isdigit(date_str[len+2])
178     && date_str[len+3] == '\0'
179     )
180     ) {
181     /* Dreaded MMDDhhmm[[CC]YY][.ss] format!
182     * It does not match -d or -s format.
183     * Some users actually do use it.
184     */
185     len -= 8;
186     if (len < 0 || len > 4 || (len & 1))
187     bb_error_msg_and_die(bb_msg_invalid_date, date_str);
188     if (len != 0) { /* move YY or CCYY to front */
189     char buf[4];
190     memcpy(buf, date_str + 8, len);
191     memmove(date_str + len, date_str, 8);
192     memcpy(date_str, buf, len);
193     }
194     }
195     #endif
196 niro 816 argv++;
197 niro 984 }
198 niro 532 }
199 niro 816 if (*argv)
200     bb_show_usage();
201 niro 532
202     /* Now we have parsed all the information except the date format
203 niro 984 * which depends on whether the clock is being set or read */
204 niro 532
205 niro 984 if (opt & OPT_REFERENCE) {
206 niro 532 struct stat statbuf;
207     xstat(filename, &statbuf);
208 niro 1123 ts.tv_sec = statbuf.st_mtime;
209     #if ENABLE_FEATURE_DATE_NANO
210     ts.tv_nsec = statbuf.st_mtim.tv_nsec;
211     #endif
212 niro 984 } else {
213 niro 1123 #if ENABLE_FEATURE_DATE_NANO
214     /* libc has incredibly messy way of doing this,
215     * typically requiring -lrt. We just skip all this mess */
216     syscall(__NR_clock_gettime, CLOCK_REALTIME, &ts);
217     #else
218     time(&ts.tv_sec);
219     #endif
220 niro 984 }
221 niro 1123 localtime_r(&ts.tv_sec, &tm_time);
222 niro 816
223     /* If date string is given, update tm_time, and maybe set date */
224 niro 532 if (date_str != NULL) {
225 niro 816 /* Zero out fields - take her back to midnight! */
226 niro 532 tm_time.tm_sec = 0;
227     tm_time.tm_min = 0;
228     tm_time.tm_hour = 0;
229    
230     /* Process any date input to UNIX time since 1 Jan 1970 */
231 niro 984 if (ENABLE_FEATURE_DATE_ISOFMT && (opt & OPT_HINT)) {
232 niro 816 if (strptime(date_str, fmt_str2dt, &tm_time) == NULL)
233     bb_error_msg_and_die(bb_msg_invalid_date, date_str);
234     } else {
235 niro 984 parse_datestr(date_str, &tm_time);
236     }
237 niro 532
238 niro 984 /* Correct any day of week and day of year etc. fields */
239     tm_time.tm_isdst = -1; /* Be sure to recheck dst */
240 niro 1123 ts.tv_sec = validate_tm_time(date_str, &tm_time);
241 niro 816
242 niro 532 maybe_set_utc(opt);
243    
244     /* if setting time, set it */
245 niro 1123 if ((opt & OPT_SET) && stime(&ts.tv_sec) < 0) {
246 niro 984 bb_perror_msg("can't set date");
247 niro 532 }
248     }
249    
250     /* Display output */
251    
252     /* Deal with format string */
253 niro 816 if (fmt_dt2str == NULL) {
254 niro 532 int i;
255 niro 984 fmt_dt2str = buf_fmt_dt2str;
256 niro 532 if (ENABLE_FEATURE_DATE_ISOFMT && ifmt >= 0) {
257 niro 984 /* -I[SPEC]: 0:date 1:hours 2:minutes 3:seconds */
258     strcpy(fmt_dt2str, "%Y-%m-%dT%H:%M:%S");
259     i = 8 + 3 * ifmt;
260     if (ifmt != 0) {
261     /* TODO: if (ifmt==4) i += sprintf(&fmt_dt2str[i], ",%09u", nanoseconds); */
262 niro 816 format_utc:
263     fmt_dt2str[i++] = '%';
264 niro 984 fmt_dt2str[i++] = (opt & OPT_UTC) ? 'Z' : 'z';
265 niro 532 }
266 niro 984 fmt_dt2str[i] = '\0';
267     } else if (opt & OPT_RFC2822) {
268     /* -R. undo busybox.c setlocale */
269 niro 532 if (ENABLE_LOCALE_SUPPORT)
270     setlocale(LC_TIME, "C");
271 niro 816 strcpy(fmt_dt2str, "%a, %d %b %Y %H:%M:%S ");
272 niro 984 i = sizeof("%a, %d %b %Y %H:%M:%S ")-1;
273 niro 532 goto format_utc;
274 niro 984 } else { /* default case */
275 niro 816 fmt_dt2str = (char*)"%a %b %e %H:%M:%S %Z %Y";
276 niro 984 }
277 niro 532 }
278 niro 1123 #if ENABLE_FEATURE_DATE_NANO
279     else {
280     /* User-specified fmt_dt2str */
281     /* Search for and process "%N" */
282     char *p = fmt_dt2str;
283     while ((p = strchr(p, '%')) != NULL) {
284     int n, m;
285     unsigned pres, scale;
286 niro 532
287 niro 1123 p++;
288     if (*p == '%') {
289     p++;
290     continue;
291     }
292     n = strspn(p, "0123456789");
293     if (p[n] != 'N') {
294     p += n;
295     continue;
296     }
297     /* We have "%[nnn]N" */
298     p[-1] = '\0';
299     p[n] = '\0';
300     scale = 1;
301     pres = 9;
302     if (n) {
303     pres = xatoi_u(p);
304     if (pres == 0)
305     pres = 9;
306     m = 9 - pres;
307     while (--m >= 0)
308     scale *= 10;
309     }
310    
311     m = p - fmt_dt2str;
312     p += n + 1;
313     fmt_dt2str = xasprintf("%s%0*u%s", fmt_dt2str, pres, (unsigned)ts.tv_nsec / scale, p);
314     p = fmt_dt2str + m;
315     }
316     }
317     #endif
318    
319 niro 816 #define date_buf bb_common_bufsiz1
320     if (*fmt_dt2str == '\0') {
321 niro 532 /* With no format string, just print a blank line */
322 niro 816 date_buf[0] = '\0';
323 niro 532 } else {
324     /* Handle special conversions */
325 niro 816 if (strncmp(fmt_dt2str, "%f", 2) == 0) {
326     fmt_dt2str = (char*)"%Y.%m.%d-%H:%M:%S";
327 niro 532 }
328     /* Generate output string */
329 niro 816 strftime(date_buf, sizeof(date_buf), fmt_dt2str, &tm_time);
330 niro 532 }
331 niro 816 puts(date_buf);
332 niro 532
333     return EXIT_SUCCESS;
334     }