summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2017-08-21 17:57:54 +0200
committerLeah Neukirchen <leah@vuxu.org>2017-08-21 17:57:54 +0200
commitb5803897df7792d3137e8050ef3c3fb96288d24a (patch)
tree6ea0a895545c5473f8b9e878ecce7ea9891f3893
downloadholes-b5803897df7792d3137e8050ef3c3fb96288d24a.tar.gz
holes-b5803897df7792d3137e8050ef3c3fb96288d24a.tar.xz
holes-b5803897df7792d3137e8050ef3c3fb96288d24a.zip
initial commit of holes
-rw-r--r--Makefile23
-rw-r--r--README35
-rw-r--r--holes.143
-rw-r--r--holes.c89
4 files changed, 190 insertions, 0 deletions
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 <leah@vuxu.org>
+
+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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+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;
+}