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