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