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 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* Copyright (c) 1987, 1988 Microsoft Corporation */ 31 /* All Rights Reserved */ 32 33 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <stdio.h> 38 #include <string.h> 39 #include <strings.h> 40 #include <stdlib.h> 41 #include <ctype.h> 42 #include <libgen.h> 43 #include <fcntl.h> 44 #include <pwd.h> 45 #include <time.h> 46 #include <unistd.h> 47 #include <locale.h> 48 #include <sys/time.h> 49 #include <errno.h> 50 51 #define BADTIME "bad time specification" 52 53 static char *myname; 54 55 static int isnumber(char *); 56 static int atoi_for2(char *); 57 static void usage(const int); 58 static void touchabort(const char *); 59 static void parse_time(char *, timestruc_t *); 60 static void parse_datetime(char *, timestruc_t *); 61 static void timestruc_to_timeval(timestruc_t *, struct timeval *); 62 63 int 64 main(int argc, char *argv[]) 65 { 66 int c; 67 68 int aflag = 0; 69 int cflag = 0; 70 int rflag = 0; 71 int mflag = 0; 72 int tflag = 0; 73 int stflag = 0; 74 int status = 0; 75 int usecurrenttime = 1; 76 int timespecified; 77 int optc; 78 int fd; 79 struct stat stbuf; 80 struct stat prstbuf; 81 struct timeval times[2]; 82 83 (void) setlocale(LC_ALL, ""); 84 #if !defined(TEXT_DOMAIN) 85 #define TEXT_DOMAIN "SYS_TEST" 86 #endif 87 (void) textdomain(TEXT_DOMAIN); 88 89 myname = basename(argv[0]); 90 if (strcmp(myname, "settime") == NULL) { 91 cflag++; 92 stflag++; 93 while ((optc = getopt(argc, argv, "f:")) != EOF) 94 switch (optc) { 95 case 'f': 96 rflag++; 97 usecurrenttime = 0; 98 if (stat(optarg, &prstbuf) == -1) { 99 (void) fprintf(stderr, "%s: ", myname); 100 perror(optarg); 101 return (2); 102 } 103 break; 104 case '?': 105 usage(stflag); 106 break; 107 }; 108 } else 109 while ((optc = getopt(argc, argv, "acfmr:t:")) != EOF) 110 switch (optc) { 111 case 'a': 112 aflag++; 113 usecurrenttime = 0; 114 break; 115 case 'c': 116 cflag++; 117 break; 118 case 'f': /* silently ignore for UCB compat */ 119 break; 120 case 'm': 121 mflag++; 122 usecurrenttime = 0; 123 break; 124 case 'r': /* same as settime's -f option */ 125 rflag++; 126 usecurrenttime = 0; 127 if (stat(optarg, &prstbuf) == -1) { 128 (void) fprintf(stderr, "%s: ", myname); 129 perror(optarg); 130 return (2); 131 } 132 break; 133 case 't': 134 tflag++; 135 usecurrenttime = 0; 136 parse_time(optarg, &prstbuf.st_mtim); 137 prstbuf.st_atim = prstbuf.st_mtim; 138 break; 139 case '?': 140 usage(stflag); 141 break; 142 } 143 144 argc -= optind; 145 argv += optind; 146 147 if ((argc < 1) || (rflag + tflag > 1)) 148 usage(stflag); 149 150 if ((aflag == 0) && (mflag == 0)) { 151 aflag = 1; 152 mflag = 1; 153 } 154 155 /* 156 * If either -r or -t has been specified, 157 * use the specified time. 158 */ 159 timespecified = rflag || tflag; 160 161 if (timespecified == 0) { 162 if (argc >= 2 && isnumber(*argv) && (strlen(*argv) == 8 || 163 strlen(*argv) == 10)) { 164 /* 165 * time is specified as an operand. 166 * use it. 167 */ 168 parse_datetime(*argv++, &prstbuf.st_mtim); 169 prstbuf.st_atim = prstbuf.st_mtim; 170 usecurrenttime = 0; 171 timespecified = 1; 172 argc--; 173 } else { 174 /* 175 * no time information is specified. 176 * use the current time. 177 */ 178 (void) gettimeofday(times, NULL); 179 times[1] = times[0]; 180 } 181 } 182 for (c = 0; c < argc; c++) { 183 if (stat(argv[c], &stbuf)) { 184 /* 185 * if stat failed for reasons other than ENOENT, 186 * the file should not be created, since this 187 * can clobber the contents of an existing file 188 * (for example, a large file that results in overflow). 189 */ 190 if (errno != ENOENT) { 191 (void) fprintf(stderr, 192 gettext("%s: %s cannot stat\n"), 193 myname, argv[c]); 194 status++; 195 continue; 196 } else if (cflag) { 197 continue; 198 } else if ((fd = creat(argv[c], 199 (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH))) 200 < 0) { 201 (void) fprintf(stderr, 202 gettext("%s: %s cannot create\n"), 203 myname, argv[c]); 204 status++; 205 continue; 206 } else { 207 (void) close(fd); 208 if (stat(argv[c], &stbuf)) { 209 (void) fprintf(stderr, 210 gettext("%s: %s cannot stat\n"), 211 myname, argv[c]); 212 status++; 213 continue; 214 } 215 } 216 } 217 218 if (mflag == 0) { 219 /* Keep the mtime of the file */ 220 timestruc_to_timeval(&stbuf.st_mtim, times + 1); 221 } else { 222 if (timespecified) { 223 /* Set the specified time */ 224 timestruc_to_timeval(&prstbuf.st_mtim, 225 times + 1); 226 } 227 /* Otherwise, use the current time by gettimeofday */ 228 } 229 230 if (aflag == 0) { 231 /* Keep the atime of the file */ 232 timestruc_to_timeval(&stbuf.st_atim, times); 233 } else { 234 if (timespecified) { 235 /* Set the specified time */ 236 timestruc_to_timeval(&prstbuf.st_atim, times); 237 } 238 /* Otherwise, use the current time by gettimeofday */ 239 } 240 241 if (utimes(argv[c], (usecurrenttime) ? NULL : times)) { 242 (void) fprintf(stderr, 243 gettext("%s: cannot change times on %s\n"), 244 myname, argv[c]); 245 status++; 246 continue; 247 } 248 } 249 return (status); 250 } 251 252 static int 253 isnumber(char *s) 254 { 255 int c; 256 257 while ((c = *s++) != '\0') 258 if (!isdigit(c)) 259 return (0); 260 return (1); 261 } 262 263 264 static void 265 parse_time(char *t, timestruc_t *ts) 266 { 267 int century = 0; 268 int seconds = 0; 269 char *p; 270 time_t when; 271 struct tm tm; 272 273 /* 274 * time in the following format (defined by the touch(1) spec): 275 * [[CC]YY]MMDDhhmm[.SS] 276 */ 277 if ((p = strchr(t, '.')) != NULL) { 278 if (strchr(p+1, '.') != NULL) 279 touchabort(BADTIME); 280 seconds = atoi_for2(p+1); 281 *p = '\0'; 282 } 283 284 (void) memset(&tm, 0, sizeof (struct tm)); 285 when = time(0); 286 tm.tm_year = localtime(&when)->tm_year; 287 288 switch (strlen(t)) { 289 case 12: /* CCYYMMDDhhmm */ 290 century = atoi_for2(t); 291 t += 2; 292 /* FALLTHROUGH */ 293 case 10: /* YYMMDDhhmm */ 294 tm.tm_year = atoi_for2(t); 295 t += 2; 296 if (century == 0) { 297 if (tm.tm_year < 69) 298 tm.tm_year += 100; 299 } else 300 tm.tm_year += (century - 19) * 100; 301 /* FALLTHROUGH */ 302 case 8: /* MMDDhhmm */ 303 tm.tm_mon = atoi_for2(t) - 1; 304 t += 2; 305 tm.tm_mday = atoi_for2(t); 306 t += 2; 307 tm.tm_hour = atoi_for2(t); 308 t += 2; 309 tm.tm_min = atoi_for2(t); 310 tm.tm_sec = seconds; 311 break; 312 default: 313 touchabort(BADTIME); 314 } 315 316 if ((when = mktime(&tm)) == -1) 317 touchabort(BADTIME); 318 if (tm.tm_isdst) 319 when -= (timezone-altzone); 320 321 ts->tv_sec = when; 322 ts->tv_nsec = 0; 323 } 324 325 static void 326 parse_datetime(char *t, timestruc_t *ts) 327 { 328 time_t when; 329 struct tm tm; 330 331 /* 332 * time in the following format (defined by the touch(1) spec): 333 * MMDDhhmm[yy] 334 */ 335 336 (void) memset(&tm, 0, sizeof (struct tm)); 337 when = time(0); 338 tm.tm_year = localtime(&when)->tm_year; 339 340 switch (strlen(t)) { 341 case 10: /* MMDDhhmmyy */ 342 tm.tm_year = atoi_for2(t+8); 343 if (tm.tm_year < 69) 344 tm.tm_year += 100; 345 /* FALLTHROUGH */ 346 case 8: /* MMDDhhmm */ 347 tm.tm_mon = atoi_for2(t) - 1; 348 t += 2; 349 tm.tm_mday = atoi_for2(t); 350 t += 2; 351 tm.tm_hour = atoi_for2(t); 352 t += 2; 353 tm.tm_min = atoi_for2(t); 354 break; 355 default: 356 touchabort(BADTIME); 357 } 358 359 if ((when = mktime(&tm)) == -1) 360 touchabort(BADTIME); 361 if (tm.tm_isdst) 362 when -= (timezone - altzone); 363 364 ts->tv_sec = when; 365 ts->tv_nsec = 0; 366 } 367 368 static int 369 atoi_for2(char *p) 370 { 371 int value; 372 373 value = (*p - '0') * 10 + *(p+1) - '0'; 374 if ((value < 0) || (value > 99)) 375 touchabort(BADTIME); 376 return (value); 377 } 378 379 static void 380 touchabort(const char *message) 381 { 382 (void) fprintf(stderr, "%s: %s\n", myname, gettext(message)); 383 exit(1); 384 } 385 386 static void 387 usage(const int settime) 388 { 389 if (settime) 390 (void) fprintf(stderr, gettext( 391 "usage: %s [-f file] [mmddhhmm[yy]] file...\n"), 392 myname); 393 else 394 (void) fprintf(stderr, gettext( 395 "usage: %s [-acm] [-r ref_file] file...\n" 396 " %s [-acm] [MMDDhhmm[yy]] file...\n" 397 " %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n"), 398 myname, myname, myname); 399 exit(2); 400 } 401 402 /* 403 * nanoseconds are rounded off to microseconds by flooring. 404 */ 405 static void 406 timestruc_to_timeval(timestruc_t *ts, struct timeval *tv) 407 { 408 tv->tv_sec = ts->tv_sec; 409 tv->tv_usec = ts->tv_nsec / 1000; 410 } 411