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 * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 3087706Smarkm#include <sys/cdefs.h> 3187706Smarkm 3287706Smarkm__FBSDID("$FreeBSD$"); 3387706Smarkm 341590Srgrimes#ifndef lint 3577247Skrisstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 3887706Smarkm#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4177247Skrisstatic const char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; 4277247Skris#endif 431590Srgrimes 441590Srgrimes#include <sys/types.h> 451590Srgrimes#include <sys/stat.h> 461590Srgrimes#include <sys/time.h> 471590Srgrimes 48236852Sjilles#include <ctype.h> 491590Srgrimes#include <err.h> 501590Srgrimes#include <errno.h> 511590Srgrimes#include <fcntl.h> 52168571Sgrog#include <libgen.h> 531590Srgrimes#include <stdio.h> 541590Srgrimes#include <stdlib.h> 551590Srgrimes#include <string.h> 561590Srgrimes#include <time.h> 571590Srgrimes#include <unistd.h> 581590Srgrimes 59297782Sjillesstatic void stime_arg1(const char *, struct timespec *); 60297782Sjillesstatic void stime_arg2(const char *, int, struct timespec *); 61297782Sjillesstatic void stime_darg(const char *, struct timespec *); 62297782Sjillesstatic void stime_file(const char *, struct timespec *); 63249805Seadlerstatic int timeoffset(const char *); 64249806Seadlerstatic void usage(const char *); 651590Srgrimes 661590Srgrimesint 67102944Sdwmalonemain(int argc, char *argv[]) 681590Srgrimes{ 691590Srgrimes struct stat sb; 70297782Sjilles struct timespec ts[2]; 71297782Sjilles int atflag; 72230979Sjh int Aflag, aflag, cflag, mflag, ch, fd, len, rval, timeset; 731590Srgrimes char *p; 74168525Sgrog char *myname; 751590Srgrimes 76168571Sgrog myname = basename(argv[0]); 77230979Sjh Aflag = aflag = cflag = mflag = timeset = 0; 78297782Sjilles atflag = 0; 79297783Sjilles ts[0].tv_sec = ts[1].tv_sec = 0; 80297783Sjilles ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW; 811590Srgrimes 82236852Sjilles while ((ch = getopt(argc, argv, "A:acd:fhmr:t:")) != -1) 831590Srgrimes switch(ch) { 84168525Sgrog case 'A': 85168525Sgrog Aflag = timeoffset(optarg); 86168525Sgrog break; 871590Srgrimes case 'a': 881590Srgrimes aflag = 1; 891590Srgrimes break; 901590Srgrimes case 'c': 911590Srgrimes cflag = 1; 921590Srgrimes break; 93236852Sjilles case 'd': 94236852Sjilles timeset = 1; 95297782Sjilles stime_darg(optarg, ts); 96236852Sjilles break; 971590Srgrimes case 'f': 98230979Sjh /* No-op for compatibility. */ 991590Srgrimes break; 10083826Sobrien case 'h': 10183826Sobrien cflag = 1; 102297782Sjilles atflag = AT_SYMLINK_NOFOLLOW; 10383826Sobrien break; 1041590Srgrimes case 'm': 1051590Srgrimes mflag = 1; 1061590Srgrimes break; 1071590Srgrimes case 'r': 1081590Srgrimes timeset = 1; 109297782Sjilles stime_file(optarg, ts); 1101590Srgrimes break; 1111590Srgrimes case 't': 1121590Srgrimes timeset = 1; 113297782Sjilles stime_arg1(optarg, ts); 1141590Srgrimes break; 1151590Srgrimes default: 116168525Sgrog usage(myname); 1171590Srgrimes } 1181590Srgrimes argc -= optind; 1191590Srgrimes argv += optind; 1201590Srgrimes 121168571Sgrog if (aflag == 0 && mflag == 0) 1221590Srgrimes aflag = mflag = 1; 1231590Srgrimes 124168571Sgrog if (timeset) { 125168571Sgrog if (Aflag) { 126168571Sgrog /* 127168571Sgrog * We're setting the time to an offset from a specified 128168571Sgrog * time. God knows why, but it means that we can set 129168571Sgrog * that time once and for all here. 130168571Sgrog */ 131168571Sgrog if (aflag) 132297782Sjilles ts[0].tv_sec += Aflag; 133168571Sgrog if (mflag) 134297782Sjilles ts[1].tv_sec += Aflag; 135168571Sgrog Aflag = 0; /* done our job */ 1361590Srgrimes } 137168571Sgrog } else { 138168571Sgrog /* 139168571Sgrog * If no -r or -t flag, at least two operands, the first of 140168571Sgrog * which is an 8 or 10 digit number, use the obsolete time 141168571Sgrog * specification, otherwise use the current time. 142168571Sgrog */ 143168571Sgrog if (argc > 1) { 144168571Sgrog strtol(argv[0], &p, 10); 145168571Sgrog len = p - argv[0]; 146168571Sgrog if (*p == '\0' && (len == 8 || len == 10)) { 147168571Sgrog timeset = 1; 148297782Sjilles stime_arg2(*argv++, len == 10, ts); 149168571Sgrog } 150168571Sgrog } 151168571Sgrog /* Both times default to the same. */ 152297782Sjilles ts[1] = ts[0]; 1531590Srgrimes } 1541590Srgrimes 155297783Sjilles if (!aflag) 156297783Sjilles ts[0].tv_nsec = UTIME_OMIT; 157297783Sjilles if (!mflag) 158297783Sjilles ts[1].tv_nsec = UTIME_OMIT; 159297783Sjilles 1601590Srgrimes if (*argv == NULL) 161168525Sgrog usage(myname); 1621590Srgrimes 163168571Sgrog if (Aflag) 164168571Sgrog cflag = 1; 165168571Sgrog 1661590Srgrimes for (rval = 0; *argv; ++argv) { 1671590Srgrimes /* See if the file exists. */ 168297782Sjilles if (fstatat(AT_FDCWD, *argv, &sb, atflag) != 0) { 169198175Sjh if (errno != ENOENT) { 170198175Sjh rval = 1; 171198175Sjh warn("%s", *argv); 172198175Sjh continue; 173198175Sjh } 1741590Srgrimes if (!cflag) { 1751590Srgrimes /* Create the file. */ 1761590Srgrimes fd = open(*argv, 1771590Srgrimes O_WRONLY | O_CREAT, DEFFILEMODE); 1781590Srgrimes if (fd == -1 || fstat(fd, &sb) || close(fd)) { 1791590Srgrimes rval = 1; 1801590Srgrimes warn("%s", *argv); 1811590Srgrimes continue; 1821590Srgrimes } 1831590Srgrimes 1841590Srgrimes /* If using the current time, we're done. */ 1851590Srgrimes if (!timeset) 1861590Srgrimes continue; 1871590Srgrimes } else 1881590Srgrimes continue; 18948566Sbillf } 1901590Srgrimes 191168571Sgrog /* 192168571Sgrog * We're adjusting the times based on the file times, not a 193168571Sgrog * specified time (that gets handled above). 194168571Sgrog */ 195168525Sgrog if (Aflag) { 196168571Sgrog if (aflag) { 197297782Sjilles ts[0] = sb.st_atim; 198297782Sjilles ts[0].tv_sec += Aflag; 199168571Sgrog } 200168571Sgrog if (mflag) { 201297782Sjilles ts[1] = sb.st_mtim; 202297782Sjilles ts[1].tv_sec += Aflag; 203168571Sgrog } 204168525Sgrog } 2051590Srgrimes 206297782Sjilles if (!utimensat(AT_FDCWD, *argv, ts, atflag)) 2071590Srgrimes continue; 2081590Srgrimes 209230979Sjh rval = 1; 210230979Sjh warn("%s", *argv); 2111590Srgrimes } 2121590Srgrimes exit(rval); 2131590Srgrimes} 2141590Srgrimes 2151590Srgrimes#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; 2161590Srgrimes 217249805Seadlerstatic void 218297782Sjillesstime_arg1(const char *arg, struct timespec *tvp) 2191590Srgrimes{ 22037259Sbde time_t now; 2211590Srgrimes struct tm *t; 2221590Srgrimes int yearset; 2231590Srgrimes char *p; 224297783Sjilles 225297783Sjilles now = time(NULL); 22637259Sbde if ((t = localtime(&now)) == NULL) 2271590Srgrimes err(1, "localtime"); 2281590Srgrimes /* [[CC]YY]MMDDhhmm[.SS] */ 2291590Srgrimes if ((p = strchr(arg, '.')) == NULL) 2301590Srgrimes t->tm_sec = 0; /* Seconds defaults to 0. */ 2311590Srgrimes else { 2321590Srgrimes if (strlen(p + 1) != 2) 2331590Srgrimes goto terr; 2341590Srgrimes *p++ = '\0'; 2351590Srgrimes t->tm_sec = ATOI2(p); 2361590Srgrimes } 2378874Srgrimes 2381590Srgrimes yearset = 0; 2391590Srgrimes switch(strlen(arg)) { 2401590Srgrimes case 12: /* CCYYMMDDhhmm */ 2411590Srgrimes t->tm_year = ATOI2(arg); 2429446Sjoerg t->tm_year *= 100; 2431590Srgrimes yearset = 1; 244102412Scharnier /* FALLTHROUGH */ 2451590Srgrimes case 10: /* YYMMDDhhmm */ 2461590Srgrimes if (yearset) { 2471590Srgrimes yearset = ATOI2(arg); 2481590Srgrimes t->tm_year += yearset; 2491590Srgrimes } else { 2501590Srgrimes yearset = ATOI2(arg); 2511590Srgrimes if (yearset < 69) 2521590Srgrimes t->tm_year = yearset + 2000; 2531590Srgrimes else 2541590Srgrimes t->tm_year = yearset + 1900; 2551590Srgrimes } 2561590Srgrimes t->tm_year -= 1900; /* Convert to UNIX time. */ 2571590Srgrimes /* FALLTHROUGH */ 2581590Srgrimes case 8: /* MMDDhhmm */ 2591590Srgrimes t->tm_mon = ATOI2(arg); 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); 2641590Srgrimes break; 2651590Srgrimes default: 2661590Srgrimes goto terr; 2671590Srgrimes } 2681590Srgrimes 2691590Srgrimes t->tm_isdst = -1; /* Figure out DST. */ 2701590Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); 2711590Srgrimes if (tvp[0].tv_sec == -1) 272249805Seadler goto terr; 2731590Srgrimes 274297782Sjilles tvp[0].tv_nsec = tvp[1].tv_nsec = 0; 275249805Seadler return; 276249805Seadler 277249805Seadlerterr: 278249805Seadler errx(1, "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); 2791590Srgrimes} 2801590Srgrimes 281249805Seadlerstatic void 282297782Sjillesstime_arg2(const char *arg, int year, struct timespec *tvp) 2831590Srgrimes{ 28437259Sbde time_t now; 2851590Srgrimes struct tm *t; 286297783Sjilles 287297783Sjilles now = time(NULL); 28837259Sbde if ((t = localtime(&now)) == NULL) 2891590Srgrimes err(1, "localtime"); 2901590Srgrimes 2911590Srgrimes t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ 2921590Srgrimes --t->tm_mon; /* Convert from 01-12 to 00-11 */ 2931590Srgrimes t->tm_mday = ATOI2(arg); 2941590Srgrimes t->tm_hour = ATOI2(arg); 2951590Srgrimes t->tm_min = ATOI2(arg); 29642307Sdanny if (year) { 2971590Srgrimes t->tm_year = ATOI2(arg); 29842310Sdanny if (t->tm_year < 39) /* support 2000-2038 not 1902-1969 */ 29942307Sdanny t->tm_year += 100; 30042307Sdanny } 3011590Srgrimes 3021590Srgrimes t->tm_isdst = -1; /* Figure out DST. */ 3031590Srgrimes tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); 3041590Srgrimes if (tvp[0].tv_sec == -1) 3051590Srgrimes errx(1, 3061590Srgrimes "out of range or illegal time specification: MMDDhhmm[yy]"); 3071590Srgrimes 308297782Sjilles tvp[0].tv_nsec = tvp[1].tv_nsec = 0; 3091590Srgrimes} 3101590Srgrimes 311249805Seadlerstatic void 312297782Sjillesstime_darg(const char *arg, struct timespec *tvp) 313236852Sjilles{ 314236852Sjilles struct tm t = { .tm_sec = 0 }; 315236852Sjilles const char *fmt, *colon; 316236852Sjilles char *p; 317236852Sjilles int val, isutc = 0; 318236852Sjilles 319297782Sjilles tvp[0].tv_nsec = 0; 320236852Sjilles t.tm_isdst = -1; 321236852Sjilles colon = strchr(arg, ':'); 322236852Sjilles if (colon == NULL || strchr(colon + 1, ':') == NULL) 323236852Sjilles goto bad; 324236852Sjilles fmt = strchr(arg, 'T') != NULL ? "%Y-%m-%dT%H:%M:%S" : 325236852Sjilles "%Y-%m-%d %H:%M:%S"; 326236852Sjilles p = strptime(arg, fmt, &t); 327236852Sjilles if (p == NULL) 328236852Sjilles goto bad; 329236852Sjilles /* POSIX: must have at least one digit after dot */ 330236852Sjilles if ((*p == '.' || *p == ',') && isdigit((unsigned char)p[1])) { 331236852Sjilles p++; 332297782Sjilles val = 100000000; 333236852Sjilles while (isdigit((unsigned char)*p)) { 334297782Sjilles tvp[0].tv_nsec += val * (*p - '0'); 335236852Sjilles p++; 336236852Sjilles val /= 10; 337236852Sjilles } 338236852Sjilles } 339236852Sjilles if (*p == 'Z') { 340236852Sjilles isutc = 1; 341236852Sjilles p++; 342236852Sjilles } 343236852Sjilles if (*p != '\0') 344236852Sjilles goto bad; 345236852Sjilles 346236852Sjilles tvp[0].tv_sec = isutc ? timegm(&t) : mktime(&t); 347236852Sjilles 348236852Sjilles tvp[1] = tvp[0]; 349236852Sjilles return; 350236852Sjilles 351236852Sjillesbad: 352236852Sjilles errx(1, "out of range or illegal time specification: YYYY-MM-DDThh:mm:SS[.frac][tz]"); 353236852Sjilles} 354236852Sjilles 355168525Sgrog/* Calculate a time offset in seconds, given an arg of the format [-]HHMMSS. */ 356168525Sgrogint 357249805Seadlertimeoffset(const char *arg) 358168525Sgrog{ 359168525Sgrog int offset; 360168525Sgrog int isneg; 361168525Sgrog 362168525Sgrog offset = 0; 363168525Sgrog isneg = *arg == '-'; 364168525Sgrog if (isneg) 365168525Sgrog arg++; 366168525Sgrog switch (strlen(arg)) { 367168525Sgrog default: /* invalid */ 368168525Sgrog errx(1, "Invalid offset spec, must be [-][[HH]MM]SS"); 369168525Sgrog 370168525Sgrog case 6: /* HHMMSS */ 371168525Sgrog offset = ATOI2(arg); 372168525Sgrog /* FALLTHROUGH */ 373168525Sgrog case 4: /* MMSS */ 374168525Sgrog offset = offset * 60 + ATOI2(arg); 375168525Sgrog /* FALLTHROUGH */ 376168525Sgrog case 2: /* SS */ 377168525Sgrog offset = offset * 60 + ATOI2(arg); 378168525Sgrog } 379168525Sgrog if (isneg) 380168525Sgrog return (-offset); 381168525Sgrog else 382168525Sgrog return (offset); 383168525Sgrog} 384168525Sgrog 385249805Seadlerstatic void 386297782Sjillesstime_file(const char *fname, struct timespec *tsp) 3871590Srgrimes{ 3881590Srgrimes struct stat sb; 3891590Srgrimes 3901590Srgrimes if (stat(fname, &sb)) 3911590Srgrimes err(1, "%s", fname); 392297782Sjilles tsp[0] = sb.st_atim; 393297782Sjilles tsp[1] = sb.st_mtim; 3941590Srgrimes} 3951590Srgrimes 396249805Seadlerstatic void 397249806Seadlerusage(const char *myname) 3981590Srgrimes{ 399236852Sjilles fprintf(stderr, "usage: %s [-A [-][[hh]mm]SS] [-achm] [-r file] " 400236852Sjilles "[-t [[CC]YY]MMDDhhmm[.SS]]\n" 401236852Sjilles " [-d YYYY-MM-DDThh:mm:SS[.frac][tz]] " 402236852Sjilles "file ...\n", myname); 4031590Srgrimes exit(1); 4041590Srgrimes} 405