8d96afd4fb
reproduce: touch -t 12345678 mytralala would only give a warning and use uninitialised time values. The proper way is to treat it as an error. Clear the "struct tm" and kill some lines just in case.
162 lines
2.7 KiB
C
162 lines
2.7 KiB
C
/* See LICENSE file for copyright and license details. */
|
|
#include <sys/stat.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <utime.h>
|
|
|
|
#include "util.h"
|
|
|
|
static int aflag;
|
|
static int cflag;
|
|
static int mflag;
|
|
static struct timespec times[2];
|
|
|
|
static void
|
|
touch(const char *file)
|
|
{
|
|
int fd;
|
|
struct stat st;
|
|
int r;
|
|
|
|
if ((r = stat(file, &st)) < 0) {
|
|
if (errno != ENOENT)
|
|
eprintf("stat %s:", file);
|
|
if (cflag)
|
|
return;
|
|
} else if (!r) {
|
|
if (!aflag)
|
|
times[0] = st.st_atim;
|
|
if (!mflag)
|
|
times[1] = st.st_mtim;
|
|
if (utimensat(AT_FDCWD, file, times, 0) < 0)
|
|
eprintf("utimensat %s:", file);
|
|
return;
|
|
}
|
|
|
|
if ((fd = open(file, O_CREAT | O_EXCL, 0644)) < 0)
|
|
eprintf("open %s:", file);
|
|
close(fd);
|
|
|
|
touch(file);
|
|
}
|
|
|
|
static time_t
|
|
parsetime(char *str, time_t current)
|
|
{
|
|
struct tm *cur, t = { 0 };
|
|
int zulu = 0;
|
|
char *format;
|
|
size_t len = strlen(str);
|
|
|
|
cur = localtime(¤t);
|
|
t.tm_isdst = -1;
|
|
|
|
switch (len) {
|
|
/* -t flag argument */
|
|
case 8:
|
|
t.tm_year = cur->tm_year;
|
|
format = "%m%d%H%M";
|
|
break;
|
|
case 10:
|
|
format = "%y%m%d%H%M";
|
|
break;
|
|
case 11:
|
|
t.tm_year = cur->tm_year;
|
|
format = "%m%d%H%M.%S";
|
|
break;
|
|
case 12:
|
|
format = "%Y%m%d%H%M";
|
|
break;
|
|
case 13:
|
|
format = "%y%m%d%H%M.%S";
|
|
break;
|
|
case 15:
|
|
format = "%Y%m%d%H%M.%S";
|
|
break;
|
|
/* -d flag argument */
|
|
case 19:
|
|
format = "%Y-%m-%dT%H:%M:%S";
|
|
break;
|
|
case 20:
|
|
/* only Zulu-timezone supported */
|
|
if (str[19] != 'Z')
|
|
eprintf("Invalid time zone\n");
|
|
str[19] = 0;
|
|
zulu = 1;
|
|
format = "%Y-%m-%dT%H:%M:%S";
|
|
break;
|
|
default:
|
|
eprintf("Invalid date format length\n", str);
|
|
}
|
|
|
|
if (!strptime(str, format, &t))
|
|
eprintf("strptime %s: Invalid date format\n", str);
|
|
if (zulu) {
|
|
t.tm_hour += t.tm_gmtoff / 60;
|
|
t.tm_gmtoff = 0;
|
|
t.tm_zone = "Z";
|
|
}
|
|
|
|
return mktime(&t);
|
|
}
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
eprintf("usage: %s [-acm] [-d time | -r ref_file | -t time | -T time] "
|
|
"file ...\n", argv0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
struct stat st;
|
|
char *ref = NULL;
|
|
clock_gettime(CLOCK_REALTIME, ×[0]);
|
|
|
|
ARGBEGIN {
|
|
case 'a':
|
|
aflag = 1;
|
|
break;
|
|
case 'c':
|
|
cflag = 1;
|
|
break;
|
|
case 'd':
|
|
case 't':
|
|
times[0].tv_sec = parsetime(EARGF(usage()), times[0].tv_sec);
|
|
break;
|
|
case 'm':
|
|
mflag = 1;
|
|
break;
|
|
case 'r':
|
|
ref = EARGF(usage());
|
|
if (stat(ref, &st) < 0)
|
|
eprintf("stat '%s':", ref);
|
|
times[0] = st.st_atim;
|
|
times[1] = st.st_mtim;
|
|
break;
|
|
case 'T':
|
|
times[0].tv_sec = estrtonum(EARGF(usage()), 0, LLONG_MAX);
|
|
break;
|
|
default:
|
|
usage();
|
|
} ARGEND
|
|
|
|
if (!argc)
|
|
usage();
|
|
if (!aflag && !mflag)
|
|
aflag = mflag = 1;
|
|
if (!ref)
|
|
times[1] = times[0];
|
|
|
|
for (; *argv; argc--, argv++)
|
|
touch(*argv);
|
|
|
|
return 0;
|
|
}
|