diff --git a/bash-completion-patchutils b/bash-completion-patchutils
index 73e32aed..a344dc51 100644
--- a/bash-completion-patchutils
+++ b/bash-completion-patchutils
@@ -7,7 +7,7 @@ _patchutils_common_opts() {
}
_patchutils_filter_opts() {
- echo "--exclude -x --exclude-from-file -X --include -i --include-from-file -I --hunks -# --lines --files -F --annotate --as-numbered-lines --format --remove-timestamps --clean --decompress -z --strip-match -p --strip --addprefix --addoldprefix --addnewprefix"
+ echo "--exclude -x --exclude-from-file -X --include -i --include-from-file -I --hunks -# --lines --files -F --annotate --as-numbered-lines --format --remove-timestamps --clean --in-place --decompress -z --strip-match -p --strip --addprefix --addoldprefix --addnewprefix"
}
_patchutils_list_opts() {
@@ -214,7 +214,7 @@ _rediff() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
- opts="--help --version"
+ opts="--help --version --in-place"
if [[ ${cur} == -* ]]; then
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
diff --git a/doc/patchutils.xml b/doc/patchutils.xml
index 9784160b..9af56e41 100644
--- a/doc/patchutils.xml
+++ b/doc/patchutils.xml
@@ -670,6 +670,7 @@
--verbose
--clean
+ --in-place
-z
--decompress
@@ -925,6 +926,15 @@
+
+
+
+ Write output to the original input files instead of
+ standard output. This allows filtering multiple patch files
+ without manual redirection loops.
+
+
+
,
@@ -2009,12 +2019,14 @@ will pipe patch of file #2 to vim - -R
rediff
+ --in-place
ORIGINAL
EDITED
rediff
+ --in-place
EDITED
@@ -2097,6 +2109,15 @@ will pipe patch of file #2 to vim - -R
+
+
+
+ Write output to the original edited file instead of
+ standard output. When used with two arguments, the result
+ is written back to the EDITED file.
+
+
+
diff --git a/src/filterdiff.c b/src/filterdiff.c
index de2935d4..3ac66ca5 100644
--- a/src/filterdiff.c
+++ b/src/filterdiff.c
@@ -233,7 +233,7 @@ file_matches (void)
{
int f = 0;
struct range *r;
-
+
// See if the file range list includes this file. -1UL is a
// wildcard.
for (r = files; r; r = r->next)
@@ -1231,6 +1231,8 @@ const char * syntax_str =
" don't show timestamps from output (filterdiff, patchview, grepdiff)\n"
" --clean (filterdiff)\n"
" remove all comments (non-diff lines) from output (filterdiff)\n"
+" --in-place (filterdiff)\n"
+" write output to the original input files (filterdiff)\n"
" -z, --decompress\n"
" decompress .gz and .bz2 files\n"
" -n, --line-number (lsdiff, grepdiff)\n"
@@ -1454,6 +1456,7 @@ int main (int argc, char *argv[])
char format = '\0';
int regex_file_specified = 0;
int have_switches = 0;
+ int inplace_mode = 0;
setlocale (LC_TIME, "C");
determine_mode_from_name (argv[0]);
@@ -1495,6 +1498,7 @@ int main (int argc, char *argv[])
{"extended-regexp", 0, 0, 'E'},
{"empty-files-as-removed", 0, 0, 'E'},
{"file", 1, 0, 'f'},
+ {"in-place", 0, 0, 1000 + 'w'},
{0, 0, 0, 0}
};
char *end;
@@ -1502,7 +1506,7 @@ int main (int argc, char *argv[])
long_options, NULL);
if (c == -1)
break;
-
+
have_switches = 1;
switch (c) {
case 'g':
@@ -1665,6 +1669,9 @@ int main (int argc, char *argv[])
case 1000 + 'c':
clean_comments = 1;
break;
+ case 1000 + 'w':
+ inplace_mode = 1;
+ break;
default:
syntax(1);
}
@@ -1708,6 +1715,14 @@ int main (int argc, char *argv[])
error (EXIT_FAILURE, 0, "can't use --verbose and "
"--clean options simultaneously");
+ if (inplace_mode && unzip)
+ error (EXIT_FAILURE, 0,
+ "--in-place and --decompress are mutually exclusive");
+
+ if (inplace_mode && mode != mode_filter)
+ error (EXIT_FAILURE, 0,
+ "--in-place only applies to filter mode");
+
if (mode == mode_grep && !regex_file_specified) {
int err;
@@ -1739,6 +1754,10 @@ int main (int argc, char *argv[])
print_patchnames = 0;
}
+ if (inplace_mode && optind == argc)
+ error (EXIT_FAILURE, 0,
+ "--in-place cannot be used with standard input");
+
if (optind == argc) {
f = convert_format (stdin, format);
filterdiff (f, "(standard input)");
@@ -1752,7 +1771,28 @@ int main (int argc, char *argv[])
}
f = convert_format (f, format);
- filterdiff (f, argv[i]);
+
+ if (inplace_mode) {
+ /* Redirect stdout to temporary file for in-place processing */
+ FILE *temp_output = xtmpfile();
+ FILE *old_stdout = stdout;
+ stdout = temp_output;
+
+ filterdiff (f, argv[i]);
+
+ /* Restore stdout */
+ stdout = old_stdout;
+
+ /* Write temp file contents back to original file */
+ if (write_file_inplace(argv[i], temp_output) != 0) {
+ error (EXIT_FAILURE, errno, "failed to write %s", argv[i]);
+ }
+
+ fclose (temp_output);
+ } else {
+ filterdiff (f, argv[i]);
+ }
+
fclose (f);
}
}
diff --git a/src/interdiff.c b/src/interdiff.c
index 14ed824f..31fcc342 100644
--- a/src/interdiff.c
+++ b/src/interdiff.c
@@ -2095,13 +2095,13 @@ interdiff (FILE *p1, FILE *p2, const char *patch1, const char *patch2)
rewind (flip2);
if (flipdiff_inplace) {
- FILE *pp1 = xopen (patch1, "wb");
- FILE *pp2 = xopen (patch2, "wb");
-
- copy (flip1, pp2);
- copy (flip2, pp1);
- fclose (pp1);
- fclose (pp2);
+ /* Use atomic in-place writing for safety */
+ if (write_file_inplace(patch2, flip1) != 0) {
+ error (EXIT_FAILURE, errno, "failed to write %s", patch2);
+ }
+ if (write_file_inplace(patch1, flip2) != 0) {
+ error (EXIT_FAILURE, errno, "failed to write %s", patch1);
+ }
} else {
copy (flip1, stdout);
puts ("\n=== 8< === cut here === 8< ===\n");
diff --git a/src/rediff.c b/src/rediff.c
index d0b7126b..eacf9469 100644
--- a/src/rediff.c
+++ b/src/rediff.c
@@ -57,7 +57,7 @@ struct file_info
int info_pending;
};
-struct hunk
+struct hunk
{
fpos_t filepos;
struct file_info *info;
@@ -938,7 +938,7 @@ static int rediff (const char *original, const char *edited, FILE *out)
if (getline (&line, &linelen, m) == -1)
break;
}
-
+
if (feof (m))
break;
@@ -1023,8 +1023,8 @@ static int rediff (const char *original, const char *edited, FILE *out)
return 0;
}
-static char * syntax_str = "usage: %s ORIGINAL EDITED\n"
- " %s EDITED\n";
+static char * syntax_str = "usage: %s [--in-place] ORIGINAL EDITED\n"
+ " %s [--in-place] EDITED\n";
NORETURN
static void syntax (int err)
@@ -1037,18 +1037,20 @@ int main (int argc, char *argv[])
{
/* name to use in error messages */
set_progname ("rediff");
-
+ int inplace_mode = 0;
+
while (1) {
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"version", 0, 0, 'v'},
+ {"in-place", 0, 0, 1000 + 'w'},
{0, 0, 0, 0}
};
int c = getopt_long (argc, argv, "vh",
long_options, NULL);
if (c == -1)
break;
-
+
switch (c) {
case 'v':
printf("rediff - patchutils version %s\n", VERSION);
@@ -1056,45 +1058,68 @@ int main (int argc, char *argv[])
case 'h':
syntax (0);
break;
+ case 1000 + 'w':
+ inplace_mode = 1;
+ break;
default:
syntax(1);
}
-
+
}
-
+
if (argc - optind < 1)
syntax (1);
if (argc - optind == 1) {
- char *p = xmalloc (strlen (argv[0]) +
- strlen ("recountdiff") + 1);
- char *f;
- char **const new_argv = xmalloc (sizeof (char *) * (argc + 1));
- memcpy (new_argv, argv, sizeof (char *) * (argc + 1));
- new_argv[0] = p;
- strcpy (p, argv[0]);
- f = strrchr (p, '/');
- if (!f)
- f = p;
- else f++;
- strcpy (f, "recountdiff");
- execvp (new_argv[0], new_argv);
- p = xstrdup (new_argv[0]);
- f = strstr (p, "src/");
- if (f) {
- while (*(f + 4)) {
- *f = *(f + 4);
- f++;
- }
- *f = '\0';
+ if (inplace_mode) {
+ /* For single argument with --in-place, we need to handle recountdiff differently */
+ error (EXIT_FAILURE, 0, "--in-place with single argument not yet implemented");
+ } else {
+ char *p = xmalloc (strlen (argv[0]) +
+ strlen ("recountdiff") + 1);
+ char *f;
+ char **const new_argv = xmalloc (sizeof (char *) * (argc + 1));
+ memcpy (new_argv, argv, sizeof (char *) * (argc + 1));
new_argv[0] = p;
- execv (new_argv[0], new_argv);
+ strcpy (p, argv[0]);
+ f = strrchr (p, '/');
+ if (!f)
+ f = p;
+ else f++;
+ strcpy (f, "recountdiff");
+ execvp (new_argv[0], new_argv);
+ p = xstrdup (new_argv[0]);
+ f = strstr (p, "src/");
+ if (f) {
+ while (*(f + 4)) {
+ *f = *(f + 4);
+ f++;
+ }
+ *f = '\0';
+ new_argv[0] = p;
+ execv (new_argv[0], new_argv);
+ }
+ error (EXIT_FAILURE, 0, "couldn't execute recountdiff");
}
- error (EXIT_FAILURE, 0, "couldn't execute recountdiff");
}
if (access (argv[optind + 1], R_OK))
error (EXIT_FAILURE, errno, "can't read edited file");
- return rediff (argv[optind], argv[optind + 1], stdout);
+ if (inplace_mode) {
+ /* For in-place mode, write result back to the edited file */
+ FILE *temp_output = xtmpfile();
+ int result = rediff (argv[optind], argv[optind + 1], temp_output);
+
+ if (result == 0) {
+ if (write_file_inplace(argv[optind + 1], temp_output) != 0) {
+ error (EXIT_FAILURE, errno, "failed to write %s", argv[optind + 1]);
+ }
+ }
+
+ fclose (temp_output);
+ return result;
+ } else {
+ return rediff (argv[optind], argv[optind + 1], stdout);
+ }
}
diff --git a/src/util.c b/src/util.c
index e5df5438..a977137c 100644
--- a/src/util.c
+++ b/src/util.c
@@ -140,7 +140,7 @@ void patlist_add_file(struct patlist **dst, const char *fn)
char *line = NULL;
size_t linelen = 0;
size_t len;
-
+
fd = fopen (fn, "r");
if (NULL == fd)
return;
@@ -158,7 +158,7 @@ void patlist_add_file(struct patlist **dst, const char *fn)
line[len - 1] = '\0';
}
patlist_add (dst, line);
- }
+ }
fclose (fd);
}
@@ -243,11 +243,11 @@ FILE *xopen_unzip (const char *name, const char *mode)
}
if (zprog == NULL)
return xopen_seekable (name, mode);
-
+
buffer = xmalloc (buflen);
fo = xtmpfile();
fi = xpipe(zprog, &pid, "r", (char **) (const char *[]) { zprog, name, NULL });
-
+
while (!feof (fi)) {
size_t count = fread (buffer, 1, buflen, fi);
if (ferror (fi)) {
@@ -256,17 +256,17 @@ FILE *xopen_unzip (const char *name, const char *mode)
}
if (count < 1)
break;
-
+
fwrite (buffer, count, 1, fo);
if (ferror (fo))
error (EXIT_FAILURE, errno, "writing temp file");
any_data = 1;
}
-
+
free (buffer);
fclose (fi);
-
+
waitpid (pid, &status, 0);
if (any_data == 0 && WEXITSTATUS (status) != 0)
{
@@ -285,10 +285,10 @@ FILE * xpipe(const char * cmd, pid_t *pid, const char *mode, char *const argv[])
int fildes[2];
int child;
FILE *res;
-
+
if (!mode || (*mode != 'r' && *mode != 'w'))
error (EXIT_FAILURE, 0, "xpipe: bad mode: %s", mode);
-
+
fflush (NULL);
if (pipe (fildes) == -1)
error (EXIT_FAILURE, errno, "pipe failed");
@@ -320,7 +320,7 @@ FILE * xpipe(const char * cmd, pid_t *pid, const char *mode, char *const argv[])
}
if (pid != NULL)
*pid = child;
-
+
if (*mode == 'r') {
close (fildes[1]);
res = fdopen (fildes[0], "r");
@@ -342,3 +342,74 @@ void set_progname(const char *s)
progname = xstrdup(s);
}
+/* Safe in-place file writing using atomic rename */
+int write_file_inplace(const char *filename, FILE *content)
+{
+ char *temp_name = NULL;
+ FILE *temp_file = NULL;
+ int temp_fd = -1;
+ int ret = -1;
+ size_t filename_len;
+ const char temp_suffix[] = ".tmp.XXXXXX";
+
+ if (!filename || !content) {
+ error(0, 0, "write_file_inplace: invalid arguments");
+ return -1;
+ }
+
+ /* Create temporary filename */
+ filename_len = strlen(filename);
+ temp_name = xmalloc(filename_len + sizeof(temp_suffix));
+ strcpy(temp_name, filename);
+ strcat(temp_name, temp_suffix);
+
+ /* Create temporary file */
+ temp_fd = xmkstemp(temp_name);
+ temp_file = fdopen(temp_fd, "w");
+ if (!temp_file) {
+ error(0, errno, "failed to open temporary file %s", temp_name);
+ close(temp_fd);
+ unlink(temp_name);
+ goto cleanup;
+ }
+
+ /* Copy content to temporary file */
+ rewind(content);
+ while (!feof(content)) {
+ int ch = fgetc(content);
+ if (ch == EOF)
+ break;
+ if (fputc(ch, temp_file) == EOF) {
+ error(0, errno, "failed to write to temporary file %s", temp_name);
+ goto cleanup;
+ }
+ }
+
+ /* Ensure all data is written */
+ if (fflush(temp_file) != 0) {
+ error(0, errno, "failed to flush temporary file %s", temp_name);
+ goto cleanup;
+ }
+
+ fclose(temp_file);
+ temp_file = NULL;
+
+ /* Atomically replace original file */
+ if (rename(temp_name, filename) != 0) {
+ error(0, errno, "failed to rename %s to %s", temp_name, filename);
+ goto cleanup;
+ }
+
+ ret = 0; /* success */
+
+cleanup:
+ if (temp_file)
+ fclose(temp_file);
+ if (temp_name) {
+ if (ret != 0)
+ unlink(temp_name); /* cleanup on failure */
+ free(temp_name);
+ }
+ return ret;
+}
+
diff --git a/src/util.h b/src/util.h
index 7382dea2..a5882a4a 100644
--- a/src/util.h
+++ b/src/util.h
@@ -48,6 +48,9 @@ FILE *xopen_seekable(const char *file, const char *mode);
FILE *xopen_unzip(const char *file, const char *mode);
FILE *xpipe(const char *cmd, pid_t *pid, const char *mode, char *const argv[]);
+/* safe in-place file writing */
+int write_file_inplace(const char *filename, FILE *content);
+
struct patlist;
/* create a new item */