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