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 *
gtime()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
gpair()94 gpair()
95 {
96 int c, d;
97 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 int
main(int argc,char * argv[])115 main(int argc, char *argv[])
116 {
117 int c;
118 #ifdef S5EMUL
119 int days_in_month;
120 struct tm *tp;
121 #endif /* S5EMUL */
122
123 int errflg=0, optc;
124 extern char *optarg;
125 extern int optind;
126 extern int opterr;
127
128 prog = argv[0];
129 opterr = 0; /* disable getopt() error msgs */
130 while ((optc=getopt(argc, argv, "amcf")) != EOF)
131 switch (optc) {
132 case 'm':
133 mflg++;
134 aflg--;
135 break;
136 case 'a':
137 aflg++;
138 mflg--;
139 break;
140 case 'c':
141 cflg++;
142 break;
143 #ifndef S5EMUL
144 case 'f':
145 force++; /* SysV version ignores -f */
146 break;
147 #endif /*!S5EMUL*/
148 case '?':
149 errflg++;
150 }
151
152 if (((argc-optind) < 1) || errflg) {
153 (void) fprintf(stderr, "usage: %s %s file ...\n", prog, usage);
154 exit(2);
155 }
156 status = 0;
157
158 #ifdef S5EMUL
159 if (!isnumber(argv[optind])) { /* BSD version only sets Present */
160 #endif /*S5EMUL*/
161 if ((aflg <= 0) || (mflg <= 0))
162 (void) gettimeofday(&timbuf, NULL);
163 else
164 nflg++; /* no -a, -m, or date seen */
165 #ifdef S5EMUL
166 } else { /* SysV version sets arbitrary date */
167 cbp = (char *)argv[optind++];
168 if ((tp = gtime()) == NULL) {
169 (void) fprintf(stderr, "%s: bad date conversion\n",
170 prog);
171 exit(2);
172 }
173 days_in_month = dmsize[tp->tm_mon];
174 if (tp->tm_mon == 1 && isleap(tp->tm_year + 1900))
175 days_in_month = 29; /* February in leap year */
176 if (tp->tm_mon < 0 || tp->tm_mon > 11 ||
177 tp->tm_mday < 1 || tp->tm_mday > days_in_month ||
178 tp->tm_hour < 0 || tp->tm_hour > 23 ||
179 tp->tm_min < 0 || tp->tm_min > 59 ||
180 tp->tm_sec < 0 || tp->tm_sec > 59) {
181 (void) fprintf(stderr, "%s: bad date conversion\n",
182 prog);
183 exit(2);
184 }
185 timbuf = timelocal(tp);
186 }
187 #endif /*S5EMUL*/
188
189 for (c = optind; c < argc; c++) {
190 if (touch(argv[c]) < 0)
191 status++;
192 }
193 return (status);
194 }
195
196 int
touch(filename)197 touch(filename)
198 char *filename;
199 {
200 struct timeval times[2];
201 int fd;
202
203 if (stat(filename, &stbuf)) {
204 /*
205 * if stat failed for reasons other than ENOENT,
206 * the file should not be created, since this
207 * can clobber the contents of an existing file
208 * (for example, a large file that results in overflow).
209 */
210 if (errno != ENOENT) {
211 (void) fprintf(stderr,"%s: cannot stat ", prog);
212 perror(filename);
213 return (-1);
214 } else if (cflg) {
215 return (-1);
216 }
217 else if ((fd = creat(filename, 0666)) < 0) {
218 (void) fprintf(stderr, "%s: cannot create ", prog);
219 perror(filename);
220 return (-1);
221 }
222 else {
223 (void) close(fd);
224 if (stat(filename, &stbuf)) {
225 (void) fprintf(stderr,"%s: cannot stat ", prog);
226 perror(filename);
227 return (-1);
228 }
229 }
230 if (nflg)
231 return (0);
232 }
233
234 times[0] = times[1] = timbuf;
235 if (mflg <= 0)
236 timestruc_to_timeval(&stbuf.st_mtim, times + 1);
237 if (aflg <= 0)
238 timestruc_to_timeval(&stbuf.st_atim, times);
239
240 #ifndef S5EMUL
241 /*
242 * Since utime() allows the owner to change file times without
243 * regard to access permission, enforce BSD semantics here
244 * (cannot touch if read-only and not -f).
245 */
246 nowrite = access(filename, R_OK|W_OK);
247 if (nowrite && !force) {
248 (void) fprintf(stderr,
249 "%s: cannot touch %s: no write permission\n",
250 prog, filename);
251 return (-1);
252 }
253 #endif /*!S5EMUL*/
254
255 if (utimes(filename, nflg ? NULL : times)) {
256 if (nflg && (errno != EROFS) && (errno != EACCES)) {
257 /*
258 * If utime() failed to set the Present, it
259 * could be a BSD server that is complaining.
260 * If that's the case, try the old read/write trick.
261 */
262 return (oldtouch(filename, &stbuf));
263 }
264 (void) fprintf(stderr,"%s: cannot change times on ", prog);
265 perror(filename);
266 return (-1);
267 }
268 return (0);
269 }
270
271 int
oldtouch(filename,statp)272 oldtouch(filename, statp)
273 char *filename;
274 struct stat *statp;
275 {
276 int rwstatus;
277
278 if ((statp->st_mode & S_IFMT) != S_IFREG) {
279 (void) fprintf(stderr,
280 "%s: %s: only owner may touch special files on this filesystem\n",
281 prog, filename);
282 return (-1);
283 }
284
285 #ifndef S5EMUL
286 if (nowrite && force) {
287 if (chmod(filename, 0666)) {
288 fprintf(stderr, "%s: could not chmod ", prog);
289 perror(filename);
290 return (-1);
291 }
292 rwstatus = readwrite(filename, statp->st_size);
293 if (chmod(filename, (int)statp->st_mode)) {
294 fprintf(stderr, "%s: could not chmod back ", prog);
295 perror(filename);
296 return (-1);
297 }
298 return (rwstatus);
299 } else
300 #endif /*!S5EMUL*/
301 return (readwrite(filename, statp->st_size));
302 }
303
304 int
readwrite(filename,size)305 readwrite(filename, size)
306 char *filename;
307 off_t size;
308 {
309 int fd;
310 char first;
311
312 if (size) {
313 if ((fd = open(filename, 2)) < 0)
314 goto error;
315 if (read(fd, &first, 1) != 1)
316 goto closeerror;
317 if (lseek(fd, 0L, 0) == -1)
318 goto closeerror;
319 if (write(fd, &first, 1) != 1)
320 goto closeerror;
321 } else {
322 if ((fd = creat(filename, 0666)) < 0)
323 goto error;
324 }
325 if (close(fd) < 0)
326 goto error;
327 return (0);
328
329 closeerror:
330 (void) close(fd);
331 error:
332 (void) fprintf(stderr, "%s: could not touch ", prog);
333 perror(filename);
334 return (-1);
335 }
336
337 #ifdef S5EMUL
isnumber(s)338 isnumber(s)
339 char *s;
340 {
341 char c;
342
343 while (c = *s++)
344 if (!isdigit(c))
345 return (0);
346 return (1);
347 }
348 #endif /* S5EMUL */
349
350 /*
351 * nanoseconds are rounded off to microseconds by flooring.
352 */
353 static void
timestruc_to_timeval(timestruc_t * ts,struct timeval * tv)354 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv)
355 {
356 tv->tv_sec = ts->tv_sec;
357 tv->tv_usec = ts->tv_nsec / 1000;
358 }
359