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