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