Commit | Line | Data |
---|---|---|
b4008fc5 | 1 | /* |
0581f0f9 | 2 | * Copyright (c) 1993 Regents of the University of California. |
b4008fc5 KB |
3 | * All rights reserved. |
4 | * | |
cb956e54 | 5 | * %sccs.include.redist.c% |
b4008fc5 KB |
6 | */ |
7 | ||
8 | #ifndef lint | |
9 | char copyright[] = | |
0581f0f9 | 10 | "@(#) Copyright (c) 1993 Regents of the University of California.\n\ |
b4008fc5 KB |
11 | All rights reserved.\n"; |
12 | #endif /* not lint */ | |
13 | ||
f6227721 | 14 | #ifndef lint |
0581f0f9 | 15 | static char sccsid[] = "@(#)touch.c 5.2 (Berkeley) %G%"; |
b4008fc5 | 16 | #endif /* not lint */ |
0219f737 | 17 | |
0219f737 KM |
18 | #include <sys/types.h> |
19 | #include <sys/stat.h> | |
0581f0f9 | 20 | #include <sys/time.h> |
7d70e53d | 21 | |
0581f0f9 | 22 | #include <err.h> |
7d70e53d KB |
23 | #include <errno.h> |
24 | #include <fcntl.h> | |
b4008fc5 | 25 | #include <stdio.h> |
7d70e53d KB |
26 | #include <stdlib.h> |
27 | #include <string.h> | |
0581f0f9 | 28 | #include <time.h> |
7d70e53d | 29 | #include <unistd.h> |
0219f737 | 30 | |
0581f0f9 KB |
31 | int rw __P((char *, struct stat *, int)); |
32 | void stime_arg1 __P((char *, struct timeval *)); | |
33 | void stime_arg2 __P((char *, int, struct timeval *)); | |
34 | void stime_file __P((char *, struct timeval *)); | |
35 | void usage __P((void)); | |
7d70e53d KB |
36 | |
37 | int | |
0dd5dde6 | 38 | main(argc, argv) |
b4008fc5 | 39 | int argc; |
7d70e53d | 40 | char *argv[]; |
7add65c5 | 41 | { |
0581f0f9 KB |
42 | struct stat sb; |
43 | struct timeval tv[2]; | |
44 | int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; | |
45 | char *p; | |
7add65c5 | 46 | |
0581f0f9 KB |
47 | aflag = mflag = 1; |
48 | cflag = fflag = timeset = 0; | |
49 | ||
50 | if (gettimeofday(&tv[0], NULL)) | |
51 | err(1, "gettimeofday"); | |
52 | ||
53 | while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF) | |
54 | switch(ch) { | |
55 | case 'a': | |
56 | aflag = 1; | |
57 | mflag = 0; | |
58 | break; | |
7868c57c | 59 | case 'c': |
0581f0f9 | 60 | cflag = 1; |
7868c57c KB |
61 | break; |
62 | case 'f': | |
0581f0f9 KB |
63 | fflag = 1; |
64 | break; | |
65 | case 'm': | |
66 | aflag = 0; | |
67 | mflag = 1; | |
68 | break; | |
69 | case 'r': | |
70 | timeset = 1; | |
71 | stime_file(optarg, tv); | |
72 | break; | |
73 | case 't': | |
74 | timeset = 1; | |
75 | stime_arg1(optarg, tv); | |
7868c57c KB |
76 | break; |
77 | case '?': | |
78 | default: | |
0dd5dde6 | 79 | usage(); |
0219f737 | 80 | } |
0581f0f9 KB |
81 | argc -= optind; |
82 | argv += optind; | |
83 | ||
84 | /* | |
85 | * If no -r or -t flag, at least two operands, the first of which | |
86 | * is an 8 or 10 digit number, use the obsolete time specification. | |
87 | */ | |
88 | if (!timeset && argc > 1) { | |
89 | (void)strtol(argv[0], &p, 10); | |
90 | len = p - argv[0]; | |
91 | if (*p == '\0' && (len == 8 || len == 10)) { | |
92 | timeset = 1; | |
93 | stime_arg2(argv[0], len == 10, tv); | |
94 | } | |
95 | } | |
96 | ||
97 | /* Otherwise use the current time of day. */ | |
98 | if (!timeset) | |
99 | tv[1] = tv[0]; | |
100 | ||
101 | if (*argv == NULL) | |
0dd5dde6 | 102 | usage(); |
7add65c5 | 103 | |
0581f0f9 KB |
104 | for (rval = 0; *argv; ++argv) { |
105 | /* See if the file exists. */ | |
106 | if (stat(*argv, &sb)) | |
107 | if (!cflag) { | |
108 | /* Create the file. */ | |
109 | fd = open(*argv, | |
110 | O_WRONLY | O_CREAT | O_TRUNC, DEFFILEMODE); | |
111 | if (fd == -1 || fstat(fd, &sb) || close(fd)) { | |
112 | rval = 1; | |
113 | warn("%s", *argv); | |
114 | continue; | |
115 | } | |
7add65c5 | 116 | |
0581f0f9 KB |
117 | /* If using the current time, we're done. */ |
118 | if (!timeset) | |
119 | continue; | |
120 | } else | |
121 | continue; | |
122 | ||
123 | if (!aflag) | |
124 | TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); | |
125 | if (!mflag) | |
126 | TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); | |
127 | ||
128 | /* Try utimes(2). */ | |
129 | if (!utimes(*argv, tv)) | |
130 | continue; | |
131 | ||
132 | /* If the user specified a time, nothing else we can do. */ | |
133 | if (timeset) { | |
134 | rval = 1; | |
135 | warn("%s", *argv); | |
136 | } | |
137 | ||
138 | /* Try reading/writing. */ | |
139 | if (rw(*argv, &sb, fflag)) | |
140 | rval = 1; | |
0219f737 | 141 | } |
0581f0f9 KB |
142 | exit(rval); |
143 | } | |
144 | ||
145 | #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; | |
146 | ||
147 | void | |
148 | stime_arg1(arg, tvp) | |
149 | char *arg; | |
150 | struct timeval *tvp; | |
151 | { | |
152 | struct tm *t; | |
153 | int yearset; | |
154 | char *p; | |
155 | /* Start with the current time. */ | |
156 | if ((t = localtime(&tvp[0].tv_sec)) == NULL) | |
157 | err(1, "localtime"); | |
158 | /* [[CC]YY]MMDDhhmm[.SS] */ | |
159 | if ((p = strchr(arg, '.')) == NULL) | |
160 | t->tm_sec = 0; /* Seconds defaults to 0. */ | |
161 | else { | |
162 | if (strlen(p + 1) != 2) | |
163 | goto terr; | |
164 | *p++ = '\0'; | |
165 | t->tm_sec = ATOI2(p); | |
0219f737 | 166 | } |
0581f0f9 KB |
167 | |
168 | yearset = 0; | |
169 | switch(strlen(arg)) { | |
170 | case 12: /* CCYYMMDDhhmm */ | |
171 | t->tm_year = ATOI2(arg); | |
172 | t->tm_year *= 1000; | |
173 | yearset = 1; | |
174 | /* FALLTHOUGH */ | |
175 | case 10: /* YYMMDDhhmm */ | |
176 | if (yearset) { | |
177 | yearset = ATOI2(arg); | |
178 | t->tm_year += yearset; | |
179 | } else { | |
180 | yearset = ATOI2(arg); | |
181 | if (yearset < 69) | |
182 | t->tm_year = yearset + 2000; | |
183 | else | |
184 | t->tm_year = yearset + 1900; | |
7add65c5 | 185 | } |
0581f0f9 KB |
186 | t->tm_year -= 1900; /* Convert to UNIX time. */ |
187 | /* FALLTHROUGH */ | |
188 | case 8: /* MMDDhhmm */ | |
189 | t->tm_mon = ATOI2(arg); | |
190 | --t->tm_mon; /* Convert from 01-12 to 00-11 */ | |
191 | t->tm_mday = ATOI2(arg); | |
192 | t->tm_hour = ATOI2(arg); | |
193 | t->tm_min = ATOI2(arg); | |
194 | break; | |
195 | default: | |
196 | goto terr; | |
0219f737 | 197 | } |
0581f0f9 KB |
198 | |
199 | t->tm_isdst = -1; /* Figure out DST. */ | |
200 | tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); | |
201 | if (tvp[0].tv_sec == -1) | |
202 | terr: errx(1, | |
203 | "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); | |
204 | ||
205 | tvp[0].tv_usec = tvp[1].tv_usec = 0; | |
206 | } | |
207 | ||
208 | void | |
209 | stime_arg2(arg, year, tvp) | |
210 | char *arg; | |
211 | int year; | |
212 | struct timeval *tvp; | |
213 | { | |
214 | struct tm *t; | |
215 | /* Start with the current time. */ | |
216 | if ((t = localtime(&tvp[0].tv_sec)) == NULL) | |
217 | err(1, "localtime"); | |
218 | ||
219 | t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ | |
220 | --t->tm_mon; /* Convert from 01-12 to 00-11 */ | |
221 | t->tm_mday = ATOI2(arg); | |
222 | t->tm_hour = ATOI2(arg); | |
223 | t->tm_min = ATOI2(arg); | |
224 | if (year) | |
225 | t->tm_year = ATOI2(arg); | |
226 | ||
227 | t->tm_isdst = -1; /* Figure out DST. */ | |
228 | tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); | |
229 | if (tvp[0].tv_sec == -1) | |
230 | errx(1, | |
231 | "out of range or illegal time specification: MMDDhhmm[yy]"); | |
232 | ||
233 | tvp[0].tv_usec = tvp[1].tv_usec = 0; | |
234 | } | |
235 | ||
236 | void | |
237 | stime_file(fname, tvp) | |
238 | char *fname; | |
239 | struct timeval *tvp; | |
240 | { | |
241 | struct stat sb; | |
242 | ||
243 | if (stat(fname, &sb)) | |
244 | err(1, "%s", fname); | |
245 | TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); | |
246 | TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); | |
0219f737 | 247 | } |
7add65c5 | 248 | |
7d70e53d | 249 | int |
0581f0f9 KB |
250 | rw(fname, sbp, force) |
251 | char *fname; | |
252 | struct stat *sbp; | |
253 | int force; | |
0219f737 | 254 | { |
0581f0f9 KB |
255 | int fd, needed_chmod, rval; |
256 | u_char byte; | |
257 | ||
258 | /* Try regular files and directories. */ | |
259 | if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) { | |
260 | warnx("%s: %s", fname, strerror(EFTYPE)); | |
261 | return (1); | |
262 | } | |
263 | ||
264 | needed_chmod = rval = 0; | |
265 | if ((fd = open(fname, O_RDWR, 0)) == -1) { | |
266 | if (!force || chmod(fname, DEFFILEMODE)) | |
267 | goto err; | |
268 | if ((fd = open(fname, O_RDWR, 0)) == -1) | |
269 | goto err; | |
270 | needed_chmod = 1; | |
271 | } | |
272 | ||
273 | if (sbp->st_size != 0) { | |
274 | if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) | |
275 | goto err; | |
7d70e53d | 276 | if (lseek(fd, (off_t)0, SEEK_SET) == -1) |
0581f0f9 KB |
277 | goto err; |
278 | if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) | |
279 | goto err; | |
0219f737 | 280 | } else { |
0581f0f9 KB |
281 | if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { |
282 | err: rval = 1; | |
283 | warn("%s", fname); | |
284 | } else if (ftruncate(fd, (off_t)0)) { | |
285 | rval = 1; | |
286 | warn("%s: file modified", fname); | |
287 | } | |
7add65c5 | 288 | } |
0581f0f9 KB |
289 | |
290 | if (close(fd) && rval != 1) { | |
291 | rval = 1; | |
292 | warn("%s", fname); | |
293 | } | |
294 | if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { | |
295 | rval = 1; | |
296 | warn("%s: permissions modified", fname); | |
7d70e53d | 297 | } |
0581f0f9 | 298 | return (rval); |
0dd5dde6 KB |
299 | } |
300 | ||
7d70e53d | 301 | __dead void |
0dd5dde6 KB |
302 | usage() |
303 | { | |
0581f0f9 KB |
304 | (void)fprintf(stderr, |
305 | "usage: touch [-acfm] [-r file] [-t time] file ...\n"); | |
0dd5dde6 | 306 | exit(1); |
7add65c5 | 307 | } |