4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 /* Copyright (c) 1987, 1988 Microsoft Corporation */
31 /* All Rights Reserved */
33 #include <sys/types.h>
49 #define BADTIME "bad time specification"
53 static int isnumber(char *);
54 static int atoi_for2(char *);
55 static void usage(const int);
56 static void touchabort(const char *);
57 static void parse_datetime(char *, timespec_t
*);
58 static void parse_time(char *, timespec_t
*);
59 static void parse_timespec(char *, timespec_t
*);
62 main(int argc
, char *argv
[])
73 int usecurrenttime
= 1;
78 (S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IWGRP
| S_IROTH
| S_IWOTH
);
84 (void) setlocale(LC_ALL
, "");
85 #if !defined(TEXT_DOMAIN)
86 #define TEXT_DOMAIN "SYS_TEST"
88 (void) textdomain(TEXT_DOMAIN
);
90 myname
= basename(argv
[0]);
91 if (strcmp(myname
, "settime") == 0) {
94 while ((optc
= getopt(argc
, argv
, "f:")) != EOF
) {
99 if (stat(optarg
, &prstbuf
) == -1) {
100 (void) fprintf(stderr
, "%s: ", myname
);
111 while ((optc
= getopt(argc
, argv
, "acfmr:d:t:")) != EOF
) {
119 case 'f': /* silently ignore for UCB compat */
124 case 'r': /* same as settime's -f option */
127 if (stat(optarg
, &prstbuf
) == -1) {
128 (void) fprintf(stderr
, "%s: ", myname
);
136 parse_datetime(optarg
, &prstbuf
.st_mtim
);
137 prstbuf
.st_atim
= prstbuf
.st_mtim
;
142 parse_time(optarg
, &prstbuf
.st_mtim
);
143 prstbuf
.st_atim
= prstbuf
.st_mtim
;
155 if ((argc
< 1) || (rflag
+ tflag
> 1))
158 if ((aflag
== 0) && (mflag
== 0)) {
162 if ((aflag
&& !mflag
) || (mflag
&& !aflag
))
166 * If -r, -t or -d has been specified,
167 * use the specified time.
169 timespecified
= (rflag
| tflag
);
171 if (timespecified
== 0 && argc
>= 2 && isnumber(*argv
) &&
172 (strlen(*argv
) == 8 || strlen(*argv
) == 10)) {
174 * time is specified as an operand; use it.
176 parse_timespec(*argv
++, &prstbuf
.st_mtim
);
177 prstbuf
.st_atim
= prstbuf
.st_mtim
;
183 for (c
= 0; c
< argc
; c
++) {
184 if (stat(argv
[c
], &stbuf
)) {
186 * If stat failed for reasons other than EOVERFLOW or
187 * ENOENT, the file should not be created, since this
188 * can clobber the contents of an existing file.
190 if (errno
== EOVERFLOW
) {
192 * Since we have EOVERFLOW,
193 * we know the file exists.
196 } else if (errno
!= ENOENT
) {
197 (void) fprintf(stderr
,
198 gettext("%s: cannot stat %s: %s\n"),
199 myname
, argv
[c
], strerror(errno
));
204 } else if ((fd
= creat(argv
[c
], cmode
)) < 0) {
205 (void) fprintf(stderr
,
206 gettext("%s: cannot create %s: %s\n"),
207 myname
, argv
[c
], strerror(errno
));
213 if (usecurrenttime
) {
217 /* Keep the mtime of the file */
218 times
[1].tv_nsec
= UTIME_OMIT
;
219 } else if (timespecified
) {
220 /* Set the specified time */
221 times
[1] = prstbuf
.st_mtim
;
223 /* Otherwise, use the current time */
224 times
[1].tv_nsec
= UTIME_NOW
;
228 /* Keep the atime of the file */
229 times
[0].tv_nsec
= UTIME_OMIT
;
230 } else if (timespecified
) {
231 /* Set the specified time */
232 times
[0] = prstbuf
.st_atim
;
234 /* Otherwise, use the current time */
235 times
[0].tv_nsec
= UTIME_NOW
;
241 if ((fd
>= 0 && futimens(fd
, tsp
) != 0) ||
242 (fd
< 0 && utimensat(AT_FDCWD
, argv
[c
], tsp
, 0) != 0)) {
243 (void) fprintf(stderr
,
244 gettext("%s: cannot change times on %s: %s\n"),
245 myname
, argv
[c
], strerror(errno
));
261 while ((c
= *s
++) != '\0')
268 parse_datetime(char *t
, timespec_t
*ts
)
285 * The date string has the format (defined by the touch(1) spec):
286 * YYYY-MM-DDThh:mm:SS[.frac][tz]
287 * YYYY-MM-DDThh:mm:SS[,frac][tz]
288 * T is either the literal 'T' or is a space character.
289 * tz is either empty (local time) or the literal 'Z' (UTC).
290 * All other fields are strings of digits.
294 * Make a copy of the date string so it can be tokenized.
296 if (strlcpy(date
, t
, sizeof (date
)) >= sizeof (date
))
299 /* deal with the optional trailing 'Z' first */
300 p
= date
+ strlen(date
) - 1;
306 /* break out the component tokens */
308 year
= strsep(&p
, "-");
309 month
= strsep(&p
, "-");
310 day
= strsep(&p
, "T ");
311 hour
= strsep(&p
, ":");
312 minute
= strsep(&p
, ":");
313 second
= strsep(&p
, ".,");
316 /* verify the component tokens */
317 if (year
== NULL
|| strlen(year
) < 4 || !isnumber(year
) ||
318 month
== NULL
|| strlen(month
) != 2 || !isnumber(month
) ||
319 day
== NULL
|| strlen(day
) != 2 || !isnumber(day
) ||
320 hour
== NULL
|| strlen(hour
) != 2 || !isnumber(hour
) ||
321 minute
== NULL
|| strlen(minute
) != 2 || !isnumber(minute
) ||
322 second
== NULL
|| strlen(second
) != 2 || !isnumber(second
) ||
323 (fraction
!= NULL
&& (*fraction
== '\0' || !isnumber(fraction
))))
326 (void) memset(&tm
, 0, sizeof (struct tm
));
328 tm
.tm_year
= atoi(year
) - 1900;
329 tm
.tm_mon
= atoi(month
) - 1;
330 tm
.tm_mday
= atoi(day
);
331 tm
.tm_hour
= atoi(hour
);
332 tm
.tm_min
= atoi(minute
);
333 tm
.tm_sec
= atoi(second
);
335 (void) setenv("TZ", "GMT0", 1);
340 if ((when
= mktime(&tm
)) == -1 && errno
!= 0)
343 when
-= (timezone
- altzone
);
345 if (fraction
== NULL
) {
348 /* truncate beyond 9 digits (nanoseconds) */
349 if (strlen(fraction
) > 9)
351 nanoseconds
= atoi(fraction
);
353 switch (strlen(fraction
)) {
355 nanoseconds
*= 100000000;
358 nanoseconds
*= 10000000;
361 nanoseconds
*= 1000000;
364 nanoseconds
*= 100000;
367 nanoseconds
*= 10000;
384 ts
->tv_nsec
= nanoseconds
;
388 parse_time(char *t
, timespec_t
*ts
)
397 * time in the following format (defined by the touch(1) spec):
398 * [[CC]YY]MMDDhhmm[.SS]
400 if ((p
= strchr(t
, '.')) != NULL
) {
401 if (strchr(p
+1, '.') != NULL
)
403 seconds
= atoi_for2(p
+1);
407 (void) memset(&tm
, 0, sizeof (struct tm
));
409 tm
.tm_year
= localtime(&when
)->tm_year
;
412 case 12: /* CCYYMMDDhhmm */
413 century
= atoi_for2(t
);
416 case 10: /* YYMMDDhhmm */
417 tm
.tm_year
= atoi_for2(t
);
423 tm
.tm_year
+= (century
- 19) * 100;
425 case 8: /* MMDDhhmm */
426 tm
.tm_mon
= atoi_for2(t
) - 1;
428 tm
.tm_mday
= atoi_for2(t
);
430 tm
.tm_hour
= atoi_for2(t
);
432 tm
.tm_min
= atoi_for2(t
);
439 if ((when
= mktime(&tm
)) == -1)
442 when
-= (timezone
-altzone
);
449 parse_timespec(char *t
, timespec_t
*ts
)
455 * time in the following format (defined by the touch(1) spec):
459 (void) memset(&tm
, 0, sizeof (struct tm
));
461 tm
.tm_year
= localtime(&when
)->tm_year
;
464 case 10: /* MMDDhhmmyy */
465 tm
.tm_year
= atoi_for2(t
+8);
469 case 8: /* MMDDhhmm */
470 tm
.tm_mon
= atoi_for2(t
) - 1;
472 tm
.tm_mday
= atoi_for2(t
);
474 tm
.tm_hour
= atoi_for2(t
);
476 tm
.tm_min
= atoi_for2(t
);
482 if ((when
= mktime(&tm
)) == -1)
485 when
-= (timezone
- altzone
);
496 value
= (*p
- '0') * 10 + *(p
+1) - '0';
497 if ((value
< 0) || (value
> 99))
503 touchabort(const char *message
)
505 (void) fprintf(stderr
, "%s: %s\n", myname
, gettext(message
));
510 usage(const int settime
)
513 (void) fprintf(stderr
, gettext(
514 "usage: %s [-f file] [mmddhhmm[yy]] file...\n"), myname
);
517 (void) fprintf(stderr
, gettext(
518 "usage: %s [-acm] [-r ref_file] file...\n"
519 " %s [-acm] [-t [[CC]YY]MMDDhhmm[.SS]] file...\n"
520 " %s [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] file...\n"
521 " %s [-acm] [-d YYYY-MM-DDThh:mm:SS[,frac][Z]] file...\n"
522 " %s [-acm] [MMDDhhmm[yy]] file...\n"),
523 myname
, myname
, myname
, myname
, myname
);