GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/touch/touch.c Lines: 55 138 39.9 %
Date: 2017-11-07 Branches: 40 128 31.3 %

Line Branch Exec Source
1
/*	$OpenBSD: touch.c,v 1.25 2015/10/09 01:37:09 deraadt Exp $	*/
2
/*	$NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $	*/
3
4
/*
5
 * Copyright (c) 1993
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. Neither the name of the University nor the names of its contributors
17
 *    may be used to endorse or promote products derived from this software
18
 *    without specific prior written permission.
19
 *
20
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
 * SUCH DAMAGE.
31
 */
32
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <sys/time.h>
36
37
#include <ctype.h>
38
#include <err.h>
39
#include <errno.h>
40
#include <fcntl.h>
41
#include <stdio.h>
42
#include <stdlib.h>
43
#include <string.h>
44
#include <locale.h>
45
#include <time.h>
46
#include <unistd.h>
47
48
void		stime_arg1(char *, struct timespec *);
49
void		stime_arg2(char *, int, struct timespec *);
50
void		stime_argd(char *, struct timespec *);
51
void		stime_file(char *, struct timespec *);
52
__dead void	usage(void);
53
54
int
55
main(int argc, char *argv[])
56
{
57
4550
	struct timespec	 ts[2];
58
	int		 aflag, cflag, mflag, ch, fd, len, rval, timeset;
59
2275
	char		*p;
60
61
2275
	(void)setlocale(LC_ALL, "");
62
63
2275
	if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1)
64
		err(1, "pledge");
65
66
	aflag = cflag = mflag = timeset = 0;
67
4552
	while ((ch = getopt(argc, argv, "acd:fmr:t:")) != -1)
68


4
		switch (ch) {
69
		case 'a':
70
			aflag = 1;
71
			break;
72
		case 'c':
73
			cflag = 1;
74
			break;
75
		case 'd':
76
			timeset = 1;
77
			stime_argd(optarg, ts);
78
			break;
79
		case 'f':
80
			break;
81
		case 'm':
82
			mflag = 1;
83
			break;
84
		case 'r':
85
			timeset = 1;
86
			stime_file(optarg, ts);
87
			break;
88
		case 't':
89
			timeset = 1;
90
2
			stime_arg1(optarg, ts);
91
2
			break;
92
		default:
93
			usage();
94
		}
95
2275
	argc -= optind;
96
2275
	argv += optind;
97
98
	/* Default is both -a and -m. */
99
2275
	if (aflag == 0 && mflag == 0)
100
2275
		aflag = mflag = 1;
101
102
	/*
103
	 * If no -r or -t flag, at least two operands, the first of which
104
	 * is an 8 or 10 digit number, use the obsolete time specification.
105
	 */
106
2275
	if (!timeset && argc > 1) {
107
37
		(void)strtol(argv[0], &p, 10);
108
37
		len = p - argv[0];
109

37
		if (*p == '\0' && (len == 8 || len == 10)) {
110
			timeset = 1;
111
			stime_arg2(*argv++, len == 10, ts);
112
		}
113
	}
114
115
	/* Otherwise use the current time of day. */
116
2275
	if (!timeset)
117
2273
		ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW;
118
119
2275
	if (!aflag)
120
		ts[0].tv_nsec = UTIME_OMIT;
121
2275
	if (!mflag)
122
		ts[1].tv_nsec = UTIME_OMIT;
123
124
2275
	if (*argv == NULL)
125
		usage();
126
127
10200
	for (rval = 0; *argv; ++argv) {
128
		/* Update the file's timestamp if it exists. */
129
2825
		if (! utimensat(AT_FDCWD, *argv, ts, 0))
130
			continue;
131
2602
		if (errno != ENOENT) {
132
			rval = 1;
133
			warn("%s", *argv);
134
			continue;
135
		}
136
137
		/* Didn't exist; should we create it? */
138
2602
		if (cflag)
139
			continue;
140
141
		/* Create the file. */
142
2602
		fd = open(*argv, O_WRONLY | O_CREAT, DEFFILEMODE);
143

7806
		if (fd == -1 || futimens(fd, ts) || close(fd)) {
144
			rval = 1;
145
			warn("%s", *argv);
146
		}
147
	}
148
	exit(rval);
