touch.c revision 1.1.1.3 1 1.1 cgd /*
2 1.1.1.2 jtc * Copyright (c) 1993
3 1.1.1.2 jtc * The Regents of the University of California. All rights reserved.
4 1.1 cgd *
5 1.1 cgd * Redistribution and use in source and binary forms, with or without
6 1.1 cgd * modification, are permitted provided that the following conditions
7 1.1 cgd * are met:
8 1.1 cgd * 1. Redistributions of source code must retain the above copyright
9 1.1 cgd * notice, this list of conditions and the following disclaimer.
10 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer in the
12 1.1 cgd * documentation and/or other materials provided with the distribution.
13 1.1 cgd * 3. All advertising materials mentioning features or use of this software
14 1.1 cgd * must display the following acknowledgement:
15 1.1 cgd * This product includes software developed by the University of
16 1.1 cgd * California, Berkeley and its contributors.
17 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
18 1.1 cgd * may be used to endorse or promote products derived from this software
19 1.1 cgd * without specific prior written permission.
20 1.1 cgd *
21 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 1.1 cgd * SUCH DAMAGE.
32 1.1 cgd */
33 1.1 cgd
34 1.1 cgd #ifndef lint
35 1.1.1.2 jtc static char copyright[] =
36 1.1.1.2 jtc "@(#) Copyright (c) 1993\n\
37 1.1.1.2 jtc The Regents of the University of California. All rights reserved.\n";
38 1.1 cgd #endif /* not lint */
39 1.1 cgd
40 1.1 cgd #ifndef lint
41 1.1.1.3 jtc static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
42 1.1 cgd #endif /* not lint */
43 1.1 cgd
44 1.1 cgd #include <sys/types.h>
45 1.1 cgd #include <sys/stat.h>
46 1.1.1.2 jtc #include <sys/time.h>
47 1.1 cgd
48 1.1.1.2 jtc #include <err.h>
49 1.1.1.2 jtc #include <errno.h>
50 1.1.1.2 jtc #include <fcntl.h>
51 1.1.1.2 jtc #include <stdio.h>
52 1.1.1.2 jtc #include <stdlib.h>
53 1.1.1.2 jtc #include <string.h>
54 1.1.1.2 jtc #include <time.h>
55 1.1.1.2 jtc #include <unistd.h>
56 1.1.1.2 jtc
57 1.1.1.2 jtc int rw __P((char *, struct stat *, int));
58 1.1.1.2 jtc void stime_arg1 __P((char *, struct timeval *));
59 1.1.1.2 jtc void stime_arg2 __P((char *, int, struct timeval *));
60 1.1.1.2 jtc void stime_file __P((char *, struct timeval *));
61 1.1.1.2 jtc void usage __P((void));
62 1.1 cgd
63 1.1.1.2 jtc int
64 1.1 cgd main(argc, argv)
65 1.1 cgd int argc;
66 1.1.1.2 jtc char *argv[];
67 1.1 cgd {
68 1.1.1.2 jtc struct stat sb;
69 1.1.1.2 jtc struct timeval tv[2];
70 1.1.1.2 jtc int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
71 1.1.1.2 jtc char *p;
72 1.1.1.2 jtc
73 1.1.1.2 jtc aflag = cflag = fflag = mflag = timeset = 0;
74 1.1.1.2 jtc if (gettimeofday(&tv[0], NULL))
75 1.1.1.2 jtc err(1, "gettimeofday");
76 1.1.1.2 jtc
77 1.1.1.2 jtc while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF)
78 1.1.1.2 jtc switch(ch) {
79 1.1.1.2 jtc case 'a':
80 1.1.1.2 jtc aflag = 1;
81 1.1.1.2 jtc break;
82 1.1 cgd case 'c':
83 1.1.1.2 jtc cflag = 1;
84 1.1 cgd break;
85 1.1 cgd case 'f':
86 1.1.1.2 jtc fflag = 1;
87 1.1.1.2 jtc break;
88 1.1.1.2 jtc case 'm':
89 1.1.1.2 jtc mflag = 1;
90 1.1.1.2 jtc break;
91 1.1.1.2 jtc case 'r':
92 1.1.1.2 jtc timeset = 1;
93 1.1.1.2 jtc stime_file(optarg, tv);
94 1.1.1.2 jtc break;
95 1.1.1.2 jtc case 't':
96 1.1.1.2 jtc timeset = 1;
97 1.1.1.2 jtc stime_arg1(optarg, tv);
98 1.1 cgd break;
99 1.1 cgd case '?':
100 1.1 cgd default:
101 1.1 cgd usage();
102 1.1 cgd }
103 1.1.1.2 jtc argc -= optind;
104 1.1.1.2 jtc argv += optind;
105 1.1.1.2 jtc
106 1.1.1.2 jtc /* Default is both -a and -m. */
107 1.1.1.2 jtc if (aflag == 0 && mflag == 0)
108 1.1.1.2 jtc aflag = mflag = 1;
109 1.1.1.2 jtc
110 1.1.1.2 jtc /*
111 1.1.1.2 jtc * If no -r or -t flag, at least two operands, the first of which
112 1.1.1.2 jtc * is an 8 or 10 digit number, use the obsolete time specification.
113 1.1.1.2 jtc */
114 1.1.1.2 jtc if (!timeset && argc > 1) {
115 1.1.1.2 jtc (void)strtol(argv[0], &p, 10);
116 1.1.1.2 jtc len = p - argv[0];
117 1.1.1.2 jtc if (*p == '\0' && (len == 8 || len == 10)) {
118 1.1.1.2 jtc timeset = 1;
119 1.1.1.2 jtc stime_arg2(*argv++, len == 10, tv);
120 1.1.1.2 jtc }
121 1.1.1.2 jtc }
122 1.1.1.2 jtc
123 1.1.1.2 jtc /* Otherwise use the current time of day. */
124 1.1.1.2 jtc if (!timeset)
125 1.1.1.2 jtc tv[1] = tv[0];
126 1.1.1.2 jtc
127 1.1.1.2 jtc if (*argv == NULL)
128 1.1 cgd usage();
129 1.1.1.2 jtc
130 1.1.1.2 jtc for (rval = 0; *argv; ++argv) {
131 1.1.1.2 jtc /* See if the file exists. */
132 1.1.1.2 jtc if (stat(*argv, &sb))
133 1.1.1.2 jtc if (!cflag) {
134 1.1.1.2 jtc /* Create the file. */
135 1.1.1.2 jtc fd = open(*argv,
136 1.1.1.2 jtc O_WRONLY | O_CREAT, DEFFILEMODE);
137 1.1.1.2 jtc if (fd == -1 || fstat(fd, &sb) || close(fd)) {
138 1.1.1.2 jtc rval = 1;
139 1.1.1.2 jtc warn("%s", *argv);
140 1.1.1.2 jtc continue;
141 1.1.1.2 jtc }
142 1.1.1.2 jtc
143 1.1.1.2 jtc /* If using the current time, we're done. */
144 1.1.1.2 jtc if (!timeset)
145 1.1.1.2 jtc continue;
146 1.1.1.2 jtc } else
147 1.1.1.2 jtc continue;
148 1.1.1.2 jtc
149 1.1.1.2 jtc if (!aflag)
150 1.1.1.2 jtc TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
151 1.1.1.2 jtc if (!mflag)
152 1.1.1.2 jtc TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
153 1.1.1.2 jtc
154 1.1.1.2 jtc /* Try utimes(2). */
155 1.1.1.2 jtc if (!utimes(*argv, tv))
156 1.1.1.2 jtc continue;
157 1.1.1.2 jtc
158 1.1.1.2 jtc /* If the user specified a time, nothing else we can do. */
159 1.1.1.2 jtc if (timeset) {
160 1.1.1.2 jtc rval = 1;
161 1.1.1.2 jtc warn("%s", *argv);
162 1.1.1.2 jtc }
163 1.1.1.2 jtc
164 1.1.1.2 jtc /*
165 1.1.1.2 jtc * System V and POSIX 1003.1 require that a NULL argument
166 1.1.1.2 jtc * set the access/modification times to the current time.
167 1.1.1.2 jtc * The permission checks are different, too, in that the
168 1.1.1.2 jtc * ability to write the file is sufficient. Take a shot.
169 1.1.1.2 jtc */
170 1.1.1.2 jtc if (!utimes(*argv, NULL))
171 1.1.1.2 jtc continue;
172 1.1.1.2 jtc
173 1.1.1.2 jtc /* Try reading/writing. */
174 1.1.1.2 jtc if (rw(*argv, &sb, fflag))
175 1.1.1.2 jtc rval = 1;
176 1.1.1.2 jtc }
177 1.1.1.2 jtc exit(rval);
178 1.1 cgd }
179 1.1 cgd
180 1.1.1.2 jtc #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
181 1.1 cgd
182 1.1.1.2 jtc void
183 1.1.1.2 jtc stime_arg1(arg, tvp)
184 1.1.1.2 jtc char *arg;
185 1.1.1.2 jtc struct timeval *tvp;
186 1.1.1.2 jtc {
187 1.1.1.2 jtc struct tm *t;
188 1.1.1.2 jtc int yearset;
189 1.1.1.2 jtc char *p;
190 1.1.1.2 jtc /* Start with the current time. */
191 1.1.1.2 jtc if ((t = localtime(&tvp[0].tv_sec)) == NULL)
192 1.1.1.2 jtc err(1, "localtime");
193 1.1.1.2 jtc /* [[CC]YY]MMDDhhmm[.SS] */
194 1.1.1.2 jtc if ((p = strchr(arg, '.')) == NULL)
195 1.1.1.2 jtc t->tm_sec = 0; /* Seconds defaults to 0. */
196 1.1.1.2 jtc else {
197 1.1.1.2 jtc if (strlen(p + 1) != 2)
198 1.1.1.2 jtc goto terr;
199 1.1.1.2 jtc *p++ = '\0';
200 1.1.1.2 jtc t->tm_sec = ATOI2(p);
201 1.1.1.2 jtc }
202 1.1.1.2 jtc
203 1.1.1.2 jtc yearset = 0;
204 1.1.1.2 jtc switch(strlen(arg)) {
205 1.1.1.2 jtc case 12: /* CCYYMMDDhhmm */
206 1.1.1.2 jtc t->tm_year = ATOI2(arg);
207 1.1.1.3 jtc t->tm_year *= 100;
208 1.1.1.2 jtc yearset = 1;
209 1.1.1.2 jtc /* FALLTHOUGH */
210 1.1.1.2 jtc case 10: /* YYMMDDhhmm */
211 1.1.1.2 jtc if (yearset) {
212 1.1.1.2 jtc yearset = ATOI2(arg);
213 1.1.1.2 jtc t->tm_year += yearset;
214 1.1.1.2 jtc } else {
215 1.1.1.2 jtc yearset = ATOI2(arg);
216 1.1.1.2 jtc if (yearset < 69)
217 1.1.1.2 jtc t->tm_year = yearset + 2000;
218 1.1.1.2 jtc else
219 1.1.1.2 jtc t->tm_year = yearset + 1900;
220 1.1 cgd }
221 1.1.1.2 jtc t->tm_year -= 1900; /* Convert to UNIX time. */
222 1.1.1.2 jtc /* FALLTHROUGH */
223 1.1.1.2 jtc case 8: /* MMDDhhmm */
224 1.1.1.2 jtc t->tm_mon = ATOI2(arg);
225 1.1.1.2 jtc --t->tm_mon; /* Convert from 01-12 to 00-11 */
226 1.1.1.2 jtc t->tm_mday = ATOI2(arg);
227 1.1.1.2 jtc t->tm_hour = ATOI2(arg);
228 1.1.1.2 jtc t->tm_min = ATOI2(arg);
229 1.1.1.2 jtc break;
230 1.1.1.2 jtc default:
231 1.1.1.2 jtc goto terr;
232 1.1 cgd }
233 1.1.1.2 jtc
234 1.1.1.2 jtc t->tm_isdst = -1; /* Figure out DST. */
235 1.1.1.2 jtc tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
236 1.1.1.2 jtc if (tvp[0].tv_sec == -1)
237 1.1.1.2 jtc terr: errx(1,
238 1.1.1.2 jtc "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
239 1.1.1.2 jtc
240 1.1.1.2 jtc tvp[0].tv_usec = tvp[1].tv_usec = 0;
241 1.1 cgd }
242 1.1 cgd
243 1.1.1.2 jtc void
244 1.1.1.2 jtc stime_arg2(arg, year, tvp)
245 1.1.1.2 jtc char *arg;
246 1.1.1.2 jtc int year;
247 1.1.1.2 jtc struct timeval *tvp;
248 1.1.1.2 jtc {
249 1.1.1.2 jtc struct tm *t;
250 1.1.1.2 jtc /* Start with the current time. */
251 1.1.1.2 jtc if ((t = localtime(&tvp[0].tv_sec)) == NULL)
252 1.1.1.2 jtc err(1, "localtime");
253 1.1.1.2 jtc
254 1.1.1.2 jtc t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
255 1.1.1.2 jtc --t->tm_mon; /* Convert from 01-12 to 00-11 */
256 1.1.1.2 jtc t->tm_mday = ATOI2(arg);
257 1.1.1.2 jtc t->tm_hour = ATOI2(arg);
258 1.1.1.2 jtc t->tm_min = ATOI2(arg);
259 1.1.1.2 jtc if (year)
260 1.1.1.2 jtc t->tm_year = ATOI2(arg);
261 1.1.1.2 jtc
262 1.1.1.2 jtc t->tm_isdst = -1; /* Figure out DST. */
263 1.1.1.2 jtc tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
264 1.1.1.2 jtc if (tvp[0].tv_sec == -1)
265 1.1.1.2 jtc errx(1,
266 1.1.1.2 jtc "out of range or illegal time specification: MMDDhhmm[yy]");
267 1.1.1.2 jtc
268 1.1.1.2 jtc tvp[0].tv_usec = tvp[1].tv_usec = 0;
269 1.1.1.2 jtc }
270 1.1.1.2 jtc
271 1.1.1.2 jtc void
272 1.1.1.2 jtc stime_file(fname, tvp)
273 1.1.1.2 jtc char *fname;
274 1.1.1.2 jtc struct timeval *tvp;
275 1.1.1.2 jtc {
276 1.1.1.2 jtc struct stat sb;
277 1.1.1.2 jtc
278 1.1.1.2 jtc if (stat(fname, &sb))
279 1.1.1.2 jtc err(1, "%s", fname);
280 1.1.1.2 jtc TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
281 1.1.1.2 jtc TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
282 1.1.1.2 jtc }
283 1.1.1.2 jtc
284 1.1.1.2 jtc int
285 1.1.1.2 jtc rw(fname, sbp, force)
286 1.1.1.2 jtc char *fname;
287 1.1.1.2 jtc struct stat *sbp;
288 1.1.1.2 jtc int force;
289 1.1.1.2 jtc {
290 1.1.1.2 jtc int fd, needed_chmod, rval;
291 1.1.1.2 jtc u_char byte;
292 1.1.1.2 jtc
293 1.1.1.2 jtc /* Try regular files and directories. */
294 1.1.1.2 jtc if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) {
295 1.1.1.2 jtc warnx("%s: %s", fname, strerror(EFTYPE));
296 1.1.1.2 jtc return (1);
297 1.1.1.2 jtc }
298 1.1.1.2 jtc
299 1.1.1.2 jtc needed_chmod = rval = 0;
300 1.1.1.2 jtc if ((fd = open(fname, O_RDWR, 0)) == -1) {
301 1.1.1.2 jtc if (!force || chmod(fname, DEFFILEMODE))
302 1.1.1.2 jtc goto err;
303 1.1.1.2 jtc if ((fd = open(fname, O_RDWR, 0)) == -1)
304 1.1.1.2 jtc goto err;
305 1.1.1.2 jtc needed_chmod = 1;
306 1.1.1.2 jtc }
307 1.1.1.2 jtc
308 1.1.1.2 jtc if (sbp->st_size != 0) {
309 1.1.1.2 jtc if (read(fd, &byte, sizeof(byte)) != sizeof(byte))
310 1.1.1.2 jtc goto err;
311 1.1.1.2 jtc if (lseek(fd, (off_t)0, SEEK_SET) == -1)
312 1.1.1.2 jtc goto err;
313 1.1.1.2 jtc if (write(fd, &byte, sizeof(byte)) != sizeof(byte))
314 1.1.1.2 jtc goto err;
315 1.1 cgd } else {
316 1.1.1.2 jtc if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
317 1.1.1.2 jtc err: rval = 1;
318 1.1.1.2 jtc warn("%s", fname);
319 1.1.1.2 jtc } else if (ftruncate(fd, (off_t)0)) {
320 1.1.1.2 jtc rval = 1;
321 1.1.1.2 jtc warn("%s: file modified", fname);
322 1.1.1.2 jtc }
323 1.1.1.2 jtc }
324 1.1.1.2 jtc
325 1.1.1.2 jtc if (close(fd) && rval != 1) {
326 1.1.1.2 jtc rval = 1;
327 1.1.1.2 jtc warn("%s", fname);
328 1.1.1.2 jtc }
329 1.1.1.2 jtc if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) {
330 1.1.1.2 jtc rval = 1;
331 1.1.1.2 jtc warn("%s: permissions modified", fname);
332 1.1.1.2 jtc }
333 1.1.1.2 jtc return (rval);
334 1.1 cgd }
335 1.1 cgd
336 1.1.1.2 jtc __dead void
337 1.1 cgd usage()
338 1.1 cgd {
339 1.1.1.2 jtc (void)fprintf(stderr,
340 1.1.1.2 jtc "usage: touch [-acfm] [-r file] [-t time] file ...\n");
341 1.1 cgd exit(1);
342 1.1 cgd }
343