diff options
author | des <des@FreeBSD.org> | 2003-11-04 13:09:16 +0000 |
---|---|---|
committer | des <des@FreeBSD.org> | 2003-11-04 13:09:16 +0000 |
commit | 5449f489bbfc247dbe1f7ddfc6d7ff27298172b0 (patch) | |
tree | fddafe53ce6d23db8b371f7ca372c795650eca05 /usr.bin/sed/main.c | |
parent | 52ef645f2cbf701ab2e6d2a204e7ad345ac8f21d (diff) | |
download | FreeBSD-src-5449f489bbfc247dbe1f7ddfc6d7ff27298172b0.zip FreeBSD-src-5449f489bbfc247dbe1f7ddfc6d7ff27298172b0.tar.gz |
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
Diffstat (limited to 'usr.bin/sed/main.c')
-rw-r--r-- | usr.bin/sed/main.c | 132 |
1 files changed, 61 insertions, 71 deletions
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); } |