etc/protocols - sync with NetBSD-8
[minix.git] / usr.bin / touch / touch.c
blob19638152b8f9a59cf14632ae0ebca40ada7634ae
1 /* $NetBSD: touch.c,v 1.33 2015/03/02 03:17:24 enami Exp $ */
3 /*
4 * Copyright (c) 1993
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
9 * are met:
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
29 * SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT("@(#) Copyright (c) 1993\
35 The Regents of the University of California. All rights reserved.");
36 #endif /* not lint */
38 #ifndef lint
39 #if 0
40 static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
41 #endif
42 __RCSID("$NetBSD: touch.c,v 1.33 2015/03/02 03:17:24 enami Exp $");
43 #endif /* not lint */
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/time.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <locale.h>
56 #include <time.h>
57 #include <tzfile.h>
58 #include <unistd.h>
59 #include <util.h>
60 #include <getopt.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,
70 'd' },
71 { "reference", required_argument, 0,
72 'r' },
73 { NULL, 0, 0,
74 0 },
77 int
78 main(int argc, char *argv[])
80 struct stat sb;
81 struct timespec ts[2];
82 int aflag, cflag, hflag, mflag, ch, fd, len, rval, timeset;
83 char *p;
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,
94 NULL)) != -1)
95 switch(ch) {
96 case 'a':
97 aflag = 1;
98 break;
99 case 'c':
100 cflag = 1;
101 break;
102 case 'd':
103 timeset = 1;
104 stime_arg0(optarg, ts);
105 break;
106 case 'f':
107 break;
108 case 'h':
109 hflag = 1;
110 break;
111 case 'm':
112 mflag = 1;
113 break;
114 case 'r':
115 timeset = 1;
116 stime_file(optarg, ts);
117 break;
118 case 't':
119 timeset = 1;
120 stime_arg1(optarg, ts);
121 break;
122 case '?':
123 default:
124 usage();
126 argc -= optind;
127 argv += optind;
129 /* Default is both -a and -m. */
130 if (aflag == 0 && mflag == 0)
131 aflag = mflag = 1;
133 if (hflag) {
134 cflag = 1; /* Don't create new file */
135 change_file_times = lutimens;
136 get_file_status = lstat;
137 } else {
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);
148 len = p - argv[0];
149 if (*p == '\0' && (len == 8 || len == 10)) {
150 timeset = 1;
151 stime_arg2(*argv++, len == 10, ts);
155 /* Otherwise use the current time of day. */
156 if (!timeset)
157 ts[1] = ts[0];
159 if (*argv == NULL)
160 usage();
162 for (rval = EXIT_SUCCESS; *argv; ++argv) {
163 /* See if the file exists. */
164 if ((*get_file_status)(*argv, &sb)) {
165 if (!cflag) {
166 /* Create the file. */
167 fd = open(*argv,
168 O_WRONLY | O_CREAT, DEFFILEMODE);
169 if (fd == -1 || fstat(fd, &sb) || close(fd)) {
170 rval = EXIT_FAILURE;
171 warn("%s", *argv);
172 continue;
175 /* If using the current time, we're done. */
176 if (!timeset)
177 continue;
178 } else
179 continue;
181 if (!aflag)
182 ts[0] = sb.st_atimespec;
183 if (!mflag)
184 ts[1] = sb.st_mtimespec;
186 /* Try utimes(2). */
187 if (!(*change_file_times)(*argv, ts))
188 continue;
190 /* If the user specified a time, nothing else we can do. */
191 if (timeset) {
192 rval = EXIT_FAILURE;
193 warn("%s", *argv);
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))
203 continue;
205 rval = EXIT_FAILURE;
206 warn("%s", *argv);
208 exit(rval);
211 #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
213 static void
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;
222 static void
223 stime_arg1(char *arg, struct timespec *tsp)
225 struct tm *t;
226 time_t tmptime;
227 int yearset;
228 char *p;
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. */
236 else {
237 if (strlen(p + 1) != 2)
238 goto terr;
239 *p++ = '\0';
240 t->tm_sec = ATOI2(p);
243 yearset = 0;
244 switch (strlen(arg)) {
245 case 12: /* CCYYMMDDhhmm */
246 t->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
247 yearset = 1;
248 /* FALLTHROUGH */
249 case 10: /* YYMMDDhhmm */
250 if (yearset) {
251 t->tm_year += ATOI2(arg);
252 } else {
253 yearset = ATOI2(arg);
254 if (yearset < 69)
255 t->tm_year = yearset + 2000 - TM_YEAR_BASE;
256 else
257 t->tm_year = yearset + 1900 - TM_YEAR_BASE;
259 /* FALLTHROUGH */
260 case 8: /* MMDDhhmm */
261 t->tm_mon = ATOI2(arg);
262 --t->tm_mon; /* Convert from 01-12 to 00-11 */
263 /* FALLTHROUGH */
264 case 6:
265 t->tm_mday = ATOI2(arg);
266 /* FALLTHROUGH */
267 case 4:
268 t->tm_hour = ATOI2(arg);
269 /* FALLTHROUGH */
270 case 2:
271 t->tm_min = ATOI2(arg);
272 break;
273 default:
274 goto terr;
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;
286 static void
287 stime_arg2(char *arg, int year, struct timespec *tsp)
289 struct tm *t;
290 time_t tmptime;
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);
301 if (year) {
302 year = ATOI2(arg);
303 if (year < 69)
304 t->tm_year = year + 2000 - TM_YEAR_BASE;
305 else
306 t->tm_year = year + 1900 - TM_YEAR_BASE;
308 t->tm_sec = 0;
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)
313 errx(EXIT_FAILURE,
314 "out of range or illegal time specification: MMDDhhmm[yy]");
316 tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
319 static void
320 stime_file(char *fname, struct timespec *tsp)
322 struct stat sb;
324 if (stat(fname, &sb))
325 err(1, "%s", fname);
326 tsp[0] = sb.st_atimespec;
327 tsp[1] = sb.st_mtimespec;
330 static void
331 usage(void)
333 (void)fprintf(stderr,
334 "Usage: %s [-acfhm] [-d|--date datetime] [-r|--reference file] [-t time] file ...\n",
335 getprogname());
336 exit(EXIT_FAILURE);