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