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