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