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