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