1cccc84ca563cc0d0b5a041affdfa70eace1444e
[platform/upstream/coreutils.git] / src / touch.c
1 /* touch -- change modification and access times of files
2    Copyright (C) 87, 1989-1991, 1995-2003 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 "posixver.h"
32 #include "quote.h"
33 #include "safe-read.h"
34 #include "utimens.h"
35
36 /* The official name of this program (e.g., no `g' prefix).  */
37 #define PROGRAM_NAME "touch"
38
39 #define WRITTEN_BY \
40 _("Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie, and Randy Smith.")
41
42 #ifndef STDC_HEADERS
43 time_t time ();
44 #endif
45
46 /* Bitmasks for `change_times'. */
47 #define CH_ATIME 1
48 #define CH_MTIME 2
49
50 /* The name by which this program was run. */
51 char *program_name;
52
53 /* Which timestamps to change. */
54 static int change_times;
55
56 /* (-c) If nonzero, don't create if not already there. */
57 static int no_create;
58
59 /* (-d) If nonzero, date supplied on command line in get_date formats. */
60 static int flexible_date;
61
62 /* (-r) If nonzero, use times from a reference file. */
63 static int use_ref;
64
65 /* (-t) If nonzero, date supplied on command line in POSIX format. */
66 static int posix_date;
67
68 /* If nonzero, the only thing we have to do is change both the
69    modification and access time to the current time, so we don't
70    have to own the file, just be able to read and write it.
71    On some systems, we can do this if we own the file, even though
72    we have neither read nor write access to it.  */
73 static int amtime_now;
74
75 /* New time to use when setting time. */
76 static struct timespec newtime;
77
78 /* File to use for -r. */
79 static char *ref_file;
80
81 /* Info about the reference file. */
82 static struct stat ref_stats;
83
84 /* For long options that have no equivalent short option, use a
85    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
86 enum
87 {
88   TIME_OPTION = CHAR_MAX + 1
89 };
90
91 static struct option const longopts[] =
92 {
93   {"time", required_argument, 0, TIME_OPTION},
94   {"no-create", no_argument, 0, 'c'},
95   {"date", required_argument, 0, 'd'},
96   {"file", required_argument, 0, 'r'}, /* FIXME: phase out --file */
97   {"reference", required_argument, 0, 'r'},
98   {GETOPT_HELP_OPTION_DECL},
99   {GETOPT_VERSION_OPTION_DECL},
100   {0, 0, 0, 0}
101 };
102
103 /* Valid arguments to the `--time' option. */
104 static char const* const time_args[] =
105 {
106   "atime", "access", "use", "mtime", "modify", 0
107 };
108
109 /* The bits in `change_times' that those arguments set. */
110 static int const time_masks[] =
111 {
112   CH_ATIME, CH_ATIME, CH_ATIME, CH_MTIME, CH_MTIME
113 };
114
115 /* Update the time of file FILE according to the options given.
116    Return 0 if successful, 1 if an error occurs. */
117
118 static int
119 touch (const char *file)
120 {
121   int status;
122   struct stat sbuf;
123   int fd = -1;
124   int open_errno = 0;
125
126   if (! no_create)
127     {
128       /* Try to open FILE, creating it if necessary.  */
129       fd = open (file, O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY,
130                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
131
132       /* Don't save a copy of errno if it's EISDIR, since that would lead
133          touch to give a bogus diagnostic for e.g., `touch /' (assuming
134          we don't own / or have write access to it).  On Solaris 5.6,
135          and probably other systems, it is EINVAL.  On SunOS4, it's EPERM.  */
136       if (fd == -1 && errno != EISDIR && errno != EINVAL && errno != EPERM)
137         open_errno = errno;
138     }
139
140   if (! amtime_now)
141     {
142       /* We're setting only one of the time values.  stat the target to get
143          the other one.  If we have the file descriptor already, use fstat.
144          Otherwise, either we're in no-create mode (and hence didn't call open)
145          or FILE is inaccessible or a directory, so we have to use stat.  */
146       if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
147         {
148           if (open_errno)
149             error (0, open_errno, _("creating %s"), quote (file));
150           else
151             {
152               if (no_create && errno == ENOENT)
153                 return 0;
154               error (0, errno, _("failed to get attributes of %s"),
155                      quote (file));
156             }
157           if (fd != -1)
158             close (fd);
159           return 1;
160         }
161     }
162
163   if (fd != -1 && close (fd) < 0)
164     {
165       error (0, errno, _("creating %s"), quote (file));
166       return 1;
167     }
168
169   if (amtime_now)
170     {
171       /* Pass NULL to utime so it will not fail if we just have
172          write access to the file, but don't own it.  */
173       status = utime (file, NULL);
174     }
175   else
176     {
177       struct timespec timespec[2];
178
179       /* There's currently no interface to set file timestamps with
180          better than 1-second resolution, so discard any fractional
181          part of the source timestamp.  */
182
183       if (use_ref)
184         {
185           timespec[0].tv_sec = ref_stats.st_atime;
186           timespec[0].tv_nsec = TIMESPEC_NS (ref_stats.st_atim);
187           timespec[1].tv_sec = ref_stats.st_mtime;
188           timespec[1].tv_nsec = TIMESPEC_NS (ref_stats.st_mtim);
189         }
190       else
191         timespec[0] = timespec[1] = newtime;
192
193       if (!(change_times & CH_ATIME))
194         {
195           timespec[0].tv_sec = sbuf.st_atime;
196           timespec[0].tv_nsec = TIMESPEC_NS (sbuf.st_atim);
197         }
198
199       if (!(change_times & CH_MTIME))
200         {
201           timespec[1].tv_sec = sbuf.st_mtime;
202           timespec[1].tv_nsec = TIMESPEC_NS (sbuf.st_mtim);
203         }
204
205       status = utimens (file, timespec);
206     }
207
208   if (status)
209     {
210       if (open_errno)
211         {
212           /* The wording of this diagnostic should cover at least two cases:
213              - the file does not exist, but the parent directory is unwritable
214              - the file exists, but it isn't writable
215              I think it's not worth trying to distinguish them.  */
216           error (0, open_errno, _("cannot touch %s"), quote (file));
217         }
218       else
219         {
220           if (no_create && errno == ENOENT)
221             return 0;
222           error (0, errno, _("setting times of %s"), quote (file));
223         }
224       return 1;
225     }
226
227   return 0;
228 }
229
230 void
231 usage (int status)
232 {
233   if (status != 0)
234     fprintf (stderr, _("Try `%s --help' for more information.\n"),
235              program_name);
236   else
237     {
238       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
239       fputs (_("\
240 Update the access and modification times of each FILE to the current time.\n\
241 \n\
242 "), stdout);
243       fputs (_("\
244 Mandatory arguments to long options are mandatory for short options too.\n\
245 "), stdout);
246       fputs (_("\
247   -a                     change only the access time\n\
248   -c, --no-create        do not create any files\n\
249   -d, --date=STRING      parse STRING and use it instead of current time\n\
250   -f                     (ignored)\n\
251   -m                     change only the modification time\n\
252 "), stdout);
253       fputs (_("\
254   -r, --reference=FILE   use this file's times instead of current time\n\
255   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
256   --time=WORD            set time given by WORD: access atime use (same as -a)\n\
257                            modify mtime (same as -m)\n\
258 "), stdout);
259       fputs (HELP_OPTION_DESCRIPTION, stdout);
260       fputs (VERSION_OPTION_DESCRIPTION, stdout);
261       fputs (_("\
262 \n\
263 Note that the -d and -t options accept different time-date formats.\n\
264 "), stdout);
265       printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
266     }
267   exit (status);
268 }
269
270 int
271 main (int argc, char **argv)
272 {
273   int c;
274   int date_set = 0;
275   int err = 0;
276
277   initialize_main (&argc, &argv);
278   program_name = argv[0];
279   setlocale (LC_ALL, "");
280   bindtextdomain (PACKAGE, LOCALEDIR);
281   textdomain (PACKAGE);
282
283   atexit (close_stdout);
284
285   change_times = no_create = use_ref = posix_date = flexible_date = 0;
286
287   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
288     {
289       switch (c)
290         {
291         case 0:
292           break;
293
294         case 'a':
295           change_times |= CH_ATIME;
296           break;
297
298         case 'c':
299           no_create++;
300           break;
301
302         case 'd':
303           flexible_date++;
304           newtime.tv_sec = get_date (optarg, NULL);
305           newtime.tv_nsec = 0; /* FIXME: get_date should set this.  */
306           if (newtime.tv_sec == (time_t) -1)
307             error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (optarg));
308           date_set++;
309           break;
310
311         case 'f':
312           break;
313
314         case 'm':
315           change_times |= CH_MTIME;
316           break;
317
318         case 'r':
319           use_ref++;
320           ref_file = optarg;
321           break;
322
323         case 't':
324           posix_date++;
325           if (! posixtime (&newtime.tv_sec, optarg,
326                            PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS))
327             error (EXIT_FAILURE, 0, _("invalid date format %s"), quote (optarg));
328           newtime.tv_nsec = 0;
329           date_set++;
330           break;
331
332         case TIME_OPTION:       /* --time */
333           change_times |= XARGMATCH ("--time", optarg,
334                                      time_args, time_masks);
335           break;
336
337         case_GETOPT_HELP_CHAR;
338
339         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
340
341         default:
342           usage (EXIT_FAILURE);
343         }
344     }
345
346   if (change_times == 0)
347     change_times = CH_ATIME | CH_MTIME;
348
349   if ((use_ref && (posix_date || flexible_date))
350       || (posix_date && flexible_date))
351     {
352       error (0, 0, _("cannot specify times from more than one source"));
353       usage (EXIT_FAILURE);
354     }
355
356   if (use_ref)
357     {
358       if (stat (ref_file, &ref_stats))
359         error (EXIT_FAILURE, errno,
360                _("failed to get attributes of %s"), quote (ref_file));
361       date_set++;
362     }
363
364   /* The obsolete `MMDDhhmm[YY]' form is valid IFF there are
365      two or more non-option arguments.  */
366   if (!date_set && 2 <= argc - optind && !STREQ (argv[optind - 1], "--")
367       && posix2_version () < 200112)
368     {
369       if (posixtime (&newtime.tv_sec, argv[optind], PDS_TRAILING_YEAR))
370         {
371           newtime.tv_nsec = 0;
372           if (! getenv ("POSIXLY_CORRECT"))
373             {
374               struct tm const *tm = localtime (&newtime.tv_sec);
375               error (0, 0,
376                      _("warning: `touch %s' is obsolete; use\
377  `touch -t %04d%02d%02d%02d%02d.%02d'"),
378                      argv[optind],
379                      tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
380                      tm->tm_hour, tm->tm_min, tm->tm_sec);
381             }
382
383           optind++;
384           date_set++;
385         }
386     }
387   if (!date_set)
388     {
389       if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
390         amtime_now = 1;
391       else
392         {
393           /* Get time of day, but only to microsecond resolution,
394              since 'utimes' currently supports only microsecond
395              resolution at best.  It would be cleaner here to invoke
396              gettime, but then we would have to link in more shared
397              libraries on platforms like Solaris, and we'd rather not
398              have 'touch' depend on libraries that it doesn't
399              need.  */
400           struct timeval timeval;
401           if (gettimeofday (&timeval, NULL) != 0)
402             error (EXIT_FAILURE, errno, _("cannot get time of day"));
403           newtime.tv_sec = timeval.tv_sec;
404           newtime.tv_nsec = timeval.tv_usec * 1000;
405         }
406     }
407
408   if (optind == argc)
409     {
410       error (0, 0, _("file arguments missing"));
411       usage (EXIT_FAILURE);
412     }
413
414   for (; optind < argc; ++optind)
415     err += touch (argv[optind]);
416
417   exit (err != 0);
418 }