2db00d3049714d682bdfcca612868e12b6876fc6
[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 = -1;
114   int open_errno = 0;
115
116   if (! no_create)
117     {
118       /* Try to open FILE, creating it if necessary.  */
119       fd = open (file, O_WRONLY | O_CREAT,
120                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
121       if (fd == -1)
122         open_errno = errno;
123     }
124
125   if (! amtime_now)
126     {
127       /* We're setting only one of the time values.  stat the target to get
128          the other one.  If we have the file descriptor already, use fstat.
129          Otherwise, either we're in no-create mode (and hence didn't call open)
130          or FILE is inaccessible or a directory, so we have to use stat.  */
131       if (fd != -1 ? fstat (fd, &sbuf) : stat (file, &sbuf))
132         {
133           error (0, open_errno ? open_errno : errno, "%s", file);
134           close (fd);
135           return 1;
136         }
137     }
138
139   if (fd != -1 && close (fd) < 0)
140     {
141       error (0, errno, "%s", file);
142       return 1;
143     }
144
145   if (amtime_now)
146     {
147       /* Pass NULL to utime so it will not fail if we just have
148          write access to the file, but don't own it.  */
149       status = utime (file, NULL);
150     }
151   else
152     {
153       struct utimbuf utb;
154
155       /* There's currently no interface to set file timestamps with
156          better than 1-second resolution, so discard any fractional
157          part of the source timestamp.  */
158
159       if (use_ref)
160         {
161           utb.actime = ref_stats.st_atime;
162           utb.modtime = ref_stats.st_mtime;
163         }
164       else
165         utb.actime = utb.modtime = newtime;
166
167       if (!(change_times & CH_ATIME))
168         utb.actime = sbuf.st_atime;
169
170       if (!(change_times & CH_MTIME))
171         utb.modtime = sbuf.st_mtime;
172
173       status = utime (file, &utb);
174     }
175
176   if (status)
177     {
178       error (0, open_errno ? open_errno : errno, "%s", file);
179       return 1;
180     }
181
182   return 0;
183 }
184
185 void
186 usage (int status)
187 {
188   if (status != 0)
189     fprintf (stderr, _("Try `%s --help' for more information.\n"),
190              program_name);
191   else
192     {
193       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
194       printf (_("  or : %s [-acm] MMDDhhmm[YY] FILE... (obsolescent)\n"),
195               program_name);
196       printf (_("\
197 Update the access and modification times of each FILE to the current time.\n\
198 \n\
199   -a                     change only the access time\n\
200   -c, --no-create        do not create any files\n\
201   -d, --date=STRING      parse STRING and use it instead of current time\n\
202   -f                     (ignored)\n\
203   -m                     change only the modification time\n\
204   -r, --reference=FILE   use this file's times instead of current time\n\
205   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
206   --time=WORD            set time given by WORD: access atime use (same as -a)\n\
207                            modify mtime (same as -m)\n\
208       --help             display this help and exit\n\
209       --version          output version information and exit\n\
210 \n\
211 Note that the three time-date formats recognized for the -d and -t options\n\
212 and for the obsolescent argument are all different.\n\
213 "));
214       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
215       close_stdout ();
216     }
217   exit (status);
218 }
219
220 int
221 main (int argc, char **argv)
222 {
223   int c;
224   int date_set = 0;
225   int err = 0;
226
227   program_name = argv[0];
228   setlocale (LC_ALL, "");
229   bindtextdomain (PACKAGE, LOCALEDIR);
230   textdomain (PACKAGE);
231
232   change_times = no_create = use_ref = posix_date = flexible_date = 0;
233   newtime = (time_t) -1;
234
235   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
236     {
237       switch (c)
238         {
239         case 0:
240           break;
241
242         case 'a':
243           change_times |= CH_ATIME;
244           break;
245
246         case 'c':
247           no_create++;
248           break;
249
250         case 'd':
251           flexible_date++;
252           newtime = get_date (optarg, NULL);
253           if (newtime == (time_t) -1)
254             error (1, 0, _("invalid date format `%s'"), optarg);
255           date_set++;
256           break;
257
258         case 'f':
259           break;
260
261         case 'm':
262           change_times |= CH_MTIME;
263           break;
264
265         case 'r':
266           use_ref++;
267           ref_file = optarg;
268           break;
269
270         case 't':
271           posix_date++;
272           newtime = posixtime (optarg,
273                                PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS);
274           if (newtime == (time_t) -1)
275             error (1, 0, _("invalid date format `%s'"), optarg);
276           date_set++;
277           break;
278
279         case CHAR_MAX + 1:      /* --time */
280           change_times |= XARGMATCH ("--time", optarg,
281                                      time_args, time_masks);
282           break;
283
284         case_GETOPT_HELP_CHAR;
285
286         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
287
288         default:
289           usage (1);
290         }
291     }
292
293   if (change_times == 0)
294     change_times = CH_ATIME | CH_MTIME;
295
296   if ((use_ref && (posix_date || flexible_date))
297       || (posix_date && flexible_date))
298     {
299       error (0, 0, _("cannot specify times from more than one source"));
300       usage (1);
301     }
302
303   if (use_ref)
304     {
305       if (stat (ref_file, &ref_stats))
306         error (1, errno, "%s", ref_file);
307       date_set++;
308     }
309
310   if (!date_set && optind < argc && !STREQ (argv[optind - 1], "--"))
311     {
312       newtime = posixtime (argv[optind], PDS_TRAILING_YEAR);
313       if (newtime != (time_t) -1)
314         {
315           optind++;
316           date_set++;
317         }
318     }
319   if (!date_set)
320     {
321       if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
322         amtime_now = 1;
323       else
324         time (&newtime);
325     }
326
327   if (optind == argc)
328     {
329       error (0, 0, _("file arguments missing"));
330       usage (1);
331     }
332
333   for (; optind < argc; ++optind)
334     err += touch (argv[optind]);
335
336   exit (err != 0);
337 }