aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDag-Erling Smørgrav <des@FreeBSD.org>2003-11-04 13:09:16 +0000
committerDag-Erling Smørgrav <des@FreeBSD.org>2003-11-04 13:09:16 +0000
commitf54cda1467567b0bb2591162af68bc0769051c8d (patch)
treefddafe53ce6d23db8b371f7ca372c795650eca05
parent95755cc99b8c434aa31eaf96b61217bb8c3a7b88 (diff)
downloadsrc-f54cda1467567b0bb2591162af68bc0769051c8d.tar.gz
src-f54cda1467567b0bb2591162af68bc0769051c8d.zip
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
Notes
Notes: svn path=/head/; revision=122049
-rw-r--r--usr.bin/sed/extern.h3
-rw-r--r--usr.bin/sed/main.c132
-rw-r--r--usr.bin/sed/process.c40
3 files changed, 84 insertions, 91 deletions
diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h
index df49f7e36ee0..8312d6a1bf8c 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 13f9de4dc634..216cd421bdab 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 5baed7a44b0c..772fd0b92893 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