xref: /illumos-gate/usr/src/ucbcmd/touch/touch.c (revision 7c478bd9)
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 2002 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #ifndef lint
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 #endif
30 
31 #include <stdio.h>
32 #include <ctype.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/file.h>
36 #include <sys/time.h>
37 #include <time.h>
38 #include <errno.h>
39 #include <unistd.h>
40 
41 #define	isleap(y) (((y) % 4) == 0 && ((y) % 100) != 0 || ((y) % 400) == 0)
42 
43 struct	stat	stbuf;
44 int	status;
45 #ifdef S5EMUL
46 int dmsize[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
47 #endif S5EMUL
48 
49 static char usage[] =
50 #ifdef S5EMUL
51 		"[-amc] [mmddhhmm[yy]]";
52 #else /*!S5EMUL*/
53 		"[-amcf]";
54 int	force = 0;
55 int	nowrite;
56 #endif /*!S5EMUL*/
57 
58 int	mflg=1, aflg=1, cflg=0, nflg=0;
59 char	*prog;
60 
61 #ifdef S5EMUL
62 char	*cbp;
63 #endif S5EMUL
64 time_t	time();
65 off_t	lseek();
66 time_t	timelocal(), timegm();
67 struct timeval timbuf;
68 static void timestruc_to_timeval(timestruc_t *, struct timeval *);
69 
70 #ifdef S5EMUL
71 struct tm *
72 gtime()
73 {
74 	static struct tm newtime;
75 	long nt;
76 
77 	newtime.tm_mon = gpair() - 1;
78 	newtime.tm_mday = gpair();
79 	newtime.tm_hour = gpair();
80 	if (newtime.tm_hour == 24) {
81 		newtime.tm_hour = 0;
82 		newtime.tm_mday++;
83 	}
84 	newtime.tm_min = gpair();
85 	newtime.tm_sec = 0;
86 	newtime.tm_year = gpair();
87 	if (newtime.tm_year < 0) {
88 		(void) time(&nt);
89 		newtime.tm_year = localtime(&nt)->tm_year;
90 	}
91 	return (&newtime);
92 }
93 
94 gpair()
95 {
96 	register int c, d;
97 	register char *cp;
98 
99 	cp = cbp;
100 	if (*cp == 0)
101 		return (-1);
102 	c = (*cp++ - '0') * 10;
103 	if (c<0 || c>100)
104 		return (-1);
105 	if (*cp == 0)
106 		return (-1);
107 	if ((d = *cp++ - '0') < 0 || d > 9)
108 		return (-1);
109 	cbp = cp;
110 	return (c+d);
111 }
112 #endif /*S5EMUL*/
113 
114 main(argc, argv)
115 	int argc;
116 	char *argv[];
117 {
118 	register c;
119 #ifdef S5EMUL
120 	int days_in_month;
121 	struct tm *tp;
122 #endif S5EMUL
123 
124 	int errflg=0, optc;
125 	extern char *optarg;
126 	extern int optind;
127 	extern int opterr;
128 
129 	prog = argv[0];
130 	opterr = 0;			/* disable getopt() error msgs */
131 	while ((optc=getopt(argc, argv, "amcf")) != EOF)
132 		switch (optc) {
133 		case 'm':
134 			mflg++;
135 			aflg--;
136 			break;
137 		case 'a':
138 			aflg++;
139 			mflg--;
140 			break;
141 		case 'c':
142 			cflg++;
143 			break;
144 #ifndef S5EMUL
145 		case 'f':
146 			force++;	/* SysV version ignores -f */
147 			break;
148 #endif /*!S5EMUL*/
149 		case '?':
150 			errflg++;
151 		}
152 
153 	if (((argc-optind) < 1) || errflg) {
154 		(void) fprintf(stderr, "usage: %s %s file ...\n", prog, usage);
155 		exit(2);
156 	}
157 	status = 0;
158 
159 #ifdef S5EMUL
160 	if (!isnumber(argv[optind])) {	/* BSD version only sets Present */
161 #endif /*S5EMUL*/
162 		if ((aflg <= 0) || (mflg <= 0))
163 			(void) gettimeofday(&timbuf, NULL);
164 		else
165 			nflg++;		/* no -a, -m, or date seen */
166 #ifdef S5EMUL
167 	} else {			/* SysV version sets arbitrary date */
168 		cbp = (char *)argv[optind++];
169 		if ((tp = gtime()) == NULL) {
170 			(void) fprintf(stderr, "%s: bad date conversion\n",
171 			    prog);
172 			exit(2);
173 		}
174 		days_in_month = dmsize[tp->tm_mon];
175 		if (tp->tm_mon == 1 && isleap(tp->tm_year + 1900))
176 			days_in_month = 29;	/* February in leap year */
177 		if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
178 		    tp->tm_mday < 1 || tp->tm_mday > days_in_month ||
179 		    tp->tm_hour < 0 || tp->tm_hour > 23 ||
180 		    tp->tm_min < 0 || tp->tm_min > 59 ||
181 		    tp->tm_sec < 0 || tp->tm_sec > 59) {
182 			(void) fprintf(stderr, "%s: bad date conversion\n",
183 			    prog);
184 			exit(2);
185 		}
186 		timbuf = timelocal(tp);
187 	}
188 #endif /*S5EMUL*/
189 
190 	for (c = optind; c < argc; c++) {
191 		if (touch(argv[c]) < 0)
192 			status++;
193 	}
194 	exit(status);
195 	/* NOTREACHED */
196 }
197 
198 int
199 touch(filename)
200 	char *filename;
201 {
202 	struct timeval times[2];
203 	register int fd;
204 
205 	if (stat(filename, &stbuf)) {
206 		/*
207 		 * if stat failed for reasons other than ENOENT,
208 		 * the file should not be created, since this
209 		 * can clobber the contents of an existing file
210 		 * (for example, a large file that results in overflow).
211 		 */
212 		if (errno != ENOENT) {
213 			(void) fprintf(stderr,"%s: cannot stat ", prog);
214 			perror(filename);
215 			return (-1);
216 		} else if (cflg) {
217 			return (-1);
218 		}
219 		else if ((fd = creat(filename, 0666)) < 0) {
220 			(void) fprintf(stderr, "%s: cannot create ", prog);
221 			perror(filename);
222 			return (-1);
223 		}
224 		else {
225 			(void) close(fd);
226 			if (stat(filename, &stbuf)) {
227 				(void) fprintf(stderr,"%s: cannot stat ", prog);
228 				perror(filename);
229 				return (-1);
230 			}
231 		}
232 		if (nflg)
233 			return (0);
234 	}
235 
236 	times[0] = times[1] = timbuf;
237 	if (mflg <= 0)
238 		timestruc_to_timeval(&stbuf.st_mtim, times + 1);
239 	if (aflg <= 0)
240 		timestruc_to_timeval(&stbuf.st_atim, times);
241 
242 #ifndef S5EMUL
243 	/*
244 	 * Since utime() allows the owner to change file times without
245 	 * regard to access permission, enforce BSD semantics here
246 	 * (cannot touch if read-only and not -f).
247 	 */
248 	nowrite = access(filename, R_OK|W_OK);
249 	if (nowrite && !force) {
250 		(void) fprintf(stderr,
251 		    "%s: cannot touch %s: no write permission\n",
252 		    prog, filename);
253 		return (-1);
254 	}
255 #endif /*!S5EMUL*/
256 
257 	if (utimes(filename, nflg ? NULL : times)) {
258 		if (nflg && (errno != EROFS) && (errno != EACCES)) {
259 			/*
260 			 * If utime() failed to set the Present, it
261 			 * could be a BSD server that is complaining.
262 			 * If that's the case, try the old read/write trick.
263 			 */
264 			return (oldtouch(filename, &stbuf));
265 		}
266 		(void) fprintf(stderr,"%s: cannot change times on ", prog);
267 		perror(filename);
268 		return (-1);
269 	}
270 	return (0);
271 }
272 
273 int
274 oldtouch(filename, statp)
275 	char *filename;
276 	register struct stat *statp;
277 {
278 	int rwstatus;
279 
280 	if ((statp->st_mode & S_IFMT) != S_IFREG) {
281 		(void) fprintf(stderr,
282 		    "%s: %s: only owner may touch special files on this filesystem\n",
283 		    prog, filename);
284 		return (-1);
285 	}
286 
287 #ifndef S5EMUL
288 	if (nowrite && force) {
289 		if (chmod(filename, 0666)) {
290 			fprintf(stderr, "%s: could not chmod ", prog);
291 			perror(filename);
292 			return (-1);
293 		}
294 		rwstatus = readwrite(filename, statp->st_size);
295 		if (chmod(filename, (int)statp->st_mode)) {
296 			fprintf(stderr, "%s: could not chmod back ", prog);
297 			perror(filename);
298 			return (-1);
299 		}
300 		return (rwstatus);
301 	} else
302 #endif /*!S5EMUL*/
303 		return (readwrite(filename, statp->st_size));
304 }
305 
306 int
307 readwrite(filename, size)
308 	char	*filename;
309 	off_t	size;
310 {
311 	int fd;
312 	char first;
313 
314 	if (size) {
315 		if ((fd = open(filename, 2)) < 0)
316 			goto error;
317 		if (read(fd, &first, 1) != 1)
318 			goto closeerror;
319 		if (lseek(fd, 0L, 0) == -1)
320 			goto closeerror;
321 		if (write(fd, &first, 1) != 1)
322 			goto closeerror;
323 	} else {
324 		if ((fd = creat(filename, 0666)) < 0)
325 			goto error;
326 	}
327 	if (close(fd) < 0)
328 		goto error;
329 	return (0);
330 
331 closeerror:
332 	(void) close(fd);
333 error:
334 	(void) fprintf(stderr, "%s: could not touch ", prog);
335 	perror(filename);
336 	return (-1);
337 }
338 
339 #ifdef S5EMUL
340 isnumber(s)
341 	char *s;
342 {
343 	register c;
344 
345 	while (c = *s++)
346 		if (!isdigit(c))
347 			return (0);
348 	return (1);
349 }
350 #endif S5EMUL
351 
352 /*
353  * nanoseconds are rounded off to microseconds by flooring.
354  */
355 static void
356 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
357 {
358 	tv->tv_sec = ts->tv_sec;
359 	tv->tv_usec = ts->tv_nsec / 1000;
360 }
361