Reimplement in-place editing in a slightly less disgusting manner. Also,

make an effort to preserve the ownership and mode of the file we are
editing.

Sponsored by:	Registrar AS
This commit is contained in:
Dag-Erling Smørgrav 2003-11-04 13:09:16 +00:00
parent 95755cc99b
commit f54cda1467
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=122049
3 changed files with 85 additions and 92 deletions

View File

@ -45,7 +45,8 @@ extern size_t maxnsub;
extern u_long linenum;
extern int appendnum;
extern int aflag, eflag, nflag;
extern const char *fname;
extern const char *fname, *outfname;
extern FILE *infile, *outfile;
extern int rflags; /* regex flags to use */
void cfclose(struct s_command *, struct s_command *);

View File

@ -96,7 +96,8 @@ struct s_flist {
*/
static struct s_flist *files, **fl_nextp = &files;
static FILE *curfile; /* Current open file */
FILE *infile; /* Current input file */
FILE *outfile; /* Current output file */
int aflag, eflag, nflag;
int rflags = 0;
@ -107,6 +108,9 @@ static int rval; /* Exit status */
* units, but span across input files.
*/
const char *fname; /* File name. */
const char *outfname; /* Output file name */
static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */
static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */
const char *inplace; /* Inplace edit file extension. */
u_long linenum;
@ -292,66 +296,102 @@ cu_fgets(char *buf, int n, int *more)
int
mf_fgets(SPACE *sp, enum e_spflag spflag)
{
struct stat sb;
size_t len;
char *p;
int c;
static int firstfile;
if (curfile == NULL) {
if (infile == NULL) {
/* stdin? */
if (files->fname == NULL) {
if (inplace != NULL)
errx(1, "-i may not be used with stdin");
curfile = stdin;
infile = stdin;
fname = "stdin";
outfile = stdout;
outfname = "stdout";
}
firstfile = 1;
}
for (;;) {
if (curfile != NULL && (c = getc(curfile)) != EOF) {
(void)ungetc(c, curfile);
if (infile != NULL && (c = getc(infile)) != EOF) {
(void)ungetc(c, infile);
break;
}
/* If we are here then either eof or no files are open yet */
if (curfile == stdin) {
if (infile == stdin) {
sp->len = 0;
return (0);
}
if (curfile != NULL) {
fclose(curfile);
if (infile != NULL) {
fclose(infile);
if (*oldfname != '\0' &&
rename(fname, oldfname) != 0) {
warn("rename()");
unlink(tmpfname);
exit(1);
}
if (*tmpfname != '\0')
rename(tmpfname, fname);
*tmpfname = *oldfname = '\0';
outfname = NULL;
}
if (firstfile == 0) {
if (firstfile == 0)
files = files->next;
} else
else
firstfile = 0;
if (files == NULL) {
sp->len = 0;
return (0);
}
if (inplace != NULL) {
if (inplace_edit(&files->fname) == -1)
continue;
}
fname = files->fname;
if ((curfile = fopen(fname, "r")) == NULL) {
if (inplace != NULL) {
if (lstat(fname, &sb) != 0)
err(1, "%s", fname);
if (!(sb.st_mode & S_IFREG))
errx(1, "%s: %s %s", fname,
"in-place editing only",
"works for regular files");
if (*inplace != '\0') {
strlcpy(oldfname, fname,
sizeof(oldfname));
len = strlcat(oldfname, inplace,
sizeof(oldfname));
if (len > sizeof(oldfname))
errx(1, "%s: name too long", fname);
}
len = snprintf(tmpfname, sizeof(tmpfname),
".!%ld!%s", (long)getpid(), fname);
if (len >= sizeof(tmpfname))
errx(1, "%s: name too long", fname);
unlink(tmpfname);
if ((outfile = fopen(tmpfname, "w")) == NULL)
err(1, "%s", fname);
fchown(fileno(outfile), sb.st_uid, sb.st_gid);
fchmod(fileno(outfile), sb.st_mode & ALLPERMS);
outfname = tmpfname;
} else {
outfile = stdout;
outfname = "stdout";
}
if ((infile = fopen(fname, "r")) == NULL) {
warn("%s", fname);
rval = 1;
continue;
}
if (inplace != NULL && *inplace == '\0')
unlink(fname);
}
/*
* We are here only when curfile is open and we still have something
* We are here only when infile is open and we still have something
* to read from it.
*
* Use fgetln so that we can handle essentially infinite input data.
* Can't use the pointer into the stdio buffer as the process space
* because the ungetc() can cause it to move.
*/
p = fgetln(curfile, &len);
if (ferror(curfile))
p = fgetln(infile, &len);
if (ferror(infile))
errx(1, "%s: %s", fname, strerror(errno ? errno : EIO));
if (len != 0 && p[len - 1] == '\n')
len--;
@ -395,56 +435,6 @@ add_file(char *s)
fl_nextp = &fp->next;
}
/*
* Modify a pointer to a filename for inplace editing and reopen stdout
*/
static int
inplace_edit(char **filename)
{
struct stat orig;
char backup[MAXPATHLEN];
if (lstat(*filename, &orig) == -1)
err(1, "lstat");
if ((orig.st_mode & S_IFREG) == 0) {
warnx("cannot inplace edit %s, not a regular file", *filename);
return (-1);
}
if (*inplace == '\0') {
/*
* This is a bit of a hack: we use mkstemp() to avoid the
* mktemp() link-time warning, although mktemp() would fit in
* this context much better. We're only interested in getting
* a name for use in the rename(); there aren't any security
* issues here that don't already exist in relation to the
* original file and its directory.
*/
int fd;
strlcpy(backup, *filename, sizeof(backup));
strlcat(backup, ".XXXXXXXXXX", sizeof(backup));
fd = mkstemp(backup);
if (fd == -1)
errx(1, "could not create backup of %s", *filename);
else
close(fd);
} else {
strlcpy(backup, *filename, sizeof(backup));
strlcat(backup, inplace, sizeof(backup));
}
if (rename(*filename, backup) == -1)
err(1, "rename(\"%s\", \"%s\")", *filename, backup);
if (freopen(*filename, "w", stdout) == NULL)
err(1, "open(\"%s\")", *filename);
if (fchmod(fileno(stdout), orig.st_mode) == -1)
err(1, "chmod(\"%s\")", *filename);
*filename = strdup(backup);
if (*filename == NULL)
err(1, "malloc");
return (0);
}
int
lastline(void)
{
@ -452,8 +442,8 @@ lastline(void)
if (files->next != NULL)
return (0);
if ((ch = getc(curfile)) == EOF)
if ((ch = getc(infile)) == EOF)
return (1);
ungetc(ch, curfile);
ungetc(ch, infile);
return (0);
}

View File

@ -86,7 +86,7 @@ static regex_t *defpreg;
size_t maxnsub;
regmatch_t *match;
#define OUT(s) { fwrite(s, sizeof(u_char), psl, stdout); putchar('\n'); }
#define OUT(s) { fwrite(s, sizeof(u_char), psl, outfile); fputc('\n', outfile); }
void
process(void)
@ -130,7 +130,7 @@ process(void)
pd = 1;
psl = 0;
if (cp->a2 == NULL || lastaddr)
(void)printf("%s", cp->t);
(void)fprintf(outfile, "%s", cp->t);
break;
case 'd':
pd = 1;
@ -162,7 +162,7 @@ process(void)
cspace(&HS, ps, psl, 0);
break;
case 'i':
(void)printf("%s", cp->t);
(void)fprintf(outfile, "%s", cp->t);
break;
case 'l':
lputs(ps);
@ -252,7 +252,7 @@ process(void)
case '}':
break;
case '=':
(void)printf("%lu\n", linenum);
(void)fprintf(outfile, "%lu\n", linenum);
}
cp = cp->next;
} /* for all cp */
@ -438,7 +438,7 @@ flush_appends(void)
switch (appends[i].type) {
case AP_STRING:
fwrite(appends[i].s, sizeof(char), appends[i].len,
stdout);
outfile);
break;
case AP_FILE:
/*
@ -452,12 +452,12 @@ flush_appends(void)
if ((f = fopen(appends[i].s, "r")) == NULL)
break;
while ((count = fread(buf, sizeof(char), sizeof(buf), f)))
(void)fwrite(buf, sizeof(char), count, stdout);
(void)fwrite(buf, sizeof(char), count, outfile);
(void)fclose(f);
break;
}
if (ferror(stdout))
errx(1, "stdout: %s", strerror(errno ? errno : EIO));
if (ferror(outfile))
errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
appendx = sdone = 0;
}
@ -470,6 +470,8 @@ lputs(char *s)
struct winsize win;
static int termwidth = -1;
if (outfile != stdout)
termwidth = 60;
if (termwidth == -1) {
if ((p = getenv("COLUMNS")) && *p != '\0')
termwidth = atoi(p);
@ -482,32 +484,32 @@ lputs(char *s)
for (count = 0; *s; ++s) {
if (count + 5 >= termwidth) {
(void)printf("\\\n");
(void)fprintf(outfile, "\\\n");
count = 0;
}
if (isprint((unsigned char)*s) && *s != '\\') {
(void)putchar(*s);
(void)fputc(*s, outfile);
count++;
} else if (*s == '\n') {
(void)putchar('$');
(void)putchar('\n');
(void)fputc('$', outfile);
(void)fputc('\n', outfile);
count = 0;
} else {
escapes = "\\\a\b\f\r\t\v";
(void)putchar('\\');
(void)fputc('\\', outfile);
if ((p = strchr(escapes, *s))) {
(void)putchar("\\abfrtv"[p - escapes]);
(void)fputc("\\abfrtv"[p - escapes], outfile);
count += 2;
} else {
(void)printf("%03o", *(u_char *)s);
(void)fprintf(outfile, "%03o", *(u_char *)s);
count += 4;
}
}
}
(void)putchar('$');
(void)putchar('\n');
if (ferror(stdout))
errx(1, "stdout: %s", strerror(errno ? errno : EIO));
(void)fputc('$', outfile);
(void)fputc('\n', outfile);
if (ferror(outfile))
errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO));
}
static __inline int