Package: moreutils
Version: 0.11
Tags: patch

Attached is a patch against moreutils to add a -p/--preserve option to
sponge that prevents it from modifying the output if the input is
identical. This means that something like this:

        generate-web-page | sponge -p > ~/public_html/index.html

no longer results in the output file looking modified to web browsers.

To implement this, I am afraid I felt it necessary to change a bunch of
other things, too. The code no longer keeps the entire input file in
memory, instead it uses a temporary file. I also added option parsing
with getopt_long.

-- 
Fundamental truth #4: Typing URLs always introduces errors. Always copy
+paste.
=== added file 'check-sponge'
--- /dev/null	
+++ check-sponge	
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Check that ./sponge behaves correctly.
+
+set -e
+
+die()
+{
+    echo "$@" 1>&2
+    exit 1
+}
+
+input=$(mktemp)
+output=$(mktemp)
+
+echo This is a sponge test data file. > "$input"
+
+./sponge < "$input" "$output"
+cmp "$input" "$output" || die "sponge created the wrong output"
+
+TZ=GMT touch -t 197001010000 "$output"
+./sponge -p < "$input" "$output"
+mtime=$(TZ=GMT stat -c %Y "$output")
+[ "$mtime" == 0 ] || die "sponge modified the timestamp"
+
+TZ=GMT touch -t 197001010000 "$output"
+echo different input | ./sponge -p "$output"
+mtime=$(TZ=GMT stat -c %Y "$output")
+[ "$mtime" != 0 ] || die "sponge modified the timestamp"
+
+TZ=GMT touch -t 197001010000 "$output"
+./sponge < "$input" "$output"
+mtime=$(TZ=GMT stat -c %Y "$output")
+[ "$mtime" != 0 ] || die "sponge didn't modify the timestamp"
+
+rm -f "$input" "$output"

=== modified file 'Makefile'
--- Makefile	
+++ Makefile	
@@ -16,8 +16,9 @@
 	mkdir -p $(PREFIX)/usr/share/man/man1
 	install $(MANS) $(PREFIX)/usr/share/man/man1
 
-check: isutf8
+check: isutf8 sponge
 	./check-isutf8
+	./check-sponge
 
 isutf8.1: isutf8.docbook
 	docbook2x-man $<

=== modified file 'sponge.c'
--- sponge.c	
+++ sponge.c	
@@ -1,24 +1,24 @@
 /*
- *  sponge.c - read in all available info from stdin, then output it to
- *  file named on the command line
- *
- *  Copyright ©  2006  Tollef Fog Heen
- *
- *  This program is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU General Public License
- *  version 2 as published by the Free Software Foundation.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
+ * sponge.c - read in all available info from stdin, then output it to
+ * file named on the command line
+ *
+ * Copyright ©  2006  Tollef Fog Heen, Lars Wirzenius
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  * USA
- *
  */
+
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -29,62 +29,176 @@
 #include <errno.h>
 #include <string.h>
 
