1 /* touch.c : change timestamp of a file
2 *
3 * Copyright 2012 Choubey Ji <warior.linux@gmail.com>
4 *
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html
6 *
7 * TODO: have another go at merging the -t and -d stanzas
8
9 USE_TOUCH(NEWTOY(touch, "acd:mr:t:h[!dtr]", TOYFLAG_BIN))
10
11 config TOUCH
12 bool "touch"
13 default y
14 help
15 usage: touch [-amch] [-d DATE] [-t TIME] [-r FILE] FILE...
16
17 Update the access and modification times of each FILE to the current time.
18
19 -a change access time
20 -m change modification time
21 -c don't create file
22 -h change symlink
23 -d set time to DATE (in YYYY-MM-DDThh:mm:SS[.frac][tz] format)
24 -t set time to TIME (in [[CC]YY]MMDDhhmm[.ss][frac] format)
25 -r set time same as reference FILE
26 */
27
28 #define FOR_touch
29 #include "toys.h"
30
GLOBALS(char * time;char * file;char * date;)31 GLOBALS(
32 char *time;
33 char *file;
34 char *date;
35 )
36
37 void touch_main(void)
38 {
39 struct timespec ts[2];
40 char **ss;
41 int fd, i;
42
43 // use current time if no -t or -d
44 ts[0].tv_nsec = UTIME_NOW;
45 if (toys.optflags & (FLAG_t|FLAG_d)) {
46 char *s, *date;
47 struct tm tm;
48 int len = 0;
49
50 localtime_r(&(ts->tv_sec), &tm);
51
52 // Set time from -d?
53
54 if (toys.optflags & FLAG_d) {
55 date = TT.date;
56 i = strlen(date);
57 if (i) {
58 // Trailing Z means UTC timezone, don't expect libc to know this.
59 if (toupper(date[i-1])=='Z') {
60 date[i-1] = 0;
61 setenv("TZ", "UTC0", 1);
62 localtime_r(&(ts->tv_sec), &tm);
63 }
64 s = strptime(date, "%Y-%m-%dT%T", &tm);
65 ts->tv_nsec = 0;
66 if (s && *s=='.' && isdigit(s[1]))
67 sscanf(s, ".%lu%n", &ts->tv_nsec, &len);
68 else len = 0;
69 } else s = 0;
70
71 // Set time from -t?
72
73 } else {
74 strcpy(toybuf, "%Y%m%d%H%M");
75 date = TT.time;
76 i = ((s = strchr(date, '.'))) ? s-date : strlen(date);
77 if (i < 8 || i%2) error_exit("bad '%s'", date);
78 for (i=0;i<3;i++) {
79 s = strptime(date, toybuf+(i&2), &tm);
80 if (s) break;
81 toybuf[1]='y';
82 }
83 ts->tv_nsec = 0;
84 if (s && *s=='.' && sscanf(s, ".%2u%n", &(tm.tm_sec), &len) == 1) {
85 sscanf(s += len, "%lu%n", &ts->tv_nsec, &len);
86 len++;
87 } else len = 0;
88 }
89 if (len) {
90 s += len;
91 if (ts->tv_nsec > 999999999) s = 0;
92 else while (len++ < 10) ts->tv_nsec *= 10;
93 }
94
95 errno = 0;
96 ts->tv_sec = mktime(&tm);
97 if (!s || *s || errno == EOVERFLOW) perror_exit("bad '%s'", date);
98 }
99 ts[1]=ts[0];
100
101 // Set time from -r?
102
103 if (TT.file) {
104 struct stat st;
105
106 xstat(TT.file, &st);
107 ts[0] = st.st_atim;
108 ts[1] = st.st_mtim;
109 }
110
111 // Which time(s) should we actually change?
112 i = toys.optflags & (FLAG_a|FLAG_m);
113 if (i && i!=(FLAG_a|FLAG_m)) ts[i==FLAG_m].tv_nsec = UTIME_OMIT;
114
115 // Loop through files on command line
116 for (ss = toys.optargs; *ss;) {
117
118 // cheat: FLAG_h is rightmost flag, so its value is 1
119 if (!utimensat(AT_FDCWD, *ss, ts,
120 (toys.optflags & FLAG_h)*AT_SYMLINK_NOFOLLOW)) ss++;
121 else if (toys.optflags & FLAG_c) ss++;
122 else if (access(*ss, F_OK) && (-1!=(fd = open(*ss, O_CREAT, 0666))))
123 close(fd);
124 else perror_msg("'%s'", *ss++);
125 }
126 }
127