bskip/bseek patch from Chris Sylvain <csylvain@umm.edu> merged in
authorJim Meyering <jim@meyering.net>
Fri, 24 Nov 2000 21:54:31 +0000 (21:54 +0000)
committerJim Meyering <jim@meyering.net>
Fri, 24 Nov 2000 21:54:31 +0000 (21:54 +0000)
src/dd.c

index 3400094cbb71fd5e9f1895f249bdb8b0f3072e9d..259bfa2de574be750382bc652d391748d6f8a3f2 100644 (file)
--- a/src/dd.c
+++ b/src/dd.c
@@ -102,6 +102,12 @@ static uintmax_t skip_records = 0;
 /* Skip this many records of `output_blocksize' bytes before output. */
 static uintmax_t seek_record = 0;
 
+/* Skip this many bytes before input. */
+static uintmax_t skip_bytes = 0;
+
+/* Skip this many bytes before output. */
+static uintmax_t seek_bytes = 0;
+
 /* Copy only this many records.  The default is effectively infinity.  */
 static uintmax_t max_records = (uintmax_t) -1;
 
@@ -294,10 +300,15 @@ Copy a file, converting and formatting according to the options.\n\
   obs=BYTES       write BYTES bytes at a time\n\
   of=FILE         write to FILE instead of stdout\n\
   seek=BLOCKS     skip BLOCKS obs-sized blocks at start of output\n\
+  bseek=BYTES     skip BYTES at start of output\n\
   skip=BLOCKS     skip BLOCKS ibs-sized blocks at start of input\n\
+  bskip=BYTES     skip BYTES at start of input\n\
       --help      display this help and exit\n\
       --version   output version information and exit\n\
 \n\
+Only one of each of the [b]seek or [b]skip option pairs may be used\n\
+at any one time.\n\
+\n\
 BYTES may be followed by the following multiplicative suffixes:\n\
 xM M, c 1, w 2, b 512, kD 1000, k 1024, MD 1,000,000, M 1,048,576,\n\
 GD 1,000,000,000, G 1,073,741,824, and so on for T, P, E, Z, Y.\n\
@@ -581,9 +592,29 @@ scanargs (int argc, char **argv)
                          || conversion_blocksize == 0);
            }
          else if (STREQ (name, "skip"))
-           skip_records = n;
+           {
+             skip_records = n;
+             /* may use option only when bskip=BYTES option is not used */
+             invalid |= skip_bytes != 0;
+           }
          else if (STREQ (name, "seek"))
-           seek_record = n;
+           {
+             seek_record = n;
+             /* may use option only when bseek=BYTES option is not used */
+             invalid |= seek_bytes != 0;
+           }
+         else if (STREQ (name, "bseek"))
+           {
+             seek_bytes = n;
+             /* may use option only when seek=BLOCKS option is not used */
+             invalid |= seek_record != 0;
+           }
+         else if (STREQ (name, "bskip"))
+           {
+             skip_bytes = n;
+             /* may use option only when skip=BLOCKS option is not used */
+             invalid |= skip_records != 0;
+           }
          else if (STREQ (name, "count"))
            max_records = n;
          else
@@ -788,6 +819,63 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
     }
 }
 
