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>