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 }