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 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" 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 .Dt TOUCH 1
.Os .Os
.Sh NAME .Sh NAME
.Nm touch .Nm touch
.Nd update date last modified of a file .Nd change file access and modification times
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm touch .Nm touch
.Op Fl c .Op Fl acfm
.Op Fl f .Op Fl r Ar file
.Op Fl t [[CC]YY]MMDDhhmm[.SS]
.Ar file ... .Ar file ...
.Sh DESCRIPTION .Sh DESCRIPTION
The The
.Nm touch .Nm touch
utility utility sets the modification and access times of files to the
changes the modification and or access times current time of day.
of the given If the file doesn't exist, it is created with default permissions.
.Ar file .
.Pp .Pp
Available options: The following options are available:
.Bl -tag -width Ds .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 .It Fl c
Do not create a specified file if it does not exist. Do not create the file if it does not exist.
Do not write any diagnostic messages concerning this The
condition.
.It Fl f
Attempt to force the
.Nm touch .Nm touch
in spite of read and write permissions on a utility does not treat this as an error.
.Ar file . 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 .Sh SEE ALSO
.Xr utimes 2 .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 .Sh HISTORY
A A
.Nm .Nm touch
command appeared in command appeared in
.At v7 . .At v7 .
This command is .Sh STANDARDS
.Ud . 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. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -33,129 +33,318 @@
#ifndef lint #ifndef lint
char copyright[] = 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"; All rights reserved.\n";
#endif /* not lint */ #endif /* not lint */
#ifndef 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 */ #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/types.h>
#include <sys/file.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static int dontcreate; /* set if -c option */ int rw __P((char *, struct stat *, int));
static int force; /* set if -f option */ 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) main(argc, argv)
int argc; int argc;
char **argv; char *argv[];
{ {
extern int optind; struct stat sb;
int ch, retval; struct timeval tv[2];
int aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset;
char *p;
dontcreate = force = retval = 0; aflag = cflag = fflag = mflag = timeset = 0;
while ((ch = getopt(argc, argv, "cf")) != EOF) if (gettimeofday(&tv[0], NULL))
switch((char)ch) { err(1, "gettimeofday");
while ((ch = getopt(argc, argv, "acfmr:t:")) != EOF)
switch(ch) {
case 'a':
aflag = 1;
break;
case 'c': case 'c':
dontcreate = 1; cflag = 1;
break; break;
case 'f': 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; break;
case '?': case '?':
default: default:
usage(); 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(); usage();
do {
retval |= touch(*argv); for (rval = 0; *argv; ++argv) {
} while (*++argv); /* See if the file exists. */
exit(retval); 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) #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2;
char *filename;
void
stime_arg1(arg, tvp)
char *arg;
struct timeval *tvp;
{ {
struct stat statbuffer; struct tm *t;
int yearset;
if (stat(filename, &statbuffer) == -1) { char *p;
if (!dontcreate) /* Start with the current time. */
return(readwrite(filename, 0L)); if ((t = localtime(&tvp[0].tv_sec)) == NULL)
fprintf(stderr, "touch: %s: does not exist\n", filename); err(1, "localtime");
return(1); /* [[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", yearset = 0;
filename); switch(strlen(arg)) {
return(1); case 12: /* CCYYMMDDhhmm */
} t->tm_year = ATOI2(arg);
if (!access(filename, R_OK | W_OK)) t->tm_year *= 1000;
return(readwrite(filename,statbuffer.st_size)); yearset = 1;
if (force) { /* FALLTHOUGH */
int retval; case 10: /* YYMMDDhhmm */
if (yearset) {
if (chmod(filename, 0666)) { yearset = ATOI2(arg);
fprintf(stderr, "touch: %s: couldn't chmod: ", t->tm_year += yearset;
filename); } else {
perror((char *)NULL); yearset = ATOI2(arg);
return(1); if (yearset < 69)
t->tm_year = yearset + 2000;
else
t->tm_year = yearset + 1900;
} }
retval = readwrite(filename, statbuffer.st_size); t->tm_year -= 1900; /* Convert to UNIX time. */
if (chmod(filename, statbuffer.st_mode)) { /* FALLTHROUGH */
fprintf(stderr, "touch: %s: couldn't chmod back: ", case 8: /* MMDDhhmm */
filename); t->tm_mon = ATOI2(arg);
perror((char *)NULL); --t->tm_mon; /* Convert from 01-12 to 00-11 */
return(1); t->tm_mday = ATOI2(arg);
} t->tm_hour = ATOI2(arg);
return(retval); 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) void
char *filename; stime_arg2(arg, year, tvp)
off_t size; char *arg;
int year;
struct timeval *tvp;
{ {
int filedescriptor; struct tm *t;
char first; /* Start with the current time. */
off_t lseek(); if ((t = localtime(&tvp[0].tv_sec)) == NULL)
err(1, "localtime");
if (size) { t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */
filedescriptor = open(filename, O_RDWR, 0); --t->tm_mon; /* Convert from 01-12 to 00-11 */
if (filedescriptor == -1) { t->tm_mday = ATOI2(arg);
error: fprintf(stderr, "touch: %s: ", filename); t->tm_hour = ATOI2(arg);
perror((char *)NULL); t->tm_min = ATOI2(arg);
return(1); if (year)
} t->tm_year = ATOI2(arg);
if (read(filedescriptor, &first, 1) != 1)
goto error; t->tm_isdst = -1; /* Figure out DST. */
if (lseek(filedescriptor, 0L, 0) == -1) tvp[0].tv_sec = tvp[1].tv_sec = mktime(t);
goto error; if (tvp[0].tv_sec == -1)
if (write(filedescriptor, &first, 1) != 1) errx(1,
goto error; "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 { } else {
filedescriptor = creat(filename, 0666); if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) {
if (filedescriptor == -1) err: rval = 1;
goto error; warn("%s", fname);
} else if (ftruncate(fd, (off_t)0)) {
rval = 1;
warn("%s: file modified", fname);
}
} }
if (close(filedescriptor) == -1)
goto error; if (close(fd) && rval != 1) {
return(0); 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() usage()
{ {
fprintf(stderr, "usage: touch [-cf] file ...\n"); (void)fprintf(stderr,
"usage: touch [-acfm] [-r file] [-t time] file ...\n");
exit(1); exit(1);
} }