744900e7773c47d61d97a14c35ed8c828287ab0d
[platform/upstream/coreutils.git] / src / date.c
1 /* date - print or set the system date and time
2    Copyright (C) 89, 90, 91, 92, 93, 94, 1995 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software
16    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18    David MacKenzie <djm@gnu.ai.mit.edu> */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
24
25 #include "version.h"
26 #include "system.h"
27 #include "getline.h"
28 #include "error.h"
29
30 #ifdef TM_IN_SYS_TIME
31 #include <sys/time.h>
32 #else
33 #include <time.h>
34 #endif
35
36 #ifndef STDC_HEADERS
37 size_t strftime ();
38 time_t time ();
39 #endif
40
41 int putenv ();
42 int stime ();
43
44 char *xrealloc ();
45 time_t get_date ();
46 time_t posixtime ();
47
48 static void show_date __P ((const char *format, time_t when));
49 static void usage __P ((int status));
50
51 /* The name this program was run with, for error messages. */
52 char *program_name;
53
54 /* If nonzero, display usage information and exit.  */
55 static int show_help;
56
57 /* If nonzero, print the version on standard output and exit.  */
58 static int show_version;
59
60 /* If non-zero, display time in RFC-822 format for mail or news. */
61 static int rfc_format = 0;
62
63 /* If nonzero, print or set Coordinated Universal Time.  */
64 static int universal_time = 0;
65
66 static struct option const long_options[] =
67 {
68   {"date", required_argument, NULL, 'd'},
69   {"file", required_argument, NULL, 'f'},
70   {"help", no_argument, &show_help, 1},
71   {"reference", required_argument, NULL, 'r'},
72   {"rfc-822", no_argument, NULL, 'R'},
73   {"set", required_argument, NULL, 's'},
74   {"uct", no_argument, NULL, 'u'},
75   {"utc", no_argument, NULL, 'u'},
76   {"universal", no_argument, NULL, 'u'},
77   {"version", no_argument, &show_version, 1},
78   {NULL, 0, NULL, 0}
79 };
80
81 /* Parse each line in INPUT_FILENAME as with --date and display the
82    each resulting time and date.  If the file cannot be opened, tell why
83    then exit.  Issue a diagnostic for any lines that cannot be parsed.
84    If any line cannot be parsed, return nonzero;  otherwise return zero.  */
85
86 static int
87 batch_convert (const char *input_filename, const char *format)
88 {
89   int status;
90   FILE *in_stream;
91   char *line;
92   int line_length;
93   size_t buflen;
94   time_t when;
95
96   if (strcmp (input_filename, "-") == 0)
97     {
98       input_filename = _("standard input");
99       in_stream = stdin;
100     }
101   else
102     {
103       in_stream = fopen (input_filename, "r");
104       if (in_stream == NULL)
105         {
106           error (1, errno, "`%s'", input_filename);
107         }
108     }
109
110   line = NULL;
111   buflen = 0;
112
113   status = 0;
114   while (1)
115     {
116       line_length = getline (&line, &buflen, in_stream);
117       if (line_length < 0)
118         {
119           /* FIXME: detect/handle error here.  */
120           break;
121         }
122       when = get_date (line, NULL);
123       if (when == -1)
124         {
125           if (line[line_length - 1] == '\n')
126             line[line_length - 1] = '\0';
127           error (0, 0, _("invalid date `%s'"), line);
128           status = 1;
129         }
130       else
131         {
132           show_date (format, when);
133         }
134     }
135
136   if (fclose (in_stream) == EOF)
137     error (2, errno, input_filename);
138
139   if (line != NULL)
140     free (line);
141
142   return status;
143 }
144
145 void
146 main (int argc, char **argv)
147 {
148   int optc;
149   const char *datestr = NULL;
150   const char *set_datestr = NULL;
151   time_t when;
152   int set_date = 0;
153   char *format;
154   char *batch_file = NULL;
155   char *reference = NULL;
156   struct stat refstats;
157   int n_args;
158   int status;
159   int option_specified_date;
160
161   program_name = argv[0];
162
163   while ((optc = getopt_long (argc, argv, "d:f:r:Rs:u", long_options, NULL))
164          != EOF)
165     switch (optc)
166       {
167       case 0:
168         break;
169       case 'd':
170         datestr = optarg;
171         break;
172       case 'f':
173         batch_file = optarg;
174         break;
175       case 'r':
176         reference = optarg;
177         break;
178       case 'R':
179         rfc_format = 1;
180         break;
181       case 's':
182         set_datestr = optarg;
183         set_date = 1;
184         break;
185       case 'u':
186         universal_time = 1;
187         if (putenv ("TZ=UTC0") != 0)
188           error (1, 0, "memory exhausted");
189 #if LOCALTIME_CACHE
190         tzset ();
191 #endif
192         break;
193       default:
194         usage (1);
195       }
196
197   if (show_version)
198     {
199       printf ("date - %s\n", version_string);
200       exit (0);
201     }
202
203   if (show_help)
204     usage (0);
205
206   n_args = argc - optind;
207
208   option_specified_date = ((datestr ? 1 : 0)
209                            + (batch_file ? 1 : 0)
210                            + (reference ? 1 : 0));
211
212   if (option_specified_date > 1)
213     {
214       error (0, 0,
215         _("the options to specify dates for printing are mutually exclusive"));
216       usage (1);
217     }
218
219   if (set_date && option_specified_date)
220     {
221       error (0, 0,
222           _("the options to print and set the time may not be used together"));
223       usage (1);
224     }
225
226   if (n_args > 1)
227     {
228       error (0, 0, _("too many non-option arguments"));
229       usage (1);
230     }
231
232   if ((set_date || option_specified_date)
233       && n_args == 1 && argv[optind][0] != '+')
234     {
235       error (0, 0, _("\
236 when using an option to specify date(s), any\n\
237 non-option argument must be a format string beginning with `+'"));
238       usage (1);
239     }
240
241   if (set_date)
242     datestr = set_datestr;
243
244   if (batch_file != NULL)
245     {
246       status = batch_convert (batch_file,
247                               (n_args == 1 ? argv[optind] + 1 : NULL));
248     }
249   else
250     {
251       status = 0;
252
253       if (!option_specified_date && !set_date)
254         {
255           if (n_args == 1 && argv[optind][0] != '+')
256             {
257               /* Prepare to set system clock to the specified date/time
258                  given in the POSIX-format.  */
259               set_date = 1;
260               datestr = argv[optind];
261               when = posixtime (datestr);
262               format = NULL;
263             }
264           else
265             {
266               /* Prepare to print the current date/time.  */
267               datestr = _("undefined");
268               time (&when);
269               format = (n_args == 1 ? argv[optind] + 1 : NULL);
270             }
271         }
272       else
273         {
274           /* (option_specified_date || set_date) */
275           if (reference != NULL)
276             {
277               if (stat (reference, &refstats))
278                 error (1, errno, "%s", reference);
279               when = refstats.st_mtime;
280             }
281           else
282             when = get_date (datestr, NULL);
283           format = (n_args == 1 ? argv[optind] + 1 : NULL);
284         }
285
286       if (when == -1)
287         error (1, 0, _("invalid date `%s'"), datestr);
288
289       if (set_date)
290         {
291           /* Set the system clock to the specified date, then regardless of
292              the success of that operation, format and print that date.  */
293           if (stime (&when) == -1)
294             error (0, errno, _("cannot set date"));
295         }
296
297       show_date (format, when);
298     }
299
300   if (fclose (stdout) == EOF)
301     error (2, errno, _("write error"));
302
303   exit (status);
304 }
305
306 /* Display the date and/or time in WHEN according to the format specified
307    in FORMAT, followed by a newline.  If FORMAT is NULL, use the
308    standard output format (ctime style but with a timezone inserted). */
309
310 static void
311 show_date (const char *format, time_t when)
312 {
313   struct tm *tm;
314   char *out = NULL;
315   size_t out_length = 0;
316
317   tm = localtime (&when);
318
319   if (format == NULL)
320     {
321       /* Print the date in the default format.  Vanilla ANSI C strftime
322          doesn't support %e, but POSIX requires it.  If you don't use
323          a GNU strftime, make sure yours supports %e.
324          If you are not using GNU strftime, you want to change %z
325          in the RFC format to %Z; this gives, however, an invalid
326          RFC time format outside the continental United States and GMT. */
327
328       format = (rfc_format
329                 ? (universal_time
330                    ? "%a, %_d %b %Y %H:%M:%S GMT"
331                    : "%a, %_d %b %Y %H:%M:%S %z")
332                 : "%a %b %e %H:%M:%S %Z %Y");
333     }
334   else if (*format == '\0')
335     {
336       printf ("\n");
337       return;
338     }
339
340   do
341     {
342       out_length += 200;
343       out = (char *) xrealloc (out, out_length);
344     }
345   while (strftime (out, out_length, format, tm) == 0);
346
347   printf ("%s\n", out);
348   free (out);
349 }
350
351 static void
352 usage (int status)
353 {
354   if (status != 0)
355     fprintf (stderr, _("Try `%s --help' for more information.\n"),
356              program_name);
357   else
358     {
359       printf (_("\
360 Usage: %s [OPTION]... [+FORMAT]\n\
361   or:  %s [OPTION] [MMDDhhmm[[CC]YY][.ss]]\n\
362 "),
363               program_name, program_name);
364       printf (_("\
365 Display the current time in the given FORMAT, or set the system date.\n\
366 \n\
367   -d, --date=STRING        display time described by STRING, not `now'\n\
368   -f, --file=DATEFILE      like --date once for each line of DATEFILE\n\
369   -r, --reference=FILE     display the last modification time of FILE\n\
370   -R, --rfc-822            output RFC-822 compliant date string\n\
371   -s, --set=STRING         set time described by STRING\n\
372   -u, --utc, --universal   print or set Coordinated Universal Time\n\
373       --help               display this help and exit\n\
374       --version            output version information and exit\n\
375 "));
376       printf (_("\
377 \n\
378 FORMAT controls the output.  The only valid option for the second form\n\
379 specifies Coordinated Universal Time.  Interpreted sequences are:\n\
380 \n\
381   %%%%   a literal %%\n\
382   %%a   locale's abbreviated weekday name (Sun..Sat)\n\
383   %%A   locale's full weekday name, variable length (Sunday..Saturday)\n\
384   %%b   locale's abbreviated month name (Jan..Dec)\n\
385   %%B   locale's full month name, variable length (January..December)\n\
386   %%c   locale's date and time (Sat Nov 04 12:02:33 EST 1989)\n\
387   %%d   day of month (01..31)\n\
388   %%D   date (mm/dd/yy)\n\
389   %%h   same as %%b\n\
390   %%H   hour (00..23)\n\
391   %%I   hour (01..12)\n\
392   %%j   day of year (001..366)\n\
393   %%k   hour ( 0..23)\n\
394   %%l   hour ( 1..12)\n\
395   %%m   month (01..12)\n\
396   %%M   minute (00..59)\n\
397   %%n   a newline\n\
398   %%p   locale's AM or PM\n\
399   %%r   time, 12-hour (hh:mm:ss [AP]M)\n\
400   %%s   seconds since 00:00:00, Jan 1, 1970 (a GNU extension)\n\
401   %%S   second (00..61)\n\
402   %%t   a horizontal tab\n\
403   %%T   time, 24-hour (hh:mm:ss)\n\
404   %%U   week number of year with Sunday as first day of week (00..53)\n\
405   %%w   day of week (0..6);  0 represents Sunday\n\
406   %%W   week number of year with Monday as first day of week (00..53)\n\
407   %%x   locale's date representation (mm/dd/yy)\n\
408   %%X   locale's time representation (%%H:%%M:%%S)\n\
409   %%y   last two digits of year (00..99)\n\
410   %%Y   year (1970...)\n\
411   %%Z   time zone (e.g., EDT), or nothing if no time zone is determinable\n\
412 \n\
413 By default, date pads numeric fields with zeroes.  GNU date recognizes\n\
414 the following modifiers between `%%' and a numeric directive.\n\
415 \n\
416   `-' (hyphen) do not pad the field\n\
417   `_' (underscore) pad the field with spaces\n\
418 "));
419     }
420   exit (status);
421 }