Fix one serious bug and one potential problem with in-place editing code:

- original version of code worked incorrectly when more than one
  input files were specified - it was moving the last line from the 1st file
  to be the first line of the 2nd, last line of the 2nd to be the first
  line of the 3rd and so on;

- use mmap()->write() to create temporary file instead of
  malloc()->read()->write(), which was not only slower, but also did not
  bother to free allocated memory once backup file was created, potentially
  leading to memory exhausting when regex is applied to a big file or a large
  number of small ones.
This commit is contained in:
Maxim Sobolev 2002-06-14 01:28:52 +00:00
parent ad1026f912
commit 8701af62ee

View File

@ -48,6 +48,8 @@ static const char copyright[] =
static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94";
#endif
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
@ -300,48 +302,57 @@ mf_fgets(sp, spflag)
size_t len;
char *p;
int c;
static int firstfile;
if (f == NULL)
/* Advance to first non-empty file */
for (;;) {
if (files == NULL) {
lastline = 1;
return (0);
}
if (files->fname == NULL) {
if (inplace != NULL)
errx(1, "-i may not be used with stdin");
f = stdin;
fname = "stdin";
} else {
if (inplace != NULL) {
if (inplace_edit(&files->fname) == -1)
continue;
}
fname = files->fname;
if ((f = fopen(fname, "r")) == NULL) {
warn("%s", fname);
rval = 1;
files = files->next;
continue;
}
if (inplace != NULL && *inplace == '\0')
unlink(fname);
}
if ((c = getc(f)) != EOF) {
(void)ungetc(c, f);
break;
}
(void)fclose(f);
files = files->next;
if (f == NULL) {
/* stdin? */
if (files->fname == NULL) {
if (inplace != NULL)
errx(1, "-i may not be used with stdin");
f = stdin;
fname = "stdin";
}
if (lastline) {
sp->len = 0;
return (0);
firstfile = 1;
}
sp->len = 0;
for (;;) {
if (f != NULL && (c = getc(f)) != EOF) {
(void)ungetc(c, f);
break;
}
/* If we are here then either eof or no files are open yet */
if (f == stdin) {
lastline = 1;
return (0);
}
if (f != NULL) {
fclose(f);
}
if (firstfile == 0) {
files = files->next;
} else
firstfile = 0;
if (files == NULL) {
lastline = 1;
return (0);
}
if (inplace != NULL) {
if (inplace_edit(&files->fname) == -1)
continue;
}
fname = files->fname;
if ((f = fopen(fname, "r")) == NULL) {
warn("%s", fname);
continue;
}
if (inplace != NULL && *inplace == '\0')
unlink(fname);
}
/*
* We are here only when f 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.
@ -352,35 +363,14 @@ mf_fgets(sp, spflag)
cspace(sp, p, len, spflag);
linenum++;
/* Advance to next non-empty file */
while ((c = getc(f)) == EOF) {
(void)fclose(f);
next: files = files->next;
if (files == NULL) {
lastline = 1;
return (1);
}
if (files->fname == NULL) {
if (inplace != NULL)
errx(1, "-i may not be used with stdin");
f = stdin;
fname = "stdin";
if (files->next == NULL) {
if ((c = getc(f)) != EOF) {
(void)ungetc(c, f);
} else {
if (inplace != NULL) {
if (inplace_edit(&files->fname) == -1)
continue;
}
fname = files->fname;
if ((f = fopen(fname, "r")) == NULL) {
warn("%s", fname);
rval = 1;
goto next;
}
if (inplace != NULL && *inplace == '\0')
unlink(fname);
lastline = 1;
}
}
(void)ungetc(c, f);
return (1);
}
@ -449,7 +439,7 @@ inplace_edit(filename)
} else {
strlcpy(backup, *filename, MAXPATHLEN);
strlcat(backup, inplace, MAXPATHLEN);
output = open(backup, O_WRONLY | O_CREAT | O_EXCL);
output = open(backup, O_WRONLY | O_CREAT | O_TRUNC);
if (output == -1)
err(1, "open(%s)", backup);
}
@ -459,13 +449,13 @@ inplace_edit(filename)
err(1, "open(%s)", *filename);
if (fchmod(output, orig.st_mode & ~S_IFMT) == -1)
err(1, "chmod");
buffer = malloc(orig.st_size);
if (buffer == NULL)
errx(1, "malloc failed");
if (read(input, buffer, orig.st_size) == -1)
err(1, "read");
buffer = (char *)mmap(0, orig.st_size, PROT_READ, MAP_SHARED, input, 0);
if (buffer == MAP_FAILED)
err(1, "mmap(%s)", *filename);
if (write(output, buffer, orig.st_size) == -1)
err(1, "write");
err(1, "write(%s)", backup);
if (munmap(buffer, orig.st_size) == -1)
err(1, "munmap(%s)", *filename);
close(input);
close(output);
freopen(*filename, "w", stdout);