-void usage() {
-	printf("sponge <file>: suck in all input from stdin and write it to <file>");
-	exit(0);
-}
+#define _GNU_SOURCE
+#include <getopt.h>
+
+
+static struct option options[] = {
+    { "help", no_argument, NULL, 'h' },
+    { "preserve", no_argument, NULL, 'p' },
+};
+
+
+static void usage() {
+    printf("\
+Usage: sponge [-hp] [--help] [--preserve] file\n\
+where\n\
+    -h, --help      Print out this help.\n\
+    -p, --preserve  Preserve output file if contents match.\n\
+");
+    exit(0);
+}
+
+
+static char *xmalloc(size_t n)
+{
+    void *p;
+    
+    p = malloc(n);
+    if (p == NULL) {
+        perror("malloc");
+        exit(1);
+    }
+    return p;
+}
+
+
+static int make_temp(const char *final_name, char **temp, int *fd)
+{
+    static const char suffix[] = "XXXXXX";
+    size_t size;
+    char *template;
+    
+    size = strlen(final_name) + sizeof(suffix);
+    template = xmalloc(size);
+    snprintf(template, size, "%s%s", final_name, suffix);
+    
+    *fd = mkstemp(template);
+    if (*fd == -1) {
+        perror("mkstemp");
+        free(template);
+        return -1;
+    } else {
+        *temp = template;
+        return 0;
+    }
+}
+
+
+static int same_contents(int fd1, const char *filename)
+{
+    int fd2;
+    char buf1[8192];
+    char buf2[sizeof(buf1)];
+    ssize_t n1, n2;
+    int ret;
+    struct stat st1, st2;
+    
+    if (stat(filename, &st2) != -1) {
+        if (fstat(fd1, &st1) == -1) {
+            perror("fstat");
+            exit(1);
+        }
+        if (st1.st_size != st2.st_size)
+            return 0;
+    }
+    
+    if (lseek(fd1, 0, 0) == -1) {
+        perror("lseek");
+        exit(1);
+    }
+    
+    fd2 = open(filename, O_RDONLY, 0);
+    if (fd2 == -1)
+        return 0;
+    
+    ret = 1;
+    
+    do {
+        n1 = read(fd1, buf1, sizeof(buf1));
+        if (n1 == -1) {
+            perror("read");
+            exit(1);
+        }
+
+        n2 = read(fd2, buf2, sizeof(buf2));
+        if (n2 == -1) {
+            perror("read");
+            exit(1);
+        }
+
+        if (n1 != n2 || memcmp(buf1, buf2, n1) != 0) {
+            ret = 0;
+            break;
+        }
+    } while (n1 != 0);
+    
+    close(fd2);
+    return ret;
+}
+
 
 int main(int argc, char **argv) {
-	char *buf, *bufstart;
-	size_t bufsize = 8192;
-	size_t bufused = 0;
-	ssize_t i = 0;
-	int outfd;
-	
-	if (argc != 2) {
-		usage();
-	}
-  
-	bufstart = buf = malloc(bufsize);
-	if (!buf) {
-		perror("malloc");
-		exit(1);
-	}
-
-	while ((i = read(0, buf, bufsize - bufused)) > 0) {
-		bufused = bufused+i;
-		if (bufused == bufsize) {
-			bufsize *= 2;
-			bufstart = realloc(bufstart, bufsize);
-			if (!bufstart) {
-				perror("realloc");
-				exit(1);
-			}
-		}
-		buf = bufstart + bufused;
-	}
-	if (i == -1) {
-		perror("read");
-		exit(1);
-	}
-  
-	outfd = open(argv[1], O_CREAT | O_TRUNC | O_WRONLY, 0666);
-	if (outfd == -1) {
-		fprintf(stderr, "Can't open %s: %s\n", argv[1], strerror(errno));
-		exit(1);
-	}
-
-	i = write(outfd, bufstart, bufused);
-	if (i == -1) {
-		perror("write");
-		exit(1);
-	}
-
-	i = close(outfd);
-	if (i == -1) {
-		perror("close");
-		exit(1);
-	}
-
-	return 0;
-}
+    char buf[8192];
+    ssize_t i = 0;
+    int opt;
+    int long_index;
+    int preserve;
+    char *output;
+    char *temp;
+    int tempfd;
+    
+    preserve = 0;
+    
+    for (;;) {
+        opt = getopt_long(argc, argv, "hp", options, &long_index);
+        if (opt == -1)
+            break;
+
+        switch (opt) {
+        case 'p':
+            preserve = 1;
+            break;
+            
+        case 'h':
+        default:
+            usage();
+            break;
+        }
+    }
+    
+    if (optind + 1 != argc) {
+        fprintf(stderr, "Missing filename argument.\n");
+        exit(1);
+    }
+    
+    output = argv[optind];
+    
+    if (make_temp(output, &temp, &tempfd) == -1)
+        exit(1);
+    
+    while ((i = read(0, buf, sizeof(buf))) > 0) {
+        if (write(tempfd, buf, i) != i) {
+            perror("write");
+            exit(1);
+        }
+    }
+    if (i == -1) {
+        perror("read");
+        exit(1);
+    }
+    
+    if (preserve && same_contents(tempfd, output)) {
+        remove(temp);
+        return 0;
+    }
+    
+    if (rename(temp, output) == -1) {
+        perror("rename");
+        exit(1);
+    }
+
+    close(tempfd);
+    
+    return 0;
+}

=== modified file 'sponge.docbook'
--- sponge.docbook	
+++ sponge.docbook	
@@ -31,7 +31,7 @@
 			<firstname>Joey</firstname>
 			<surname>Hess</surname>
 		</author>
-		<date>2006-02-19</date>
+		<date>2006-06-14</date>
 	</refentryinfo>
 
 	<refmeta>
@@ -61,4 +61,30 @@
 			the same file. </para>
 
 	</refsect1>
+
+	<refsect1>
+		<title>OPTIONS</title>
+		
+                <variablelist>
+                
+                  <varlistentry>
+                    <term><option>-h</option></term>
+                    <term><option>--help</option></term>
+                    <listitem>
+                      <para>Print out a help summary.</para>
+                    </listitem>
+                  </varlistentry>
+                  
+                  <varlistentry>
+                    <term><option>-p</option></term>
+                    <term><option>--preserve</option></term>
+                    <listitem>
+                      <para>Don't overwrite the output if the input is
+                      identical.</para>
+                    </listitem>
+                  </varlistentry>
+                  
+                </variablelist>
+
+	</refsect1>
 </refentry>

Reply via email to