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:
parent
52ef645f2c
commit
5449f489bb
@ -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 *);
|
||||
|
@ -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 @@ again:
|
||||
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);
|
||||
}
|
||||
|
@ -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 @@ redirect:
|
||||
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 @@ redirect:
|
||||
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 @@ redirect:
|
||||
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user