xref: /titanic_44/usr/src/cmd/touch/touch.c (revision 3b862e9a9ce59d5dbf0177b9eb293109fde6bf36)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5eaacacc0SMilan Jurik  * Common Development and Distribution License (the "License").
6eaacacc0SMilan Jurik  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
21*3b862e9aSRoger A. Faulkner 
227c478bd9Sstevel@tonic-gate /*
23*3b862e9aSRoger A. Faulkner  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
287c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
317c478bd9Sstevel@tonic-gate /*	  All Rights Reserved	*/
327c478bd9Sstevel@tonic-gate 
337c478bd9Sstevel@tonic-gate #include <sys/types.h>
347c478bd9Sstevel@tonic-gate #include <sys/stat.h>
357c478bd9Sstevel@tonic-gate #include <stdio.h>
367c478bd9Sstevel@tonic-gate #include <string.h>
377c478bd9Sstevel@tonic-gate #include <strings.h>
387c478bd9Sstevel@tonic-gate #include <stdlib.h>
397c478bd9Sstevel@tonic-gate #include <ctype.h>
407c478bd9Sstevel@tonic-gate #include <libgen.h>
417c478bd9Sstevel@tonic-gate #include <fcntl.h>
427c478bd9Sstevel@tonic-gate #include <pwd.h>
437c478bd9Sstevel@tonic-gate #include <time.h>
447c478bd9Sstevel@tonic-gate #include <unistd.h>
457c478bd9Sstevel@tonic-gate #include <locale.h>
467c478bd9Sstevel@tonic-gate #include <sys/time.h>
477c478bd9Sstevel@tonic-gate #include <errno.h>
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #define	BADTIME	"bad time specification"
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate static char	*myname;
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static int isnumber(char *);
547c478bd9Sstevel@tonic-gate static int atoi_for2(char *);
557c478bd9Sstevel@tonic-gate static void usage(const int);
567c478bd9Sstevel@tonic-gate static void touchabort(const char *);
57*3b862e9aSRoger A. Faulkner static void parse_datetime(char *, timespec_t *);
58*3b862e9aSRoger A. Faulkner static void parse_time(char *, timespec_t *);
59*3b862e9aSRoger A. Faulkner static void parse_timespec(char *, timespec_t *);
607c478bd9Sstevel@tonic-gate 
617c478bd9Sstevel@tonic-gate int
627c478bd9Sstevel@tonic-gate main(int argc, char *argv[])
637c478bd9Sstevel@tonic-gate {
647c478bd9Sstevel@tonic-gate 	int c;
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 	int		aflag	= 0;
677c478bd9Sstevel@tonic-gate 	int		cflag	= 0;
687c478bd9Sstevel@tonic-gate 	int		rflag	= 0;
697c478bd9Sstevel@tonic-gate 	int		mflag	= 0;
707c478bd9Sstevel@tonic-gate 	int		tflag	= 0;
717c478bd9Sstevel@tonic-gate 	int		stflag	= 0;
727c478bd9Sstevel@tonic-gate 	int		status	= 0;
737c478bd9Sstevel@tonic-gate 	int		usecurrenttime = 1;
747c478bd9Sstevel@tonic-gate 	int		timespecified;
757c478bd9Sstevel@tonic-gate 	int		optc;
76*3b862e9aSRoger A. Faulkner 	int		fd = -1;
77*3b862e9aSRoger A. Faulkner 	mode_t		cmode =
78*3b862e9aSRoger A. Faulkner 	    (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
797c478bd9Sstevel@tonic-gate 	struct stat	stbuf;
807c478bd9Sstevel@tonic-gate 	struct stat	prstbuf;
81*3b862e9aSRoger A. Faulkner 	timespec_t	times[2];
82*3b862e9aSRoger A. Faulkner 	timespec_t	*tsp;
837c478bd9Sstevel@tonic-gate 
847c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
857c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)
867c478bd9Sstevel@tonic-gate #define	TEXT_DOMAIN "SYS_TEST"
877c478bd9Sstevel@tonic-gate #endif
887c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	myname = basename(argv[0]);
91*3b862e9aSRoger A. Faulkner 	if (strcmp(myname, "settime") == 0) {
927c478bd9Sstevel@tonic-gate 		cflag++;
937c478bd9Sstevel@tonic-gate 		stflag++;
94*3b862e9aSRoger A. Faulkner 		while ((optc = getopt(argc, argv, "f:")) != EOF) {
957c478bd9Sstevel@tonic-gate 			switch (optc) {
967c478bd9Sstevel@tonic-gate 			case 'f':
977c478bd9Sstevel@tonic-gate 				rflag++;
987c478bd9Sstevel@tonic-gate 				usecurrenttime = 0;
997c478bd9Sstevel@tonic-gate 				if (stat(optarg, &prstbuf) == -1) {
1007c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "%s: ", myname);
1017c478bd9Sstevel@tonic-gate 					perror(optarg);
1027c478bd9Sstevel@tonic-gate 					return (2);
1037c478bd9Sstevel@tonic-gate 				}
1047c478bd9Sstevel@tonic-gate 				break;
1057c478bd9Sstevel@tonic-gate 			case '?':
1067c478bd9Sstevel@tonic-gate 				usage(stflag);
1077c478bd9Sstevel@tonic-gate 				break;
108*3b862e9aSRoger A. Faulkner 			}
109*3b862e9aSRoger A. Faulkner 		}
110*3b862e9aSRoger A. Faulkner 	} else {
111*3b862e9aSRoger A. Faulkner 		while ((optc = getopt(argc, argv, "acfmr:d:t:")) != EOF) {
1127c478bd9Sstevel@tonic-gate 			switch (optc) {
1137c478bd9Sstevel@tonic-gate 			case 'a':
1147c478bd9Sstevel@tonic-gate 				aflag++;
1157c478bd9Sstevel@tonic-gate 				break;
1167c478bd9Sstevel@tonic-gate 			case 'c':
1177c478bd9Sstevel@tonic-gate 				cflag++;
1187c478bd9Sstevel@tonic-gate 				break;
1197c478bd9Sstevel@tonic-gate 			case 'f':	/* silently ignore for UCB compat */
1207c478bd9Sstevel@tonic-gate 				break;
1217c478bd9Sstevel@tonic-gate 			case 'm':
1227c478bd9Sstevel@tonic-gate 				mflag++;
1237c478bd9Sstevel@tonic-gate 				break;
1247c478bd9Sstevel@tonic-gate 			case 'r':	/* same as settime's -f option */
1257c478bd9Sstevel@tonic-gate 				rflag++;
1267c478bd9Sstevel@tonic-gate 				usecurrenttime = 0;
1277c478bd9Sstevel@tonic-gate 				if (stat(optarg, &prstbuf) == -1) {
1287c478bd9Sstevel@tonic-gate 					(void) fprintf(stderr, "%s: ", myname);
1297c478bd9Sstevel@tonic-gate 					perror(optarg);
1307c478bd9Sstevel@tonic-gate 					return (2);
1317c478bd9Sstevel@tonic-gate 				}
1327c478bd9Sstevel@tonic-gate 				break;
133*3b862e9aSRoger A. Faulkner 			case 'd':
134*3b862e9aSRoger A. Faulkner 				tflag++;
135*3b862e9aSRoger A. Faulkner 				usecurrenttime = 0;
136*3b862e9aSRoger A. Faulkner 				parse_datetime(optarg, &prstbuf.st_mtim);
137*3b862e9aSRoger A. Faulkner 				prstbuf.st_atim = prstbuf.st_mtim;
138*3b862e9aSRoger A. Faulkner 				break;
1397c478bd9Sstevel@tonic-gate 			case 't':
1407c478bd9Sstevel@tonic-gate 				tflag++;
1417c478bd9Sstevel@tonic-gate 				usecurrenttime = 0;
1427c478bd9Sstevel@tonic-gate 				parse_time(optarg, &prstbuf.st_mtim);
1437c478bd9Sstevel@tonic-gate 				prstbuf.st_atim = prstbuf.st_mtim;
1447c478bd9Sstevel@tonic-gate 				break;
1457c478bd9Sstevel@tonic-gate 			case '?':
1467c478bd9Sstevel@tonic-gate 				usage(stflag);
1477c478bd9Sstevel@tonic-gate 				break;
1487c478bd9Sstevel@tonic-gate 			}
149*3b862e9aSRoger A. Faulkner 		}
150*3b862e9aSRoger A. Faulkner 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	argc -= optind;
1537c478bd9Sstevel@tonic-gate 	argv += optind;
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	if ((argc < 1) || (rflag + tflag > 1))
1567c478bd9Sstevel@tonic-gate 		usage(stflag);
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate 	if ((aflag == 0) && (mflag == 0)) {
1597c478bd9Sstevel@tonic-gate 		aflag = 1;
1607c478bd9Sstevel@tonic-gate 		mflag = 1;
1617c478bd9Sstevel@tonic-gate 	}
162*3b862e9aSRoger A. Faulkner 	if ((aflag && !mflag) || (mflag && !aflag))
163*3b862e9aSRoger A. Faulkner 		usecurrenttime = 0;
1647c478bd9Sstevel@tonic-gate 
1657c478bd9Sstevel@tonic-gate 	/*
166*3b862e9aSRoger A. Faulkner 	 * If -r, -t or -d has been specified,
1677c478bd9Sstevel@tonic-gate 	 * use the specified time.
1687c478bd9Sstevel@tonic-gate 	 */
169*3b862e9aSRoger A. Faulkner 	timespecified = (rflag | tflag);
1707c478bd9Sstevel@tonic-gate 
171*3b862e9aSRoger A. Faulkner 	if (timespecified == 0 && argc >= 2 && isnumber(*argv) &&
172*3b862e9aSRoger A. Faulkner 	    (strlen(*argv) == 8 || strlen(*argv) == 10)) {
1737c478bd9Sstevel@tonic-gate 		/*
174*3b862e9aSRoger A. Faulkner 		 * time is specified as an operand; use it.
1757c478bd9Sstevel@tonic-gate 		 */
176*3b862e9aSRoger A. Faulkner 		parse_timespec(*argv++, &prstbuf.st_mtim);
1777c478bd9Sstevel@tonic-gate 		prstbuf.st_atim = prstbuf.st_mtim;
1787c478bd9Sstevel@tonic-gate 		usecurrenttime = 0;
1797c478bd9Sstevel@tonic-gate 		timespecified = 1;
1807c478bd9Sstevel@tonic-gate 		argc--;
1817c478bd9Sstevel@tonic-gate 	}
182*3b862e9aSRoger A. Faulkner 
1837c478bd9Sstevel@tonic-gate 	for (c = 0; c < argc; c++) {
1847c478bd9Sstevel@tonic-gate 		if (stat(argv[c], &stbuf)) {
1857c478bd9Sstevel@tonic-gate 			/*
186eaacacc0SMilan Jurik 			 * If stat failed for reasons other than EOVERFLOW or
187eaacacc0SMilan Jurik 			 * ENOENT, the file should not be created, since this
188eaacacc0SMilan Jurik 			 * can clobber the contents of an existing file.
1897c478bd9Sstevel@tonic-gate 			 */
190eaacacc0SMilan Jurik 			if (errno == EOVERFLOW) {
191eaacacc0SMilan Jurik 				/*
192*3b862e9aSRoger A. Faulkner 				 * Since we have EOVERFLOW,
193*3b862e9aSRoger A. Faulkner 				 * we know the file exists.
194eaacacc0SMilan Jurik 				 */
195*3b862e9aSRoger A. Faulkner 				/* EMPTY */;
196eaacacc0SMilan Jurik 			} else if (errno != ENOENT) {
197eaacacc0SMilan Jurik 				(void) fprintf(stderr,
198eaacacc0SMilan Jurik 				    gettext("%s: cannot stat %s: %s\n"),
199eaacacc0SMilan Jurik 				    myname, argv[c], strerror(errno));
2007c478bd9Sstevel@tonic-gate 				status++;
2017c478bd9Sstevel@tonic-gate 				continue;
2027c478bd9Sstevel@tonic-gate 			} else if (cflag) {
2037c478bd9Sstevel@tonic-gate 				continue;
204*3b862e9aSRoger A. Faulkner 			} else if ((fd = creat(argv[c], cmode)) < 0) {
2057c478bd9Sstevel@tonic-gate 				(void) fprintf(stderr,
206eaacacc0SMilan Jurik 				    gettext("%s: cannot create %s: %s\n"),
207eaacacc0SMilan Jurik 				    myname, argv[c], strerror(errno));
2087c478bd9Sstevel@tonic-gate 				status++;
2097c478bd9Sstevel@tonic-gate 				continue;
2107c478bd9Sstevel@tonic-gate 			}
2117c478bd9Sstevel@tonic-gate 		}
2127c478bd9Sstevel@tonic-gate 
213*3b862e9aSRoger A. Faulkner 		if (usecurrenttime) {
214*3b862e9aSRoger A. Faulkner 			tsp = NULL;
215*3b862e9aSRoger A. Faulkner 		} else {
2167c478bd9Sstevel@tonic-gate 			if (mflag == 0) {
2177c478bd9Sstevel@tonic-gate 				/* Keep the mtime of the file */
218*3b862e9aSRoger A. Faulkner 				times[1].tv_nsec = UTIME_OMIT;
219*3b862e9aSRoger A. Faulkner 			} else if (timespecified) {
2207c478bd9Sstevel@tonic-gate 				/* Set the specified time */
221*3b862e9aSRoger A. Faulkner 				times[1] = prstbuf.st_mtim;
222*3b862e9aSRoger A. Faulkner 			} else {
223*3b862e9aSRoger A. Faulkner 				/* Otherwise, use the current time */
224*3b862e9aSRoger A. Faulkner 				times[1].tv_nsec = UTIME_NOW;
2257c478bd9Sstevel@tonic-gate 			}
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 			if (aflag == 0) {
2287c478bd9Sstevel@tonic-gate 				/* Keep the atime of the file */
229*3b862e9aSRoger A. Faulkner 				times[0].tv_nsec = UTIME_OMIT;
230*3b862e9aSRoger A. Faulkner 			} else if (timespecified) {
2317c478bd9Sstevel@tonic-gate 				/* Set the specified time */
232*3b862e9aSRoger A. Faulkner 				times[0] = prstbuf.st_atim;
233*3b862e9aSRoger A. Faulkner 			} else {
234*3b862e9aSRoger A. Faulkner 				/* Otherwise, use the current time */
235*3b862e9aSRoger A. Faulkner 				times[0].tv_nsec = UTIME_NOW;
2367c478bd9Sstevel@tonic-gate 			}
2377c478bd9Sstevel@tonic-gate 
238*3b862e9aSRoger A. Faulkner 			tsp = times;
239*3b862e9aSRoger A. Faulkner 		}
240*3b862e9aSRoger A. Faulkner 
241*3b862e9aSRoger A. Faulkner 		if ((fd >= 0 && futimens(fd, tsp) != 0) ||
242*3b862e9aSRoger A. Faulkner 		    utimensat(AT_FDCWD, argv[c], tsp, 0) != 0) {
2437c478bd9Sstevel@tonic-gate 			(void) fprintf(stderr,
244eaacacc0SMilan Jurik 			    gettext("%s: cannot change times on %s: %s\n"),
245eaacacc0SMilan Jurik 			    myname, argv[c], strerror(errno));
2467c478bd9Sstevel@tonic-gate 			status++;
247*3b862e9aSRoger A. Faulkner 		}
248*3b862e9aSRoger A. Faulkner 		if (fd >= 0) {
249*3b862e9aSRoger A. Faulkner 			(void) close(fd);
250*3b862e9aSRoger A. Faulkner 			fd = -1;
2517c478bd9Sstevel@tonic-gate 		}
2527c478bd9Sstevel@tonic-gate 	}
2537c478bd9Sstevel@tonic-gate 	return (status);
2547c478bd9Sstevel@tonic-gate }
2557c478bd9Sstevel@tonic-gate 
2567c478bd9Sstevel@tonic-gate static int
2577c478bd9Sstevel@tonic-gate isnumber(char *s)
2587c478bd9Sstevel@tonic-gate {
2597c478bd9Sstevel@tonic-gate 	int c;
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 	while ((c = *s++) != '\0')
2627c478bd9Sstevel@tonic-gate 		if (!isdigit(c))
2637c478bd9Sstevel@tonic-gate 			return (0);
2647c478bd9Sstevel@tonic-gate 	return (1);
2657c478bd9Sstevel@tonic-gate }
2667c478bd9Sstevel@tonic-gate 
267*3b862e9aSRoger A. Faulkner static void
268*3b862e9aSRoger A. Faulkner parse_datetime(char *t, timespec_t *ts)
269*3b862e9aSRoger A. Faulkner {
270*3b862e9aSRoger A. Faulkner 	char		date[64];
271*3b862e9aSRoger A. Faulkner 	char		*year;
272*3b862e9aSRoger A. Faulkner 	char		*month;
273*3b862e9aSRoger A. Faulkner 	char		*day;
274*3b862e9aSRoger A. Faulkner 	char		*hour;
275*3b862e9aSRoger A. Faulkner 	char		*minute;
276*3b862e9aSRoger A. Faulkner 	char		*second;
277*3b862e9aSRoger A. Faulkner 	char		*fraction;
278*3b862e9aSRoger A. Faulkner 	int		utc = 0;
279*3b862e9aSRoger A. Faulkner 	char		*p;
280*3b862e9aSRoger A. Faulkner 	time_t		when;
281*3b862e9aSRoger A. Faulkner 	int		nanoseconds;
282*3b862e9aSRoger A. Faulkner 	struct tm	tm;
283*3b862e9aSRoger A. Faulkner 
284*3b862e9aSRoger A. Faulkner 	/*
285*3b862e9aSRoger A. Faulkner 	 * The date string has the format (defined by the touch(1) spec):
286*3b862e9aSRoger A. Faulkner 	 *	YYYY-MM-DDThh:mm:SS[.frac][tz]
287*3b862e9aSRoger A. Faulkner 	 *	YYYY-MM-DDThh:mm:SS[,frac][tz]
288*3b862e9aSRoger A. Faulkner 	 * T is either the literal 'T' or is a space character.
289*3b862e9aSRoger A. Faulkner 	 * tz is either empty (local time) or the literal 'Z' (UTC).
290*3b862e9aSRoger A. Faulkner 	 * All other fields are strings of digits.
291*3b862e9aSRoger A. Faulkner 	 */
292*3b862e9aSRoger A. Faulkner 
293*3b862e9aSRoger A. Faulkner 	/*
294*3b862e9aSRoger A. Faulkner 	 * Make a copy of the date string so it can be tokenized.
295*3b862e9aSRoger A. Faulkner 	 */
296*3b862e9aSRoger A. Faulkner 	if (strlcpy(date, t, sizeof (date)) >= sizeof (date))
297*3b862e9aSRoger A. Faulkner 		touchabort(BADTIME);
298*3b862e9aSRoger A. Faulkner 
299*3b862e9aSRoger A. Faulkner 	/* deal with the optional trailing 'Z' first */
300*3b862e9aSRoger A. Faulkner 	p = date + strlen(date) - 1;
301*3b862e9aSRoger A. Faulkner 	if (*p == 'Z') {
302*3b862e9aSRoger A. Faulkner 		utc = 1;
303*3b862e9aSRoger A. Faulkner 		*p = '\0';
304*3b862e9aSRoger A. Faulkner 	}
305*3b862e9aSRoger A. Faulkner 
306*3b862e9aSRoger A. Faulkner 	/* break out the component tokens */
307*3b862e9aSRoger A. Faulkner 	p = date;
308*3b862e9aSRoger A. Faulkner 	year = strsep(&p, "-");
309*3b862e9aSRoger A. Faulkner 	month = strsep(&p, "-");
310*3b862e9aSRoger A. Faulkner 	day = strsep(&p, "T ");
311*3b862e9aSRoger A. Faulkner 	hour = strsep(&p, ":");
312*3b862e9aSRoger A. Faulkner 	minute = strsep(&p, ":");
313*3b862e9aSRoger A. Faulkner 	second = strsep(&p, ".,");
314*3b862e9aSRoger A. Faulkner 	fraction = p;
315*3b862e9aSRoger A. Faulkner 
316*3b862e9aSRoger A. Faulkner 	/* verify the component tokens */
317*3b862e9aSRoger A. Faulkner 	if (year == NULL || strlen(year) < 4 || !isnumber(year) ||
318*3b862e9aSRoger A. Faulkner 	    month == NULL || strlen(month) != 2 || !isnumber(month) ||
319*3b862e9aSRoger A. Faulkner 	    day == NULL || strlen(day) != 2 || !isnumber(day) ||
320*3b862e9aSRoger A. Faulkner 	    hour == NULL || strlen(hour) != 2 || !isnumber(hour) ||
321*3b862e9aSRoger A. Faulkner 	    minute == NULL || strlen(minute) != 2 || !isnumber(minute) ||
322*3b862e9aSRoger A. Faulkner 	    second == NULL || strlen(second) != 2 || !isnumber(second) ||
323*3b862e9aSRoger A. Faulkner 	    (fraction != NULL && (*fraction == '\0' || !isnumber(fraction))))
324*3b862e9aSRoger A. Faulkner 		touchabort(BADTIME);
325*3b862e9aSRoger A. Faulkner 
326*3b862e9aSRoger A. Faulkner 	(void) memset(&tm, 0, sizeof (struct tm));
327*3b862e9aSRoger A. Faulkner 
328*3b862e9aSRoger A. Faulkner 	tm.tm_year = atoi(year) - 1900;
329*3b862e9aSRoger A. Faulkner 	tm.tm_mon = atoi(month) - 1;
330*3b862e9aSRoger A. Faulkner 	tm.tm_mday = atoi(day);
331*3b862e9aSRoger A. Faulkner 	tm.tm_hour = atoi(hour);
332*3b862e9aSRoger A. Faulkner 	tm.tm_min = atoi(minute);
333*3b862e9aSRoger A. Faulkner 	tm.tm_sec = atoi(second);
334*3b862e9aSRoger A. Faulkner 	if (utc) {
335*3b862e9aSRoger A. Faulkner 		(void) setenv("TZ", "GMT0", 1);
336*3b862e9aSRoger A. Faulkner 		tzset();
337*3b862e9aSRoger A. Faulkner 	}
338*3b862e9aSRoger A. Faulkner 
339*3b862e9aSRoger A. Faulkner 	errno = 0;
340*3b862e9aSRoger A. Faulkner 	if ((when = mktime(&tm)) == -1 && errno != 0)
341*3b862e9aSRoger A. Faulkner 		touchabort(BADTIME);
342*3b862e9aSRoger A. Faulkner 	if (tm.tm_isdst)
343*3b862e9aSRoger A. Faulkner 		when -= (timezone - altzone);
344*3b862e9aSRoger A. Faulkner 
345*3b862e9aSRoger A. Faulkner 	if (fraction == NULL) {
346*3b862e9aSRoger A. Faulkner 		nanoseconds = 0;
347*3b862e9aSRoger A. Faulkner 	} else {
348*3b862e9aSRoger A. Faulkner 		/* truncate beyond 9 digits (nanoseconds) */
349*3b862e9aSRoger A. Faulkner 		if (strlen(fraction) > 9)
350*3b862e9aSRoger A. Faulkner 			fraction[9] = '\0';
351*3b862e9aSRoger A. Faulkner 		nanoseconds = atoi(fraction);
352*3b862e9aSRoger A. Faulkner 
353*3b862e9aSRoger A. Faulkner 		switch (strlen(fraction)) {
354*3b862e9aSRoger A. Faulkner 		case 1:
355*3b862e9aSRoger A. Faulkner 			nanoseconds *= 100000000;
356*3b862e9aSRoger A. Faulkner 			break;
357*3b862e9aSRoger A. Faulkner 		case 2:
358*3b862e9aSRoger A. Faulkner 			nanoseconds *= 10000000;
359*3b862e9aSRoger A. Faulkner 			break;
360*3b862e9aSRoger A. Faulkner 		case 3:
361*3b862e9aSRoger A. Faulkner 			nanoseconds *= 1000000;
362*3b862e9aSRoger A. Faulkner 			break;
363*3b862e9aSRoger A. Faulkner 		case 4:
364*3b862e9aSRoger A. Faulkner 			nanoseconds *= 100000;
365*3b862e9aSRoger A. Faulkner 			break;
366*3b862e9aSRoger A. Faulkner 		case 5:
367*3b862e9aSRoger A. Faulkner 			nanoseconds *= 10000;
368*3b862e9aSRoger A. Faulkner 			break;
369*3b862e9aSRoger A. Faulkner 		case 6:
370*3b862e9aSRoger A. Faulkner 			nanoseconds *= 1000;
371*3b862e9aSRoger A. Faulkner 			break;
372*3b862e9aSRoger A. Faulkner 		case 7:
373*3b862e9aSRoger A. Faulkner 			nanoseconds *= 100;
374*3b862e9aSRoger A. Faulkner 			break;
375*3b862e9aSRoger A. Faulkner 		case 8:
376*3b862e9aSRoger A. Faulkner 			nanoseconds *= 10;
377*3b862e9aSRoger A. Faulkner 			break;
378*3b862e9aSRoger A. Faulkner 		case 9:
379*3b862e9aSRoger A. Faulkner 			break;
380*3b862e9aSRoger A. Faulkner 		}
381*3b862e9aSRoger A. Faulkner 	}
382*3b862e9aSRoger A. Faulkner 
383*3b862e9aSRoger A. Faulkner 	ts->tv_sec = when;
384*3b862e9aSRoger A. Faulkner 	ts->tv_nsec = nanoseconds;
385*3b862e9aSRoger A. Faulkner }
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate static void
388*3b862e9aSRoger A. Faulkner parse_time(char *t, timespec_t *ts)
3897c478bd9Sstevel@tonic-gate {
3907c478bd9Sstevel@tonic-gate 	int		century = 0;
3917c478bd9Sstevel@tonic-gate 	int		seconds = 0;
3927c478bd9Sstevel@tonic-gate 	char		*p;
3937c478bd9Sstevel@tonic-gate 	time_t		when;
3947c478bd9Sstevel@tonic-gate 	struct tm	tm;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/*
3977c478bd9Sstevel@tonic-gate 	 * time in the following format (defined by the touch(1) spec):
3987c478bd9Sstevel@tonic-gate 	 *	[[CC]YY]MMDDhhmm[.SS]
3997c478bd9Sstevel@tonic-gate 	 */
4007c478bd9Sstevel@tonic-gate 	if ((p = strchr(t, '.')) != NULL) {
4017c478bd9Sstevel@tonic-gate 		if (strchr(p+1, '.') != NULL)
4027c478bd9Sstevel@tonic-gate 			touchabort(BADTIME);
4037c478bd9Sstevel@tonic-gate 		seconds = atoi_for2(p+1);
4047c478bd9Sstevel@tonic-gate 		*p = '\0';
4057c478bd9Sstevel@tonic-gate 	}
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	(void) memset(&tm, 0, sizeof (struct tm));
4087c478bd9Sstevel@tonic-gate 	when = time(0);
4097c478bd9Sstevel@tonic-gate 	tm.tm_year = localtime(&when)->tm_year;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	switch (strlen(t)) {
4127c478bd9Sstevel@tonic-gate 		case 12:	/* CCYYMMDDhhmm */
4137c478bd9Sstevel@tonic-gate 			century = atoi_for2(t);
4147c478bd9Sstevel@tonic-gate 			t += 2;
4157c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
4167c478bd9Sstevel@tonic-gate 		case 10:	/* YYMMDDhhmm */
4177c478bd9Sstevel@tonic-gate 			tm.tm_year = atoi_for2(t);
4187c478bd9Sstevel@tonic-gate 			t += 2;
4197c478bd9Sstevel@tonic-gate 			if (century == 0) {
4207c478bd9Sstevel@tonic-gate 				if (tm.tm_year < 69)
4217c478bd9Sstevel@tonic-gate 					tm.tm_year += 100;
4227c478bd9Sstevel@tonic-gate 			} else
4237c478bd9Sstevel@tonic-gate 				tm.tm_year += (century - 19) * 100;
4247c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
4257c478bd9Sstevel@tonic-gate 		case 8:		/* MMDDhhmm */
4267c478bd9Sstevel@tonic-gate 			tm.tm_mon = atoi_for2(t) - 1;
4277c478bd9Sstevel@tonic-gate 			t += 2;
4287c478bd9Sstevel@tonic-gate 			tm.tm_mday = atoi_for2(t);
4297c478bd9Sstevel@tonic-gate 			t += 2;
4307c478bd9Sstevel@tonic-gate 			tm.tm_hour = atoi_for2(t);
4317c478bd9Sstevel@tonic-gate 			t += 2;
4327c478bd9Sstevel@tonic-gate 			tm.tm_min = atoi_for2(t);
4337c478bd9Sstevel@tonic-gate 			tm.tm_sec = seconds;
4347c478bd9Sstevel@tonic-gate 			break;
4357c478bd9Sstevel@tonic-gate 		default:
4367c478bd9Sstevel@tonic-gate 			touchabort(BADTIME);
4377c478bd9Sstevel@tonic-gate 	}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	if ((when = mktime(&tm)) == -1)
4407c478bd9Sstevel@tonic-gate 		touchabort(BADTIME);
4417c478bd9Sstevel@tonic-gate 	if (tm.tm_isdst)
4427c478bd9Sstevel@tonic-gate 		when -= (timezone-altzone);
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	ts->tv_sec = when;
4457c478bd9Sstevel@tonic-gate 	ts->tv_nsec = 0;
4467c478bd9Sstevel@tonic-gate }
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate static void
449*3b862e9aSRoger A. Faulkner parse_timespec(char *t, timespec_t *ts)
4507c478bd9Sstevel@tonic-gate {
4517c478bd9Sstevel@tonic-gate 	time_t		when;
4527c478bd9Sstevel@tonic-gate 	struct tm	tm;
4537c478bd9Sstevel@tonic-gate 
4547c478bd9Sstevel@tonic-gate 	/*
4557c478bd9Sstevel@tonic-gate 	 * time in the following format (defined by the touch(1) spec):
4567c478bd9Sstevel@tonic-gate 	 *	MMDDhhmm[yy]
4577c478bd9Sstevel@tonic-gate 	 */
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	(void) memset(&tm, 0, sizeof (struct tm));
4607c478bd9Sstevel@tonic-gate 	when = time(0);
4617c478bd9Sstevel@tonic-gate 	tm.tm_year = localtime(&when)->tm_year;
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	switch (strlen(t)) {
4647c478bd9Sstevel@tonic-gate 		case 10:	/* MMDDhhmmyy */
4657c478bd9Sstevel@tonic-gate 			tm.tm_year = atoi_for2(t+8);
4667c478bd9Sstevel@tonic-gate 			if (tm.tm_year < 69)
4677c478bd9Sstevel@tonic-gate 				tm.tm_year += 100;
4687c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
4697c478bd9Sstevel@tonic-gate 		case 8:		/* MMDDhhmm */
4707c478bd9Sstevel@tonic-gate 			tm.tm_mon = atoi_for2(t) - 1;
4717c478bd9Sstevel@tonic-gate 			t += 2;
4727c478bd9Sstevel@tonic-gate 			tm.tm_mday = atoi_for2(t);
4737c478bd9Sstevel@tonic-gate 			t += 2;
4747c478bd9Sstevel@tonic-gate 			tm.tm_hour = atoi_for2(t);
4757c478bd9Sstevel@tonic-gate 			t += 2;
4767c478bd9Sstevel@tonic-gate 			tm.tm_min = atoi_for2(t);
4777c478bd9Sstevel@tonic-gate 			break;
4787c478bd9Sstevel@tonic-gate 		default:
4797c478bd9Sstevel@tonic-gate 			touchabort(BADTIME);
4807c478bd9Sstevel@tonic-gate 	}
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	if ((when = mktime(&tm)) == -1)
4837c478bd9Sstevel@tonic-gate 		touchabort(BADTIME);
4847c478bd9Sstevel@tonic-gate 	if (tm.tm_isdst)
4857c478bd9Sstevel@tonic-gate 		when -= (timezone - altzone);
4867c478bd9Sstevel@tonic-gate 
4877c478bd9Sstevel@tonic-gate 	ts->tv_sec = when;
4887c478bd9Sstevel@tonic-gate 	ts->tv_nsec = 0;
4897c478bd9Sstevel@tonic-gate }
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate static int
4927c478bd9Sstevel@tonic-gate atoi_for2(char *p)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate 	int value;
4957c478bd9Sstevel@tonic-gate 
4967c478bd9Sstevel@tonic-gate 	value = (*p - '0') * 10 + *(p+1) - '0';
4977c478bd9Sstevel@tonic-gate 	if ((value < 0) || (value > 99))
4987c478bd9Sstevel@tonic-gate 		touchabort(BADTIME);
4997c478bd9Sstevel@tonic-gate 	return (value);
5007c478bd9Sstevel@tonic-gate }
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate static void
5037c478bd9Sstevel@tonic-gate touchabort(const char *message)
5047c478bd9Sstevel@tonic-gate {
5057c478bd9Sstevel@tonic-gate 	(void) fprintf(stderr, "%s: %s\n", myname, gettext(message));
5067c478bd9Sstevel@tonic-gate 	exit(1);
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate static void
5107c478bd9Sstevel@tonic-gate usage(const int settime)
5117c478bd9Sstevel@tonic-gate {
512*3b862e9aSRoger A. Faulkner 	if (settime) {
5137c478bd9Sstevel@tonic-gate 		(void) fprintf(stderr, gettext(
514eaacacc0SMilan Jurik 		    "usage: %s [-f file] [mmddhhmm[yy]] file...\n"), myname);
5157c478bd9Sstevel@tonic-gate 		exit(2);
5167c478bd9Sstevel@tonic-gate 	}
517*3b862e9aSRoger A. Faulkner 	(void) fprintf(stderr, gettext(
518*3b862e9aSRoger A. Faulkner 	    "usage: %s [-acm] [-r ref_file] file...\n"
519*3b862e9aSRoger A. Faulkner 	    "       %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n"
520*3b862e9aSRoger A. Faulkner 	    "       %s [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] file...\n"
521*3b862e9aSRoger A. Faulkner 	    "       %s [-acm] [-d YYYY-MM-DDThh:mm:SS[,frac][Z]] file...\n"
522*3b862e9aSRoger A. Faulkner 	    "       %s [-acm] [MMDDhhmm[yy]] file...\n"),
523*3b862e9aSRoger A. Faulkner 	    myname, myname, myname, myname, myname);
524*3b862e9aSRoger A. Faulkner 	exit(2);
5257c478bd9Sstevel@tonic-gate }
526