Annotation of src/usr.bin/touch/touch.c, Revision 1.1.1.1
1.1 deraadt 1: /* $NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $ */
2:
3: /*
4: * Copyright (c) 1993
5: * The Regents of the University of California. All rights reserved.
6: *
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. All advertising materials mentioning features or use of this software
16: * must display the following acknowledgement:
17: * This product includes software developed by the University of
18: * California, Berkeley and its contributors.
19: * 4. Neither the name of the University nor the names of its contributors
20: * may be used to endorse or promote products derived from this software
21: * without specific prior written permission.
22: *
23: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33: * SUCH DAMAGE.
34: */
35:
36: #ifndef lint
37: static char copyright[] =
38: "@(#) Copyright (c) 1993\n\
39: The Regents of the University of California. All rights reserved.\n";
40: #endif /* not lint */
41:
42: #ifndef lint
43: #if 0
44: static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
45: #endif
46: static char rcsid[] = "$NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $";
47: #endif /* not lint */
48:
49: #include <sys/types.h>
50: #include <sys/stat.h>
51: #include <sys/time.h>
52:
53: #include <err.h>
54: #include <errno.h>
55: #include <fcntl.h>
56: #include <stdio.h>
57: #include <stdlib.h>
58: #include <string.h>
59: #include <locale.h>
60: #include <time.h>
61: #include <unistd.h>
62:
63: int rw __P((char *, struct stat *, int));
64: void stime_arg1 __P((char *, struct timeval *));
65: void stime_arg2 __P((char *, int, struct timeval *));
66: void stime_file __P((char *, struct timeval *));
67: void usage __P((void));
68:
69: int
70: main(argc, argv)
71: int argc;
72: char *argv[];
73: {
74: struct stat sb;
75: struct timeval tv[2];
76: int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
77: char *p;
78:
79: setlocale(LC_ALL, "");
80:
81: aflag = cflag = fflag = mflag = timeset = 0;
82: if (gettimeofday(&tv[0], NULL))
83: err(1, "gettimeofday");
84:
85: while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF)
86: switch(ch) {
87: case 'a':
88: aflag = 1;
89: break;
90: case 'c':
91: cflag = 1;
92: break;
93: case 'f':
94: fflag = 1;
95: break;
96: case 'm':
97: mflag = 1;
98: break;
99: case 'r':
100: timeset = 1;
101: stime_file(optarg, tv);
102: break;
103: case 't':
104: timeset = 1;
105: stime_arg1(optarg, tv);
106: break;
107: case '?':
108: default:
109: usage();
110: }
111: argc -= optind;
112: argv += optind;
113:
114: /* Default is both -a and -m. */
115: if (aflag == 0 && mflag == 0)
116: aflag = mflag = 1;
117:
118: /*
119: * If no -r or -t flag, at least two operands, the first of which
120: * is an 8 or 10 digit number, use the obsolete time specification.
121: */
122: if (!timeset && argc > 1) {
123: (void)strtol(argv[0], &p, 10);
124: len = p - argv[0];
125: if (*p == '\0' && (len == 8 || len == 10)) {
126: timeset = 1;
127: stime_arg2(*argv++, len == 10, tv);
128: }
129: }
130:
131: /* Otherwise use the current time of day. */
132: if (!timeset)
133: tv[1] = tv[0];
134:
135: if (*argv == NULL)
136: usage();
137:
138: for (rval = 0; *argv; ++argv) {
139: /* See if the file exists. */
140: if (stat(*argv, &sb))
141: if (!cflag) {
142: /* Create the file. */
143: fd = open(*argv,
144: O_WRONLY | O_CREAT, DEFFILEMODE);
145: if (fd == -1 || fstat(fd, &sb) || close(fd)) {
146: rval = 1;
147: warn("%s", *argv);
148: continue;
149: }
150:
151: /* If using the current time, we're done. */
152: if (!timeset)
153: continue;
154: } else
155: continue;
156:
157: if (!aflag)
158: TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
159: if (!mflag)
160: TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
161:
162: /* Try utimes(2). */
163: if (!utimes(*argv, tv))
164: continue;
165:
166: /* If the user specified a time, nothing else we can do. */
167: if (timeset) {
168: rval = 1;
169: warn("%s", *argv);
170: }
171:
172: /*
173: * System V and POSIX 1003.1 require that a NULL argument
174: * set the access/modification times to the current time.
175: * The permission checks are different, too, in that the
176: * ability to write the file is sufficient. Take a shot.
177: */
178: if (!utimes(*argv, NULL))
179: continue;
180:
181: /* Try reading/writing. */
182: if (rw(*argv, &sb, fflag))
183: rval = 1;
184: }
185: exit(rval);
186: }
187:
188: #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
189:
190: void
191: stime_arg1(arg, tvp)
192: char *arg;
193: struct timeval *tvp;
194: {
195: struct tm *t;
196: time_t tmptime;
197: int yearset;
198: char *p;
199: /* Start with the current time. */
200: tmptime = tvp[0].tv_sec;
201: if ((t = localtime(&tmptime)) == NULL)
202: err(1, "localtime");
203: /* [[CC]YY]MMDDhhmm[.SS] */
204: if ((p = strchr(arg, '.')) == NULL)
205: t->tm_sec = 0; /* Seconds defaults to 0. */
206: else {
207: if (strlen(p + 1) != 2)
208: goto terr;
209: *p++ = '\0';
210: t->tm_sec = ATOI2(p);
211: }
212:
213: yearset = 0;
214: switch(strlen(arg)) {
215: case 12: /* CCYYMMDDhhmm */
216: t->tm_year = ATOI2(arg);
217: t->tm_year *= 100;
218: yearset = 1;
219: /* FALLTHOUGH */
220: case 10: /* YYMMDDhhmm */
221: if (yearset) {
222: yearset = ATOI2(arg);
223: t->tm_year += yearset;
224: } else {
225: yearset = ATOI2(arg);
226: if (yearset < 69)
227: t->tm_year = yearset + 2000;
228: else
229: t->tm_year = yearset + 1900;
230: }
231: t->tm_year -= 1900; /* Convert to UNIX time. */
232: /* FALLTHROUGH */
233: case 8: /* MMDDhhmm */
234: t->tm_mon = ATOI2(arg);
235: --t->tm_mon; /* Convert from 01-12 to 00-11 */
236: t->tm_mday = ATOI2(arg);
237: t->tm_hour = ATOI2(arg);
238: t->tm_min = ATOI2(arg);
239: break;
240: default:
241: goto terr;
242: }
243:
244: t->tm_isdst = -1; /* Figure out DST. */
245: tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
246: if (tvp[0].tv_sec == -1)
247: terr: errx(1,
248: "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
249:
250: tvp[0].tv_usec = tvp[1].tv_usec = 0;
251: }
252:
253: void
254: stime_arg2(arg, year, tvp)
255: char *arg;
256: int year;
257: struct timeval *tvp;
258: {
259: struct tm *t;
260: time_t tmptime;
261: /* Start with the current time. */
262: tmptime = tvp[0].tv_sec;
263: if ((t = localtime(&tmptime)) == NULL)
264: err(1, "localtime");
265:
266: t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
267: --t->tm_mon; /* Convert from 01-12 to 00-11 */
268: t->tm_mday = ATOI2(arg);
269: t->tm_hour = ATOI2(arg);
270: t->tm_min = ATOI2(arg);
271: if (year)
272: t->tm_year = ATOI2(arg);
273:
274: t->tm_isdst = -1; /* Figure out DST. */
275: tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
276: if (tvp[0].tv_sec == -1)
277: errx(1,
278: "out of range or illegal time specification: MMDDhhmm[yy]");
279:
280: tvp[0].tv_usec = tvp[1].tv_usec = 0;
281: }
282:
283: void
284: stime_file(fname, tvp)
285: char *fname;
286: struct timeval *tvp;
287: {
288: struct stat sb;
289:
290: if (stat(fname, &sb))
291: err(1, "%s", fname);
292: TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
293: TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
294: }
295:
296: int
297: rw(fname, sbp, force)
298: char *fname;
299: struct stat *sbp;
300: int force;
301: {
302: int fd, needed_chmod, rval;
303: u_char byte;
304:
305: /* Try regular files and directories. */
306: if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) {
307: warnx("%s: %s", fname, strerror(EFTYPE));
308: return (1);
309: }
310:
311: needed_chmod = rval = 0;
312: if ((fd = open(fname, O_RDWR, 0)) == -1) {
313: if (!force || chmod(fname, DEFFILEMODE))
314: goto err;
315: if ((fd = open(fname, O_RDWR, 0)) == -1)
316: goto err;
317: needed_chmod = 1;
318: }
319:
320: if (sbp->st_size != 0) {
321: if (read(fd, &byte, sizeof(byte)) != sizeof(byte))
322: goto err;
323: if (lseek(fd, (off_t)0, SEEK_SET) == -1)
324: goto err;
325: if (write(fd, &byte, sizeof(byte)) != sizeof(byte))
326: goto err;
327: } else {
328: if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
329: err: rval = 1;
330: warn("%s", fname);
331: } else if (ftruncate(fd, (off_t)0)) {
332: rval = 1;
333: warn("%s: file modified", fname);
334: }
335: }
336:
337: if (close(fd) && rval != 1) {
338: rval = 1;
339: warn("%s", fname);
340: }
341: if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) {
342: rval = 1;
343: warn("%s: permissions modified", fname);
344: }
345: return (rval);
346: }
347:
348: __dead void
349: usage()
350: {
351: (void)fprintf(stderr,
352: "usage: touch [-acfm] [-r file] [-t time] file ...\n");
353: exit(1);
354: }