+/* Throw away BYTES on file descriptor FDESC,
+   which is open with read permission for FILE.
+   Store up to BYTES of the data (one BLOCKSIZE at a time) in BUF,
+   if necessary. */
+
+static void
+bskip (int fdesc, char *file, uintmax_t bytes, size_t blocksize,
+       unsigned char *buf)
+{
+  struct stat stats;
+
+  /* Use fstat instead of checking for errno == ESPIPE because
+     lseek doesn't work on some special files but doesn't return an
+     error, either. */
+  /* FIXME: can this really happen?  What system?  */
+  if (fstat (fdesc, &stats))
+    {
+      error (0, errno, "%s", file);
+      quit (1);
+    }
+
+  /* Try lseek and if an error indicates it was an inappropriate
+     operation, fall back on using read.  */
+  if (lseek (fdesc, bytes, SEEK_SET) == -1)
+    {
+      int nread;
+
+      while ((bytes-=blocksize) >= 0)
+       {
+         nread = safe_read (fdesc, buf, blocksize);
+         if (nread < 0)
+           {
+             error (0, errno, "%s", file);
+             quit (1);
+           }
+         /* POSIX doesn't say what to do when dd detects it has been
+            asked to skip past EOF, so I assume it's non-fatal.
+            FIXME: maybe give a warning.  */
+         if (nread == 0)
+           break;
+       }
+
+      /* sop up any residue .. */
+      if (bytes < 0)
+       {
+         bytes += blocksize;
+
+         nread = safe_read (fdesc, buf, bytes);
+         if (nread < 0)
+           {
+             error (0, errno, "%s", file);
+             quit (1);
+           }
+       }
+    }
+}
+
 /* Copy NREAD bytes of BUF, with no conversions.  */
 
 static void
@@ -935,6 +1023,9 @@ dd_copy (void)
   if (skip_records != 0)
     skip (STDIN_FILENO, input_file, skip_records, input_blocksize, ibuf);
 
+  if (skip_bytes != 0)
+    bskip (STDIN_FILENO, input_file, skip_bytes, input_blocksize, ibuf);
+
   if (seek_record != 0)
     {
       /* FIXME: this loses for
@@ -948,6 +1039,9 @@ dd_copy (void)
            obuf);
     }
 
+  if (seek_bytes != 0)
+    bskip (STDOUT_FILENO, output_file, seek_bytes, output_blocksize, obuf);
+
   if (max_records == 0)
     quit (exit_status);
 
@@ -1143,17 +1237,28 @@ main (int argc, char **argv)
       /* Open the output file with *read* access only if we might
         need to read to satisfy a `seek=' request.  If we can't read
         the file, go ahead with write-only access; it might work.  */
-      if ((! seek_record
+      if (((seek_record == 0
+           && seek_bytes == 0)
           || open_fd (STDOUT_FILENO, output_file, O_RDWR | opts, perms) < 0)
          && open_fd (STDOUT_FILENO, output_file, O_WRONLY | opts, perms) < 0)
        error (1, errno, _("opening %s"), quote (output_file));
+
 #if HAVE_FTRUNCATE
-      if (seek_record != 0 && !(conversions_mask & C_NOTRUNC))
+      if ((seek_record != 0 || seek_bytes != 0)
+         && !(conversions_mask & C_NOTRUNC))
        {
          struct stat stdout_stat;
-         off_t o = seek_record * output_blocksize;
-         if (o / output_blocksize != seek_record)
-           error (1, 0, _("file offset out of range"));
+         off_t o;
+         if (seek_record)
+           {
+             o = seek_record * output_blocksize;
+             if (o / output_blocksize != seek_record)
+               error (1, 0, _("file offset out of range"));
+           }
+         else
+           {
+             o = seek_bytes;
+           }
 
          /* Attempt to use ftruncate only if STDOUT refers to a regular file.
             On Linux 2.4.0, ftruncate fails with for non-regular files.  */
@@ -1162,9 +1267,16 @@ main (int argc, char **argv)
              && ftruncate (STDOUT_FILENO, o) < 0)
            {
              char buf[LONGEST_HUMAN_READABLE + 1];
-             error (1, errno, _("advancing past %s blocks in output file %s"),
-                    human_readable (seek_record, buf, 1, 1),
-                    quote (output_file));
+             if (seek_record)
+               error (1, errno,
+                      _("advancing past %s blocks in output file %s"),
+                      human_readable (seek_record, buf, 1, 1),
+                      quote (output_file));
+             else
+               error (1, errno,
+                      _("advancing past %s bytes in output file %s"),
+                      human_readable (seek_bytes, buf, 1, 1),
+                      quote (output_file));
            }
        }
 #endif