Import NetBSD touch command

Replaces the 'touch' functionality provided by commands/touch.
This commit is contained in:
Zachary Storer 2014-03-15 11:55:55 -06:00 committed by Lionel Sambuc
parent 5552d86152
commit 71d1d39e61
6 changed files with 200 additions and 138 deletions

View file

@ -26,7 +26,7 @@ SUBDIR= add_route arp ash at backup btrace \
slip spell sprofalyze sprofdiff srccrc \ slip spell sprofalyze sprofdiff srccrc \
svclog svrctl swifi synctree sysenv \ svclog svrctl swifi synctree sysenv \
syslogd tail tcpd tcpdp tcpstat telnet \ syslogd tail tcpd tcpdp tcpstat telnet \
telnetd term termcap tget time touch \ telnetd term termcap tget time \
truncate udpstat umount \ truncate udpstat umount \
unstack update uud uue version vol \ unstack update uud uue version vol \
whereis which writeisofs fetch \ whereis which writeisofs fetch \

View file

@ -1,6 +0,0 @@
# $NetBSD: Makefile,v 1.3 1994/12/07 09:19:47 jtc Exp $
# @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= touch
.include <bsd.prog.mk>

View file

@ -25,7 +25,7 @@ SUBDIR= asa \
\ \
sdiff sed seq shlock \ sdiff sed seq shlock \
shuffle sort split stat su \ shuffle sort split stat su \
tee tic tput \ tee tic touch tput \
tr true tsort tty ul uname unexpand unifdef \ tr true tsort tty ul uname unexpand unifdef \
uniq units unvis unzip users \ uniq units unvis unzip users \
uuidgen vis \ uuidgen vis \

8
usr.bin/touch/Makefile Normal file
View file

@ -0,0 +1,8 @@
# $NetBSD: Makefile,v 1.4 2012/07/25 01:23:46 christos Exp $
# @(#)Makefile 8.1 (Berkeley) 6/6/93
PROG= touch
LDADD+= -lutil
DPADD+= ${LIBUTIL}
.include <bsd.prog.mk>

View file

