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