Annotation of src/usr.bin/touch/touch.c, Revision 1.11
1.11 ! otto 1: /* $OpenBSD: touch.c,v 1.10 2003/06/10 22:20:53 deraadt 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: #ifndef lint
34: static char copyright[] =
35: "@(#) Copyright (c) 1993\n\
36: The Regents of the University of California. All rights reserved.\n";
37: #endif /* not lint */
38:
39: #ifndef lint
40: #if 0
41: static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
42: #endif
1.11 ! otto 43: static char rcsid[] = "$OpenBSD: touch.c,v 1.10 2003/06/10 22:20:53 deraadt Exp $";
1.1 deraadt 44: #endif /* not lint */
45:
46: #include <sys/types.h>
47: #include <sys/stat.h>
48: #include <sys/time.h>
49:
50: #include <err.h>
51: #include <errno.h>
52: #include <fcntl.h>
53: #include <stdio.h>
54: #include <stdlib.h>
55: #include <string.h>
56: #include <locale.h>
57: #include <time.h>
1.5 pjanzen 58: #include <tzfile.h>
1.1 deraadt 59: #include <unistd.h>
60:
1.7 millert 61: void stime_arg1(char *, struct timeval *);
62: void stime_arg2(char *, int, struct timeval *);
63: void stime_file(char *, struct timeval *);
64: void usage(void);
1.1 deraadt 65:
66: int
1.10 deraadt 67: main(int argc, char *argv[])
1.1 deraadt 68: {
69: struct stat sb;
70: struct timeval tv[2];
1.11 ! otto 71: int aflag, cflag, mflag, ch, fd, len, rval, timeset;
1.1 deraadt 72: char *p;
73:
74: setlocale(LC_ALL, "");
75:
1.11 ! otto 76: aflag = cflag = mflag = timeset = 0;
1.1 deraadt 77: if (gettimeofday(&tv[0], NULL))
78: err(1, "gettimeofday");
79:
1.3 millert 80: while ((ch = getopt(argc, argv, "acfmr:t:")) != -1)
1.1 deraadt 81: switch(ch) {
82: case 'a':
83: aflag = 1;
84: break;
85: case 'c':
86: cflag = 1;
87: break;
88: case 'f':
89: break;
90: case 'm':
91: mflag = 1;
92: break;
93: case 'r':
94: timeset = 1;
95: stime_file(optarg, tv);
96: break;
97: case 't':
98: timeset = 1;
99: stime_arg1(optarg, tv);
100: break;
101: default:
102: usage();
103: }
104: argc -= optind;
105: argv += optind;
106:
107: /* Default is both -a and -m. */
108: if (aflag == 0 && mflag == 0)
109: aflag = mflag = 1;
110:
111: /*
112: * If no -r or -t flag, at least two operands, the first of which
113: * is an 8 or 10 digit number, use the obsolete time specification.
114: */
115: if (!timeset && argc > 1) {
116: (void)strtol(argv[0], &p, 10);
117: len = p - argv[0];
118: if (*p == '\0' && (len == 8 || len == 10)) {
119: timeset = 1;
120: stime_arg2(*argv++, len == 10, tv);
121: }
122: }
123:
124: /* Otherwise use the current time of day. */
125: if (!timeset)
126: tv[1] = tv[0];
127:
128: if (*argv == NULL)
129: usage();
130:
131: for (rval = 0; *argv; ++argv) {
132: /* See if the file exists. */
1.4 pjanzen 133: if (stat(*argv, &sb)) {
1.1 deraadt 134: if (!cflag) {
135: /* Create the file. */
136: fd = open(*argv,
137: O_WRONLY | O_CREAT, DEFFILEMODE);
138: if (fd == -1 || fstat(fd, &sb) || close(fd)) {
139: rval = 1;
140: warn("%s", *argv);
141: continue;
142: }
143:
144: /* If using the current time, we're done. */
145: if (!timeset)
146: continue;
147: } else
148: continue;
1.4 pjanzen 149: }
1.1 deraadt 150:
151: if (!aflag)
152: TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
153: if (!mflag)
154: TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
155:
156: /* Try utimes(2). */
157: if (!utimes(*argv, tv))
158: continue;
159:
160: /* If the user specified a time, nothing else we can do. */
161: if (timeset) {
162: rval = 1;
163: warn("%s", *argv);
164: }
165:
166: /*
167: * System V and POSIX 1003.1 require that a NULL argument
168: * set the access/modification times to the current time.
169: * The permission checks are different, too, in that the
170: * ability to write the file is sufficient. Take a shot.
171: */
172: if (!utimes(*argv, NULL))
173: continue;
174:
1.11 ! otto 175: rval = 1;
! 176: warn("%s", *argv);
1.1 deraadt 177: }
178: exit(rval);
179: }
180:
1.6 pjanzen 181: #define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
1.1 deraadt 182:
183: void
1.10 deraadt 184: stime_arg1(char *arg, struct timeval *tvp)
1.1 deraadt 185: {
186: struct tm *t;
187: time_t tmptime;
188: int yearset;
189: char *p;
190: /* Start with the current time. */
191: tmptime = tvp[0].tv_sec;
192: if ((t = localtime(&tmptime)) == NULL)
193: err(1, "localtime");
194: /* [[CC]YY]MMDDhhmm[.SS] */
195: if ((p = strchr(arg, '.')) == NULL)
196: t->tm_sec = 0; /* Seconds defaults to 0. */
197: else {
198: if (strlen(p + 1) != 2)
199: goto terr;
200: *p++ = '\0';
201: t->tm_sec = ATOI2(p);
202: }
203:
204: yearset = 0;
205: switch(strlen(arg)) {
206: case 12: /* CCYYMMDDhhmm */
1.6 pjanzen 207: t->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
1.1 deraadt 208: yearset = 1;
1.8 henning 209: /* FALLTHROUGH */
1.1 deraadt 210: case 10: /* YYMMDDhhmm */
211: if (yearset) {
212: yearset = ATOI2(arg);
213: t->tm_year += yearset;
214: } else {
215: yearset = ATOI2(arg);
216: if (yearset < 69)
1.5 pjanzen 217: t->tm_year = yearset + 2000 - TM_YEAR_BASE;
1.1 deraadt 218: else
1.5 pjanzen 219: t->tm_year = yearset + 1900 - TM_YEAR_BASE;
1.1 deraadt 220: }
221: /* FALLTHROUGH */
222: case 8: /* MMDDhhmm */
223: t->tm_mon = ATOI2(arg);
224: --t->tm_mon; /* Convert from 01-12 to 00-11 */
225: t->tm_mday = ATOI2(arg);
226: t->tm_hour = ATOI2(arg);
227: t->tm_min = ATOI2(arg);
228: break;
229: default:
230: goto terr;
231: }
232:
233: t->tm_isdst = -1; /* Figure out DST. */
234: tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
235: if (tvp[0].tv_sec == -1)
236: terr: errx(1,
237: "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
238:
239: tvp[0].tv_usec = tvp[1].tv_usec = 0;
240: }
241:
242: void
1.10 deraadt 243: stime_arg2(char *arg, int year, struct timeval *tvp)
1.1 deraadt 244: {
245: struct tm *t;
246: time_t tmptime;
247: /* Start with the current time. */
248: tmptime = tvp[0].tv_sec;
249: if ((t = localtime(&tmptime)) == NULL)
250: err(1, "localtime");
251:
1.4 pjanzen 252: t->tm_mon = ATOI2(arg); /* MMDDhhmm[YY] */
1.1 deraadt 253: --t->tm_mon; /* Convert from 01-12 to 00-11 */
254: t->tm_mday = ATOI2(arg);
255: t->tm_hour = ATOI2(arg);
256: t->tm_min = ATOI2(arg);
1.4 pjanzen 257: if (year) {
1.5 pjanzen 258: year = ATOI2(arg);
259: if (year < 69)
260: t->tm_year = year + 2000 - TM_YEAR_BASE;
261: else
262: t->tm_year = year + 1900 - TM_YEAR_BASE;
1.4 pjanzen 263: }
1.5 pjanzen 264: t->tm_sec = 0;
1.1 deraadt 265:
266: t->tm_isdst = -1; /* Figure out DST. */
267: tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
268: if (tvp[0].tv_sec == -1)
269: errx(1,
1.4 pjanzen 270: "out of range or illegal time specification: MMDDhhmm[YY]");
1.1 deraadt 271:
272: tvp[0].tv_usec = tvp[1].tv_usec = 0;
273: }
274:
275: void
1.10 deraadt 276: stime_file(char *fname, struct timeval *tvp)
1.1 deraadt 277: {
278: struct stat sb;
279:
280: if (stat(fname, &sb))
281: err(1, "%s", fname);
282: TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
283: TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
284: }
285:
286: __dead void
1.10 deraadt 287: usage(void)
1.1 deraadt 288: {
289: (void)fprintf(stderr,
1.11 ! otto 290: "usage: touch [-acm] [-r file] [-t time] file ...\n");
1.1 deraadt 291: exit(1);
292: }