sbase

suckless unix tools
git clone git://git.2f30.org/sbase
Log | Files | Refs | README | LICENSE

touch.c (2796B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <sys/stat.h>
      3 
      4 #include <errno.h>
      5 #include <fcntl.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <time.h>
      9 #include <unistd.h>
     10 
     11 #include "util.h"
     12 
     13 static int aflag;
     14 static int cflag;
     15 static int mflag;
     16 static struct timespec times[2] = {{.tv_nsec = UTIME_NOW}};
     17 
     18 static void
     19 touch(const char *file)
     20 {
     21 	int fd, ret;
     22 
     23 	if (utimensat(AT_FDCWD, file, times, 0) == 0)
     24 		return;
     25 	if (errno != ENOENT)
     26 		eprintf("utimensat %s:", file);
     27 	if (cflag)
     28 		return;
     29 	if ((fd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0)
     30 		eprintf("open %s:", file);
     31 	ret = futimens(fd, times);
     32 	close(fd);
     33 	if (ret < 0)
     34 		eprintf("futimens %s:", file);
     35 }
     36 
     37 static time_t
     38 parsetime(char *str)
     39 {
     40 	time_t now;
     41 	struct tm *cur, t = { 0 };
     42 	int zulu = 0;
     43 	char *format;
     44 	size_t len = strlen(str);
     45 
     46 	if ((now = time(NULL)) == -1)
     47 		eprintf("time:");
     48 	if (!(cur = localtime(&now)))
     49 		eprintf("localtime:");
     50 	t.tm_isdst = -1;
     51 
     52 	switch (len) {
     53 	/* -t flag argument */
     54 	case 8:
     55 		t.tm_year = cur->tm_year;
     56 		format = "%m%d%H%M";
     57 		break;
     58 	case 10:
     59 		format = "%y%m%d%H%M";
     60 		break;
     61 	case 11:
     62 		t.tm_year = cur->tm_year;
     63 		format = "%m%d%H%M.%S";
     64 		break;
     65 	case 12:
     66 		format = "%Y%m%d%H%M";
     67 		break;
     68 	case 13:
     69 		format = "%y%m%d%H%M.%S";
     70 		break;
     71 	case 15:
     72 		format = "%Y%m%d%H%M.%S";
     73 		break;
     74 	/* -d flag argument */
     75 	case 19:
     76 		format = "%Y-%m-%dT%H:%M:%S";
     77 		break;
     78 	case 20:
     79 		/* only Zulu-timezone supported */
     80 		if (str[19] != 'Z')
     81 			eprintf("Invalid time zone\n");
     82 		str[19] = 0;
     83 		zulu = 1;
     84 		format = "%Y-%m-%dT%H:%M:%S";
     85 		break;
     86 	default:
     87 		eprintf("Invalid date format length\n", str);
     88 	}
     89 
     90 	if (!strptime(str, format, &t))
     91 		eprintf("strptime %s: Invalid date format\n", str);
     92 	if (zulu) {
     93 		t.tm_hour += t.tm_gmtoff / 60;
     94 		t.tm_gmtoff = 0;
     95 		t.tm_zone = "Z";
     96 	}
     97 
     98 	return mktime(&t);
     99 }
    100 
    101 static void
    102 usage(void)
    103 {
    104 	eprintf("usage: %s [-acm] [-d time | -r ref_file | -t time | -T time] "
    105 	        "file ...\n", argv0);
    106 }
    107 
    108 int
    109 main(int argc, char *argv[])
    110 {
    111 	struct stat st;
    112 	char *ref = NULL;
    113 
    114 	ARGBEGIN {
    115 	case 'a':
    116 		aflag = 1;
    117 		break;
    118 	case 'c':
    119 		cflag = 1;
    120 		break;
    121 	case 'd':
    122 	case 't':
    123 		times[0].tv_sec = parsetime(EARGF(usage()));
    124 		times[0].tv_nsec = 0;
    125 		break;
    126 	case 'm':
    127 		mflag = 1;
    128 		break;
    129 	case 'r':
    130 		ref = EARGF(usage());
    131 		if (stat(ref, &st) < 0)
    132 			eprintf("stat '%s':", ref);
    133 		times[0] = st.st_atim;
    134 		times[1] = st.st_mtim;
    135 		break;
    136 	case 'T':
    137 		times[0].tv_sec = estrtonum(EARGF(usage()), 0, LLONG_MAX);
    138 		times[0].tv_nsec = 0;
    139 		break;
    140 	default:
    141 		usage();
    142 	} ARGEND
    143 
    144 	if (!argc)
    145 		usage();
    146 	if (!aflag && !mflag)
    147 		aflag = mflag = 1;
    148 	if (!ref)
    149 		times[1] = times[0];
    150 	if (!aflag)
    151 		times[0].tv_nsec = UTIME_OMIT;
    152 	if (!mflag)
    153 		times[1].tv_nsec = UTIME_OMIT;
    154 
    155 	for (; *argv; argc--, argv++)
    156 		touch(*argv);
    157 
    158 	return 0;
    159 }