home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / utils / file / hole-0.2 / hole-0 / hole / hole.c next >
Encoding:
C/C++ Source or Header  |  1995-03-07  |  3.6 KB  |  161 lines

  1. /*
  2.    Program that creates files with holes in them.
  3.    Usage: hole output-file < input-file
  4.  
  5.    The reason for this program is that the current GNU cp and tar
  6.    utilities do not create files with holes so files with lots of
  7.    zeros in them, like the libc shared library, will occupy the full
  8.    amount of space even when unpacked from a sparse tar file. This
  9.    program writes out the output file by seeking over regions of
  10.    consecutive nulls so that the holes do not take up any space.
  11.  
  12.    This is good for making rescue disks that use libc-lite.so.4.x.xx
  13.    as the shared library shrinks by around 260K (for
  14.    libc-lite.so.4.6.27) after making holes.
  15.    */
  16.  
  17. #include <sys/stat.h>
  18. #include <sys/types.h>
  19. #include <errno.h>
  20. #include <fcntl.h>
  21. #include <stdarg.h>
  22. #include <stdio.h>
  23. #include <unistd.h>
  24.  
  25. const char *progname = "(unknown)";
  26.  
  27. /* show program error if sys = 0
  28.    system error if sys = 1 */
  29.  
  30. void errorexit(int sys, const char *msg, va_list ap) {
  31.   int save_errno = errno;
  32.   char buf[1000];
  33.  
  34.   fprintf(stderr, "%s: error: ", progname);
  35.   vsprintf(buf, msg, ap);
  36.   errno = save_errno;
  37.   if (sys)
  38.     perror(buf);
  39.   else
  40.     fprintf(stderr, "%s\n", buf);
  41.   va_end(ap);
  42.   exit(1);
  43. }
  44.  
  45. /* show error and exit */
  46.  
  47. void error(const char *msg, ...) {
  48.   va_list ap;
  49.   va_start(ap, msg);
  50.   errorexit(0, msg, ap);
  51. }
  52.  
  53. /* show system related error and exit */
  54.  
  55. void syserror(const char *msg, ...) {
  56.   va_list ap;
  57.   va_start(ap, msg);
  58.   errorexit(1, msg, ap);
  59. }
  60.  
  61. /* homemade output buffering routines with hole-making */
  62.  
  63. #define BUFSIZE 4096
  64.  
  65. char buf[BUFSIZE]; /* output buffer */
  66. int bufcnt = 0;    /* number of characters in buffer */
  67. int outfd;         /* output file descriptor */
  68. int zerocnt = 0;   /* number of consecutive null bytes currently */
  69.  
  70. /* flush the buffer or the nulls */
  71.  
  72. void flushbuf(void) {
  73.   if (bufcnt) {
  74.     int bytes = write(outfd, buf, bufcnt);
  75.     if (bytes < 0)
  76.       syserror("write error");
  77.     else if (bytes < bufcnt)
  78.       error("short write in file");
  79.     bufcnt = 0;
  80.   }
  81.   else if (zerocnt) {
  82.  
  83.     /* to make a hole in the file, it suffices to lseek
  84.        n bytes past the current file position */
  85.  
  86.     if (lseek(outfd, zerocnt, SEEK_CUR) < 0)
  87.       syserror("lseek error");
  88.     zerocnt = 0;
  89.   }
  90. }
  91.  
  92. /* flush buffers and end the file */
  93.  
  94. void flushend(void) {
  95.   if (zerocnt) {
  96.     char c = '\0';
  97.  
  98.     /* at the end of file, if we have a string of zeros, we can't just
  99.        do an lseek because nothing will happen. Instead, lseek n-1 bytes
  100.        and write out the last null byte */
  101.  
  102.     zerocnt--;
  103.     flushbuf();
  104.     if (write(outfd, &c, 1) != 1)
  105.       error("write error");
  106.   }
  107.   else
  108.     flushbuf();
  109. }
  110.  
  111. /* output one character */
  112.  
  113. void outbuf(char *obuf, int count) {
  114.   for ( ; count; ++obuf, --count)
  115.  
  116.     /* every time we switch between outputting nulls and outputting
  117.        other characters, flush the buffer */
  118.  
  119.     if (*obuf) {
  120.  
  121.       /* flush the buffer also when it is full, of course */
  122.  
  123.       if (zerocnt || bufcnt + 1 >= BUFSIZE)
  124.     flushbuf();
  125.       buf[bufcnt++] = *obuf;
  126.     }
  127.     else {
  128.       if (bufcnt)
  129.     flushbuf();
  130.       zerocnt++;
  131.     }
  132. }
  133.  
  134. int main(int argc, char *argv[]) {
  135.   int inbytes;
  136.   char inbuf[BUFSIZE];
  137.  
  138.   progname = argv[0];
  139.   if (argc < 2)
  140.     error("usage: %s output-file < input-file", progname);
  141.  
  142.   /* open file for writing, create if necessary but error if file
  143.      already exists. */
  144.  
  145.   if ((outfd = open(argv[1], O_CREAT | O_EXCL | O_WRONLY, 0666)) < 0)
  146.     syserror("error creating `%s'", argv[1]);
  147.  
  148.   /* read block and send it to output routine */
  149.  
  150.   while ((inbytes = read(STDIN_FILENO, inbuf, BUFSIZE)) > 0)
  151.     outbuf(inbuf, inbytes);
  152.  
  153.   if (inbytes < 0)
  154.     syserror("read error on standard input");
  155.  
  156.   flushend();
  157.   close(outfd);
  158.  
  159.   return 0;
  160. }
  161.