1 /*
2 * touch - update access and modification times of a file
3 *
4 * Gunnar Ritter, Freiburg i. Br., Germany, July 2001.
5 */
6 /*
7 * Copyright (c) 2003 Gunnar Ritter
8 *
9 * This software is provided 'as-is', without any express or implied
10 * warranty. In no event will the authors be held liable for any damages
11 * arising from the use of this software.
12 *
13 * Permission is granted to anyone to use this software for any purpose,
14 * including commercial applications, and to alter it and redistribute
15 * it freely, subject to the following restrictions:
16 *
17 * 1. The origin of this software must not be misrepresented; you must not
18 * claim that you wrote the original software. If you use this software
19 * in a product, an acknowledgment in the product documentation would be
20 * appreciated but is not required.
21 *
22 * 2. Altered source versions must be plainly marked as such, and must not be
23 * misrepresented as being the original software.
24 *
25 * 3. This notice may not be removed or altered from any source distribution.
26 */
27
28 #if __GNUC__ >= 3 && __GNUC_MINOR__ >= 4 || __GNUC__ >= 4
29 #define USED __attribute__ ((used))
30 #elif defined __GNUC__
31 #define USED __attribute__ ((unused))
32 #else
33 #define USED
34 #endif
35 #if defined (SUS)
36 static const char sccsid[] USED = "@(#)touch_sus.sl 1.21 (gritter) 5/29/05";
37 #else /* !SUS */
38 static const char sccsid[] USED = "@(#)touch.sl 1.21 (gritter) 5/29/05";
39 #endif /* !SUS */
40
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <fcntl.h>
44 #include <unistd.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <stdlib.h>
48 #include <errno.h>
49 #include <libgen.h>
50 #include <utime.h>
51 #include <ctype.h>
52 #include <time.h>
53
54 static unsigned errcnt; /* count of errors */
55 static int aflag; /* select access time */
56 static int cflag; /* do not create files */
57 static int mflag; /* select modification time */
58 static int settime; /* this is the settime command */
59 static int date_time; /* use date_time operand (not -t) */
60 static time_t nacc = -1; /* new access time */
61 static time_t nmod = -1; /* now modification time */
62 static time_t now; /* current time */
63 static char *progname; /* argv[0] to main() */
64 static int nulltime; /* can use settime(NULL) */
65
66 /*
67 * perror()-alike.
68 */
69 static void
pnerror(int eno,const char * string)70 pnerror(int eno, const char *string)
71 {
72 fprintf(stderr, "%s: %s: %s\n", progname, string, strerror(eno));
73 errcnt++;
74 }
75
76 /*
77 * Touch the named file.
78 */
79 static void
touch(const char * fn)80 touch(const char *fn)
81 {
82 struct stat st;
83 struct utimbuf ut;
84
85 if (stat(fn, &st) < 0) {
86 if (errno == ENOENT) {
87 int fd;
88
89 if (cflag) {
90 #ifndef SUS
91 errcnt++;
92 #endif /* !SUS */
93 return;
94 }
95 if ((fd = creat(fn, 0666)) < 0) {
96 fprintf(stderr, "%s: %s cannot create\n",
97 progname, fn);
98 errcnt++;
99 return;
100 }
101 if (fstat(fd, &st) < 0) {
102 fprintf(stderr, "%s: %s cannot stat\n",
103 progname, fn);
104 errcnt++;
105 close(fd);
106 return;
107 }
108 close(fd);
109 } else {
110 fprintf(stderr, "%s: %s cannot stat\n",
111 progname, fn);
112 errcnt++;
113 return;
114 }
115 }
116 if (aflag)
117 ut.actime = nacc;
118 else
119 ut.actime = st.st_atime;
120 if (mflag)
121 ut.modtime = nmod;
122 else
123 ut.modtime = st.st_mtime;
124 if (utime(fn, nulltime ? NULL : &ut) < 0) {
125 fprintf(stderr, "%s: cannot change times on %s\n",
126 progname, fn);
127 errcnt++;
128 }
129 }
130
131 static void
badtime(void)132 badtime(void)
133 {
134 if (date_time)
135 fprintf(stderr, "date: bad conversion\n");
136 else
137 fprintf(stderr, "%s: bad time specification\n", progname);
138 exit(2);
139 }
140
141 /*
142 * Convert s to int with maximum of m.
143 */
144 static int
atot(const char * s,int m)145 atot(const char *s, int m)
146 {
147 char *x;
148 int i;
149
150 i = (int)strtol(s, &x, 10);
151 if (*x != '\0' || i > m || i < 0 || *s == '+' || *s == '-')
152 badtime();
153 return i;
154 }
155
156 /*
157 * Interpret a time argument, old style: MMDDhhmm[YY].
158 */
159 static void
otime(char * cp)160 otime(char *cp)
161 {
162 struct tm *stm;
163 time_t t;
164
165 date_time = 1;
166 t = now;
167 stm = localtime(&t);
168 stm->tm_isdst = -1;
169 stm->tm_sec = 0;
170 switch (strlen(cp)) {
171 case 10:
172 stm->tm_year = atot(&cp[8], 99);
173 if (stm->tm_year < 69)
174 stm->tm_year += 100;
175 cp[8] = '\0';
176 /*FALLTHRU*/
177 case 8:
178 stm->tm_min = atot(&cp[6], 59);
179 cp[6] = '\0';
180 stm->tm_hour = atot(&cp[4], 23);
181 cp[4] = '\0';
182 if ((stm->tm_mday = atot(&cp[2], 31)) == 0)
183 badtime();
184 cp[2] = '\0';
185 if ((stm->tm_mon = atot(cp, 12)) == 0)
186 badtime();
187 stm->tm_mon--;
188 break;
189 default:
190 badtime();
191 }
192 if ((t = mktime(stm)) == (time_t)-1)
193 badtime();
194 nacc = nmod = t;
195 }
196
197 /*
198 * Interpret a time argument, new style: [[CC]YY]MMDDhhmm[.SS].
199 */
200 static void
ptime(char * cp)201 ptime(char *cp)
202 {
203 char year[5];
204 struct tm *stm;
205 time_t t;
206 size_t sz = strlen(cp);
207
208 t = now;
209 stm = localtime(&t);
210 stm->tm_isdst = -1;
211 if (sz == 11 || sz == 13 || sz == 15) {
212 if (cp[sz - 3] != '.')
213 badtime();
214 stm->tm_sec = atot(&cp[sz - 2], 61);
215 cp[sz - 3] = '\0';
216 sz -= 3;
217 } else
218 stm->tm_sec = 0;
219 if (sz == 12) {
220 year[0] = cp[0], year[1] = cp[1], year[2] = cp[2],
221 year[3] = cp[3], year[4] = '\0';
222 if ((stm->tm_year = atot(year, 30000)) < 1970)
223 badtime();
224 stm->tm_year -= 1900;
225 cp += 4, sz -= 4;
226 } else if (sz == 10) {
227 year[0] = cp[0], year[1] = cp[1], year[2] = '\0';
228 stm->tm_year = atot(year, 99);
229 if (stm->tm_year < 69)
230 stm->tm_year += 100;
231 cp += 2, sz -= 2;
232 }
233 if (sz != 8)
234 badtime();
235 stm->tm_min = atot(&cp[6], 59);
236 cp[6] = '\0';
237 stm->tm_hour = atot(&cp[4], 23);
238 cp[4] = '\0';
239 if ((stm->tm_mday = atot(&cp[2], 31)) == 0)
240 badtime();
241 cp[2] = '\0';
242 if ((stm->tm_mon = atot(cp, 12)) == 0)
243 badtime();
244 stm->tm_mon--;
245 if ((t = mktime(stm)) == (time_t)-1)
246 badtime();
247 nacc = nmod = t;
248 }
249
250 /*
251 * Get reference time from a file.
252 */
253 static void
reffile(const char * cp)254 reffile(const char *cp)
255 {
256 struct stat st;
257
258 if (stat(cp, &st) < 0) {
259 pnerror(errno, cp);
260 exit(1);
261 }
262 nacc = st.st_atime;
263 nmod = st.st_mtime;
264 }
265
266 static void
usage(void)267 usage(void)
268 {
269 if (settime == 0)
270 fprintf(stderr, "usage: %s [-amc] [mmddhhmm[yy]] file ...\n",
271 progname);
272 else
273 fprintf(stderr, "usage: %s [-f file] [mmddhhmm[yy]] file ...\n",
274 progname);
275 exit(2);
276 }
277
278 int
main(int argc,char ** argv)279 main(int argc, char **argv)
280 {
281 int i;
282
283 #ifdef __GLIBC__
284 putenv("POSIXLY_CORRECT=1");
285 #endif
286 progname = basename(argv[0]);
287 if (strcmp(progname, "settime") == 0) {
288 settime = 1;
289 cflag = 1;
290 }
291 time(&now);
292 while ((i = getopt(argc, argv, settime ? "f:" : "amcr:t:f")) != EOF) {
293 switch (i) {
294 case 'a':
295 aflag = 1;
296 break;
297 case 'c':
298 cflag = 1;
299 break;
300 case 'm':
301 mflag = 1;
302 break;
303 case 'f':
304 if (settime == 0)
305 break;
306 /*FALLTHRU*/
307 case 'r':
308 if (nacc != (time_t)-1 || nmod != (time_t)-1)
309 usage();
310 reffile(optarg);
311 break;
312 case 't':
313 if (nacc != (time_t)-1 || nmod != (time_t)-1)
314 usage();
315 ptime(optarg);
316 break;
317 default:
318 usage();
319 }
320 }
321 if (nacc == (time_t)-1 && nmod == (time_t)-1 && argv[optind]
322 #ifdef SUS
323 && argv[optind + 1]) {
324 char *cp;
325 for (cp = argv[optind]; *cp && isdigit(*cp & 0377); cp++);
326 if (*cp == '\0' && (cp - argv[optind] == 8 ||
327 cp - argv[optind] == 10))
328 #else /* !SUS */
329 ) {
330 if (isdigit(argv[optind][0]))
331 #endif /* !SUS */
332 otime(argv[optind++]);
333 }
334 if (nacc == (time_t)-1 && nmod == (time_t)-1 && aflag == 0 &&
335 mflag == 0)
336 nulltime = 1;
337 if (nacc == (time_t)-1)
338 nacc = now;
339 if (nmod == (time_t)-1)
340 nmod = now;
341 if (aflag == 0 && mflag == 0)
342 aflag = mflag = 1;
343 #ifdef SUS
344 if (optind >= argc)
345 usage();
346 #else /* !SUS */
347 if (optind >= argc && date_time == 0)
348 usage();
349 #endif /* !SUS */
350 for (i = optind; i < argc; i++)
351 touch(argv[i]);
352 return errcnt < 0100 ? errcnt : 077;
353 }
354