0123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
|
// touch.c, version 1.0.2
// OMKOV coreutils implementation of POSIX touch
// Copyright (C) 2020, Jakob Wakeling
// MIT Licence
/*
TODO Implement [.frac] support for -d option.
*/
#define _XOPEN_SOURCE 700
#include "util/error.h"
#include "util/optget.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#define VERSION "1.0.1"
static struct lop lops[] = {
{ "help", ARG_NUL, 256 },
{ "version", ARG_NUL, 257 },
{ NULL, 0, 0 }
};
static bool aflag, cflag, mflag;
static bool rflag, notnow;
static struct timespec times[2] = {{ .tv_nsec = UTIME_NOW }};
static inline void dparse(const char *str);
static inline void tparse(const char *str);
static inline void rparse(const char *str);
static inline int touch(const char *file);
static void hlp(void);
static void ver(void);
int main(int ac, char *av[]) { A0 = av[0];
struct opt opt = OPTGET_INIT; opt.str = "acd:mr:t:"; opt.lops = lops; int o;
while ((o = optget(&opt, av, 1)) != -1) switch (o) {
case 'a': { aflag = true; break; }
case 'c': { cflag = true; break; }
case 'd': { if (notnow) { goto invalid; } dparse(opt.arg); break; }
case 'm': { mflag = true; break; }
case 'r': { if (notnow) { goto invalid; } rparse(opt.arg); break; }
case 't': { if (notnow) { goto invalid; } tparse(opt.arg); break; }
case 256: { hlp(); return 0; }
case 257: { ver(); return 0; }
default: { return 1; }
invalid: { error(1, "invalid option combination"); }
}
if (opt.ind == ac) { error(1, "missing operand"); }
if (!rflag) { times[1] = times[0]; }
if (!aflag && !mflag) { aflag = mflag = true; }
else if (!aflag) { times[0].tv_nsec = UTIME_OMIT; }
else if (!mflag) { times[1].tv_nsec = UTIME_OMIT; }
bool warned = false;
for (char **p = &av[opt.ind]; *p; ++p) if (touch(*p)) {
warn("%s: %s", *p, serr()); warned = true;
}
return warned;
}
static inline void dparse(const char *str) {
struct tm t = { 0 }; t.tm_isdst = -1;
register size_t len = strlen(str);
if (len == 19 && strptime(str, "%Y-%m-%dT%H:%M:%S", &t));
else if (len == 20 && strptime(str, "%Y-%m-%dT%H:%M:%SZ", &t)) {
tzset(); t.tm_sec -= timezone;
} else { error(1, "%s: invalid time format", str); }
struct timespec ts; ts.tv_sec = mktime(&t); ts.tv_nsec = 0;
times[0] = ts; notnow = true; return;
}
static inline void tparse(const char *str) {
time_t now; struct tm *cur, t = { 0 };
if ((now = time(NULL)) == -1 || !(cur = localtime(&now))) {
error(1, "%s: %s", str, serr());
} t.tm_isdst = -1;
register size_t len = strlen(str);
if (len == 8 && strptime(str, "%m%d%H%M", &t));
else if (len == 10 && strptime(str, "%y%m%d%H%M", &t));
else if (len == 11 && strptime(str, "%m%d%H%M.%S", &t));
else if (len == 12 && strptime(str, "%Y%m%d%H%M", &t));
else if (len == 13 && strptime(str, "%y%m%d%H%M.%S", &t));
else if (len == 15 && strptime(str, "%Y%m%d%H%M.%S", &t));
else { error(1, "%s: invalid time format", str); }
struct timespec ts; ts.tv_sec = mktime(&t); ts.tv_nsec = 0;
times[0] = ts; notnow = true; return;
}
static inline void rparse(const char *file) {
struct stat fs; if (stat(file, &fs)) {
error(1, "%s: %s", file, serr());
} times[0] = fs.st_atim; times[1] = fs.st_mtim;
rflag = true; notnow = true; return;
}
static inline int touch(const char *file) {
if (utimensat(AT_FDCWD, file, times, 0)) {
if (errno != ENOENT) { return 1; } if (cflag) { return 0; }
unsigned m = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
register int fd; if ((fd = creat(file, m)) == -1) { return 1; }
if (futimens(fd, times)) { close(fd); return 1; } close(fd);
} return 0;
}
static void hlp(void) {
puts("touch - change file access and modify times\n");
puts("usage: touch [-acm] [-d time|-r file|-t time] file...\n");
puts("options:");
puts(" -a Change the access time only");
puts(" -c Do not create new files");
puts(" -d time Use YYYY-MM-DDThh:mm:SS[.frac][tz]");
puts(" -m Change the modification time only");
puts(" -r file Use a specified files times");
puts(" -t time Use [[CC]YY]MMDDhhmm[.SS]");
puts(" --help Display help information");
puts(" --version Display version information");
}
static void ver(void) {
puts("OMKOV coreutils touch, version " VERSION);
puts("Copyright (C) 2020, Jakob Wakeling");
puts("MIT Licence (https://opensource.org/licenses/MIT)");
}
|