BSD 4_4 release
[unix-history] / usr / src / usr.bin / ex / ex3.7preserve / ex3.7preserve.c
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1991, 1993\n\
The Regents of the University of California. All rights reserved.\n";
#endif /* not lint */
#ifndef lint
static char sccsid[] = "@(#)ex3.7preserve.c 8.1 (Berkeley) 6/9/93";
#endif /* not lint */
#include <sys/param.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <pwd.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "pathnames.h"
/*
* Expreserve - preserve a file in _PATH_PRESERVE.
* Bill Joy UCB November 13, 1977
*
* This routine is very naive - it doesn't remove anything from
* _PATH_PRESERVE... this may mean that we leave stuff there...
* the danger in doing anything with _PATH_PRESERVE is that the
* clock may be screwed up and we may get confused.
*
* We are called in two ways - first from the editor with no arguments
* and the standard input open on the temp file. Second with an
* argument to preserve the entire contents of _PATH_VARTMP (root only).
*
* BUG: should do something about preserving Rx... (register contents)
* temporaries.
*/
#ifdef VMUNIX
#define HBLKS 2
#define LBLKS 900
#else
#define HBLKS 1
#define LBLKS 125
#endif
#define FNSIZE 128
struct header {
time_t Time; /* Time temp file last updated */
int Uid; /* This users identity */
#ifdef VMUNIX
int Flines;
#else
short Flines; /* Number of lines in file */
#endif
char Savedfile[FNSIZE]; /* The current file name */
short Blocks[LBLKS]; /* Blocks where line pointers stashed */
} H;
main(argc, argv)
int argc;
char **argv;
{
extern int optind;
register DIR *tf;
struct dirent *dirent;
struct stat stbuf;
int aflag, ch, err;
char path[MAXPATHLEN];
aflag = 0;
while ((ch = getopt(argc, argv, "a")) != EOF)
switch(ch) {
case 'a':
aflag = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
if (chdir(_PATH_PRESERVE) < 0)
error(_PATH_PRESERVE);
/*
* If only one argument, then preserve the standard input.
*/
if (!aflag)
exit(copyout(NULL) ? 1 : 0);
/*
* If not super user, then can only preserve standard input.
*/
if (getuid()) {
errno = EPERM;
error(NULL);
}
/*
* ... else preserve all the stuff in /var/tmp, removing
* it as we go.
*/
if (!(tf = opendir(_PATH_VARTMP)))
error(_PATH_VARTMP);
err = 0;
while ((dirent = readdir(tf)) != NULL) {
/* Ex temporaries must begin with Ex. */
if (dirent->d_name[0] != 'E' || dirent->d_name[1] != 'x')
continue;
(void)sprintf(path, "%s/%s", _PATH_VARTMP, dirent->d_name);
if (stat(path, &stbuf) || !S_ISREG(stbuf.st_mode))
continue;
/*
* Save the bastard.
*/
err |= copyout(path);
}
(void)closedir(tf);
exit(err);
}
copyout(name)
char *name;
{
struct stat sb;
register int ifd, ofd, nr, nw, off, rval;
char buf[8*1024], fname[20];
/* Open any given file name. */
if (name) {
if ((ifd = open(name, O_RDWR)) < 0)
return(1);
(void)fstat(ifd, &sb);
if (!sb.st_size) {
(void)unlink(name);
return(0);
}
} else {
ifd = STDIN_FILENO;
/* vi hands us an fd, it's not necessarily at the beginning. */
(void)lseek(ifd, 0l, SEEK_SET);
}
if (read(ifd, &H, sizeof(H)) != sizeof(H))
goto format;
/*
* Consistency checks so we don't copy out garbage.
*/
if (H.Flines < 0) {
(void)fprintf(stderr,
"ex3.7preserve: negative number of lines\n");
goto format;
}
if (H.Blocks[0] != HBLKS || H.Blocks[1] != HBLKS+1) {
(void)fprintf(stderr,
"ex3.7preserve: blocks %d %d\n", H.Blocks[0], H.Blocks[1]);
goto format;
}
if (!name && H.Uid != getuid()) {
(void)fprintf(stderr, "ex3.7preserve: wrong user-id\n");
goto format;
}
if (lseek(ifd, 0l, SEEK_SET)) {
(void)fprintf(stderr,
"ex3.7preserve: negative number of lines\n");
format: (void)fprintf(stderr, "ex3.7preserve: %s\n", strerror(EFTYPE));
return (1);
}
/*
* If no name was assigned to the file, then give it the name
* LOST, by putting this in the header. This involves overwriting
* the "input" file.
*/
if (H.Savedfile[0] == 0) {
(void)strcpy(H.Savedfile, "LOST");
(void)write(ifd, &H, sizeof(H));
H.Savedfile[0] = 0;
(void)lseek(ifd, 0l, SEEK_SET);
}
/* File is good. Get a name and create a file for the copy. */
(void)strcpy(fname, "ExXXXXXX");
if ((ofd = mkstemp(fname)) == -1)
return(1);
/* Copy the file. */
rval = 0;
while ((nr = read(ifd, buf, sizeof(buf))) > 0)
for (off = 0; off < nr; nr -= nw, off += nw)
if ((nw = write(ofd, buf + off, nr)) < 0) {
(void)fprintf(stderr,
"ex3.7preserve: tmp file: %s\n",
strerror(errno));
rval = 1;
break;
}
if (nr < 0) {
(void)fprintf(stderr, "ex3.7preserve: %s: %s\n",
name ? name : "stdin", strerror(errno));
rval = 1;
}
if (rval)
(void)unlink(fname);
else {
(void)fchown(ofd, H.Uid, 0);
notify(H.Uid, H.Savedfile, (int)name, H.Time);
if (name)
(void)unlink(name);
}
(void)close(ifd);
(void)close(ofd);
return(rval);
}
/* Notify user uid that his file fname has been saved. */
notify(uid, fname, flag, time)
int uid;
char *fname;
time_t time;
{
struct passwd *pp;
register FILE *mf;
static int reenter;
static char hostname[MAXHOSTNAMELEN], *timestamp;
char cmd[MAXPATHLEN + 50], croak[50];
pp = getpwuid(uid);
if (pp == NULL)
return;
if (!reenter) {
reenter = 1;
(void)gethostname(hostname, sizeof(hostname));
timestamp = ctime(&time);
timestamp[16] = 0; /* blast from seconds on */
}
(void)snprintf(cmd, sizeof(cmd),
"%s -i -t -F \"The Editor\" -f root", _PATH_SENDMAIL);
mf = popen(cmd, "w");
if (mf == NULL)
return;
(void)fprintf(mf,
"Reply-To: root@%s\nFrom: root@%s (The Editor)\nTo: %s\n",
hostname, hostname, pp->pw_name);
/*
* flag says how the editor croaked: "the editor was killed" is
* perhaps still not an ideal error message. Usually, either it
* was forcably terminated or the phone was hung up, but we don't
* know which.
*/
(void)snprintf(croak, sizeof(croak),
flag ? "the system went down"
: "the editor was killed");
if (!fname || !fname[0]) {
fname = "LOST";
fprintf(mf, "Subject: editor saved \"LOST\"\n\n");
fprintf(mf, "You were editing a file without a name\n");
fprintf(mf, "at %s on the machine %s when %s.\n",
timestamp, hostname, croak);
fprintf(mf,
"Since the file had no name, it has been named \"LOST\".\n");
} else {
fprintf(mf, "Subject: editor saved \"%s\"\n\n", fname);
fprintf(mf, "You were editing the file %s\n", fname);
fprintf(mf, "at %s on the machine %s\n", timestamp, hostname);
fprintf(mf, "when %s.\n", croak);
}
fprintf(mf, "\nYou can retrieve most of your changes to this file\n");
fprintf(mf, "using the \"recover\" command of the editor.\n");
fprintf(mf,
"An easy way to do this is to give the command \"vi -r %s\".\n",
fname);
fprintf(mf, "This method also works using \"ex\" and \"edit\".\n\n");
pclose(mf);
}
/*
* people making love
* never exactly the same
* just like a snowflake
*/
error(msg)
char *msg;
{
(void)fprintf(stderr, "ex3.7preserve: ");
if (msg)
(void)fprintf(stderr, "%s: ", msg);
(void)fprintf(stderr, "%s\n", strerror(errno));
exit(1);
}
usage()
{
(void)fprintf(stderr, "usage: ex3.7preserve [-a]\n");
exit(1);
}