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