touch.c revision 48566
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 351590Srgrimesstatic char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 411590Srgrimesstatic char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; 421590Srgrimes#endif /* not lint */ 431590Srgrimes 441590Srgrimes#include <sys/types.h> 451590Srgrimes#include <sys/stat.h> 461590Srgrimes#include <sys/time.h> 471590Srgrimes 481590Srgrimes#include <err.h> 491590Srgrimes#include <errno.h> 501590Srgrimes#include <fcntl.h> 511590Srgrimes#include <stdio.h> 521590Srgrimes#include <stdlib.h> 531590Srgrimes#include <string.h> 541590Srgrimes#include <time.h> 551590Srgrimes#include <unistd.h> 561590Srgrimes 571590Srgrimesint rw __P((char *, struct stat *, int)); 581590Srgrimesvoid stime_arg1 __P((char *, struct timeval *)); 591590Srgrimesvoid stime_arg2 __P((char *, int, struct timeval *)); 601590Srgrimesvoid stime_file __P((char *, struct timeval *)); 611590Srgrimesvoid usage __P((void)); 621590Srgrimes 631590Srgrimesint 641590Srgrimesmain(argc, argv) 651590Srgrimes int argc; 661590Srgrimes char *argv[]; 671590Srgrimes{ 681590Srgrimes struct stat sb; 691590Srgrimes struct timeval tv[2]; 701590Srgrimes int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; 711590Srgrimes char *p; 721590Srgrimes 731590Srgrimes aflag = cflag = fflag = mflag = timeset = 0; 741590Srgrimes if (gettimeofday(&tv[0], NULL)) 751590Srgrimes err(1, "gettimeofday"); 761590Srgrimes 7724360Simp while ((ch = getopt(argc, argv, "acfmr:t:")) != -1) 781590Srgrimes switch(ch) { 791590Srgrimes case 'a': 801590Srgrimes aflag = 1; 811590Srgrimes break; 821590Srgrimes case 'c': 831590Srgrimes cflag = 1; 841590Srgrimes break; 851590Srgrimes case 'f': 861590Srgrimes fflag = 1; 871590Srgrimes break; 881590Srgrimes case 'm': 891590Srgrimes mflag = 1; 901590Srgrimes break; 911590Srgrimes case 'r': 921590Srgrimes timeset = 1; 931590Srgrimes stime_file(optarg, tv); 941590Srgrimes break; 951590Srgrimes case 't': 961590Srgrimes timeset = 1; 971590Srgrimes stime_arg1(optarg, tv); 981590Srgrimes break; 991590Srgrimes case '?': 1001590Srgrimes default: 1011590Srgrimes usage(); 1021590Srgrimes } 1031590Srgrimes argc -= optind; 1041590Srgrimes argv += optind; 1051590Srgrimes 1061590Srgrimes /* Default is both -a and -m. */ 1071590Srgrimes if (aflag == 0 && mflag == 0) 1081590Srgrimes aflag = mflag = 1; 1091590Srgrimes 1101590Srgrimes /* 1111590Srgrimes * If no -r or -t flag, at least two operands, the first of which 1121590Srgrimes * is an 8 or 10 digit number, use the obsolete time specification. 1131590Srgrimes */ 1141590Srgrimes if (!timeset && argc > 1) { 1151590Srgrimes (void)strtol(argv[0], &p, 10); 1161590Srgrimes len = p - argv[0]; 1171590Srgrimes if (*p == '\0' && (len == 8 || len == 10)) { 1181590Srgrimes timeset = 1; 1191590Srgrimes stime_arg2(*argv++, len == 10, tv); 1201590Srgrimes } 1211590Srgrimes } 1221590Srgrimes 1231590Srgrimes /* Otherwise use the current time of day. */ 1241590Srgrimes if (!timeset) 1251590Srgrimes tv[1] = tv[0]; 1261590Srgrimes 1271590Srgrimes if (*argv == NULL) 1281590Srgrimes usage(); 1291590Srgrimes 1301590Srgrimes for (rval = 0; *argv; ++argv) { 1311590Srgrimes /* See if the file exists. */ 13248566Sbillf if (stat(*argv, &sb)) { 1331590Srgrimes if (!cflag) { 1341590Srgrimes /* Create the file. */ 1351590Srgrimes fd = open(*argv, 1361590Srgrimes O_WRONLY | O_CREAT, DEFFILEMODE); 1371590Srgrimes if (fd == -1 || fstat(fd, &sb) || close(fd)) { 1381590Srgrimes rval = 1; 1391590Srgrimes warn("%s", *argv); 1401590Srgrimes continue; 1411590Srgrimes } 1421590Srgrimes 1431590Srgrimes /* If using the current time, we're done. */ 1441590Srgrimes if (!timeset) 1451590Srgrimes continue; 1461590Srgrimes } else 1471590Srgrimes continue; 14848566Sbillf } 1491590Srgrimes 1501590Srgrimes if (!aflag) 1511590Srgrimes TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); 1521590Srgrimes if (!mflag) 1531590Srgrimes TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); 1541590Srgrimes 1551590Srgrimes /* Try utimes(2). */ 1561590Srgrimes if (!utimes(*argv, tv)) 1571590Srgrimes continue; 1581590Srgrimes 1591590Srgrimes /* If the user specified a time, nothing else we can do. */ 1601590Srgrimes if (timeset) { 1611590Srgrimes rval = 1; 1621590Srgrimes warn("%s", *argv); 1631590Srgrimes } 1641590Srgrimes 1651590Srgrimes /* 1661590Srgrimes * System V and POSIX 1003.1 require that a NULL argument 1671590Srgrimes * set the access/modification times to the current time. 1681590Srgrimes * The permission checks are different, too, in that the 1691590Srgrimes * ability to write the file is sufficient. Take a shot. 1701590Srgrimes */ 1711590Srgrimes if (!utimes(*argv, NULL)) 1721590Srgrimes continue; 1731590Srgrimes 1741590Srgrimes /* Try reading/writing. */ 1751590Srgrimes if (rw(*argv, &sb, fflag)) 1761590Srgrimes rval = 1; 1771590Srgrimes } 1781590Srgrimes exit(rval); 1791590Srgrimes} 1801590Srgrimes 1811590Srgrimes#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 1821590Srgrimes 1831590Srgrimesvoid 1841590Srgrimesstime_arg1(arg, tvp) 1851590Srgrimes char *arg; 1861590Srgrimes struct timeval *tvp; 1871590Srgrimes{ 18837259Sbde time_t now; 1891590Srgrimes struct tm *t; 1901590Srgrimes int yearset; 1911590Srgrimes char *p; 1921590Srgrimes /* Start with the current time. */ 19337259Sbde now = tvp[0].tv_sec; 19437259Sbde if ((t = localtime(&now)) == NULL) 1951590Srgrimes err(1, "localtime"); 1961590Srgrimes /* [[CC]YY]MMDDhhmm[.SS] */ 1971590Srgrimes if ((p = strchr(arg, '.')) == NULL) 1981590Srgrimes t->tm_sec = 0; /* Seconds defaults to 0. */ 1991590Srgrimes else { 2001590Srgrimes if (strlen(p + 1) != 2) 2011590Srgrimes goto terr; 2021590Srgrimes *p++ = '\0'; 2031590Srgrimes t->tm_sec = ATOI2(p); 2041590Srgrimes } 2058874Srgrimes 2061590Srgrimes yearset = 0; 2071590Srgrimes switch(strlen(arg)) { 2081590Srgrimes case 12: /* CCYYMMDDhhmm */ 2091590Srgrimes t->tm_year = ATOI2(arg); 2109446Sjoerg t->tm_year *= 100; 2111590Srgrimes yearset = 1; 2121590Srgrimes /* FALLTHOUGH */ 2131590Srgrimes case 10: /* YYMMDDhhmm */ 2141590Srgrimes if (yearset) { 2151590Srgrimes yearset = ATOI2(arg); 2161590Srgrimes t->tm_year += yearset; 2171590Srgrimes } else { 2181590Srgrimes yearset = ATOI2(arg); 2191590Srgrimes if (yearset < 69) 2201590Srgrimes t->tm_year = yearset + 2000; 2211590Srgrimes else 2221590Srgrimes t->tm_year = yearset + 1900; 2231590Srgrimes } 2241590Srgrimes t->tm_year -= 1900; /* Convert to UNIX time. */ 2251590Srgrimes /* FALLTHROUGH */ 2261590Srgrimes case 8: /* MMDDhhmm */ 2271590Srgrimes t->tm_mon = ATOI2(arg); 2281590Srgrimes --t->tm_mon; /* Convert from 01-12 to 00-11 */ 2291590Srgrimes t->tm_mday = ATOI2(arg); 2301590Srgrimes t->tm_hour = ATOI2(arg); 2311590Srgrimes t->tm_min = ATOI2(arg); 2321590Srgrimes break; 2331590Srgrimes default: 2341590Srgrimes goto terr; 2351590Srgrimes } 2361590Srgrimes 2371590Srgrimes t->tm_isdst = -1; /* Figure out DST. */ 2381590Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); 2391590Srgrimes if (tvp[0].tv_sec == -1) 2401590Srgrimesterr: errx(1, 2411590Srgrimes "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 2421590Srgrimes 2431590Srgrimes tvp[0].tv_usec = tvp[1].tv_usec = 0; 2441590Srgrimes} 2451590Srgrimes 2461590Srgrimesvoid 2471590Srgrimesstime_arg2(arg, year, tvp) 2481590Srgrimes char *arg; 2491590Srgrimes int year; 2501590Srgrimes struct timeval *tvp; 2511590Srgrimes{ 25237259Sbde time_t now; 2531590Srgrimes struct tm *t; 2541590Srgrimes /* Start with the current time. */ 25537259Sbde now = tvp[0].tv_sec; 25637259Sbde if ((t = localtime(&now)) == NULL) 2571590Srgrimes err(1, "localtime"); 2581590Srgrimes 2591590Srgrimes t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ 2601590Srgrimes --t->tm_mon; /* Convert from 01-12 to 00-11 */ 2611590Srgrimes t->tm_mday = ATOI2(arg); 2621590Srgrimes t->tm_hour = ATOI2(arg); 2631590Srgrimes t->tm_min = ATOI2(arg); 26442307Sdanny if (year) { 2651590Srgrimes t->tm_year = ATOI2(arg); 26642310Sdanny if (t->tm_year < 39) /* support 2000-2038 not 1902-1969 */ 26742307Sdanny t->tm_year += 100; 26842307Sdanny } 2691590Srgrimes 2701590Srgrimes t->tm_isdst = -1; /* Figure out DST. */ 2711590Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); 2721590Srgrimes if (tvp[0].tv_sec == -1) 2731590Srgrimes errx(1, 2741590Srgrimes "out of range or illegal time specification: MMDDhhmm[yy]"); 2751590Srgrimes 2761590Srgrimes tvp[0].tv_usec = tvp[1].tv_usec = 0; 2771590Srgrimes} 2781590Srgrimes 2791590Srgrimesvoid 2801590Srgrimesstime_file(fname, tvp) 2811590Srgrimes char *fname; 2821590Srgrimes struct timeval *tvp; 2831590Srgrimes{ 2841590Srgrimes struct stat sb; 2851590Srgrimes 2861590Srgrimes if (stat(fname, &sb)) 2871590Srgrimes err(1, "%s", fname); 2881590Srgrimes TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); 2891590Srgrimes TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); 2901590Srgrimes} 2911590Srgrimes 2921590Srgrimesint 2931590Srgrimesrw(fname, sbp, force) 2941590Srgrimes char *fname; 2951590Srgrimes struct stat *sbp; 2961590Srgrimes int force; 2971590Srgrimes{ 2981590Srgrimes int fd, needed_chmod, rval; 2991590Srgrimes u_char byte; 3001590Srgrimes 3011590Srgrimes /* Try regular files and directories. */ 3021590Srgrimes if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) { 3031590Srgrimes warnx("%s: %s", fname, strerror(EFTYPE)); 3041590Srgrimes return (1); 3051590Srgrimes } 3061590Srgrimes 3071590Srgrimes needed_chmod = rval = 0; 3081590Srgrimes if ((fd = open(fname, O_RDWR, 0)) == -1) { 3091590Srgrimes if (!force || chmod(fname, DEFFILEMODE)) 3101590Srgrimes goto err; 3111590Srgrimes if ((fd = open(fname, O_RDWR, 0)) == -1) 3121590Srgrimes goto err; 3131590Srgrimes needed_chmod = 1; 3141590Srgrimes } 3151590Srgrimes 3161590Srgrimes if (sbp->st_size != 0) { 3171590Srgrimes if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) 3181590Srgrimes goto err; 3191590Srgrimes if (lseek(fd, (off_t)0, SEEK_SET) == -1) 3201590Srgrimes goto err; 3211590Srgrimes if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) 3221590Srgrimes goto err; 3231590Srgrimes } else { 3241590Srgrimes if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { 3251590Srgrimeserr: rval = 1; 3261590Srgrimes warn("%s", fname); 3271590Srgrimes } else if (ftruncate(fd, (off_t)0)) { 3281590Srgrimes rval = 1; 3291590Srgrimes warn("%s: file modified", fname); 3301590Srgrimes } 3311590Srgrimes } 3321590Srgrimes 3331590Srgrimes if (close(fd) && rval != 1) { 3341590Srgrimes rval = 1; 3351590Srgrimes warn("%s", fname); 3361590Srgrimes } 3371590Srgrimes if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { 3381590Srgrimes rval = 1; 3391590Srgrimes warn("%s: permissions modified", fname); 3401590Srgrimes } 3411590Srgrimes return (rval); 3421590Srgrimes} 3431590Srgrimes 34418286Sbdevoid 3451590Srgrimesusage() 3461590Srgrimes{ 34747042Sdes (void)fprintf(stderr, "usage: touch [-acfm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n"); 3481590Srgrimes exit(1); 3491590Srgrimes} 350