149
}
150
151
#define	ATOI2(s)	((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0'))
152
153
void
154
stime_arg1(char *arg, struct timespec *tsp)
155
{
156
	struct tm	*lt;
157
4
	time_t		 tmptime;
158
	int		 yearset;
159
	char		*dot, *p;
160
					/* Start with the current time. */
161
2
	tmptime = time(NULL);
162
2
	if ((lt = localtime(&tmptime)) == NULL)
163
		err(1, "localtime");
164
					/* [[CC]YY]MMDDhhmm[.SS] */
165
52
	for (p = arg, dot = NULL; *p != '\0'; p++) {
166
24
		if (*p == '.' && dot == NULL)
167
			dot = p;
168
24
		else if (!isdigit((unsigned char)*p))
169
			goto terr;
170
	}
171
2
	if (dot == NULL)
172
2
		lt->tm_sec = 0;		/* Seconds defaults to 0. */
173
	else {
174
		*dot++ = '\0';
175
		if (strlen(dot) != 2)
176
			goto terr;
177
		lt->tm_sec = ATOI2(dot);
178
		if (lt->tm_sec > 61)	/* Could be leap second. */
179
			goto terr;
180
	}
181
182
	yearset = 0;
183

6
	switch (strlen(arg)) {
184
	case 12:			/* CCYYMMDDhhmm */
185
2
		lt->tm_year = (ATOI2(arg) * 100) - 1900;
186
2
		yearset = 1;
187
		/* FALLTHROUGH */
188
	case 10:			/* YYMMDDhhmm */
189
2
		if (yearset) {
190
			yearset = ATOI2(arg);
191
2
			lt->tm_year += yearset;
192
2
		} else {
193
			yearset = ATOI2(arg);
194
			/* POSIX logic: [00,68]=>20xx, [69,99]=>19xx */
195
			lt->tm_year = yearset;
196
			if (yearset < 69)
197
				lt->tm_year += 100;
198
		}
199
		/* FALLTHROUGH */
200
	case 8:				/* MMDDhhmm */
201
2
		lt->tm_mon = ATOI2(arg);
202

4
		if (lt->tm_mon > 12 || lt->tm_mon == 0)
203
			goto terr;
204
2
		--lt->tm_mon;		/* Convert from 01-12 to 00-11 */
205
2
		lt->tm_mday = ATOI2(arg);
206

4
		if (lt->tm_mday > 31 || lt->tm_mday == 0)
207
			goto terr;
208
2
		lt->tm_hour = ATOI2(arg);
209
2
		if (lt->tm_hour > 23)
210
			goto terr;
211
2
		lt->tm_min = ATOI2(arg);
212
2
		if (lt->tm_min > 59)
213
			goto terr;
214
		break;
215
	default:
216
		goto terr;
217
	}
218
219
2
	lt->tm_isdst = -1;		/* Figure out DST. */
220
2
	tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
221
2
	if (tsp[0].tv_sec == -1)
222
terr:		errx(1,
223
	"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
224
225
2
	tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
226
2
}
227
228
void
229
stime_arg2(char *arg, int year, struct timespec *tsp)
230
{
231
	struct tm	*lt;
232
	time_t		 tmptime;
233
					/* Start with the current time. */
234
	tmptime = time(NULL);
235
	if ((lt = localtime(&tmptime)) == NULL)
236
		err(1, "localtime");
237
238
	lt->tm_mon = ATOI2(arg);	/* MMDDhhmm[YY] */
239
	if (lt->tm_mon > 12 || lt->tm_mon == 0)
240
		goto terr;
241
	--lt->tm_mon;			/* Convert from 01-12 to 00-11 */
242
	lt->tm_mday = ATOI2(arg);
243
	if (lt->tm_mday > 31 || lt->tm_mday == 0)
244
		goto terr;
245
	lt->tm_hour = ATOI2(arg);
246
	if (lt->tm_hour > 23)
247
		goto terr;
248
	lt->tm_min = ATOI2(arg);
249
	if (lt->tm_min > 59)
250
		goto terr;
251
	if (year) {
252
		year = ATOI2(arg);
253
		/* POSIX logic: [00,68]=>20xx, [69,99]=>19xx */
254
		lt->tm_year = year;
255
		if (year < 69)
256
			lt->tm_year += 100;
257
	}
258
	lt->tm_sec = 0;
259
260
	lt->tm_isdst = -1;		/* Figure out DST. */
261
	tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt);
262
	if (tsp[0].tv_sec == -1)
263
terr:		errx(1,
264
	"out of range or illegal time specification: MMDDhhmm[YY]");
265
266
	tsp[0].tv_nsec = tsp[1].tv_nsec = 0;
267
}
268
269
void
270
stime_file(char *fname, struct timespec *tsp)
271
{
272
	struct stat	sb;
273
274
	if (stat(fname, &sb))
275
		err(1, "%s", fname);
276
	tsp[0] = sb.st_atim;
277
	tsp[1] = sb.st_mtim;
278
}
279
280
void
281
stime_argd(char *arg, struct timespec *tsp)
282
{
283
	struct tm	tm;
284
	char		*frac, *p;
285
	int		utc = 0;
286
287
	/* accept YYYY-MM-DD(T| )hh:mm:ss[(.|,)frac][Z] */
288
	memset(&tm, 0, sizeof(tm));
289
	p = strptime(arg, "%F", &tm);
290
	if (p == NULL || (*p != 'T' && *p != ' '))
291
		goto terr;
292
	p = strptime(p + 1, "%T", &tm);
293
	if (p == NULL)
294
		goto terr;
295
	tsp[0].tv_nsec = 0;
296
	if (*p == '.' || *p == ',') {
297
		frac = ++p;
298
		while (isdigit((unsigned char)*p)) {
299
			if (p - frac < 9) {
300
				tsp[0].tv_nsec = tsp[0].tv_nsec * 10 +
301
				    *p - '0';
302
			}
303
			p++;
304
		}
305
		if (p == frac)
306
			goto terr;
307
308
		/* fill in the trailing zeros */
309
		while (p - frac-- < 9)
310
			tsp[0].tv_nsec *= 10;
311
	}
312
	if (*p == 'Z') {
313
		utc = 1;
314
		p++;
315
	}
316
	if (*p != '\0')
317
		goto terr;
318
319
	tm.tm_isdst = -1;
320
	tsp[0].tv_sec = utc ? timegm(&tm) : mktime(&tm);
321
	if (tsp[0].tv_sec == -1)
322
terr:		errx(1,
323
  "out of range or illegal time specification: YYYY-MM-DDThh:mm:ss[.frac][Z]");
324
	tsp[1] = tsp[0];
325
}
326
327
__dead void
328
usage(void)
329
{
330
	(void)fprintf(stderr,
331
"usage: touch [-acm] [-d ccyy-mm-ddTHH:MM:SS[.frac][Z]] [-r file]\n"
332
"             [-t [[cc]yy]mmddHHMM[.SS]] file ...\n");
333
	exit(1);
334
}