new touch

This commit is contained in:
glass 1993-04-30 23:07:07 +00:00
parent ec60b03fb9
commit 8b8c96cad4
2 changed files with 386 additions and 105 deletions

View File

@ -32,44 +32,136 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)touch.1 6.4 (Berkeley) 6/27/91
.\" @(#)touch.1 6.5 (Berkeley) 3/6/93
.\"
.Dd June 27, 1991
.Dd March 6, 1993
.Dt TOUCH 1
.Os
.Sh NAME
.Nm touch
.Nd update date last modified of a file
.Nd change file access and modification times
.Sh SYNOPSIS
.Nm touch
.Op Fl c
.Op Fl f
.Op Fl acfm
.Op Fl r Ar file
.Op Fl t [[CC]YY]MMDDhhmm[.SS]
.Ar file ...
.Sh DESCRIPTION
The
.Nm touch
utility
changes the modification and or access times
of the given
.Ar file .
utility sets the modification and access times of files to the
current time of day.
If the file doesn't exist, it is created with default permissions.
.Pp
Available options:
The following options are available:
.Bl -tag -width Ds
.It Fl a
Change the access time of the file.
The modification time of the file is not changed unless the
.Fl m
flag is also specified.
.It Fl c
Do not create a specified file if it does not exist.
Do not write any diagnostic messages concerning this
condition.
.It Fl f
Attempt to force the
Do not create the file if it does not exist.
The
.Nm touch
in spite of read and write permissions on a
.Ar file .
utility does not treat this as an error.
No error messages are displayed and the exit value is not affected.
.It Fl f
Attempt to force the update, even if the file permissions do not
currently permit it.
.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
Use the access and modifications times from the specified file
instead of the current time of day.
.It Fl t
Change the access and modification times to the specified time.
The argument should be in the form
.Dq [[CC]YY]MMDDhhmm[.SS]
where each pair of letters represents the following:
.Pp
.Bl -tag -width Ds -compact -offset indent
.It Ar CC
The first two digits of the year (the century).
.It Ar YY
The second two digits of the year.
If
.Dq YY
is specified, but
.Dq CC
is not, a value for
.Dq YY
between 69 and 99 results in a
.Dq YY
value of 19.
Otherwise, a
.Dq YY
value of 20 is used.
.It Ar MM
The month of the year, from 1 to 12.
.It Ar DD
the day of the month, from 1 to 31.
.It Ar hh
The hour of the day, from 0 to 23.
.It Ar mm
The minute of the hour, from 0 to 59.
.It Ar SS
The second of the minute, from 0 to 61.
.El
.Pp
If the
.Dq CC
and
.Dq YY
letter pairs are not specified, the values default to the current
year.
If the
.Dq SS
letter pair is not specified, the value defaults to 0.
.El
.Pp
The
.Nm touch
utility exits 0 on success, and >0 if an error occurs.
.Sh SEE ALSO
.Xr utimes 2
.Sh COMPATIBILITY
The obsolescent form of
.Nm touch ,
where a time format is specified as the first argument, is supported.
When no
.Fl r
or
.Fl t
option is specified, there are at least two arguments, and the first
argument is a string of digits either eight or ten characters in length,
the first argument is interprested as a time specification of the form
.Dq MMDDhhmm[YY] .
.Pp
The
.Dq MM ,
.Dq DD ,
.Dq hh
and
.Dq mm
letter pairs are treated as their counterparts specified to the
.Fl t
option.
If the
.Dq YY
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 HISTORY
A
.Nm
.Nm touch
command appeared in
.At v7 .
This command is
.Ud .
.Sh STANDARDS
The
.Nm touch
function is expected to be a superset of the
.St -p1003.2
specification.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1988 Regents of the University of California.
* Copyright (c) 1993 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -33,129 +33,318 @@
#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1988 Regents of the University of California.\n\
"@(#) Copyright (c) 1993 Regents of the University of California.\n\
All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)touch.c 4.8 (Berkeley) 6/1/90";
static char sccsid[] = "@(#)touch.c 5.5 (Berkeley) 3/7/93";
#endif /* not lint */
/*
* Attempt to set the modify date of a file to the current date. If the
* file exists, read and write its first character. If the file doesn't
* exist, create it, unless -c option prevents it. If the file is read-only,
* -f forces chmod'ing and touch'ing.
*/
#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int dontcreate; /* set if -c option */
static int force; /* set if -f option */
int rw __P((char *, struct stat *, int));
void stime_arg1 __P((char *, struct timeval *));
void stime_arg2 __P((char *, int, struct timeval *));
void stime_file __P((char *, struct timeval *));
void usage __P((void));
/*temporary we aren't 4.4 portability hack*/
#define TIMET_TO_TIMEVAL(timevalp, tp) (timevalp)->tv_sec = *(tp);
int
main(argc, argv)
int argc;
char **argv;
char *argv[];
{
extern int optind;
int ch, retval;
struct stat sb;
struct timeval tv[2];
int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
char *p;
dontcreate = force = retval = 0;
while ((ch = getopt(argc, argv, "cf")) != EOF)
switch((char)ch) {
aflag = cflag = fflag = mflag = timeset = 0;
if (gettimeofday(&tv[0], NULL))
err(1, "gettimeofday");
while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF)
switch(ch) {
case 'a':
aflag = 1;
break;
case 'c':
dontcreate = 1;
cflag = 1;
break;
case 'f':
force = 1;
fflag = 1;
break;
case 'm':
mflag = 1;
break;
case 'r':
timeset = 1;
stime_file(optarg, tv);
break;
case 't':
timeset = 1;
stime_arg1(optarg, tv);
break;
case '?':
default:
usage();
}
if (!*(argv += optind))
argc -= optind;
argv += optind;
/* Default is both -a and -m. */
if (aflag == 0 && mflag == 0)
aflag = mflag = 1;
/*
* 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.
*/
if (!timeset && argc > 1) {
(void)strtol(argv[0], &p, 10);
len = p - argv[0];
if (*p == '\0' && (len == 8 || len == 10)) {
timeset = 1;
stime_arg2(argv[0], len == 10, tv);
}
}
/* Otherwise use the current time of day. */
if (!timeset)
tv[1] = tv[0];
if (*argv == NULL)
usage();
do {
retval |= touch(*argv);
} while (*++argv);
exit(retval);
for (rval = 0; *argv; ++argv) {
/* See if the file exists. */
if (stat(*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;
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);*/
TIMET_TO_TIMEVAL(&tv[0], &sb.st_atime);
if (!mflag)
/* TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec);*/
TIMET_TO_TIMEVAL(&tv[1], &sb.st_mtime);
/* Try utimes(2). */
if (!utimes(*argv, tv))
continue;
/* If the user specified a time, nothing else we can do. */
if (timeset) {
rval = 1;
warn("%s", *argv);
}
/*
* System V and POSIX 1003.1 require that a NULL argument
* 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;
/* Try reading/writing. */
if (rw(*argv, &sb, fflag))
rval = 1;
}
exit(rval);
}
touch(filename)
char *filename;
#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
void
stime_arg1(arg, tvp)
char *arg;
struct timeval *tvp;
{
struct stat statbuffer;
if (stat(filename, &statbuffer) == -1) {
if (!dontcreate)
return(readwrite(filename, 0L));
fprintf(stderr, "touch: %s: does not exist\n", filename);
return(1);
struct tm *t;
int yearset;
char *p;
/* Start with the current time. */
if ((t = localtime(&tvp[0].tv_sec)) == NULL)
err(1, "localtime");
/* [[CC]YY]MMDDhhmm[.SS] */
if ((p = strchr(arg, '.')) == NULL)
t->tm_sec = 0; /* Seconds defaults to 0. */
else {
if (strlen(p + 1) != 2)
goto terr;
*p++ = '\0';
t->tm_sec = ATOI2(p);
}
if ((statbuffer.st_mode & S_IFMT) != S_IFREG) {
fprintf(stderr, "touch: %s: can only touch regular files\n",
filename);
return(1);
}
if (!access(filename, R_OK | W_OK))
return(readwrite(filename,statbuffer.st_size));
if (force) {
int retval;
if (chmod(filename, 0666)) {
fprintf(stderr, "touch: %s: couldn't chmod: ",
filename);
perror((char *)NULL);
return(1);
yearset = 0;
switch(strlen(arg)) {
case 12: /* CCYYMMDDhhmm */
t->tm_year = ATOI2(arg);
t->tm_year *= 1000;
yearset = 1;
/* FALLTHOUGH */
case 10: /* YYMMDDhhmm */
if (yearset) {
yearset = ATOI2(arg);
t->tm_year += yearset;
} else {
yearset = ATOI2(arg);
if (yearset < 69)
t->tm_year = yearset + 2000;
else
t->tm_year = yearset + 1900;
}
retval = readwrite(filename, statbuffer.st_size);
if (chmod(filename, statbuffer.st_mode)) {
fprintf(stderr, "touch: %s: couldn't chmod back: ",
filename);
perror((char *)NULL);
return(1);
}
return(retval);
t->tm_year -= 1900; /* Convert to UNIX time. */
/* FALLTHROUGH */
case 8: /* MMDDhhmm */
t->tm_mon = ATOI2(arg);
--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);
break;
default:
goto terr;
}
fprintf(stderr, "touch: %s: cannot touch\n", filename);
return(1);
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: [[CC]YY]MMDDhhmm[.SS]");
tvp[0].tv_usec = tvp[1].tv_usec = 0;
}
readwrite(filename, size)
char *filename;
off_t size;
void
stime_arg2(arg, year, tvp)
char *arg;
int year;
struct timeval *tvp;
{
int filedescriptor;
char first;
off_t lseek();
struct tm *t;
/* Start with the current time. */
if ((t = localtime(&tvp[0].tv_sec)) == NULL)
err(1, "localtime");
if (size) {
filedescriptor = open(filename, O_RDWR, 0);
if (filedescriptor == -1) {
error: fprintf(stderr, "touch: %s: ", filename);
perror((char *)NULL);
return(1);
}
if (read(filedescriptor, &first, 1) != 1)
goto error;
if (lseek(filedescriptor, 0L, 0) == -1)
goto error;
if (write(filedescriptor, &first, 1) != 1)
goto error;
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)
t->tm_year = ATOI2(arg);
t->tm_isdst = -1; /* Figure out DST. */
tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
if (tvp[0].tv_sec == -1)
errx(1,
"out of range or illegal time specification: MMDDhhmm[yy]");
tvp[0].tv_usec = tvp[1].tv_usec = 0;
}
void
stime_file(fname, tvp)
char *fname;
struct timeval *tvp;
{
struct stat sb;
if (stat(fname, &sb))
err(1, "%s", fname);
/* TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec);*/
TIMET_TO_TIMEVAL(tvp, &sb.st_atime);
/* TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec);*/
TIMET_TO_TIMEVAL(tvp + 1, &sb.st_mtime);
}
int
rw(fname, sbp, force)
char *fname;
struct stat *sbp;
int force;
{
int fd, needed_chmod, rval;
u_char byte;
/* Try regular files and directories. */
if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) {
warnx("%s: %s", fname, strerror(EFTYPE));
return (1);
}
needed_chmod = rval = 0;
if ((fd = open(fname, O_RDWR, 0)) == -1) {
if (!force || chmod(fname, DEFFILEMODE))
goto err;
if ((fd = open(fname, O_RDWR, 0)) == -1)
goto err;
needed_chmod = 1;
}
if (sbp->st_size != 0) {
if (read(fd, &byte, sizeof(byte)) != sizeof(byte))
goto err;
if (lseek(fd, (off_t)0, SEEK_SET) == -1)
goto err;
if (write(fd, &byte, sizeof(byte)) != sizeof(byte))
goto err;
} else {
filedescriptor = creat(filename, 0666);
if (filedescriptor == -1)
goto error;
if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
err: rval = 1;
warn("%s", fname);
} else if (ftruncate(fd, (off_t)0)) {
rval = 1;
warn("%s: file modified", fname);
}
}
if (close(filedescriptor) == -1)
goto error;
return(0);
if (close(fd) && rval != 1) {
rval = 1;
warn("%s", fname);
}
if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) {
rval = 1;
warn("%s: permissions modified", fname);
}
return (rval);
}
__dead void
usage()
{
fprintf(stderr, "usage: touch [-cf] file ...\n");
(void)fprintf(stderr,
"usage: touch [-acfm] [-r file] [-t time] file ...\n");
exit(1);
}