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