// 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 #include #include #include #include #include #include #include #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)"); }