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