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 \
svclog svrctl swifi synctree sysenv \
syslogd tail tcpd tcpdp tcpstat telnet \
telnetd term termcap tget time touch \
telnetd term termcap tget time \
truncate udpstat umount \
unstack update uud uue version vol \
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 \
shuffle sort split stat su \
tee tic tput \
tee tic touch tput \
tr true tsort tty ul uname unexpand unifdef \
uniq units unvis unzip users \
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.8 1995/08/31 22:10:05 jtc Exp $
.\" $NetBSD: touch.1,v 1.25 2012/10/24 02:46:25 pgoyette Exp $
.\"
.\" Copyright (c) 1991, 1993
.\" The Regents of the University of California. All rights reserved.
@ -33,27 +32,30 @@
.\"
.\" @(#)touch.1 8.3 (Berkeley) 4/28/95
.\"
.Dd $Mdocdate: August 6 2007 $
.Dd October 22, 2012
.Dt TOUCH 1
.Os
.Sh NAME
.Nm touch
.Nd change file access and modification times
.Sh SYNOPSIS
.Nm touch
.Op Fl acm
.Nm
.Op Fl acfhm
.Op Fl d Ar human-datetime
.Op Fl Fl date Ar human-datetime
.Op Fl r Ar file
.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS]
.Ar
.Op Fl Fl reference Ar file
.Op Fl t Ar datetime
.Ar file ...
.Sh DESCRIPTION
The
.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.
If the file doesn't exist, it is created with default permissions.
.Pp
The options are as follows:
.Bl -tag -width Ds
The following options are available:
.Bl -tag -width "-d human-datetime"
.It Fl a
Change the access time of the file.
The modification time of the file is not changed unless the
@ -65,17 +67,35 @@ The
.Nm
utility does not treat this as an error.
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
Change the modification time of the file.
The access time of the file is not changed unless the
.Fl a
flag is also specified.
.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.
.It Fl t Ar [[CC]YY]MMDDhhmm[.SS]
.It Fl t Ar datetime
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]
where each pair of letters represents the following:
.Pp
@ -120,22 +140,21 @@ letter pair is not specified, the value defaults to 0.
.El
.Pp
The
.Nm
utility exits 0 on success or >0 if an error occurred.
.Sh SEE ALSO
.Xr utimes 2
.Sh STANDARDS
The
.Nm
utility is compliant with the
.St -p1003.1-2004
specification.
.Pp
.Fl d ,
.Fl r ,
and
.Fl t
options are mutually exclusive.
If more than one of these options is present, the last one is used.
.Sh EXIT STATUS
.Ex -std
.Sh COMPATIBILITY
The obsolescent form of
.Nm touch ,
.Nm ,
where a time format is specified as the first argument, is supported.
When no
.Fl r
.Fl d ,
.Fl r ,
or
.Fl t
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.
If the
.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.
.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
A
.Nm
utility appeared in
.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.11 1995/08/31 22:10:06 jtc Exp $ */
/* $NetBSD: touch.c,v 1.32 2012/10/22 21:51:58 christos Exp $ */
/*
* Copyright (c) 1993
@ -30,11 +29,23 @@
* 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/stat.h>
#include <sys/time.h>
#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@ -43,42 +54,60 @@
#include <string.h>
#include <locale.h>
#include <time.h>
#include <utime.h>
#include <tzfile.h>
#include <unistd.h>
#include <util.h>
#include <getopt.h>
void stime_arg1(char *, struct timeval *);
void stime_arg2(char *, int, struct timeval *);
void stime_file(char *, struct timeval *);
__dead void usage(void);
static void stime_arg0(char *, struct timeval *);
static void stime_arg1(char *, struct timeval *);
static void stime_arg2(char *, int, struct timeval *);
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
main(int argc, char *argv[])
{
struct stat sb;
struct timeval tv[2];
int aflag, cflag, mflag, ch, fd, len, rval, timeset;
char *p;
struct stat sb;
struct timeval tv[2];
int aflag, cflag, hflag, mflag, ch, fd, len, rval, timeset;
char *p;
int (*change_file_times)(const char *, const struct timeval *);
int (*get_file_status)(const char *, struct stat *);
(void)setlocale(LC_ALL, "");
__progname = argv[0];
setlocale(LC_ALL, "");
aflag = cflag = mflag = timeset = 0;
aflag = cflag = hflag = mflag = timeset = 0;
if (gettimeofday(&tv[0], NULL))
err(1, "gettimeofday");
while ((ch = getopt(argc, argv, "acfmr:t:")) != -1)
switch (ch) {
while ((ch = getopt_long(argc, argv, "acd:fhmr:t:", touch_longopts,
NULL)) != -1)
switch(ch) {
case 'a':
aflag = 1;
break;
case 'c':
cflag = 1;
break;
case 'd':
timeset = 1;
stime_arg0(optarg, tv);
break;
case 'f':
break;
case 'h':
hflag = 1;
break;
case 'm':
mflag = 1;
break;
@ -90,6 +119,7 @@ main(int argc, char *argv[])
timeset = 1;
stime_arg1(optarg, tv);
break;
case '?':
default:
usage();
}
@ -100,6 +130,15 @@ main(int argc, char *argv[])
if (aflag == 0 && mflag == 0)
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
* is an 8 or 10 digit number, use the obsolete time specification.
@ -120,15 +159,15 @@ main(int argc, char *argv[])
if (*argv == NULL)
usage();
for (rval = 0; *argv; ++argv) {
for (rval = EXIT_SUCCESS; *argv; ++argv) {
/* See if the file exists. */
if (stat(*argv, &sb)) {
if ((*get_file_status)(*argv, &sb)) {
if (!cflag) {
/* Create the file. */
fd = open(*argv,
O_WRONLY | O_CREAT, DEFFILEMODE);
if (fd == -1 || fstat(fd, &sb) || close(fd)) {
rval = 1;
rval = EXIT_FAILURE;
warn("%s", *argv);
continue;
}
@ -139,19 +178,18 @@ main(int argc, char *argv[])
} 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))
if (!(*change_file_times)(*argv, tv))
continue;
/* If the user specified a time, nothing else we can do. */
if (timeset) {
rval = 1;
rval = EXIT_FAILURE;
warn("%s", *argv);
}
@ -161,10 +199,10 @@ main(int argc, char *argv[])
* The permission checks are different, too, in that the
* ability to write the file is sufficient. Take a shot.
*/
if (!utimes(*argv, NULL))
if (!(*change_file_times)(*argv, NULL))
continue;
rval = 1;
rval = EXIT_FAILURE;
warn("%s", *argv);
}
exit(rval);
@ -172,135 +210,128 @@ main(int argc, char *argv[])
#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)
{
struct tm *lt;
time_t tmptime;
int yearset;
char *dot, *p;
struct tm *t;
time_t tmptime;
int yearset;
char *p;
/* Start with the current time. */
tmptime = tvp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL)
err(1, "localtime");
if ((t = localtime(&tmptime)) == NULL)
err(EXIT_FAILURE, "localtime");
/* [[CC]YY]MMDDhhmm[.SS] */
for (p = arg, dot = NULL; *p != '\0'; p++) {
if (*p == '.' && dot == NULL)
dot = p;
else if (!isdigit((unsigned char)*p))
goto terr;
}
if (dot == NULL)
lt->tm_sec = 0; /* Seconds defaults to 0. */
if ((p = strchr(arg, '.')) == NULL)
t->tm_sec = 0; /* Seconds defaults to 0. */
else {
*dot++ = '\0';
if (strlen(dot) != 2)
goto terr;
lt->tm_sec = ATOI2(dot);
if (lt->tm_sec > 61) /* Could be leap second. */
if (strlen(p + 1) != 2)
goto terr;
*p++ = '\0';
t->tm_sec = ATOI2(p);
}
yearset = 0;
switch (strlen(arg)) {
case 12: /* CCYYMMDDhhmm */
lt->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
t->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE;
yearset = 1;
/* FALLTHROUGH */
case 10: /* YYMMDDhhmm */
if (yearset) {
yearset = ATOI2(arg);
lt->tm_year += yearset;
t->tm_year += ATOI2(arg);
} else {
yearset = ATOI2(arg);
/* Preserve current century. */
lt->tm_year = ((lt->tm_year / 100) * 100) + yearset;
if (yearset < 69)
t->tm_year = yearset + 2000 - TM_YEAR_BASE;
else
t->tm_year = yearset + 1900 - TM_YEAR_BASE;
}
/* FALLTHROUGH */
case 8: /* MMDDhhmm */
lt->tm_mon = ATOI2(arg);
if (lt->tm_mon > 12 || lt->tm_mon == 0)
goto terr;
--lt->tm_mon; /* Convert from 01-12 to 00-11 */
lt->tm_mday = 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;
t->tm_mon = ATOI2(arg);
--t->tm_mon; /* Convert from 01-12 to 00-11 */
/* FALLTHROUGH */
case 6:
t->tm_mday = ATOI2(arg);
/* FALLTHROUGH */
case 4:
t->tm_hour = ATOI2(arg);
/* FALLTHROUGH */
case 2:
t->tm_min = ATOI2(arg);
break;
default:
goto terr;
}
lt->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt);
t->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
if (tvp[0].tv_sec == -1)
terr: errx(1,
terr: errx(EXIT_FAILURE,
"out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]");
tvp[0].tv_usec = tvp[1].tv_usec = 0;
}
void
static void
stime_arg2(char *arg, int year, struct timeval *tvp)
{
struct tm *lt;
time_t tmptime;
struct tm *t;
time_t tmptime;
/* Start with the current time. */
tmptime = tvp[0].tv_sec;
if ((lt = localtime(&tmptime)) == NULL)
err(1, "localtime");
if ((t = localtime(&tmptime)) == NULL)
err(EXIT_FAILURE, "localtime");
lt->tm_mon = ATOI2(arg); /* MMDDhhmm[YY] */
if (lt->tm_mon > 12 || lt->tm_mon == 0)
goto terr;
--lt->tm_mon; /* Convert from 01-12 to 00-11 */
lt->tm_mday = 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;
t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
--t->tm_mon; /* Convert from 01-12 to 00-11 */
t->tm_mday = ATOI2(arg);
t->tm_hour = ATOI2(arg);
t->tm_min = ATOI2(arg);
if (year) {
year = ATOI2(arg); /* Preserve current century. */
lt->tm_year = ((lt->tm_year / 100) * 100) + year;
year = ATOI2(arg);
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. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(lt);
t->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
if (tvp[0].tv_sec == -1)
terr: errx(1,
"out of range or illegal time specification: MMDDhhmm[YY]");
errx(EXIT_FAILURE,
"out of range or illegal time specification: MMDDhhmm[yy]");
tvp[0].tv_usec = tvp[1].tv_usec = 0;
}
void
static void
stime_file(char *fname, struct timeval *tvp)
{
struct stat sb;
struct stat sb;
if (stat(fname, &sb))
err(1, "%s", fname);
TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);
TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);
TIMESPEC_TO_TIMEVAL(&tvp[0], &sb.st_atimespec);
TIMESPEC_TO_TIMEVAL(&tvp[1], &sb.st_mtimespec);
}
__dead void
static void
usage(void)
{
extern char *__progname;
(void)fprintf(stderr,
"usage: %s [-acm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...\n",
__progname);
exit(1);
"Usage: %s [-acfhm] [-d|--date datetime] [-r|--reference file] [-t time] file ...\n",
getprogname());
exit(EXIT_FAILURE);
}