(usage): Reorder --time and --help so --help is the latter (just
[platform/upstream/coreutils.git] / src / touch.c
1 /* touch -- change modification and access times of files
2    Copyright (C) 1987, 1989, 1990, 1991, 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 /* Options:
19    -a, --time={atime,access,use}        Change access time only.
20    -c, --no-create              Do not create files that do not exist.
21    -d, --date=TIME              Specify time and date in various formats.
22    -f                           Ignored.
23    -m, --time={mtime,modify}    Change modification time only.
24    -r, --reference=FILE         Use the time and date of reference file FILE.
25    -t TIME                      Specify time and date in the form
26                                 `MMDDhhmm[[CC]YY][.ss]'.
27
28    If no options are given, -am is the default, using the current time.
29    The -r, -t, and -d options are mutually exclusive.  If a file does not
30    exist, create it unless -c is given.
31
32    Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
33    and Randy Smith. */
34
35 #include <config.h>
36 #include <stdio.h>
37 #include <getopt.h>
38 #include <sys/types.h>
39
40 #include "system.h"
41 #include "version.h"
42 #include "error.h"
43 #include "argmatch.h"
44
45 #ifndef STDC_HEADERS
46 time_t mktime ();
47 time_t time ();
48 #endif
49
50 time_t get_date ();
51 time_t posixtime ();
52 int safe_read ();
53 int full_write ();
54 void invalid_arg ();
55
56 #ifndef HAVE_UTIME_NULL
57 static int utime_now ();
58 #endif
59
60 /* Bitmasks for `change_times'. */
61 #define CH_ATIME 1
62 #define CH_MTIME 2
63
64 /* The name by which this program was run. */
65 char *program_name;
66
67 /* Which timestamps to change. */
68 static int change_times;
69
70 /* (-c) If nonzero, don't create if not already there. */
71 static int no_create;
72
73 /* (-d) If nonzero, date supplied on command line in get_date formats. */
74 static int flexible_date;
75
76 /* (-r) If nonzero, use times from a reference file. */
77 static int use_ref;
78
79 /* (-t) If nonzero, date supplied on command line in POSIX format. */
80 static int posix_date;
81
82 /* If nonzero, the only thing we have to do is change both the
83    modification and access time to the current time, so we don't
84    have to own the file, just be able to read and write it.  */
85 static int amtime_now;
86
87 /* New time to use when setting time. */
88 static time_t newtime;
89
90 /* File to use for -r. */
91 static char *ref_file;
92
93 /* Info about the reference file. */
94 static struct stat ref_stats;
95
96 /* If nonzero, display usage information and exit.  */
97 static int show_help;
98
99 /* If nonzero, print the version on standard output and exit.  */
100 static int show_version;
101
102 static struct option const longopts[] =
103 {
104   {"time", required_argument, 0, 130},
105   {"no-create", no_argument, 0, 'c'},
106   {"date", required_argument, 0, 'd'},
107   {"file", required_argument, 0, 'r'},
108   {"reference", required_argument, 0, 'r'},
109   {"help", no_argument, &show_help, 1},
110   {"version", no_argument, &show_version, 1},
111   {0, 0, 0, 0}
112 };
113
114 /* Valid arguments to the `--time' option. */
115 static char const* const time_args[] =
116 {
117   "atime", "access", "use", "mtime", "modify", 0
118 };
119
120 /* The bits in `change_times' that those arguments set. */
121 static int const time_masks[] =
122 {
123   CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
124 };
125
126 /* Update the time of file FILE according to the options given.
127    Return 0 if successful, 1 if an error occurs. */
128
129 static int
130 touch (char *file)
131 {
132   int status;
133   struct stat sbuf;
134   int fd;
135
136   if (stat (file, &sbuf))
137     {
138       if (errno != ENOENT)
139         {
140           error (0, errno, "%s", file);
141           return 1;
142         }
143       if (no_create)
144         return 0;
145       fd = creat (file, 0666);
146       if (fd == -1)
147         {
148           error (0, errno, "%s", file);
149           return 1;
150         }
151       if (amtime_now)
152         {
153           if (close (fd) < 0)
154             {
155               error (0, errno, "%s", file);
156               return 1;
157             }
158           return 0;             /* We've done all we have to. */
159         }
160       if (fstat (fd, &sbuf))
161         {
162           error (0, errno, "%s", file);
163           close (fd);
164           return 1;
165         }
166       if (close (fd) < 0)
167         {
168           error (0, errno, "%s", file);
169           return 1;
170         }
171     }
172
173   if (amtime_now)
174     {
175 #ifndef HAVE_UTIME_NULL
176       status = utime_now (file, sbuf.st_size);
177 #else
178       /* Pass NULL to utime so it will not fail if we just have
179          write access to the file, but don't own it.  */
180       status = utime (file, NULL);
181 #endif
182     }
183   else
184     {
185       struct utimbuf utb;
186
187       if (use_ref)
188         {
189           utb.actime = ref_stats.st_atime;
190           utb.modtime = ref_stats.st_mtime;
191         }
192       else
193         utb.actime = utb.modtime = newtime;
194
195       if (!(change_times & CH_ATIME))
196         utb.actime = sbuf.st_atime;
197
198       if (!(change_times & CH_MTIME))
199         utb.modtime = sbuf.st_mtime;
200
201       status = utime (file, &utb);
202     }
203
204   if (status)
205     {
206       error (0, errno, "%s", file);
207       return 1;
208     }
209
210   return 0;
211 }
212
213 #ifndef HAVE_UTIME_NULL
214 /* Emulate utime (file, NULL) for systems (like 4.3BSD) that do not
215    interpret it to set the access and modification times of FILE to
216    the current time.  FILESIZE is the correct size of FILE, used to
217    make sure empty files are not lengthened to 1 byte.
218    Return 0 if successful, -1 if not. */
219
220 static int
221 utime_now (file, filesize)
222      char *file;
223      off_t filesize;
224 {
225   int fd;
226   char c;
227   int status = 0;
228
229   fd = open (file, O_RDWR, 0666);
230   if (fd < 0
231       || safe_read (fd, &c, sizeof (char)) < 0
232       || lseek (fd, (off_t) 0, SEEK_SET) < 0
233       || full_write (fd, &c, sizeof (char)) < 0
234       || ftruncate (fd, filesize) < 0
235       || close (fd) < 0)
236     status = -1;
237   return status;
238 }
239 #endif
240
241 static void
242 usage (int status)
243 {
244   if (status != 0)
245     fprintf (stderr, _("Try `%s --help' for more information.\n"),
246              program_name);
247   else
248     {
249       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
250       printf (_("\
251 Update the access and modification times of each FILE to the current time.\n\
252 \n\
253   -a                     change only the access time\n\
254   -c                     do not create any files\n\
255   -d, --date=STRING      parse STRING and use it instead of current time\n\
256   -f                     (ignored)\n\
257   -m                     change only the modification time\n\
258   -r, --reference=FILE   use this file's times instead of current time\n\
259   -t STAMP               use MMDDhhmm[[CC]YY][.ss] instead of current time\n\
260       --time=WORD        access -a, atime -a, mtime -m, modify -m, use -a\n\
261       --help             display this help and exit\n\
262       --version          output version information and exit\n\
263 \n\
264 STAMP may be used without -t if none of -drt, nor --, are used.\n"));
265     }
266   exit (status);
267 }
268
269 void
270 main (int argc, char **argv)
271 {
272   int c, i;
273   int date_set = 0;
274   int err = 0;
275
276   program_name = argv[0];
277   change_times = no_create = use_ref = posix_date = flexible_date = 0;
278   newtime = (time_t) -1;
279
280   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, (int *) 0))
281          != EOF)
282     {
283       switch (c)
284         {
285         case 0:
286           break;
287
288         case 'a':
289           change_times |= CH_ATIME;
290           break;
291
292         case 'c':
293           no_create++;
294           break;
295
296         case 'd':
297           flexible_date++;
298           newtime = get_date (optarg, NULL);
299           if (newtime == (time_t) -1)
300             error (1, 0, _("invalid date format `%s'"), optarg);
301           date_set++;
302           break;
303
304         case 'f':
305           break;
306
307         case 'm':
308           change_times |= CH_MTIME;
309           break;
310
311         case 'r':
312           use_ref++;
313           ref_file = optarg;
314           break;
315
316         case 't':
317           posix_date++;
318           newtime = posixtime (optarg);
319           if (newtime == (time_t) -1)
320             error (1, 0, _("invalid date format `%s'"), optarg);
321           date_set++;
322           break;
323
324         case 130:
325           i = argmatch (optarg, time_args);
326           if (i < 0)
327             {
328               invalid_arg (_("time selector"), optarg, i);
329               usage (1);
330             }
331           change_times |= time_masks[i];
332           break;
333
334         default:
335           usage (1);
336         }
337     }
338
339   if (show_version)
340     {
341       printf ("touch - %s\n", version_string);
342       exit (0);
343     }
344
345   if (show_help)
346     usage (0);
347
348   if (change_times == 0)
349     change_times = CH_ATIME | CH_MTIME;
350
351   if ((use_ref && (posix_date || flexible_date))
352       || (posix_date && flexible_date))
353     {
354       error (0, 0, _("cannot specify times from more than one source"));
355       usage (1);
356     }
357
358   if (use_ref)
359     {
360       if (stat (ref_file, &ref_stats))
361         error (1, errno, "%s", ref_file);
362       date_set++;
363     }
364
365   if (!date_set && optind < argc && strcmp (argv[optind - 1], "--"))
366     {
367       newtime = posixtime (argv[optind]);
368       if (newtime != (time_t) -1)
369         {
370           optind++;
371           date_set++;
372         }
373     }
374   if (!date_set)
375     {
376       if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
377         amtime_now = 1;
378       else
379         time (&newtime);
380     }
381
382   if (optind == argc)
383     {
384       error (0, 0, _("file arguments missing"));
385       usage (1);
386     }
387
388   for (; optind < argc; ++optind)
389     err += touch (argv[optind]);
390
391   exit (err != 0);
392 }