1 /* $NetBSD: touch.c,v 1.33 2015/03/02 03:17:24 enami Exp $ */
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
34 __COPYRIGHT("@(#) Copyright (c) 1993\
35 The Regents of the University of California. All rights reserved.");
40 static char sccsid
[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
42 __RCSID("$NetBSD: touch.c,v 1.33 2015/03/02 03:17:24 enami Exp $");
45 #include <sys/types.h>
62 static void stime_arg0(char *, struct timespec
*);
63 static void stime_arg1(char *, struct timespec
*);
64 static void stime_arg2(char *, int, struct timespec
*);
65 static void stime_file(char *, struct timespec
*);
66 __dead
static void usage(void);
68 struct option touch_longopts
[] = {
69 { "date", required_argument
, 0,
71 { "reference", required_argument
, 0,
78 main(int argc
, char *argv
[])
81 struct timespec ts
[2];
82 int aflag
, cflag
, hflag
, mflag
, ch
, fd
, len
, rval
, timeset
;
84 int (*change_file_times
)(const char *, const struct timespec
*);
85 int (*get_file_status
)(const char *, struct stat
*);
87 setlocale(LC_ALL
, "");
89 aflag
= cflag
= hflag
= mflag
= timeset
= 0;
90 if (clock_gettime(CLOCK_REALTIME
, &ts
[0]))
91 err(1, "clock_gettime");
93 while ((ch
= getopt_long(argc
, argv
, "acd:fhmr:t:", touch_longopts
,
104 stime_arg0(optarg
, ts
);
116 stime_file(optarg
, ts
);
120 stime_arg1(optarg
, ts
);
129 /* Default is both -a and -m. */
130 if (aflag
== 0 && mflag
== 0)
134 cflag
= 1; /* Don't create new file */
135 change_file_times
= lutimens
;
136 get_file_status
= lstat
;
138 change_file_times
= utimens
;
139 get_file_status
= stat
;
143 * If no -r or -t flag, at least two operands, the first of which
144 * is an 8 or 10 digit number, use the obsolete time specification.
146 if (!timeset
&& argc
> 1) {
147 (void)strtol(argv
[0], &p
, 10);
149 if (*p
== '\0' && (len
== 8 || len
== 10)) {
151 stime_arg2(*argv
++, len
== 10, ts
);
155 /* Otherwise use the current time of day. */
162 for (rval
= EXIT_SUCCESS
; *argv
; ++argv
) {
163 /* See if the file exists. */
164 if ((*get_file_status
)(*argv
, &sb
)) {
166 /* Create the file. */
168 O_WRONLY
| O_CREAT
, DEFFILEMODE
);
169 if (fd
== -1 || fstat(fd
, &sb
) || close(fd
)) {
175 /* If using the current time, we're done. */
182 ts
[0] = sb
.st_atimespec
;
184 ts
[1] = sb
.st_mtimespec
;
187 if (!(*change_file_times
)(*argv
, ts
))
190 /* If the user specified a time, nothing else we can do. */
197 * System V and POSIX 1003.1 require that a NULL argument
198 * set the access/modification times to the current time.
199 * The permission checks are different, too, in that the
200 * ability to write the file is sufficient. Take a shot.
202 if (!(*change_file_times
)(*argv
, NULL
))
211 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
214 stime_arg0(char *arg
, struct timespec
*tsp
)
216 tsp
[1].tv_sec
= tsp
[0].tv_sec
= parsedate(arg
, NULL
, NULL
);
217 if (tsp
[0].tv_sec
== -1)
218 errx(EXIT_FAILURE
, "Could not parse `%s'", arg
);
219 tsp
[0].tv_nsec
= tsp
[1].tv_nsec
= 0;
223 stime_arg1(char *arg
, struct timespec
*tsp
)
229 /* Start with the current time. */
230 tmptime
= tsp
[0].tv_sec
;
231 if ((t
= localtime(&tmptime
)) == NULL
)
232 err(EXIT_FAILURE
, "localtime");
233 /* [[CC]YY]MMDDhhmm[.SS] */
234 if ((p
= strchr(arg
, '.')) == NULL
)
235 t
->tm_sec
= 0; /* Seconds defaults to 0. */
237 if (strlen(p
+ 1) != 2)
240 t
->tm_sec
= ATOI2(p
);
244 switch (strlen(arg
)) {
245 case 12: /* CCYYMMDDhhmm */
246 t
->tm_year
= ATOI2(arg
) * 100 - TM_YEAR_BASE
;
249 case 10: /* YYMMDDhhmm */
251 t
->tm_year
+= ATOI2(arg
);
253 yearset
= ATOI2(arg
);
255 t
->tm_year
= yearset
+ 2000 - TM_YEAR_BASE
;
257 t
->tm_year
= yearset
+ 1900 - TM_YEAR_BASE
;
260 case 8: /* MMDDhhmm */
261 t
->tm_mon
= ATOI2(arg
);
262 --t
->tm_mon
; /* Convert from 01-12 to 00-11 */
265 t
->tm_mday
= ATOI2(arg
);
268 t
->tm_hour
= ATOI2(arg
);
271 t
->tm_min
= ATOI2(arg
);
277 t
->tm_isdst
= -1; /* Figure out DST. */
278 tsp
[0].tv_sec
= tsp
[1].tv_sec
= mktime(t
);
279 if (tsp
[0].tv_sec
== -1)
280 terr
: errx(EXIT_FAILURE
,
281 "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
283 tsp
[0].tv_nsec
= tsp
[1].tv_nsec
= 0;
287 stime_arg2(char *arg
, int year
, struct timespec
*tsp
)
291 /* Start with the current time. */
292 tmptime
= tsp
[0].tv_sec
;
293 if ((t
= localtime(&tmptime
)) == NULL
)
294 err(EXIT_FAILURE
, "localtime");
296 t
->tm_mon
= ATOI2(arg
); /* MMDDhhmm[yy] */
297 --t
->tm_mon
; /* Convert from 01-12 to 00-11 */
298 t
->tm_mday
= ATOI2(arg
);
299 t
->tm_hour
= ATOI2(arg
);
300 t
->tm_min
= ATOI2(arg
);
304 t
->tm_year
= year
+ 2000 - TM_YEAR_BASE
;
306 t
->tm_year
= year
+ 1900 - TM_YEAR_BASE
;
310 t
->tm_isdst
= -1; /* Figure out DST. */
311 tsp
[0].tv_sec
= tsp
[1].tv_sec
= mktime(t
);
312 if (tsp
[0].tv_sec
== -1)
314 "out of range or illegal time specification: MMDDhhmm[yy]");
316 tsp
[0].tv_nsec
= tsp
[1].tv_nsec
= 0;
320 stime_file(char *fname
, struct timespec
*tsp
)
324 if (stat(fname
, &sb
))
326 tsp
[0] = sb
.st_atimespec
;
327 tsp
[1] = sb
.st_mtimespec
;
333 (void)fprintf(stderr
,
334 "Usage: %s [-acfhm] [-d|--date datetime] [-r|--reference file] [-t time] file ...\n",