bracket bug address with <> and append a period
[platform/upstream/coreutils.git] / src / touch.c
1 /* touch -- change modification and access times of files
2    Copyright (C) 87, 89, 90, 91, 95, 1996 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 /* Options:
19    -a, --time={atime,access,use}        Change access time only.
20    -c, --no-create              Do not create files that do not exist.
21    -d, --date=TIME              Specify time and date in various formats.
22    -f                           Ignored.
23    -m, --time={mtime,modify}    Change modification time only.
24    -r, --reference=FILE         Use the time and date of reference file FILE.
25    -t TIME                      Specify time and date in the form
26                                 `MMDDhhmm[[CC]YY][.ss]'.
27
28    If no options are given, -am is the default, using the current time.
29    The -r, -t, and -d options are mutually exclusive.  If a file does not
30    exist, create it unless -c is given.
31
32    Written by Paul Rubin, Arnold Robbins, Jim Kingdon, David MacKenzie,
33    and Randy Smith. */
34
35 #include <config.h>
36 #include <stdio.h>
37 #include <getopt.h>
38 #include <sys/types.h>
39
40 #include "system.h"
41 #include "error.h"
42 #include "argmatch.h"
43
44 #ifndef STDC_HEADERS
45 time_t time ();
46 #endif
47
48 time_t get_date ();
49 time_t posixtime ();
50 int safe_read ();
51 int full_write ();
52 void invalid_arg ();
53
54 #ifndef HAVE_UTIME_NULL
55 static int utime_now ();
56 #endif
57
58 /* Bitmasks for `change_times'. */
59 #define CH_ATIME 1
60 #define CH_MTIME 2
61
62 /* The name by which this program was run. */
63 char *program_name;
64
65 /* Which timestamps to change. */
66 static int change_times;
67
68 /* (-c) If nonzero, don't create if not already there. */
69 static int no_create;
70
71 /* (-d) If nonzero, date supplied on command line in get_date formats. */
72 static int flexible_date;
73
74 /* (-r) If nonzero, use times from a reference file. */
75 static int use_ref;
76
77 /* (-t) If nonzero, date supplied on command line in POSIX format. */
78 static int posix_date;
79
80 /* If nonzero, the only thing we have to do is change both the
81    modification and access time to the current time, so we don't
82    have to own the file, just be able to read and write it.  */
83 static int amtime_now;
84
85 /* New time to use when setting time. */
86 static time_t newtime;
87
88 /* File to use for -r. */
89 static char *ref_file;
90
91 /* Info about the reference file. */
92 static struct stat ref_stats;
93
94 /* If nonzero, display usage information and exit.  */
95 static int show_help;
96
97 /* If nonzero, print the version on standard output and exit.  */
98 static int show_version;
99
100 static struct option const longopts[] =
101 {
102   {"time", required_argument, 0, 130},
103   {"no-create", no_argument, 0, 'c'},
104   {"date", required_argument, 0, 'd'},
105   {"file", required_argument, 0, 'r'},
106   {"reference", required_argument, 0, 'r'},
107   {"help", no_argument, &show_help, 1},
108   {"version", no_argument, &show_version, 1},
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 (char *file)
129 {
130   int status;
131   struct stat sbuf;
132   int fd;
133
134   if (stat (file, &sbuf))
135     {
136       if (errno != ENOENT)
137         {
138           error (0, errno, "%s", file);
139           return 1;
140         }
141       if (no_create)
142         return 0;
143       fd = creat (file, 0666);
144       if (fd == -1)
145         {
146           error (0, errno, "%s", file);
147           return 1;
148         }
149       if (amtime_now)
150         {
151           if (close (fd) < 0)
152             {
153               error (0, errno, "%s", file);
154               return 1;
155             }
156           return 0;             /* We've done all we have to. */
157         }
158       if (fstat (fd, &sbuf))
159         {
160           error (0, errno, "%s", file);
161           close (fd);
162           return 1;
163         }
164       if (close (fd) < 0)
165         {
166           error (0, errno, "%s", file);
167           return 1;
168         }
169     }
170
171   if (amtime_now)
172     {
173 #ifndef HAVE_UTIME_NULL
174       status = utime_now (file, sbuf.st_size);
175 #else
176       /* Pass NULL to utime so it will not fail if we just have
177          write access to the file, but don't own it.  */
178       status = utime (file, NULL);
179 #endif
180     }
181   else
182     {
183       struct utimbuf utb;
184
185       if (use_ref)
186         {
187           utb.actime = ref_stats.st_atime;
188           utb.modtime = ref_stats.st_mtime;
189         }
190       else
191         utb.actime = utb.modtime = newtime;
192
193       if (!(change_times & CH_ATIME))
194         utb.actime = sbuf.st_atime;
195
196       if (!(change_times & CH_MTIME))
197         utb.modtime = sbuf.st_mtime;
198
199       status = utime (file, &utb);
200     }
201
202   if (status)
203     {
204       error (0, errno, "%s", file);
205       return 1;
206     }
207
208   return 0;
209 }
210
211 #ifndef HAVE_UTIME_NULL
212 /* Emulate utime (file, NULL) for systems (like 4.3BSD) that do not
213    interpret it to set the access and modification times of FILE to
214    the current time.  FILESIZE is the correct size of FILE, used to
215    make sure empty files are not lengthened to 1 byte.
216    Return 0 if successful, -1 if not. */
217
218 static int
219 utime_now (const char *file, off_t filesize)
220 {
221   int fd;
222   char c;
223   int status = 0;
224
225   fd = open (file, O_RDWR, 0666);
226   if (fd < 0
227       || safe_read (fd, &c, sizeof (char)) < 0
228       || lseek (fd, (off_t) 0, SEEK_SET) < 0
229       || full_write (fd, &c, sizeof (char)) < 0
230       || ftruncate (fd, filesize) < 0
231       || close (fd) < 0)
232     status = -1;
233   return status;
234 }
235 #endif
236
237 static void
238 usage (int status)
239 {
240   if (status != 0)
241     fprintf (stderr, _("Try `%s --help' for more information.\n"),
242              program_name);
243   else
244     {
245       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
246       printf (_("\
247 Update the access and modification times of each FILE to the current time.\n\
248 \n\
249   -a                     change only the access time\n\
250   -c                     do not create any files\n\
251   -d, --date=STRING      parse STRING and use it instead of current time\n\
252   -f                     (ignored)\n\
253   -m                     change only the modification time\n\
254   -r, --reference=FILE   use this file's times instead of current time\n\
255   -t STAMP               use MMDDhhmm[[CC]YY][.ss] instead of current time\n\
256       --time=WORD        access -a, atime -a, mtime -m, modify -m, use -a\n\
257       --help             display this help and exit\n\
258       --version          output version information and exit\n\
259 \n\
260 STAMP may be used without -t if none of -drt, nor --, are used.\n\
261 "));
262       puts (_("\nReport bugs to <fileutils-bugs@gnu.ai.mit.edu>."));
263     }
264   exit (status);
265 }
266
267 int
268 main (int argc, char **argv)
269 {
270   int c, i;
271   int date_set = 0;
272   int err = 0;
273
274   program_name = argv[0];
275   setlocale (LC_ALL, "");
276   bindtextdomain (PACKAGE, LOCALEDIR);
277   textdomain (PACKAGE);
278
279   change_times = no_create = use_ref = posix_date = flexible_date = 0;
280   newtime = (time_t) -1;
281
282   while ((c = getopt_long (argc, argv, "acd:fmr:t:", longopts, NULL)) != -1)
283     {
284       switch (c)
285         {
286         case 0:
287           break;
288
289         case 'a':
290           change_times |= CH_ATIME;
291           break;
292
293         case 'c':
294           no_create++;
295           break;
296
297         case 'd':
298           flexible_date++;
299           newtime = get_date (optarg, NULL);
300           if (newtime == (time_t) -1)
301             error (1, 0, _("invalid date format `%s'"), optarg);
302           date_set++;
303           break;
304
305         case 'f':
306           break;
307
308         case 'm':
309           change_times |= CH_MTIME;
310           break;
311
312         case 'r':
313           use_ref++;
314           ref_file = optarg;
315           break;
316
317         case 't':
318           posix_date++;
319           newtime = posixtime (optarg);
320           if (newtime == (time_t) -1)
321             error (1, 0, _("invalid date format `%s'"), optarg);
322           date_set++;
323           break;
324
325         case 130:
326           i = argmatch (optarg, time_args);
327           if (i < 0)
328             {
329               invalid_arg (_("time selector"), optarg, i);
330               usage (1);
331             }
332           change_times |= time_masks[i];
333           break;
334
335         default:
336           usage (1);
337         }
338     }
339
340   if (show_version)
341     {
342       printf ("touch (%s) %s\n", GNU_PACKAGE, VERSION);
343       exit (0);
344     }
345
346   if (show_help)
347     usage (0);
348
349   if (change_times == 0)
350     change_times = CH_ATIME | CH_MTIME;
351
352   if ((use_ref && (posix_date || flexible_date))
353       || (posix_date && flexible_date))
354     {
355       error (0, 0, _("cannot specify times from more than one source"));
356       usage (1);
357     }
358
359   if (use_ref)
360     {
361       if (stat (ref_file, &ref_stats))
362         error (1, errno, "%s", ref_file);
363       date_set++;
364     }
365
366   if (!date_set && optind < argc && strcmp (argv[optind - 1], "--"))
367     {
368       newtime = posixtime (argv[optind]);
369       if (newtime != (time_t) -1)
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 }