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