From b5803897df7792d3137e8050ef3c3fb96288d24a Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Mon, 21 Aug 2017 17:57:54 +0200 Subject: initial commit of holes --- Makefile | 23 +++++++++++++++++ README | 35 +++++++++++++++++++++++++ holes.1 | 43 +++++++++++++++++++++++++++++++ holes.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 190 insertions(+) create mode 100644 Makefile create mode 100644 README create mode 100644 holes.1 create mode 100644 holes.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7807575 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +ALL=holes + +CFLAGS=-g -O2 -Wall -Wno-switch -Wextra -Wwrite-strings + +DESTDIR= +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +MANDIR=$(PREFIX)/share/man + +all: $(ALL) + +README: holes.1 + mandoc -Tutf8 $< | col -bx >$@ + +clean: FRC + rm -f $(ALL) + +install: FRC all + mkdir -p $(DESTDIR)$(BINDIR) $(DESTDIR)$(MANDIR)/man1 + install -m0755 $(ALL) $(DESTDIR)$(BINDIR) + install -m0644 $(ALL:=.1) $(DESTDIR)$(MANDIR)/man1 + +FRC: diff --git a/README b/README new file mode 100644 index 0000000..a5a6b5c --- /dev/null +++ b/README @@ -0,0 +1,35 @@ +HOLES(1) General Commands Manual HOLES(1) + +NAME + holes – find runs of zero bytes + +SYNOPSIS + holes [-n minlen] [files ...] + +DESCRIPTION + holes looks for runs of zero bytes (a.k.a. holes) in the specified input + files (or the standard input), and prints the start adresses (in + hexadecimal) as well as the lengths (in decimal). When multiple input + files are specified, holes prefixes each line with the file name. + + By default, only holes of at least 64 bytes are reported. This can be + changed using the -n option. + +EXIT STATUS + The holes utility exits 0 on success, and >0 if an error occurs. + +SEE ALSO + fallocate(1), truncate(1), virt-sparsify(1) + +AUTHORS + Leah Neukirchen + +LICENSE + holes is in the public domain. + + To the extent possible under law, the creator of this work has waived all + copyright and related or neighboring rights to this work. + + http://creativecommons.org/publicdomain/zero/1.0/ + +Void Linux August 21, 2017 Void Linux diff --git a/holes.1 b/holes.1 new file mode 100644 index 0000000..6d24b08 --- /dev/null +++ b/holes.1 @@ -0,0 +1,43 @@ +.Dd August 21, 2017 +.Dt HOLES 1 +.Os +.Sh NAME +.Nm holes +.Nd find runs of zero bytes +.Sh SYNOPSIS +.Nm +.Op Fl n Ar minlen +.Op Ar files\ ... +.Sh DESCRIPTION +.Nm +looks for runs of zero bytes (a.k.a. holes) in the specified input files +(or the standard input), +and prints the start adresses (in hexadecimal) +as well as the lengths (in decimal). +When multiple input files are specified, +.Nm +prefixes each line with the file name. +.Pp +By default, +only holes of at least 64 bytes are reported. +This can be changed using the +.Fl n +option. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr fallocate 1 , +.Xr truncate 1 , +.Xr virt-sparsify 1 +.Sh AUTHORS +.An Leah Neukirchen Aq Mt leah@vuxu.org +.Sh LICENSE +.Nm +is in the public domain. +.Pp +To the extent possible under law, +the creator of this work +has waived all copyright and related or +neighboring rights to this work. +.Pp +.Lk http://creativecommons.org/publicdomain/zero/1.0/ diff --git a/holes.c b/holes.c new file mode 100644 index 0000000..865ea50 --- /dev/null +++ b/holes.c @@ -0,0 +1,89 @@ +/* holes - find holes (= runs of zero bytes) in input files */ + +#include +#include +#include +#include +#include + +ssize_t minlen = 64; +int ret; +char *argv0; + +void +holes(FILE *input, char *filename) +{ + off_t offset = 0; + off_t run = 0; + int ch; + + while ((ch = getc_unlocked(input)) != EOF) { + if (ch == 0) { + run++; + } else { + if (run >= minlen) { + if (filename) + printf("%s: ", filename); + printf("%08lx %ld\n", offset - run, run); + } + run = 0; + } + offset++; + } + if (ferror(input)) { + fprintf(stderr, "%s: can't read '%s': %s\n", + argv0, filename ? filename : "-", strerror(errno)); + ret = 1; + return; + } + + if (offset == 0 || // empty file + run >= minlen) { + if (filename) + printf("%s: ", filename); + printf("%08lx %ld\n", offset - run, run); + } +} + +int +main(int argc, char *argv[]) +{ + int c, i; + + argv0 = argv[0]; + + while ((c = getopt(argc, argv, "n:")) != -1) + switch(c) { + case 'n': + minlen = atoll(optarg); + break; + default: + fprintf(stderr, + "Usage: %s [-n MINLEN] [FILES...]\n", argv0); + exit(2); + } + + if (minlen < 1) { + fprintf(stderr, "%s: MINLEN must not be smaller than 1.\n", + argv0); + exit(2); + } + + if (optind == argc) + holes(stdin, 0); + else + for (i = optind; i < argc; i++) { + FILE *input = (strcmp(argv[i], "-") == 0) ? + stdin : fopen(argv[i], "rb"); + + if (!input) { + fprintf(stderr, "%s: can't open file '%s': %s\n", + argv0, argv[i], strerror(errno)); + ret = 1; + } else { + holes(input, argc - optind > 1 ? argv[i] : 0); + } + } + + return ret; +} -- cgit 1.4.1