Annotation of src/usr.bin/touch/touch.c, Revision 1.18
1.18 ! guenther 1: /* $OpenBSD: touch.c,v 1.17 2007/08/06 19:16:06 sobrado 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.18 ! guenther 159: tmptime = tsp[0].tv_sec;
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.15 millert 192: /* Preserve current century. */
193: lt->tm_year = ((lt->tm_year / 100) * 100) + yearset;
1.1 deraadt 194: }
195: /* FALLTHROUGH */
196: case 8: /* MMDDhhmm */
1.15 millert 197: lt->tm_mon = ATOI2(arg);
198: if (lt->tm_mon > 12 || lt->tm_mon == 0)
199: goto terr;
200: --lt->tm_mon; /* Convert from 01-12 to 00-11 */
201: lt->tm_mday = ATOI2(arg);
202: if (lt->tm_mday > 31 || lt->tm_mday == 0)
203: goto terr;
204: lt->tm_hour = ATOI2(arg);
205: if (lt->tm_hour > 23)
206: goto terr;
207: lt->tm_min = ATOI2(arg);
208: if (lt->tm_min > 59)
209: goto terr;
1.1 deraadt 210: break;
211: default:
212: goto terr;
213: }
214:
1.15 millert 215: lt->tm_isdst = -1; /* Figure out DST. */
1.18 ! guenther 216: tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
! 217: if (tsp[0].tv_sec == -1)
1.1 deraadt 218: terr: errx(1,
219: "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
220:
1.18 ! guenther 221: tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
1.1 deraadt 222: }
223:
224: void
1.18 ! guenther 225: stime_arg2(char *arg, int year, struct timespec *tsp)
1.1 deraadt 226: {
1.15 millert 227: struct tm *lt;
1.13 henning 228: time_t tmptime;
1.1 deraadt 229: /* Start with the current time. */
1.18 ! guenther 230: tmptime = tsp[0].tv_sec;
1.15 millert 231: if ((lt = localtime(&tmptime)) == NULL)
1.1 deraadt 232: err(1, "localtime");
233:
1.15 millert 234: lt->tm_mon = ATOI2(arg); /* MMDDhhmm[YY] */
235: if (lt->tm_mon > 12 || lt->tm_mon == 0)
236: goto terr;
237: --lt->tm_mon; /* Convert from 01-12 to 00-11 */
238: lt->tm_mday = ATOI2(arg);
239: if (lt->tm_mday > 31 || lt->tm_mday == 0)
240: goto terr;
241: lt->tm_hour = ATOI2(arg);
242: if (lt->tm_hour > 23)
243: goto terr;
244: lt->tm_min = ATOI2(arg);
245: if (lt->tm_min > 59)
246: goto terr;
1.4 pjanzen 247: if (year) {
1.15 millert 248: year = ATOI2(arg); /* Preserve current century. */
249: lt->tm_year = ((lt->tm_year / 100) * 100) + year;
1.4 pjanzen 250: }
1.15 millert 251: lt->tm_sec = 0;
1.1 deraadt 252:
1.15 millert 253: lt->tm_isdst = -1; /* Figure out DST. */
1.18 ! guenther 254: tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
! 255: if (tsp[0].tv_sec == -1)
1.15 millert 256: terr: errx(1,
1.4 pjanzen 257: "out of range or illegal time specification: MMDDhhmm[YY]");
1.1 deraadt 258:
1.18 ! guenther 259: tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
1.1 deraadt 260: }
261:
262: void
1.18 ! guenther 263: stime_file(char *fname, struct timespec *tsp)
1.1 deraadt 264: {
1.13 henning 265: struct stat sb;
1.1 deraadt 266:
267: if (stat(fname, &sb))
268: err(1, "%s", fname);
1.18 ! guenther 269: tsp[0] = sb.st_atim;
! 270: tsp[1] = sb.st_mtim;
! 271: }
! 272:
! 273: void
! 274: stime_argd(char *arg, struct timespec *tsp)
! 275: {
! 276: struct tm tm;
! 277: char *frac, *p;
! 278: int utc = 0;
! 279:
! 280: /* accept YYYY-MM-DD(T| )hh:mm:ss[(.|,)frac][Z] */
! 281: memset(&tm, 0, sizeof(tm));
! 282: p = strptime(arg, "%F", &tm);
! 283: if (p == NULL || (*p != 'T' && *p != ' '))
! 284: goto terr;
! 285: p = strptime(p + 1, "%T", &tm);
! 286: if (p == NULL)
! 287: goto terr;
! 288: tsp[0].tv_nsec = 0;
! 289: if (*p == '.' || *p == ',') {
! 290: frac = ++p;
! 291: while (isdigit((unsigned char)*p)) {
! 292: if (p - frac < 9) {
! 293: tsp[0].tv_nsec = tsp[0].tv_nsec * 10 +
! 294: *p - '0';
! 295: }
! 296: p++;
! 297: }
! 298: if (p == frac)
! 299: goto terr;
! 300:
! 301: /* fill in the trailing zeros */
! 302: while (p - frac-- < 9)
! 303: tsp[0].tv_nsec *= 10;
! 304: }
! 305: if (*p == 'Z') {
! 306: utc = 1;
! 307: p++;
! 308: }
! 309: if (*p != '\0')
! 310: goto terr;
! 311:
! 312: tm.tm_isdst = -1;
! 313: tsp[0].tv_sec = utc ? timegm(&tm) : mktime(&tm);
! 314: if (tsp[0].tv_sec == -1)
! 315: terr: errx(1,
! 316: "out of range or illegal time specification: YYYY-MM-DDThh:mm:ss[.frac][Z]");
! 317: tsp[1] = tsp[0];
1.1 deraadt 318: }
319:
320: __dead void
1.10 deraadt 321: usage(void)
1.1 deraadt 322: {
323: (void)fprintf(stderr,
1.18 ! guenther 324: "usage: touch [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] [-r file]\n"
! 325: " [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n");
1.1 deraadt 326: exit(1);
327: }