coreutils
[Top][All Lists]
Advanced

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

Re: dd: add 'skip_bytes' and 'count_bytes' operands


From: Jérémy Compostella
Subject: Re: dd: add 'skip_bytes' and 'count_bytes' operands
Date: Mon, 06 Feb 2012 15:01:15 +0100

Pádraig, all,

I took into account general comments on my commits. I attached the
improved patch for feature. Improvements are:
- Add by file description,
- I put back accents on my name (Jérémy instead of Jeremy),

Cheers,

Jérémy
---
>From 53c65c4bc125217b935c824c193449e592011a74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Compostella?= <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.

* src/dd.c (scanargs): Compute skip_records and skip_bytes when
'skip_bytes' operand is used. Compute max_records and max_bytes when
'count_bytes' operand is used. Raise a "mutually exclusive" error when
'skip' and 'skip_bytes' are both used or when 'count' and
'count_bytes' are both used.
(skip_via_lseek): Use new 'bytes' parameter and handle potential
'records' equals to zero. Update the header comments.
(dd_copy): Skip accordingly to skip_records AND skip_bytes. Count
accordingly to max_records AND max_bytes.
* NEWS (New features): Mention it.
* doc/coreutils.texi (New features): Detail new operands and
  behaviors.
* tests/dd/bytes: New file. Tests for these two new operands.
* tests/Makefile.am (TESTS): Add it.
---
 NEWS               |   10 +++++-
 doc/coreutils.texi |   11 ++++++
 src/dd.c           |   99 +++++++++++++++++++++++++++++++++++++++------------
 tests/Makefile.am  |    1 +
 tests/dd/bytes     |   37 +++++++++++++++++++
 5 files changed, 133 insertions(+), 25 deletions(-)
 create mode 100755 tests/dd/bytes

diff --git a/NEWS b/NEWS
index 9eebbf6..adf6140 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,15 @@ 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.
+
 ** Bug fixes
 
   mv now lets you move a symlink onto a same-inode destination file that
@@ -11,7 +20,6 @@ GNU coreutils NEWS                                    -*- 
outline -*-
   referent, there is no risk of data loss, since the symlink will
   typically still point to one of the hard links.
 
-
 * Noteworthy changes in release 8.15 (2012-01-06) [stable]
 
 ** New programs
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 52838e7..e671a8b 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 9d24791..7c65653 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)
         {
@@ -1819,12 +1867,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,
@@ -1835,7 +1883,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 a94aaa2..31bb050 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]