home *** CD-ROM | disk | FTP | other *** search
- /*
- Program that creates files with holes in them.
- Usage: hole output-file < input-file
-
- The reason for this program is that the current GNU cp and tar
- utilities do not create files with holes so files with lots of
- zeros in them, like the libc shared library, will occupy the full
- amount of space even when unpacked from a sparse tar file. This
- program writes out the output file by seeking over regions of
- consecutive nulls so that the holes do not take up any space.
-
- This is good for making rescue disks that use libc-lite.so.4.x.xx
- as the shared library shrinks by around 260K (for
- libc-lite.so.4.6.27) after making holes.
- */
-
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <stdarg.h>
- #include <stdio.h>
- #include <unistd.h>
-
- const char *progname = "(unknown)";
-
- /* show program error if sys = 0
- system error if sys = 1 */
-
- void errorexit(int sys, const char *msg, va_list ap) {
- int save_errno = errno;
- char buf[1000];
-
- fprintf(stderr, "%s: error: ", progname);
- vsprintf(buf, msg, ap);
- errno = save_errno;
- if (sys)
- perror(buf);
- else
- fprintf(stderr, "%s\n", buf);
- va_end(ap);
- exit(1);
- }
-
- /* show error and exit */
-
- void error(const char *msg, ...) {
- va_list ap;
- va_start(ap, msg);
- errorexit(0, msg, ap);
- }
-
- /* show system related error and exit */
-
- void syserror(const char *msg, ...) {
- va_list ap;
- va_start(ap, msg);
- errorexit(1, msg, ap);
- }
-
- /* homemade output buffering routines with hole-making */
-
- #define BUFSIZE 4096
-
- char buf[BUFSIZE]; /* output buffer */
- int bufcnt = 0; /* number of characters in buffer */
- int outfd; /* output file descriptor */
- int zerocnt = 0; /* number of consecutive null bytes currently */
-
- /* flush the buffer or the nulls */
-
- void flushbuf(void) {
- if (bufcnt) {
- int bytes = write(outfd, buf, bufcnt);
- if (bytes < 0)
- syserror("write error");
- else if (bytes < bufcnt)
- error("short write in file");
- bufcnt = 0;
- }
- else if (zerocnt) {
-
- /* to make a hole in the file, it suffices to lseek
- n bytes past the current file position */
-
- if (lseek(outfd, zerocnt, SEEK_CUR) < 0)
- syserror("lseek error");
- zerocnt = 0;
- }
- }
-
- /* flush buffers and end the file */
-
- void flushend(void) {
- if (zerocnt) {
- char c = '\0';
-
- /* at the end of file, if we have a string of zeros, we can't just
- do an lseek because nothing will happen. Instead, lseek n-1 bytes
- and write out the last null byte */
-
- zerocnt--;
- flushbuf();
- if (write(outfd, &c, 1) != 1)
- error("write error");
- }
- else
- flushbuf();
- }
-
- /* output one character */
-
- void outbuf(char *obuf, int count) {
- for ( ; count; ++obuf, --count)
-
- /* every time we switch between outputting nulls and outputting
- other characters, flush the buffer */
-
- if (*obuf) {
-
- /* flush the buffer also when it is full, of course */
-
- if (zerocnt || bufcnt + 1 >= BUFSIZE)
- flushbuf();
- buf[bufcnt++] = *obuf;
- }
- else {
- if (bufcnt)
- flushbuf();
- zerocnt++;
- }
- }
-
- int main(int argc, char *argv[]) {
- int inbytes;
- char inbuf[BUFSIZE];
-
- progname = argv[0];
- if (argc < 2)
- error("usage: %s output-file < input-file", progname);
-
- /* open file for writing, create if necessary but error if file
- already exists. */
-
- if ((outfd = open(argv[1], O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0)
- syserror("error creating `%s'", argv[1]);
-
- /* read block and send it to output routine */
-
- while ((inbytes = read(STDIN_FILENO, inbuf, BUFSIZE)) > 0)
- outbuf(inbuf, inbytes);
-
- if (inbytes < 0)
- syserror("read error on standard input");
-
- flushend();
- close(outfd);
-
- return 0;
- }
-