coreutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

dd: add 'skip_bytes' and 'count_bytes' operands


From: Jérémy Compostella
Subject: dd: add 'skip_bytes' and 'count_bytes' operands
Date: Sun, 05 Feb 2012 13:25:46 +0100

All,

I worked on the 'skip_bytes' and 'count_bytes' features reported in the
following mail thread:
http://lists.gnu.org/archive/html/bug-coreutils/2009-10/msg00061.html

If anybody have started working on it or have guidance for it, please
let me know.

I wrote the attached patch which implements these features. As I'm new
in this project and I'm not really comfortable with project specific
good practices do not hesitate to comment this patch. I will take into
account whatever you want.

I added a special test suite to satisfy these new operands. tests/dd/bytes

Writing this patch I thought it would be nice having the seek_bytes
operand too. What is your opinion about this ?

Cheers,

Jérémy

>From 732f14abfc8b1b5aa711a41cc4dd7fdc0b3d045a Mon Sep 17 00:00:00 2001
From: Jeremy Compostella <address@hidden>
Date: Sat, 4 Feb 2012 15:25:54 +0100
Subject: [PATCH] dd: add skip_bytes and count_bytes operands

dd now accepts:
- the skip_bytes=N operand. When specified, dd skip N bytes before
  copying. 'skip' and 'skip_bytes" operands are mutually exclusive.
- the count_bytes=N operand. When specified, dd copy N bytes. 'count'
  and 'count_bytes' are mutually exclusive.

Signed-off-by: Jeremy Compostella <address@hidden>
---
 NEWS               |    8 ++++
 doc/coreutils.texi |   11 ++++++
 src/dd.c           |   99 +++++++++++++++++++++++++++++++++++++++------------
 tests/Makefile.am  |    1 +
 tests/dd/bytes     |   37 +++++++++++++++++++
 5 files changed, 132 insertions(+), 24 deletions(-)
 create mode 100755 tests/dd/bytes

diff --git a/NEWS b/NEWS
index 2b0926f..445d87b 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,14 @@ GNU coreutils NEWS                                    -*- 
outline -*-
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+** New features
+
+  dd now accepts:
+  - the skip_bytes=N operand. When specified, dd skip N
+    bytes before copying. 'skip' and 'skip_bytes' operands are mutually
+    exclusive.
+  - the count_bytes=N operand. When specified, dd copy N bytes. 'count'
+    and 'count_bytes' are mutually exclusive.
 
 * Noteworthy changes in release 8.15 (2012-01-06) [stable]
 
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 0d3b739..4124927 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8061,6 +8061,11 @@ use @var{bytes} as the fixed record length.
 @opindex skip
 Skip @var{blocks} @samp{ibs}-byte blocks in the input file before copying.
 
+@item skip_bytes=@var{n}
+@opindex skip_bytes
+Skip @var{n} bytes in the input file before copying.
+@samp{skip_bytes} and @samp{skip} are mutually exclusive.
+
 @item seek=@var{blocks}
 @opindex seek
 Skip @var{blocks} @samp{obs}-byte blocks in the output file before copying.
@@ -8070,6 +8075,12 @@ Skip @var{blocks} @samp{obs}-byte blocks in the output 
file before copying.
 Copy @var{blocks} @samp{ibs}-byte blocks from the input file, instead
 of everything until the end of the file.
 
+@item count_bytes=@var{n}
+@opindex count
+Copy @var{n} bytes from the input file, instead of everything until
+the end of the file. @samp{count} and @samp{count_bytes} are mutually
+exclusive.
+
 @item status=noxfer
 @opindex status
 Do not print the overall transfer rate and volume statistics
diff --git a/src/dd.c b/src/dd.c
index 8c687b3..461b9e0 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -156,12 +156,19 @@ static size_t conversion_blocksize = 0;
 /* Skip this many records of 'input_blocksize' bytes before input. */
 static uintmax_t skip_records = 0;
 
+/* Skip this many bytes before input in addition of 'skip_records'
+   records.  */
+static size_t skip_bytes = 0;
+
 /* Skip this many records of 'output_blocksize' bytes before output. */
 static uintmax_t seek_records = 0;
 
 /* Copy only this many records.  The default is effectively infinity.  */
 static uintmax_t max_records = (uintmax_t) -1;
 
+/* Copy this many bytes in addition to 'max_records' records.  */
+static size_t max_bytes = 0;
+
 /* Bit vector of conversions to apply. */
 static int conversions_mask = 0;
 
