tweak diagnostic
[platform/upstream/coreutils.git] / src / touch.c
1 /* touch -- change modification and access times of files
2    Copyright (C) 87, 1989-1991, 1995-2000 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
19    and Randy Smith. */
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 "argmatch.h"
28 #include "error.h"
29 #include "getdate.h"
30 #include "posixtm.h"
31 #include "quote.h"
32 #include "safe-read.h"
33
34 /* The official name of this program (e.g., no `g' prefix).  */
35 #define PROGRAM_NAME "touch"
36
37 #define AUTHORS \
38   "Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie, and Randy Smith"
39
40 #ifndef STDC_HEADERS
41 time_t time ();
42 #endif
43
44 /* Bitmasks for `change_times'. */
45 #define CH_ATIME 1
46 #define CH_MTIME 2
47
48 #if !defined O_NDELAY
49 # define O_NDELAY 0
50 #endif
51
52 #if !defined O_NONBLOCK
53 # define O_NONBLOCK O_NDELAY
54 #endif
55
56 #if !defined O_NOCTTY
57 # define O_NOCTTY 0
58 #endif
59
60 /* The name by which this program was run. */
61 char *program_name;
62
63 /* Which timestamps to change. */
64 static int change_times;
65
66 /* (-c) If nonzero, don't create if not already there. */
67 static int no_create;
68
69 /* (-d) If nonzero, date supplied on command line in get_date formats. */
70 static int flexible_date;
71
72 /* (-r) If nonzero, use times from a reference file. */
73 static int use_ref;
74
75 /* (-t) If nonzero, date supplied on command line in POSIX format. */
76 static int posix_date;
77
78 /* If nonzero, the only thing we have to do is change both the
79    modification and access time to the current time, so we don't
80    have to own the file, just be able to read and write it.
81    On some systems, we can do this if we own the file, even though
82    we have neither read nor write access to it.  */
83 static int amtime_now;
84
85 /* New time to use when setting time. */
86 static time_t newtime;
87
88 /* File to use for -r. */
89 static char *ref_file;
90
91 /* Info about the reference file. */
92 static struct stat ref_stats;
93
94 /* For long options that have no equivalent short option, use a
95    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
96 enum
97 {
98   TIME_OPTION = CHAR_MAX + 1
99 };
100
101 static struct option const longopts[] =
102 {
103   {"time", required_argument, 0, TIME_OPTION},
104   {"no-create", no_argument, 0, 'c'},
105   {"date", required_argument, 0, 'd'},
106   {"file", required_argument, 0, 'r'}, /* FIXME: phase out --file */
107   {"reference", required_argument, 0, 'r'},
108   {GETOPT_HELP_OPTION_DECL},
109   {GETOPT_VERSION_OPTION_DECL},
110   {0, 0, 0, 0}
111 };
112
113 /* Valid arguments to the `--time' option. */
114 static char const* const time_args[] =
115 {
116   "atime", "access", "use", "mtime", "modify", 0
117 };
118
119 /* The bits in `change_times' that those arguments set. */
120 static int const time_masks[] =
121 {
122   CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
123 };
124
125 /* Update the time of file FILE according to the options given.
126    Return 0 if successful, 1 if an error occurs. */
127
128 static int
129 touch (const char *file)
130 {
131   int status;
132   struct stat sbuf;
133   int fd = -1;
134   int open_errno = 0;
135
136   if (! no_create)
137     {
138       /* Try to open FILE, creating it if necessary.  */
139       fd = open (file, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
140                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
141       if (fd == -1)
142         open_errno = errno;
143     }
144
145   if (! amtime_now)
146     {
147       /* We're setting only one of the time values.  stat the target to get
148          the other one.  If we have the file descriptor already, use fstat.
149          Otherwise, either we're in no-create mode (and hence didn't call open)
150          or FILE is inaccessible or a directory, so we have to use stat.  */
151       if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
152         {
153           if (open_errno)
154             error (0, open_errno, _("creating %s"), quote (file));
155           else
156             error (0, errno, _("getting attributes of %s"), quote (file));
157           close (fd);
158           return 1;
159         }
160     }
161
162   if (fd != -1 && close (fd) < 0)
163     {
164       error (0, errno, _("creating %s"), quote (file));
165       return 1;
166     }
167
168   if (amtime_now)
169     {
170       /* Pass NULL to utime so it will not fail if we just have
171          write access to the file, but don't own it.  */
172       status = utime (file, NULL);
173     }
174   else
175     {
176       struct utimbuf utb;
177
178       /* There's currently no interface to set file timestamps with
179          better than 1-second resolution, so discard any fractional
180          part of the source timestamp.  */
181
182       if (use_ref)
183         {
184           utb.actime = ref_stats.st_atime;
185           utb.modtime = ref_stats.st_mtime;
186         }
187       else
188         utb.actime = utb.modtime = newtime;
189
190       if (!(change_times & CH_ATIME))
191         utb.actime = sbuf.st_atime;
192
193       if (!(change_times & CH_MTIME))
194         utb.modtime = sbuf.st_mtime;
195
196       status = utime (file, &utb);
197     }
198
199   if (status)
200     {
201       if (open_errno)
202         error (0, open_errno, _("creating %s"), quote (file));
203       else
204         error (0, errno, _("setting times of %s"), quote (file));
205       return 1;
206     }
207
208   return 0;
209 }
210
211 void
212 usage (int status)
213 {
214   if (status != 0)
215     fprintf (stderr, _("Try `%s --help' for more information.\n"),
216              program_name);
217   else
218     {
219       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
220       printf (_("  or : %s [-acm] MMDDhhmm[YY] FILE... (obsolescent)\n"),
221               program_name);
222       printf (_("\
223 Update the access and modification times of each FILE to the current time.\n\
224 \n\
225   -a                     change only the access time\n\
226   -c, --no-create        do not create any files\n\
227   -d, --date=STRING      parse STRING and use it instead of current time\n\
228   -f                     (ignored)\n\
229   -m                     change only the modification time\n\
230   -r, --reference=FILE   use this file's times instead of current time\n\
231   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
232   --time=WORD            set time given by WORD: access atime use (same as -a)\n\
233                            modify mtime (same as -m)\n\
234       --help             display this help and exit\n\
235       --version          output version information and exit\n\
236 \n\
237 Note that the three time-date formats recognized for the -d and -t options\n\
238 and for the obsolescent argument are all different.\n\
239 "));
240       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
241     }
242   exit (status);
243 }
244
245 int
246 main (int argc, char **argv)
247 {
248   int c;
249   int date_set = 0;
250   int err = 0;
251
252   program_name = argv[0];
253   setlocale (LC_ALL, "");
254   bindtextdomain (PACKAGE, LOCALEDIR);
255   textdomain (PACKAGE);
256
257   atexit (close_stdout);
258
259   change_times = no_create = use_ref = posix_date = flexible_date = 0;
260   newtime = (time_t) -1;
261
262   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
263     {
264       switch (c)
265         {
266         case 0:
267           break;
268
269         case 'a':
270           change_times |= CH_ATIME;
271           break;
272
273         case 'c':
274           no_create++;
275           break;
276
277         case 'd':
278           flexible_date++;
279           newtime = get_date (optarg, NULL);
280           if (newtime == (time_t) -1)
281             error (1, 0, _("invalid date format %s"), quote (optarg));
282           date_set++;
283           break;
284
285         case 'f':
286           break;
287
288         case 'm':
289           change_times |= CH_MTIME;
290           break;
291
292         case 'r':
293           use_ref++;
294           ref_file = optarg;
295           break;
296
297         case 't':
298           posix_date++;
299           newtime = posixtime (optarg,
300                                PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS);
301           if (newtime == (time_t) -1)
302             error (1, 0, _("invalid date format %s"), quote (optarg));
303           date_set++;
304           break;
305
306         case TIME_OPTION:       /* --time */
307           change_times |= XARGMATCH ("--time", optarg,
308                                      time_args, time_masks);
309           break;
310
311         case_GETOPT_HELP_CHAR;
312
313         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
314
315         default:
316           usage (1);
317         }
318     }
319
320   if (change_times == 0)
321     change_times = CH_ATIME | CH_MTIME;
322
323   if ((use_ref && (posix_date || flexible_date))
324       || (posix_date && flexible_date))
325     {
326       error (0, 0, _("cannot specify times from more than one source"));
327       usage (1);
328     }
329
330   if (use_ref)
331     {
332       if (stat (ref_file, &ref_stats))
333         error (1, errno, _("getting attributes of %s"), quote (ref_file));
334       date_set++;
335     }
336
337   if (!date_set && optind < argc && !STREQ (argv[optind - 1], "--"))
338     {
339       newtime = posixtime (argv[optind], PDS_TRAILING_YEAR);
340       if (newtime != (time_t) -1)
341         {
342           optind++;
343           date_set++;
344         }
345     }
346   if (!date_set)
347     {
348       if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
349         amtime_now = 1;
350       else
351         time (&newtime);
352     }
353
354   if (optind == argc)
355     {
356       error (0, 0, _("file arguments missing"));
357       usage (1);
358     }
359
360   for (; optind < argc; ++optind)
361     err += touch (argv[optind]);
362
363   exit (err != 0);
364 }