xref: /titanic_52/usr/src/cmd/touch/touch.c (revision 843e19887f64dde75055cf8842fc4db2171eff45)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
31 /*	  All Rights Reserved	*/
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <strings.h>
40 #include <stdlib.h>
41 #include <ctype.h>
42 #include <libgen.h>
43 #include <fcntl.h>
44 #include <pwd.h>
45 #include <time.h>
46 #include <unistd.h>
47 #include <locale.h>
48 #include <sys/time.h>
49 #include <errno.h>
50 
51 #define	BADTIME	"bad time specification"
52 
53 static char	*myname;
54 
55 static int isnumber(char *);
56 static int atoi_for2(char *);
57 static void usage(const int);
58 static void touchabort(const char *);
59 static void parse_time(char *, timestruc_t *);
60 static void parse_datetime(char *, timestruc_t *);
61 static void timestruc_to_timeval(timestruc_t *, struct timeval *);
62 
63 int
64 main(int argc, char *argv[])
65 {
66 	int c;
67 
68 	int		aflag	= 0;
69 	int		cflag	= 0;
70 	int		rflag	= 0;
71 	int		mflag	= 0;
72 	int		tflag	= 0;
73 	int		stflag	= 0;
74 	int		status	= 0;
75 	int		usecurrenttime = 1;
76 	int		timespecified;
77 	int		optc;
78 	int		fd;
79 	struct stat	stbuf;
80 	struct stat	prstbuf;
81 	struct timeval	times[2];
82 
83 	(void) setlocale(LC_ALL, "");
84 #if !defined(TEXT_DOMAIN)
85 #define	TEXT_DOMAIN "SYS_TEST"
86 #endif
87 	(void) textdomain(TEXT_DOMAIN);
88 
89 	myname = basename(argv[0]);
90 	if (strcmp(myname, "settime") == NULL) {
91 		cflag++;
92 		stflag++;
93 		while ((optc = getopt(argc, argv, "f:")) != EOF)
94 			switch (optc) {
95 			case 'f':
96 				rflag++;
97 				usecurrenttime = 0;
98 				if (stat(optarg, &prstbuf) == -1) {
99 					(void) fprintf(stderr, "%s: ", myname);
100 					perror(optarg);
101 					return (2);
102 				}
103 				break;
104 			case '?':
105 				usage(stflag);
106 				break;
107 			};
108 	} else
109 		while ((optc = getopt(argc, argv, "acfmr:t:")) != EOF)
110 			switch (optc) {
111 			case 'a':
112 				aflag++;
113 				usecurrenttime = 0;
114 				break;
115 			case 'c':
116 				cflag++;
117 				break;
118 			case 'f':	/* silently ignore for UCB compat */
119 				break;
120 			case 'm':
121 				mflag++;
122 				usecurrenttime = 0;
123 				break;
124 			case 'r':	/* same as settime's -f option */
125 				rflag++;
126 				usecurrenttime = 0;
127 				if (stat(optarg, &prstbuf) == -1) {
128 					(void) fprintf(stderr, "%s: ", myname);
129 					perror(optarg);
130 					return (2);
131 				}
132 				break;
133 			case 't':
134 				tflag++;
135 				usecurrenttime = 0;
136 				parse_time(optarg, &prstbuf.st_mtim);
137 				prstbuf.st_atim = prstbuf.st_mtim;
138 				break;
139 			case '?':
140 				usage(stflag);
141 				break;
142 			}
143 
144 	argc -= optind;
145 	argv += optind;
146 
147 	if ((argc < 1) || (rflag + tflag > 1))
148 		usage(stflag);
149 
150 	if ((aflag == 0) && (mflag == 0)) {
151 		aflag = 1;
152 		mflag = 1;
153 	}
154 
155 	/*
156 	 * If either -r or -t has been specified,
157 	 * use the specified time.
158 	 */
159 	timespecified = rflag || tflag;
160 
161 	if (timespecified == 0) {
162 		if (argc >= 2 && isnumber(*argv) && (strlen(*argv) == 8 ||
163 			strlen(*argv) == 10)) {
164 			/*
165 			 * time is specified as an operand.
166 			 * use it.
167 			 */
168 			parse_datetime(*argv++, &prstbuf.st_mtim);
169 			prstbuf.st_atim = prstbuf.st_mtim;
170 			usecurrenttime = 0;
171 			timespecified = 1;
172 			argc--;
173 		} else {
174 			/*
175 			 * no time information is specified.
176 			 * use the current time.
177 			 */
178 			(void) gettimeofday(times, NULL);
179 			times[1] = times[0];
180 		}
181 	}
182 	for (c = 0; c < argc; c++) {
183 		if (stat(argv[c], &stbuf)) {
184 			/*
185 			 * if stat failed for reasons other than ENOENT,
186 			 * the file should not be created, since this
187 			 * can clobber the contents of an existing file
188 			 * (for example, a large file that results in overflow).
189 			 */
190 			if (errno != ENOENT) {
191 				(void) fprintf(stderr,
192 				    gettext("%s: %s cannot stat\n"),
193 				    myname, argv[c]);
194 				status++;
195 				continue;
196 			} else if (cflag) {
197 				continue;
198 			} else if ((fd = creat(argv[c],
199 			    (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)))
200 			    < 0) {
201 				(void) fprintf(stderr,
202 				    gettext("%s: %s cannot create\n"),
203 				    myname, argv[c]);
204 				status++;
205 				continue;
206 			} else {
207 				(void) close(fd);
208 				if (stat(argv[c], &stbuf)) {
209 					(void) fprintf(stderr,
210 					    gettext("%s: %s cannot stat\n"),
211 					    myname, argv[c]);
212 					status++;
213 					continue;
214 				}
215 			}
216 		}
217 
218 		if (mflag == 0) {
219 			/* Keep the mtime of the file */
220 			timestruc_to_timeval(&stbuf.st_mtim, times + 1);
221 		} else {
222 			if (timespecified) {
223 				/* Set the specified time */
224 				timestruc_to_timeval(&prstbuf.st_mtim,
225 				    times + 1);
226 			}
227 			/* Otherwise, use the current time by gettimeofday */
228 		}
229 
230 		if (aflag == 0) {
231 			/* Keep the atime of the file */
232 			timestruc_to_timeval(&stbuf.st_atim, times);
233 		} else {
234 			if (timespecified) {
235 				/* Set the specified time */
236 				timestruc_to_timeval(&prstbuf.st_atim, times);
237 			}
238 			/* Otherwise, use the current time by gettimeofday */
239 		}
240 
241 		if (utimes(argv[c], (usecurrenttime) ? NULL : times)) {
242 			(void) fprintf(stderr,
243 			    gettext("%s: cannot change times on %s\n"),
244 			    myname, argv[c]);
245 			status++;
246 			continue;
247 		}
248 	}
249 	return (status);
250 }
251 
252 static int
253 isnumber(char *s)
254 {
255 	int c;
256 
257 	while ((c = *s++) != '\0')
258 		if (!isdigit(c))
259 			return (0);
260 	return (1);
261 }
262 
263 
264 static void
265 parse_time(char *t, timestruc_t *ts)
266 {
267 	int		century = 0;
268 	int		seconds = 0;
269 	char		*p;
270 	time_t		when;
271 	struct tm	tm;
272 
273 	/*
274 	 * time in the following format (defined by the touch(1) spec):
275 	 *	[[CC]YY]MMDDhhmm[.SS]
276 	 */
277 	if ((p = strchr(t, '.')) != NULL) {
278 		if (strchr(p+1, '.') != NULL)
279 			touchabort(BADTIME);
280 		seconds = atoi_for2(p+1);
281 		*p = '\0';
282 	}
283 
284 	(void) memset(&tm, 0, sizeof (struct tm));
285 	when = time(0);
286 	tm.tm_year = localtime(&when)->tm_year;
287 
288 	switch (strlen(t)) {
289 		case 12:	/* CCYYMMDDhhmm */
290 			century = atoi_for2(t);
291 			t += 2;
292 			/* FALLTHROUGH */
293 		case 10:	/* YYMMDDhhmm */
294 			tm.tm_year = atoi_for2(t);
295 			t += 2;
296 			if (century == 0) {
297 				if (tm.tm_year < 69)
298 					tm.tm_year += 100;
299 			} else
300 				tm.tm_year += (century - 19) * 100;
301 			/* FALLTHROUGH */
302 		case 8:		/* MMDDhhmm */
303 			tm.tm_mon = atoi_for2(t) - 1;
304 			t += 2;
305 			tm.tm_mday = atoi_for2(t);
306 			t += 2;
307 			tm.tm_hour = atoi_for2(t);
308 			t += 2;
309 			tm.tm_min = atoi_for2(t);
310 			tm.tm_sec = seconds;
311 			break;
312 		default:
313 			touchabort(BADTIME);
314 	}
315 
316 	if ((when = mktime(&tm)) == -1)
317 		touchabort(BADTIME);
318 	if (tm.tm_isdst)
319 		when -= (timezone-altzone);
320 
321 	ts->tv_sec = when;
322 	ts->tv_nsec = 0;
323 }
324 
325 static void
326 parse_datetime(char *t, timestruc_t *ts)
327 {
328 	time_t		when;
329 	struct tm	tm;
330 
331 	/*
332 	 * time in the following format (defined by the touch(1) spec):
333 	 *	MMDDhhmm[yy]
334 	 */
335 
336 	(void) memset(&tm, 0, sizeof (struct tm));
337 	when = time(0);
338 	tm.tm_year = localtime(&when)->tm_year;
339 
340 	switch (strlen(t)) {
341 		case 10:	/* MMDDhhmmyy */
342 			tm.tm_year = atoi_for2(t+8);
343 			if (tm.tm_year < 69)
344 				tm.tm_year += 100;
345 			/* FALLTHROUGH */
346 		case 8:		/* MMDDhhmm */
347 			tm.tm_mon = atoi_for2(t) - 1;
348 			t += 2;
349 			tm.tm_mday = atoi_for2(t);
350 			t += 2;
351 			tm.tm_hour = atoi_for2(t);
352 			t += 2;
353 			tm.tm_min = atoi_for2(t);
354 			break;
355 		default:
356 			touchabort(BADTIME);
357 	}
358 
359 	if ((when = mktime(&tm)) == -1)
360 		touchabort(BADTIME);
361 	if (tm.tm_isdst)
362 		when -= (timezone - altzone);
363 
364 	ts->tv_sec = when;
365 	ts->tv_nsec = 0;
366 }
367 
368 static int
369 atoi_for2(char *p)
370 {
371 	int value;
372 
373 	value = (*p - '0') * 10 + *(p+1) - '0';
374 	if ((value < 0) || (value > 99))
375 		touchabort(BADTIME);
376 	return (value);
377 }
378 
379 static void
380 touchabort(const char *message)
381 {
382 	(void) fprintf(stderr, "%s: %s\n", myname, gettext(message));
383 	exit(1);
384 }
385 
386 static void
387 usage(const int settime)
388 {
389 	if (settime)
390 		(void) fprintf(stderr, gettext(
391 		    "usage: %s [-f file] [mmddhhmm[yy]] file...\n"),
392 		    myname);
393 	else
394 		(void) fprintf(stderr, gettext(
395 		    "usage: %s [-acm] [-r ref_file] file...\n"
396 		    "       %s [-acm] [MMDDhhmm[yy]] file...\n"
397 		    "       %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n"),
398 		    myname, myname, myname);
399 	exit(2);
400 }
401 
402 /*
403  * nanoseconds are rounded off to microseconds by flooring.
404  */
405 static void
406 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
407 {
408 	tv->tv_sec = ts->tv_sec;
409 	tv->tv_usec = ts->tv_nsec / 1000;
410 }
411