From 5449f489bbfc247dbe1f7ddfc6d7ff27298172b0 Mon Sep 17 00:00:00 2001 From: des Date: Tue, 4 Nov 2003 13:09:16 +0000 Subject: 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 --- usr.bin/sed/extern.h | 3 +- usr.bin/sed/main.c | 132 +++++++++++++++++++++++--------------------------- usr.bin/sed/process.c | 40 +++++++-------- 3 files changed, 84 insertions(+), 91 deletions(-) (limited to 'usr.bin/sed') diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h index df49f7e..8312d6a 100644 --- a/usr.bin/sed/extern.h +++ b/usr.bin/sed/extern.h @@ -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 *); diff --git a/usr.bin/sed/main.c b/usr.bin/sed/main.c index 13f9de4..216cd42 100644 --- a/usr.bin/sed/main.c +++ b/usr.bin/sed/main.c @@ -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); } + fname = files->fname; if (inplace != NULL) { - if (inplace_edit(&files->fname) == -1) - continue; + 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"; } - fname = files->fname; - if ((curfile = fopen(fname, "r")) == NULL) { + 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); } diff --git a/usr.bin/sed/process.c b/usr.bin/sed/process.c index 5baed7a..772fd0b 100644 --- a/usr.bin/sed/process.c +++ b/usr.bin/sed/process.c @@ -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 -- cgit v1.1