Annotation of src/usr.bin/touch/touch.c, Revision 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: }