version 1.17, 2007/08/06 19:16:06 |
version 1.18, 2011/08/20 06:44:24 |
|
|
#include <tzfile.h> |
#include <tzfile.h> |
#include <unistd.h> |
#include <unistd.h> |
|
|
void stime_arg1(char *, struct timeval *); |
void stime_arg1(char *, struct timespec *); |
void stime_arg2(char *, int, struct timeval *); |
void stime_arg2(char *, int, struct timespec *); |
void stime_file(char *, struct timeval *); |
void stime_argd(char *, struct timespec *); |
|
void stime_file(char *, struct timespec *); |
__dead void usage(void); |
__dead void usage(void); |
|
|
int |
int |
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
struct stat sb; |
struct timespec ts[2]; |
struct timeval tv[2]; |
|
int aflag, cflag, mflag, ch, fd, len, rval, timeset; |
int aflag, cflag, mflag, ch, fd, len, rval, timeset; |
char *p; |
char *p; |
|
|
(void)setlocale(LC_ALL, ""); |
(void)setlocale(LC_ALL, ""); |
|
|
aflag = cflag = mflag = timeset = 0; |
aflag = cflag = mflag = timeset = 0; |
if (gettimeofday(&tv[0], NULL)) |
while ((ch = getopt(argc, argv, "acd:fmr:t:")) != -1) |
err(1, "gettimeofday"); |
|
|
|
while ((ch = getopt(argc, argv, "acfmr:t:")) != -1) |
|
switch (ch) { |
switch (ch) { |
case 'a': |
case 'a': |
aflag = 1; |
aflag = 1; |
|
|
case 'c': |
case 'c': |
cflag = 1; |
cflag = 1; |
break; |
break; |
|
case 'd': |
|
timeset = 1; |
|
stime_argd(optarg, ts); |
|
break; |
case 'f': |
case 'f': |
break; |
break; |
case 'm': |
case 'm': |
|
|
break; |
break; |
case 'r': |
case 'r': |
timeset = 1; |
timeset = 1; |
stime_file(optarg, tv); |
stime_file(optarg, ts); |
break; |
break; |
case 't': |
case 't': |
timeset = 1; |
timeset = 1; |
stime_arg1(optarg, tv); |
stime_arg1(optarg, ts); |
break; |
break; |
default: |
default: |
usage(); |
usage(); |
|
|
len = p - argv[0]; |
len = p - argv[0]; |
if (*p == '\0' && (len == 8 || len == 10)) { |
if (*p == '\0' && (len == 8 || len == 10)) { |
timeset = 1; |
timeset = 1; |
stime_arg2(*argv++, len == 10, tv); |
stime_arg2(*argv++, len == 10, ts); |
} |
} |
} |
} |
|
|
/* Otherwise use the current time of day. */ |
/* Otherwise use the current time of day. */ |
if (!timeset) |
if (!timeset) |
tv[1] = tv[0]; |
ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW; |
|
|
|
if (!aflag) |
|
ts[0].tv_nsec = UTIME_OMIT; |
|
if (!mflag) |
|
ts[1].tv_nsec = UTIME_OMIT; |
|
|
if (*argv == NULL) |
if (*argv == NULL) |
usage(); |
usage(); |
|
|
for (rval = 0; *argv; ++argv) { |
for (rval = 0; *argv; ++argv) { |
/* See if the file exists. */ |
/* Update the file's timestamp if it exists. */ |
if (stat(*argv, &sb)) { |
if (! utimensat(AT_FDCWD, *argv, ts, 0)) |
if (!cflag) { |
|
/* Create the file. */ |
|
fd = open(*argv, |
|
O_WRONLY | O_CREAT, DEFFILEMODE); |
|
if (fd == -1 || fstat(fd, &sb) || close(fd)) { |
|
rval = 1; |
|
warn("%s", *argv); |
|
continue; |
|
} |
|
|
|
/* If using the current time, we're done. */ |
|
if (!timeset) |
|
continue; |
|
} else |
|
continue; |
|
} |
|
|
|
if (!aflag) |
|
TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); |
|
if (!mflag) |
|
TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); |
|
|
|
/* Try utimes(2). */ |
|
if (!utimes(*argv, tv)) |
|
continue; |
continue; |
|
if (errno != ENOENT) { |
/* If the user specified a time, nothing else we can do. */ |
|
if (timeset) { |
|
rval = 1; |
rval = 1; |
warn("%s", *argv); |
warn("%s", *argv); |
|
continue; |
} |
} |
|
|
/* |
/* Didn't exist; should we create it? */ |
* System V and POSIX 1003.1 require that a NULL argument |
if (cflag) |
* set the access/modification times to the current time. |
|
* The permission checks are different, too, in that the |
|
* ability to write the file is sufficient. Take a shot. |
|
*/ |
|
if (!utimes(*argv, NULL)) |
|
continue; |
continue; |
|
|
rval = 1; |
/* Create the file. */ |
warn("%s", *argv); |
fd = open(*argv, O_WRONLY | O_CREAT, DEFFILEMODE); |
|
if (fd == -1 || futimens(fd, ts) || close(fd)) { |
|
rval = 1; |
|
warn("%s", *argv); |
|
} |
} |
} |
exit(rval); |
exit(rval); |
} |
} |
|
|
#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) |
#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) |
|
|
void |
void |
stime_arg1(char *arg, struct timeval *tvp) |
stime_arg1(char *arg, struct timespec *tsp) |
{ |
{ |
struct tm *lt; |
struct tm *lt; |
time_t tmptime; |
time_t tmptime; |
int yearset; |
int yearset; |
char *dot, *p; |
char *dot, *p; |
/* Start with the current time. */ |
/* Start with the current time. */ |
tmptime = tvp[0].tv_sec; |
tmptime = tsp[0].tv_sec; |
if ((lt = localtime(&tmptime)) == NULL) |
if ((lt = localtime(&tmptime)) == NULL) |
err(1, "localtime"); |
err(1, "localtime"); |
/* [[CC]YY]MMDDhhmm[.SS] */ |
/* [[CC]YY]MMDDhhmm[.SS] */ |
|
|
} |
} |
|
|
lt->tm_isdst = -1; /* Figure out DST. */ |
lt->tm_isdst = -1; /* Figure out DST. */ |
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt); |
tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt); |
if (tvp[0].tv_sec == -1) |
if (tsp[0].tv_sec == -1) |
terr: errx(1, |
terr: errx(1, |
"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); |
"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); |
|
|
tvp[0].tv_usec = tvp[1].tv_usec = 0; |
tsp[0].tv_nsec = tsp[1].tv_nsec = 0; |
} |
} |
|
|
void |
void |
stime_arg2(char *arg, int year, struct timeval *tvp) |
stime_arg2(char *arg, int year, struct timespec *tsp) |
{ |
{ |
struct tm *lt; |
struct tm *lt; |
time_t tmptime; |
time_t tmptime; |
/* Start with the current time. */ |
/* Start with the current time. */ |
tmptime = tvp[0].tv_sec; |
tmptime = tsp[0].tv_sec; |
if ((lt = localtime(&tmptime)) == NULL) |
if ((lt = localtime(&tmptime)) == NULL) |
err(1, "localtime"); |
err(1, "localtime"); |
|
|
|
|
lt->tm_sec = 0; |
lt->tm_sec = 0; |
|
|
lt->tm_isdst = -1; /* Figure out DST. */ |
lt->tm_isdst = -1; /* Figure out DST. */ |
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt); |
tsp[0].tv_sec = tsp[1].tv_sec = mktime(lt); |
if (tvp[0].tv_sec == -1) |
if (tsp[0].tv_sec == -1) |
terr: errx(1, |
terr: errx(1, |
"out of range or illegal time specification: MMDDhhmm[YY]"); |
"out of range or illegal time specification: MMDDhhmm[YY]"); |
|
|
tvp[0].tv_usec = tvp[1].tv_usec = 0; |
tsp[0].tv_nsec = tsp[1].tv_nsec = 0; |
} |
} |
|
|
void |
void |
stime_file(char *fname, struct timeval *tvp) |
stime_file(char *fname, struct timespec *tsp) |
{ |
{ |
struct stat sb; |
struct stat sb; |
|
|
if (stat(fname, &sb)) |
if (stat(fname, &sb)) |
err(1, "%s", fname); |
err(1, "%s", fname); |
TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); |
tsp[0] = sb.st_atim; |
TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); |
tsp[1] = sb.st_mtim; |
} |
} |
|
|
|
void |
|
stime_argd(char *arg, struct timespec *tsp) |
|
{ |
|
struct tm tm; |
|
char *frac, *p; |
|
int utc = 0; |
|
|
|
/* accept YYYY-MM-DD(T| )hh:mm:ss[(.|,)frac][Z] */ |
|
memset(&tm, 0, sizeof(tm)); |
|
p = strptime(arg, "%F", &tm); |
|
if (p == NULL || (*p != 'T' && *p != ' ')) |
|
goto terr; |
|
p = strptime(p + 1, "%T", &tm); |
|
if (p == NULL) |
|
goto terr; |
|
tsp[0].tv_nsec = 0; |
|
if (*p == '.' || *p == ',') { |
|
frac = ++p; |
|
while (isdigit((unsigned char)*p)) { |
|
if (p - frac < 9) { |
|
tsp[0].tv_nsec = tsp[0].tv_nsec * 10 + |
|
*p - '0'; |
|
} |
|
p++; |
|
} |
|
if (p == frac) |
|
goto terr; |
|
|
|
/* fill in the trailing zeros */ |
|
while (p - frac-- < 9) |
|
tsp[0].tv_nsec *= 10; |
|
} |
|
if (*p == 'Z') { |
|
utc = 1; |
|
p++; |
|
} |
|
if (*p != '\0') |
|
goto terr; |
|
|
|
tm.tm_isdst = -1; |
|
tsp[0].tv_sec = utc ? timegm(&tm) : mktime(&tm); |
|
if (tsp[0].tv_sec == -1) |
|
terr: errx(1, |
|
"out of range or illegal time specification: YYYY-MM-DDThh:mm:ss[.frac][Z]"); |
|
tsp[1] = tsp[0]; |
|
} |
|
|
__dead void |
__dead void |
usage(void) |
usage(void) |
{ |
{ |
extern char *__progname; |
|
|
|
(void)fprintf(stderr, |
(void)fprintf(stderr, |
"usage: %s [-acm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n", |
"usage: touch [-acm] [-d YYYY-MM-DDThh:mm:SS[.frac][Z]] [-r file]\n" |
__progname); |
" [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n"); |
exit(1); |
exit(1); |
} |
} |