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