@ -1,5 +1,4 @@
.\" $OpenBSD: touch.1,v 1.14 2007/08/06 19:16:06 sobrado Exp $ .\" $NetBSD: touch.1,v 1.25 2012/10/24 02:46:25 pgoyette Exp $
.\" $NetBSD: touch.1,v 1.8 1995/08/31 22:10:05 jtc Exp $
.\" .\"
.\" Copyright (c) 1991, 1993 .\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved. .\" The Regents of the University of California. All rights reserved.
@ -33,27 +32,30 @@
.\" .\"
.\" @(#)touch.1 8.3 (Berkeley) 4/28/95 .\" @(#)touch.1 8.3 (Berkeley) 4/28/95
.\" .\"
.Dd $Mdocdate: August 6 2007 $ .Dd October 22, 2012
.Dt TOUCH 1 .Dt TOUCH 1
.Os .Os
.Sh NAME .Sh NAME
.Nm touch .Nm touch
.Nd change file access and modification times .Nd change file access and modification times
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm touch .Nm
.Op Fl acm .Op Fl acfhm
.Op Fl d Ar human-datetime
.Op Fl Fl date Ar human-datetime
.Op Fl r Ar file .Op Fl r Ar file
.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS] .Op Fl Fl reference Ar file
.Ar .Op Fl t Ar datetime
.Ar file ...
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm .Nm
utility sets the modification and access times of files to the utility changes the access and modification times of files to the
current time of day. current time of day.
If the file doesn't exist, it is created with default permissions. If the file doesn't exist, it is created with default permissions.
.Pp .Pp
The options are as follows: The following options are available:
.Bl -tag -width Ds .Bl -tag -width "-d human-datetime"
.It Fl a .It Fl a
Change the access time of the file. Change the access time of the file.
The modification time of the file is not changed unless the The modification time of the file is not changed unless the
@ -65,17 +67,35 @@ The
.Nm .Nm
utility does not treat this as an error. utility does not treat this as an error.
No error messages are displayed and the exit value is not affected. No error messages are displayed and the exit value is not affected.
.It Fl d Ar human-datetime
.It Fl Fl date Ar human-datetime
Parse
.Ar human-datetime
using the human datetime parser
.Xr parsedate 3 .
.It Fl f
This flag has no effect; it is accepted for compatibility reasons.
.It Fl h
If
.Ar file
is a symbolic link, access and/or modification time of the link is changed.
This option implies
.Fl c .
.It Fl m .It Fl m
Change the modification time of the file. Change the modification time of the file.
The access time of the file is not changed unless the The access time of the file is not changed unless the
.Fl a .Fl a
flag is also specified. flag is also specified.
.It Fl r Ar file .It Fl r Ar file
Use the access and modification times from the specified file .It Fl Fl reference Ar file
Use the access and modifications times from
.Ar file
instead of the current time of day. instead of the current time of day.
.It Fl t Ar [[CC]YY]MMDDhhmm[.SS] .It Fl t Ar datetime
Change the access and modification times to the specified time. Change the access and modification times to the specified time.
The argument should be in the form The argument
.Ar datetime
should be in the form
.Dq [[CC]YY]MMDDhhmm[.SS] .Dq [[CC]YY]MMDDhhmm[.SS]
where each pair of letters represents the following: where each pair of letters represents the following:
.Pp .Pp
@ -120,22 +140,21 @@ letter pair is not specified, the value defaults to 0.
.El .El
.Pp .Pp
The The
.Nm .Fl d ,
utility exits 0 on success or >0 if an error occurred. .Fl r ,
.Sh SEE ALSO and
.Xr utimes 2 .Fl t
.Sh STANDARDS options are mutually exclusive.
The If more than one of these options is present, the last one is used.
.Nm .Sh EXIT STATUS
utility is compliant with the .Ex -std
.St -p1003.1-2004 .Sh COMPATIBILITY
specification.
.Pp
The obsolescent form of The obsolescent form of
.Nm touch , .Nm ,
where a time format is specified as the first argument, is supported. where a time format is specified as the first argument, is supported.
When no When no
.Fl r .Fl d ,
.Fl r ,
or or
.Fl t .Fl t
option is specified, there are at least two arguments, and the first option is specified, there are at least two arguments, and the first
@ -154,10 +173,20 @@ letter pairs are treated as their counterparts specified to the
option. option.
If the If the
.Dq YY .Dq YY
letter pair is in the range 69 to 99, the year is set from 1969 to 1999; letter pair is in the range 69 to 99, the year is set to 1969 to 1999,
otherwise, the year is set in the 21st century. otherwise, the year is set in the 21st century.
.Sh SEE ALSO
.Xr utimes 2
.Sh STANDARDS
The
.Nm
utility is expected to be a superset of the
.St -p1003.2
specification.
.Sh HISTORY .Sh HISTORY
A A
.Nm .Nm
utility appeared in utility appeared in
.At v7 . .At v7 .
.Sh BUGS
A symbolic link can't be a reference file of access and/or modification time.

View file

