Annotation of src/usr.bin/touch/touch.c, Revision 1.21
1.21 ! jmc 1: /* $OpenBSD: touch.c,v 1.20 2011/08/30 02:07:11 guenther Exp $ */
1.1 deraadt 2: /* $NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $ */
3:
4: /*
5: * Copyright (c) 1993
6: * The Regents of the University of California. All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
1.9 millert 16: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 17: * may be used to endorse or promote products derived from this software
18: * without specific prior written permission.
19: *
20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30: * SUCH DAMAGE.
31: */
32:
33: #include <sys/types.h>
34: #include <sys/stat.h>
35: #include <sys/time.h>
36:
1.15 millert 37: #include <ctype.h>
1.1 deraadt 38: #include <err.h>
39: #include <errno.h>
40: #include <fcntl.h>
41: #include <stdio.h>
42: #include <stdlib.h>
43: #include <string.h>
44: #include <locale.h>
45: #include <time.h>
1.5 pjanzen 46: #include <tzfile.h>
1.1 deraadt 47: #include <unistd.h>
48:
1.18 guenther 49: void stime_arg1(char *, struct timespec *);
50: void stime_arg2(char *, int, struct timespec *);
51: void stime_argd(char *, struct timespec *);
52: void stime_file(char *, struct timespec *);
1.13 henning 53: __dead void usage(void);
1.1 deraadt 54:
55: int
1.10 deraadt 56: main(int argc, char *argv[])
1.1 deraadt 57: {
1.18 guenther 58: struct timespec ts[2];
1.14 henning 59: int aflag, cflag, mflag, ch, fd, len, rval, timeset;
60: char *p;
1.1 deraadt 61:
1.13 henning 62: (void)setlocale(LC_ALL, "");
1.1 deraadt 63:
1.11 otto 64: aflag = cflag = mflag = timeset = 0;
1.18 guenther 65: while ((ch = getopt(argc, argv, "acd:fmr:t:")) != -1)
1.12 deraadt 66: switch (ch) {
1.1 deraadt 67: case 'a':
68: aflag = 1;
69: break;
70: case 'c':
71: cflag = 1;
72: break;
1.18 guenther 73: case 'd':
74: timeset = 1;
75: stime_argd(optarg, ts);
76: break;
1.1 deraadt 77: case 'f':
78: break;
79: case 'm':
80: mflag = 1;
81: break;
82: case 'r':
83: timeset = 1;
1.18 guenther 84: stime_file(optarg, ts);
1.1 deraadt 85: break;
86: case 't':
87: timeset = 1;
1.18 guenther 88: stime_arg1(optarg, ts);
1.1 deraadt 89: break;
90: default:
91: usage();
92: }
93: argc -= optind;
94: argv += optind;
95:
96: /* Default is both -a and -m. */
97: if (aflag == 0 && mflag == 0)
98: aflag = mflag = 1;
99:
100: /*
101: * If no -r or -t flag, at least two operands, the first of which
102: * is an 8 or 10 digit number, use the obsolete time specification.
103: */
104: if (!timeset && argc > 1) {
105: (void)strtol(argv[0], &p, 10);
106: len = p - argv[0];
107: if (*p == '\0' && (len == 8 || len == 10)) {
108: timeset = 1;
1.18 guenther 109: stime_arg2(*argv++, len == 10, ts);
1.1 deraadt 110: }
111: }
112:
113: /* Otherwise use the current time of day. */
114: if (!timeset)
1.18 guenther 115: ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
116:
117: if (!aflag)
118: ts[0].tv_nsec = UTIME_OMIT;
119: if (!mflag)
120: ts[1].tv_nsec = UTIME_OMIT;
1.1 deraadt 121:
122: if (*argv == NULL)
123: usage();
124:
125: for (rval = 0; *argv; ++argv) {
1.18 guenther 126: /* Update the file's timestamp if it exists. */
127: if (! utimensat(AT_FDCWD, *argv, ts, 0))
128: continue;
129: if (errno != ENOENT) {
130: rval = 1;
131: warn("%s", *argv);
132: continue;
1.4 pjanzen 133: }
1.1 deraadt 134:
1.18 guenther 135: /* Didn't exist; should we create it? */
136: if (cflag)
1.1 deraadt 137: continue;
138:
1.18 guenther 139: /* Create the file. */
140: fd = open(*argv, O_WRONLY | O_CREAT, DEFFILEMODE);
141: if (fd == -1 || futimens(fd, ts) || close(fd)) {
1.1 deraadt 142: rval = 1;
143: warn("%s", *argv);
144: }
145: }
146: exit(rval);
147: }
148:
1.6 pjanzen 149: #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
1.1 deraadt 150:
151: void
1.18 guenther 152: stime_arg1(char *arg, struct timespec *tsp)
1.1 deraadt 153: {
1.15 millert 154: struct tm *lt;
1.13 henning 155: time_t tmptime;
156: int yearset;
1.15 millert 157: char *dot, *p;
1.1 deraadt 158: /* Start with the current time. */
1.19 guenther 159: tmptime = time(NULL);
1.15 millert 160: if ((lt = localtime(&tmptime)) == NULL)
1.1 deraadt 161: err(1, "localtime");
162: /* [[CC]YY]MMDDhhmm[.SS] */
1.15 millert 163: for (p = arg, dot = NULL; *p != '\0'; p++) {
1.16 millert 164: if (*p == '.' && dot == NULL)
1.15 millert 165: dot = p;
166: else if (!isdigit((unsigned char)*p))
167: goto terr;
168: }
169: if (dot == NULL)
170: lt->tm_sec = 0; /* Seconds defaults to 0. */
1.1 deraadt 171: else {
1.15 millert 172: *dot++ = '\0';
173: if (strlen(dot) != 2)
174: goto terr;
175: lt->tm_sec = ATOI2(dot);
176: if (lt->tm_sec > 61) /* Could be leap second. */
1.1 deraadt 177: goto terr;
178: }
1.12 deraadt 179:
1.1 deraadt 180: yearset = 0;
1.12 deraadt 181: switch (strlen(arg)) {
1.1 deraadt 182: case 12: /* CCYYMMDDhhmm */
1.15 millert 183: lt->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
1.1 deraadt 184: yearset = 1;
1.8 henning 185: /* FALLTHROUGH */
1.1 deraadt 186: case 10: /* YYMMDDhhmm */
187: if (yearset) {
188: yearset = ATOI2(arg);
1.15 millert 189: lt->tm_year += yearset;
1.1 deraadt 190: } else {
191: yearset = ATOI2(arg);
1.20 guenther 192: /* POSIX logic: [00,68]=>20xx, [69,99]=>19xx */
193: lt->tm_year = yearset + 1900 - TM_YEAR_BASE;
194: if (yearset < 69)
195: lt->tm_year += 100;
1.1 deraadt 196: }
197: /* FALLTHROUGH */
198: case 8: /* MMDDhhmm */
1.15 millert 199: lt->tm_mon = ATOI2(arg);
200: if (lt->tm_mon > 12 || lt->tm_mon == 0)
201: goto terr;
202: --lt->tm_mon; /* Convert from 01-12 to 00-11 */
203: lt->tm_mday = ATOI2(arg);
204: if (lt->tm_mday > 31 || lt->tm_mday == 0)
205: goto terr;
206: lt->tm_hour = ATOI2(arg);
207: if (lt->tm_hour > 23)
208: goto terr;
209: lt->tm_min = ATOI2(arg);
210: if (lt->tm_min > 59)
211: goto terr;
1.1 deraadt 212: break;
213: default:
214: goto terr;
215: }
216:
1.15 millert 217: lt->tm_isdst = -1; /* Figure out DST. */
1.18 guenther 218: tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
219: if (tsp[0].tv_sec == -1)
1.1 deraadt 220: terr: errx(1,
221: "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
222:
1.18 guenther 223: tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
1.1 deraadt 224: }
225:
226: void
1.18 guenther 227: stime_arg2(char *arg, int year, struct timespec *tsp)
1.1 deraadt 228: {
1.15 millert 229: struct tm *lt;
1.13 henning 230: time_t tmptime;
1.1 deraadt 231: /* Start with the current time. */
1.19 guenther 232: tmptime = time(NULL);
1.15 millert 233: if ((lt = localtime(&tmptime)) == NULL)
1.1 deraadt 234: err(1, "localtime");
235:
1.15 millert 236: lt->tm_mon = ATOI2(arg); /* MMDDhhmm[YY] */
237: if (lt->tm_mon > 12 || lt->tm_mon == 0)
238: goto terr;
239: --lt->tm_mon; /* Convert from 01-12 to 00-11 */
240: lt->tm_mday = ATOI2(arg);
241: if (lt->tm_mday > 31 || lt->tm_mday == 0)
242: goto terr;
243: lt->tm_hour = ATOI2(arg);
244: if (lt->tm_hour > 23)
245: goto terr;
246: lt->tm_min = ATOI2(arg);
247: if (lt->tm_min > 59)
248: goto terr;
1.4 pjanzen 249: if (year) {
1.20 guenther 250: year = ATOI2(arg);
251: /* POSIX logic: [00,68]=>20xx, [69,99]=>19xx */
252: lt->tm_year = year + 1900 - TM_YEAR_BASE;
253: if (year < 69)
254: lt->tm_year += 100;
1.4 pjanzen 255: }
1.15 millert 256: lt->tm_sec = 0;
1.1 deraadt 257:
1.15 millert 258: lt->tm_isdst = -1; /* Figure out DST. */
1.18 guenther 259: tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
260: if (tsp[0].tv_sec == -1)
1.15 millert 261: terr: errx(1,
1.4 pjanzen 262: "out of range or illegal time specification: MMDDhhmm[YY]");
1.1 deraadt 263:
1.18 guenther 264: tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
1.1 deraadt 265: }
266:
267: void
1.18 guenther 268: stime_file(char *fname, struct timespec *tsp)
1.1 deraadt 269: {
1.13 henning 270: struct stat sb;
1.1 deraadt 271:
272: if (stat(fname, &sb))
273: err(1, "%s", fname);
1.18 guenther 274: tsp[0] = sb.st_atim;
275: tsp[1] = sb.st_mtim;
276: }
277:
278: void
279: stime_argd(char *arg, struct timespec *tsp)
280: {
281: struct tm tm;
282: char *frac, *p;
283: int utc = 0;
284:
285: /* accept YYYY-MM-DD(T| )hh:mm:ss[(.|,)frac][Z] */
286: memset(&tm, 0, sizeof(tm));
287: p = strptime(arg, "%F", &tm);
288: if (p == NULL || (*p != 'T' && *p != ' '))
289: goto terr;
290: p = strptime(p + 1, "%T", &tm);
291: if (p == NULL)
292: goto terr;
293: tsp[0].tv_nsec = 0;
294: if (*p == '.' || *p == ',') {
295: frac = ++p;
296: while (isdigit((unsigned char)*p)) {
297: if (p - frac < 9) {
298: tsp[0].tv_nsec = tsp[0].tv_nsec * 10 +
299: *p - '0';
300: }
301: p++;
302: }
303: if (p == frac)
304: goto terr;
305:
306: /* fill in the trailing zeros */
307: while (p - frac-- < 9)
308: tsp[0].tv_nsec *= 10;
309: }
310: if (*p == 'Z') {
311: utc = 1;
312: p++;
313: }
314: if (*p != '\0')
315: goto terr;
316:
317: tm.tm_isdst = -1;
318: tsp[0].tv_sec = utc ? timegm(&tm) : mktime(&tm);
319: if (tsp[0].tv_sec == -1)
320: terr: errx(1,
321: "out of range or illegal time specification: YYYY-MM-DDThh:mm:ss[.frac][Z]");
322: tsp[1] = tsp[0];
1.1 deraadt 323: }
324:
325: __dead void
1.10 deraadt 326: usage(void)
1.1 deraadt 327: {
328: (void)fprintf(stderr,
1.21 ! jmc 329: "usage: touch [-acm] [-d ccyy-mm-ddTHH:MM:SS[.frac][Z]] [-r file]\n"
! 330: " [-t [[cc]yy]mmddHHMM[.SS]] file ...\n");
1.1 deraadt 331: exit(1);
332: }