@@ -490,6 +497,8 @@ Copy a file, converting and formatting according to the 
operands.\n\
   cbs=BYTES       convert BYTES bytes at a time\n\
   conv=CONVS      convert the file as per the comma separated symbol list\n\
   count=BLOCKS    copy only BLOCKS input blocks\n\
+  count_bytes=N   copy only N bytes. count and count_bytes operands are 
mutually\n\
+                  exclusive\n\
   ibs=BYTES       read up to BYTES bytes at a time (default: 512)\n\
 "), stdout);
       fputs (_("\
@@ -500,6 +509,8 @@ Copy a file, converting and formatting according to the 
operands.\n\
   oflag=FLAGS     write as per the comma separated symbol list\n\
   seek=BLOCKS     skip BLOCKS obs-sized blocks at start of output\n\
   skip=BLOCKS     skip BLOCKS ibs-sized blocks at start of input\n\
+  skip_bytes=N    skip N bytes at start of input. skip and skip_bytes 
operands\n\
+                  are mutually exclusive\n\
   status=noxfer   suppress transfer statistics\n\
 "), stdout);
       fputs (_("\
@@ -1120,6 +1131,8 @@ scanargs (int argc, char *const *argv)
 {
   int i;
   size_t blocksize = 0;
+  uintmax_t count_in_bytes = 0;
+  uintmax_t skip_in_bytes = 0;
 
   for (i = optind; i < argc; i++)
     {
@@ -1176,10 +1189,14 @@ scanargs (int argc, char *const *argv)
             }
           else if (operand_is (name, "skip"))
             skip_records = n;
+          else if (operand_is (name, "skip_bytes"))
+            skip_in_bytes = n;
           else if (operand_is (name, "seek"))
             seek_records = n;
           else if (operand_is (name, "count"))
             max_records = n;
+          else if (operand_is (name, "count_bytes"))
+            count_in_bytes = n;
           else
             {
               error (0, 0, _("unrecognized operand %s"), quote (name));
@@ -1216,6 +1233,30 @@ scanargs (int argc, char *const *argv)
       usage (EXIT_FAILURE);
     }
 
+  if (skip_in_bytes != 0 && skip_records != 0)
+    {
+      error (0, 0, _("'skip_bytes' and 'skip' operands are mutually 
exclusive"));
+      usage (EXIT_FAILURE);
+    }
+
+  if (skip_in_bytes != 0)
+    {
+      skip_records = skip_in_bytes / input_blocksize;
+      skip_bytes = skip_in_bytes % input_blocksize;
+    }
+
+  if (count_in_bytes != 0 && max_records != (uintmax_t) -1)
+    {
+      error (0, 0, _("'count_bytes' and 'count' operands are mutually 
exclusive"));
+      usage (EXIT_FAILURE);
+    }
+
+  if (count_in_bytes != 0)
+    {
+      max_records = count_in_bytes / input_blocksize;
+      max_bytes = count_in_bytes % input_blocksize;
+    }
+
   /* Warn about partial reads if bs=SIZE is given and iflag=fullblock
      is not, and if counting or skipping bytes or using direct I/O.
      This helps to avoid confusion with miscounts, and to avoid issues
@@ -1411,18 +1452,19 @@ skip_via_lseek (char const *filename, int fdesc, off_t 
offset, int whence)
 # define skip_via_lseek(Filename, Fd, Offset, Whence) lseek (Fd, Offset, 
Whence)
 #endif
 
-/* Throw away RECORDS blocks of BLOCKSIZE bytes on file descriptor FDESC,
-   which is open with read permission for FILE.  Store up to BLOCKSIZE
-   bytes of the data at a time in BUF, if necessary.  RECORDS must be
-   nonzero.  If fdesc is STDIN_FILENO, advance the input offset.
-   Return the number of records remaining, i.e., that were not skipped
-   because EOF was reached.  */
+/* Throw away RECORDS blocks of BLOCKSIZE bytes plus BYTES bytes on
+   file descriptor FDESC, which is open with read permission for FILE.
+   Store up to BLOCKSIZE bytes of the data at a time in BUF, if
+   necessary. RECORDS or BYTES must be nonzero. If FDESC is
+   STDIN_FILENO, advance the input offset. Return the number of
+   records remaining, i.e., that were not skipped because EOF was
+   reached.  */
 
 static uintmax_t
 skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
-      char *buf)
+      size_t bytes, char *buf)
 {
-  uintmax_t offset = records * blocksize;
+  uintmax_t offset = records * blocksize + bytes;
 
   /* Try lseek and if an error indicates it was an inappropriate operation --
      or if the file offset is not representable as an off_t --
@@ -1491,29 +1533,35 @@ skip (int fdesc, char const *file, uintmax_t records, 
size_t blocksize,
 
       do
         {
-          ssize_t nread = iread_fnc (fdesc, buf, blocksize);
+          ssize_t nread;
+          if (records != 0)
+            nread = iread_fnc (fdesc, buf, blocksize);
+          else
+            nread = iread_fnc (fdesc, buf, bytes);
+
           if (nread < 0)
             {
               if (fdesc == STDIN_FILENO)
                 {
                   error (0, errno, _("reading %s"), quote (file));
                   if (conversions_mask & C_NOERROR)
-                    {
-                      print_stats ();
-                      continue;
-                    }
+                    print_stats ();
                 }
               else
                 error (0, lseek_errno, _("%s: cannot seek"), quote (file));
               quit (EXIT_FAILURE);
             }
-
-          if (nread == 0)
+          else if (nread == 0)
             break;
-          if (fdesc == STDIN_FILENO)
+          else if (fdesc == STDIN_FILENO)
             advance_input_offset (nread);
+
+          if (records != 0)
+            --records;
+          else
+            bytes = 0;
         }
-      while (--records != 0);
+      while (records != 0 || bytes != 0);
 
       return records;
     }
@@ -1777,11 +1825,11 @@ dd_copy (void)
       obuf = ibuf;
     }
 
-  if (skip_records != 0)
+  if (skip_records != 0 || skip_bytes != 0)
     {
-      uintmax_t us_bytes = input_offset + (skip_records * input_blocksize);
+      uintmax_t us_bytes = input_offset + (skip_records * input_blocksize) + 
skip_bytes;
       uintmax_t us_blocks = skip (STDIN_FILENO, input_file,
-                                  skip_records, input_blocksize, ibuf);
+                                  skip_records, input_blocksize, skip_bytes, 
ibuf);
       us_bytes -= input_offset;
 
       /* POSIX doesn't say what to do when dd detects it has been
@@ -1800,7 +1848,7 @@ dd_copy (void)
   if (seek_records != 0)
     {
       uintmax_t write_records = skip (STDOUT_FILENO, output_file,
-                                      seek_records, output_blocksize, obuf);
+                                      seek_records, output_blocksize, 0, obuf);
 
       if (write_records != 0)
         {
@@ -1817,12 +1865,12 @@ dd_copy (void)
         }
     }
 
-  if (max_records == 0)
+  if (max_records == 0 && max_bytes == 0)
     return exit_status;
 
   while (1)
     {
-      if (r_partial + r_full >= max_records)
+      if (r_partial + r_full >= max_records + (max_bytes ? 1 : 0))
         break;
 
       /* Zero the buffer before reading, so that if we get a read error,
@@ -1833,7 +1881,10 @@ dd_copy (void)
                 (conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
                 input_blocksize);
 
-      nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize);
+      if (r_partial + r_full >= max_records)
+        nread = iread_fnc (STDIN_FILENO, ibuf, max_bytes);
+      else
+        nread = iread_fnc (STDIN_FILENO, ibuf, input_blocksize);
 
       if (nread >= 0 && i_nocache)
         invalidate_cache (STDIN_FILENO, nread);
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 8b670fc..c6b8737 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -369,6 +369,7 @@ TESTS =                                             \
   dd/reblock                                   \
   dd/skip-seek                                 \
   dd/skip-seek2                                        \
+  dd/bytes                                     \
   dd/skip-seek-past-file                       \
   dd/stderr                                    \
   dd/unblock                                   \
diff --git a/tests/dd/bytes b/tests/dd/bytes
new file mode 100755
index 0000000..d0c72f8
--- /dev/null
+++ b/tests/dd/bytes
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ dd
+
+# count_bytes
+echo 0123456789abcefghijklm > in || fail=1
+(dd count_bytes=14 conv=swab) < in > out 2> /dev/null || fail=1
+case `cat out` in
+  1032547698baec) ;;
+  *) fail=1 ;;
+esac
+
+# skip_bytes
+echo 0123456789abcefghijklm > in || fail=1
+(dd skip_bytes=10) < in > out 2> /dev/null || fail=1
+case `cat out` in
+  abcefghijklm) ;;
+  *) fail=1 ;;
+esac
+
+Exit $fail
-- 
1.7.2.5


reply via email to

[Prev in Thread] Current Thread [Next in Thread]