@ -1,5 +1,4 @@
/* $OpenBSD: touch.c,v 1.17 2007/08/06 19:16:06 sobrado Exp $ */ /* $NetBSD: touch.c,v 1.32 2012/10/22 21:51:58 christos Exp $ */
/* $NetBSD: touch.c,v 1.11 1995/08/31 22:10:06 jtc Exp $ */
/* /*
* Copyright (c) 1993 * Copyright (c) 1993
@ -30,11 +29,23 @@
* SUCH DAMAGE. * SUCH DAMAGE.
*/ */
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95";
#endif
__RCSID("$NetBSD: touch.c,v 1.32 2012/10/22 21:51:58 christos Exp $");
#endif /* not lint */
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <ctype.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
@ -43,42 +54,60 @@
#include <string.h> #include <string.h>
#include <locale.h> #include <locale.h>
#include <time.h> #include <time.h>
#include <utime.h>
#include <tzfile.h> #include <tzfile.h>
#include <unistd.h> #include <unistd.h>
#include <util.h>
#include <getopt.h>
void stime_arg1(char *, struct timeval *); static void stime_arg0(char *, struct timeval *);
void stime_arg2(char *, int, struct timeval *); static void stime_arg1(char *, struct timeval *);
void stime_file(char *, struct timeval *); static void stime_arg2(char *, int, struct timeval *);
__dead void usage(void); static void stime_file(char *, struct timeval *);
__dead static void usage(void);
char * __progname; struct option touch_longopts[] = {
{ "date", required_argument, 0,
'd' },
{ "reference", required_argument, 0,
'r' },
{ NULL, 0, 0,
0 },
};
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
struct stat sb; struct stat sb;
struct timeval tv[2]; struct timeval tv[2];
int aflag, cflag, mflag, ch, fd, len, rval, timeset; int aflag, cflag, hflag, mflag, ch, fd, len, rval, timeset;
char *p; char *p;
int (*change_file_times)(const char *, const struct timeval *);
int (*get_file_status)(const char *, struct stat *);
(void)setlocale(LC_ALL, ""); setlocale(LC_ALL, "");
__progname = argv[0];
aflag = cflag = mflag = timeset = 0; aflag = cflag = hflag = mflag = timeset = 0;
if (gettimeofday(&tv[0], NULL)) if (gettimeofday(&tv[0], NULL))
err(1, "gettimeofday"); err(1, "gettimeofday");
while ((ch = getopt(argc, argv, "acfmr:t:")) != -1) while ((ch = getopt_long(argc, argv, "acd:fhmr:t:", touch_longopts,
switch (ch) { NULL)) != -1)
switch(ch) {
case 'a': case 'a':
aflag = 1; aflag = 1;
break; break;
case 'c': case 'c':
cflag = 1; cflag = 1;
break; break;
case 'd':
timeset = 1;
stime_arg0(optarg, tv);
break;
case 'f': case 'f':
break; break;
case 'h':
hflag = 1;
break;
case 'm': case 'm':
mflag = 1; mflag = 1;
break; break;
@ -90,6 +119,7 @@ main(int argc, char *argv[])
timeset = 1; timeset = 1;
stime_arg1(optarg, tv); stime_arg1(optarg, tv);
break; break;
case '?':
default: default:
usage(); usage();
} }
@ -100,6 +130,15 @@ main(int argc, char *argv[])
if (aflag == 0 && mflag == 0) if (aflag == 0 && mflag == 0)
aflag = mflag = 1; aflag = mflag = 1;
if (hflag) {
cflag = 1; /* Don't create new file */
change_file_times = lutimes;
get_file_status = lstat;
} else {
change_file_times = utimes;
get_file_status = stat;
}
/* /*
* If no -r or -t flag, at least two operands, the first of which * If no -r or -t flag, at least two operands, the first of which
* is an 8 or 10 digit number, use the obsolete time specification. * is an 8 or 10 digit number, use the obsolete time specification.
@ -120,15 +159,15 @@ main(int argc, char *argv[])
if (*argv == NULL) if (*argv == NULL)
usage(); usage();
for (rval = 0; *argv; ++argv) { for (rval = EXIT_SUCCESS; *argv; ++argv) {
/* See if the file exists. */ /* See if the file exists. */
if (stat(*argv, &sb)) { if ((*get_file_status)(*argv, &sb)) {
if (!cflag) { if (!cflag) {
/* Create the file. */ /* Create the file. */
fd = open(*argv, fd = open(*argv,
O_WRONLY | O_CREAT, DEFFILEMODE); O_WRONLY | O_CREAT, DEFFILEMODE);
if (fd == -1 || fstat(fd, &sb) || close(fd)) { if (fd == -1 || fstat(fd, &sb) || close(fd)) {
rval = 1; rval = EXIT_FAILURE;
warn("%s", *argv); warn("%s", *argv);
continue; continue;
} }
@ -139,19 +178,18 @@ main(int argc, char *argv[])
} else } else
continue; continue;
} }
if (!aflag) if (!aflag)
TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec);
if (!mflag) if (!mflag)
TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);
/* Try utimes(2). */ /* Try utimes(2). */
if (!utimes(*argv, tv)) if (!(*change_file_times)(*argv, tv))
continue; continue;
/* If the user specified a time, nothing else we can do. */ /* If the user specified a time, nothing else we can do. */
if (timeset) { if (timeset) {
rval = 1; rval = EXIT_FAILURE;
warn("%s", *argv); warn("%s", *argv);
} }
@ -161,10 +199,10 @@ main(int argc, char *argv[])
* The permission checks are different, too, in that the * The permission checks are different, too, in that the
* ability to write the file is sufficient. Take a shot. * ability to write the file is sufficient. Take a shot.
*/ */
if (!utimes(*argv, NULL)) if (!(*change_file_times)(*argv, NULL))
continue; continue;
rval = 1; rval = EXIT_FAILURE;
warn("%s", *argv); warn("%s", *argv);
} }
exit(rval); exit(rval);
@ -172,135 +210,128 @@ main(int argc, char *argv[])
#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 static void
stime_arg0(char *arg, struct timeval *tvp)
{
tvp[1].tv_sec = tvp[0].tv_sec = parsedate(arg, NULL, NULL);
if (tvp[0].tv_sec == -1)
errx(EXIT_FAILURE, "Could not parse `%s'", arg);
tvp[0].tv_usec = tvp[1].tv_usec = 0;
}
static void
stime_arg1(char *arg, struct timeval *tvp) stime_arg1(char *arg, struct timeval *tvp)
{ {
struct tm *lt; struct tm *t;
time_t tmptime; time_t tmptime;
int yearset; int yearset;
char *dot, *p; char *p;
/* Start with the current time. */ /* Start with the current time. */
tmptime = tvp[0].tv_sec; tmptime = tvp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL) if ((t = localtime(&tmptime)) == NULL)
err(1, "localtime"); err(EXIT_FAILURE, "localtime");
/* [[CC]YY]MMDDhhmm[.SS] */ /* [[CC]YY]MMDDhhmm[.SS] */
for (p = arg, dot = NULL; *p != '\0'; p++) { if ((p = strchr(arg, '.')) == NULL)
if (*p == '.' && dot == NULL) t->tm_sec = 0; /* Seconds defaults to 0. */
dot = p;
else if (!isdigit((unsigned char)*p))
goto terr;
}
if (dot == NULL)
lt->tm_sec = 0; /* Seconds defaults to 0. */
else { else {
*dot++ = '\0'; if (strlen(p + 1) != 2)
if (strlen(dot) != 2)
goto terr;
lt->tm_sec = ATOI2(dot);
if (lt->tm_sec > 61) /* Could be leap second. */
goto terr; goto terr;
*p++ = '\0';
t->tm_sec = ATOI2(p);
} }
yearset = 0; yearset = 0;
switch (strlen(arg)) { switch (strlen(arg)) {
case 12: /* CCYYMMDDhhmm */ case 12: /* CCYYMMDDhhmm */
lt->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE; t->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
yearset = 1; yearset = 1;
/* FALLTHROUGH */ /* FALLTHROUGH */
case 10: /* YYMMDDhhmm */ case 10: /* YYMMDDhhmm */
if (yearset) { if (yearset) {
yearset = ATOI2(arg); t->tm_year += ATOI2(arg);
lt->tm_year += yearset;
} else { } else {
yearset = ATOI2(arg); yearset = ATOI2(arg);
/* Preserve current century. */ if (yearset < 69)
lt->tm_year = ((lt->tm_year / 100) * 100) + yearset; t->tm_year = yearset + 2000 - TM_YEAR_BASE;
else
t->tm_year = yearset + 1900 - TM_YEAR_BASE;
} }
/* FALLTHROUGH */ /* FALLTHROUGH */
case 8: /* MMDDhhmm */ case 8: /* MMDDhhmm */
lt->tm_mon = ATOI2(arg); t->tm_mon = ATOI2(arg);
if (lt->tm_mon > 12 || lt->tm_mon == 0) --t->tm_mon; /* Convert from 01-12 to 00-11 */
goto terr; /* FALLTHROUGH */
--lt->tm_mon; /* Convert from 01-12 to 00-11 */ case 6:
lt->tm_mday = ATOI2(arg); t->tm_mday = ATOI2(arg);
if (lt->tm_mday > 31 || lt->tm_mday == 0) /* FALLTHROUGH */
goto terr; case 4:
lt->tm_hour = ATOI2(arg); t->tm_hour = ATOI2(arg);
if (lt->tm_hour > 23) /* FALLTHROUGH */
goto terr; case 2:
lt->tm_min = ATOI2(arg); t->tm_min = ATOI2(arg);
if (lt->tm_min > 59)
goto terr;
break; break;
default: default:
goto terr; goto terr;
} }
lt->tm_isdst = -1; /* Figure out DST. */ t->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt); tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
if (tvp[0].tv_sec == -1) if (tvp[0].tv_sec == -1)
terr: errx(1, terr: errx(EXIT_FAILURE,
"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; tvp[0].tv_usec = tvp[1].tv_usec = 0;
} }
void static void
stime_arg2(char *arg, int year, struct timeval *tvp) stime_arg2(char *arg, int year, struct timeval *tvp)
{ {
struct tm *lt; struct tm *t;
time_t tmptime; time_t tmptime;
/* Start with the current time. */ /* Start with the current time. */
tmptime = tvp[0].tv_sec; tmptime = tvp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL) if ((t = localtime(&tmptime)) == NULL)
err(1, "localtime"); err(EXIT_FAILURE, "localtime");
lt->tm_mon = ATOI2(arg); /* MMDDhhmm[YY] */ t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
if (lt->tm_mon > 12 || lt->tm_mon == 0) --t->tm_mon; /* Convert from 01-12 to 00-11 */
goto terr; t->tm_mday = ATOI2(arg);
--lt->tm_mon; /* Convert from 01-12 to 00-11 */ t->tm_hour = ATOI2(arg);
lt->tm_mday = ATOI2(arg); t->tm_min = ATOI2(arg);
if (lt->tm_mday > 31 || lt->tm_mday == 0)
goto terr;
lt->tm_hour = ATOI2(arg);
if (lt->tm_hour > 23)
goto terr;
lt->tm_min = ATOI2(arg);
if (lt->tm_min > 59)
goto terr;
if (year) { if (year) {
year = ATOI2(arg); /* Preserve current century. */ year = ATOI2(arg);
lt->tm_year = ((lt->tm_year / 100) * 100) + year; if (year < 69)
t->tm_year = year + 2000 - TM_YEAR_BASE;
else
t->tm_year = year + 1900 - TM_YEAR_BASE;
} }
lt->tm_sec = 0; t->tm_sec = 0;
lt->tm_isdst = -1; /* Figure out DST. */ t->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt); tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
if (tvp[0].tv_sec == -1) if (tvp[0].tv_sec == -1)
terr: errx(1, errx(EXIT_FAILURE,
"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; tvp[0].tv_usec = tvp[1].tv_usec = 0;
} }
void static void
stime_file(char *fname, struct timeval *tvp) stime_file(char *fname, struct timeval *tvp)
{ {
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); TIMESPEC_TO_TIMEVAL(&tvp[0], &sb.st_atimespec);
TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); TIMESPEC_TO_TIMEVAL(&tvp[1], &sb.st_mtimespec);
} }
__dead void static 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: %s [-acfhm] [-d|--date datetime] [-r|--reference file] [-t time] file ...\n",
__progname); getprogname());
exit(1); exit(EXIT_FAILURE);
} }