(open_maybe_create): Don't pass 0666 to open; it's not needed and isn't
[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 int full_write ();
44
45 /* Bitmasks for `change_times'. */
46 #define CH_ATIME 1
47 #define CH_MTIME 2
48
49 /* The name by which this program was run. */
50 char *program_name;
51
52 /* Which timestamps to change. */
53 static int change_times;
54
55 /* (-c) If nonzero, don't create if not already there. */
56 static int no_create;
57
58 /* (-d) If nonzero, date supplied on command line in get_date formats. */
59 static int flexible_date;
60
61 /* (-r) If nonzero, use times from a reference file. */
62 static int use_ref;
63
64 /* (-t) If nonzero, date supplied on command line in POSIX format. */
65 static int posix_date;
66
67 /* If nonzero, the only thing we have to do is change both the
68    modification and access time to the current time, so we don't
69    have to own the file, just be able to read and write 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'},
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 /* Open FILE, possibly creating it.  Set *FILE_CREATED to nonzero if the
106    open creates it, or to zero if the open call opened an existing file.
107    Return the result of the open call.  Be careful to avoid race conditions.  */
108
109 static int
110 open_maybe_create (const char *file, int *file_created)
111 {
112   int fd;
113
114   *file_created = 0;
115   while (1)
116     {
117       /* First, see if we can create a new FILE.  */
118       fd = open (file, O_WRONLY | O_CREAT | O_EXCL,
119                  S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
120       if (fd != -1)
121         *file_created = 1;
122
123       /* If the open succeeded or if it failed for any reason other
124          than the existence of FILE, then we're done.  */
125       if (fd != -1 || errno != EEXIST)
126         break;
127
128       /* The first open failed because FILE already exists.
129          Now try to open it for writing.  */
130       fd = open (file, O_WRONLY);
131
132       /* If the open succeeded or if it failed for any reason other
133          than the absence of FILE, then we're done.  */
134       /* The 2nd open can fail if FILE was unlinked between the two
135          open calls.  When that happens, just iterate.  */
136       if (fd != -1 || errno != ENOENT)
137         break;
138     }
139
140   return fd;
141 }
142
143 /* Update the time of file FILE according to the options given.
144    Return 0 if successful, 1 if an error occurs. */
145
146 static int
147 touch (const char *file)
148 {
149   int status;
150   struct stat sbuf;
151   int fd;
152   int file_created;
153
154   if (no_create)
155     {
156       /* Try to open an existing FILE.  */
157       fd = open (file, O_WRONLY);
158       if (fd == -1 && errno == ENOENT)
159         {
160           /* FILE doesn't exist.  So we're done.  */
161           return 0;
162         }
163       file_created = 0;
164     }
165   else
166     {
167       /* Try to open FILE, creating it if necessary.  */
168       fd = open_maybe_create (file, &file_created);
169     }
170
171   if (fd == -1)
172     {
173       error (0, errno, "%s", file);
174       return 1;
175     }
176
177   if (file_created && amtime_now)
178     {
179       if (close (fd) < 0)
180         {
181           error (0, errno, "%s", file);
182           return 1;
183         }
184       return 0;         /* We've done all we have to. */
185     }
186
187   if (fstat (fd, &sbuf))
188     {
189       error (0, errno, "%s", file);
190       close (fd);
191       return 1;
192     }
193
194   if (close (fd) < 0)
195     {
196       error (0, errno, "%s", file);
197       return 1;
198     }
199
200   if (amtime_now)
201     {
202       /* Pass NULL to utime so it will not fail if we just have
203          write access to the file, but don't own it.  */
204       status = utime (file, NULL);
205     }
206   else
207     {
208       struct utimbuf utb;
209
210       /* There's currently no interface to set file timestamps with
211          better than 1-second resolution, so discard any fractional
212          part of the source timestamp.  */
213
214       if (use_ref)
215         {
216           utb.actime = ref_stats.st_atime;
217           utb.modtime = ref_stats.st_mtime;
218         }
219       else
220         utb.actime = utb.modtime = newtime;
221
222       if (!(change_times & CH_ATIME))
223         utb.actime = sbuf.st_atime;
224
225       if (!(change_times & CH_MTIME))
226         utb.modtime = sbuf.st_mtime;
227
228       status = utime (file, &utb);
229     }
230
231   if (status)
232     {
233       error (0, errno, "%s", file);
234       return 1;
235     }
236
237   return 0;
238 }
239
240 void
241 usage (int status)
242 {
243   if (status != 0)
244     fprintf (stderr, _("Try `%s --help' for more information.\n"),
245              program_name);
246   else
247     {
248       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
249       printf (_("  or : %s [-acm] MMDDhhmm[YY] FILE... (obsolescent)\n"),
250               program_name);
251       printf (_("\
252 Update the access and modification times of each FILE to the current time.\n\
253 \n\
254   -a                     change only the access time\n\
255   -c                     do not create any files\n\
256   -d, --date=STRING      parse STRING and use it instead of current time\n\
257   -f                     (ignored)\n\
258   -m                     change only the modification time\n\
259   -r, --reference=FILE   use this file's times instead of current time\n\
260   -t STAMP               use [[CC]YY]MMDDhhmm[.ss] instead of current time\n\
261       --time=WORD        access -a, atime -a, mtime -m, modify -m, use -a\n\
262       --help             display this help and exit\n\
263       --version          output version information and exit\n\
264 \n\
265 STAMP may be used without -t if none of -drt, nor --, are used.\n\
266 Note that the three time-date formats recognized for the -d and -t options\n\
267 and for the obsolescent argument are all different.\n\
268 "));
269       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
270       close_stdout ();
271     }
272   exit (status);
273 }
274
275 int
276 main (int argc, char **argv)
277 {
278   int c;
279   int date_set = 0;
280   int err = 0;
281
282   program_name = argv[0];
283   setlocale (LC_ALL, "");
284   bindtextdomain (PACKAGE, LOCALEDIR);
285   textdomain (PACKAGE);
286
287   change_times = no_create = use_ref = posix_date = flexible_date = 0;
288   newtime = (time_t) -1;
289
290   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
291     {
292       switch (c)
293         {
294         case 0:
295           break;
296
297         case 'a':
298           change_times |= CH_ATIME;
299           break;
300
301         case 'c':
302           no_create++;
303           break;
304
305         case 'd':
306           flexible_date++;
307           newtime = get_date (optarg, NULL);
308           if (newtime == (time_t) -1)
309             error (1, 0, _("invalid date format `%s'"), optarg);
310           date_set++;
311           break;
312
313         case 'f':
314           break;
315
316         case 'm':
317           change_times |= CH_MTIME;
318           break;
319
320         case 'r':
321           use_ref++;
322           ref_file = optarg;
323           break;
324
325         case 't':
326           posix_date++;
327           newtime = posixtime (optarg,
328                                PDS_LEADING_YEAR | PDS_CENTURY | PDS_SECONDS);
329           if (newtime == (time_t) -1)
330             error (1, 0, _("invalid date format `%s'"), optarg);
331           date_set++;
332           break;
333
334         case CHAR_MAX + 1:      /* --time */
335           change_times |= XARGMATCH ("--time", optarg,
336                                      time_args, time_masks);
337           break;
338
339         case_GETOPT_HELP_CHAR;
340
341         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
342
343         default:
344           usage (1);
345         }
346     }
347
348   if (change_times == 0)
349     change_times = CH_ATIME | CH_MTIME;
350
351   if ((use_ref && (posix_date || flexible_date))
352       || (posix_date && flexible_date))
353     {
354       error (0, 0, _("cannot specify times from more than one source"));
355       usage (1);
356     }
357
358   if (use_ref)
359     {
360       if (stat (ref_file, &ref_stats))
361         error (1, errno, "%s", ref_file);
362       date_set++;
363     }
364
365   if (!date_set && optind < argc && !STREQ (argv[optind - 1], "--"))
366     {
367       newtime = posixtime (argv[optind], PDS_TRAILING_YEAR);
368       if (newtime != (time_t) -1)
369         {
370           optind++;
371           date_set++;
372         }
373     }
374   if (!date_set)
375     {
376       if ((change_times & (CH_ATIME | CH_MTIME)) == (CH_ATIME | CH_MTIME))
377         amtime_now = 1;
378       else
379         time (&newtime);
380     }
381
382   if (optind == argc)
383     {
384       error (0, 0, _("file arguments missing"));
385       usage (1);
386     }
387
388   for (; optind < argc; ++optind)
389     err += touch (argv[optind]);
390
391   exit (err != 0);
392 }