truncate: support sizes relative to an existing file
authorPádraig Brady <P@draigBrady.com>
Wed, 26 May 2010 08:27:53 +0000 (09:27 +0100)
committerPádraig Brady <P@draigBrady.com>
Fri, 28 May 2010 13:23:04 +0000 (14:23 +0100)
* doc/coreutils.texi (truncate invocation): Mention that --reference
bases the --size rather than just setting it.
* src/truncate.c (usage): Likewise. Also remove the clause
describing --size and --reference as being mutually exclusive.
(do_truncate): Add an extra parameter to hold the size
of a referenced file, and use it if positive.
(main): Pass the size of a referenced file to do_truncate().
* tests/misc/truncate-parameters: Adjust for the new combinations.
* NEWS: Mention the change
Suggested by Richard W.M. Jones

NEWS
doc/coreutils.texi
src/truncate.c
tests/misc/truncate-parameters

diff --git a/NEWS b/NEWS
index 19436fe..7a294f4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -14,6 +14,7 @@ GNU coreutils NEWS                                    -*- outline -*-
 
   sort -g now uses long doubles for greater range and precision.
 
+  truncate now supports setting file sizes relative to a reference file.
 
 * Noteworthy changes in release 8.5 (2010-04-23) [stable]
 
index 115e5fb..d1c3085 100644 (file)
@@ -10767,13 +10767,13 @@ Treat @var{size} as number of I/O blocks of the @var{file} rather than bytes.
 @itemx --reference=@var{rfile}
 @opindex -r
 @opindex --reference
-Set the size of each @var{file} to the same size as @var{rfile}.
+Base the size of each @var{file} on the size of @var{rfile}.
 
 @item -s @var{size}
 @itemx --size=@var{size}
 @opindex -s
 @opindex --size
-Set the size of each @var{file} to this @var{size}.
+Set or adjust the size of each @var{file} according to @var{size}.
 @multiplierSuffixesNoBlocks{size}
 
 @var{size} may also be prefixed by one of the following to adjust
index ece52ee..08090ab 100644 (file)
@@ -115,8 +115,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\
   -o, --io-blocks        treat SIZE as number of IO blocks instead of bytes\n\
 "), stdout);
       fputs (_("\
-  -r, --reference=FILE   use this FILE's size\n\
-  -s, --size=SIZE        use this SIZE\n"), stdout);
+  -r, --reference=RFILE  base size on RFILE\n\
+  -s, --size=SIZE        set or adjust the file size by SIZE\n"), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
       emit_size_note ();
@@ -124,10 +124,6 @@ Mandatory arguments to long options are mandatory for short options too.\n\
 SIZE may also be prefixed by one of the following modifying characters:\n\
 `+' extend by, `-' reduce by, `<' at most, `>' at least,\n\
 `/' round down to multiple of, `%' round up to multiple of.\n"), stdout);
-      fputs (_("\
-\n\
-Note that the -r and -s options are mutually exclusive.\n\
-"), stdout);
       emit_ancillary_info ();
     }
   exit (status);
@@ -135,12 +131,13 @@ Note that the -r and -s options are mutually exclusive.\n\
 
 /* return 1 on error, 0 on success */
 static int
-do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
+do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
+              rel_mode_t rel_mode)
 {
   struct stat sb;
   off_t nsize;
 
-  if ((block_mode || rel_mode) && fstat (fd, &sb) != 0)
+  if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0)
     {
       error (0, errno, _("cannot fstat %s"), quote (fname));
       return 1;
@@ -161,9 +158,9 @@ do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
     }
   if (rel_mode)
     {
-      uintmax_t const fsize = sb.st_size;
+      uintmax_t const fsize = rsize < 0 ? sb.st_size : rsize;
 
-      if (sb.st_size < 0)
+      if (rsize < 0 && sb.st_size < 0)
         {
           /* Complain only for a regular file, a directory,
              or a shared memory object, as POSIX 1003.1-2004 specifies
@@ -248,6 +245,7 @@ main (int argc, char **argv)
 {
   bool got_size = false;
   off_t size IF_LINT (= 0);
+  off_t rsize = -1;
   rel_mode_t rel_mode = rm_abs;
   mode_t omode;
   int c, errors = 0, fd = -1, oflags;
@@ -335,9 +333,16 @@ main (int argc, char **argv)
   argc -= optind;
 
   /* must specify either size or reference file */
-  if ((ref_file && got_size) || (!ref_file && !got_size))
+  if (!ref_file && !got_size)
+    {
+      error (0, 0, _("you must specify either %s or %s"),
+             quote_n (0, "--size"), quote_n (1, "--reference"));
+      usage (EXIT_FAILURE);
+    }
+  /* must specify a relative size with a reference file */
+  if (ref_file && got_size && !rel_mode)
     {
-      error (0, 0, _("you must specify one of %s or %s"),
+      error (0, 0, _("you must specify a relative %s with %s"),
              quote_n (0, "--size"), quote_n (1, "--reference"));
       usage (EXIT_FAILURE);
     }
@@ -360,7 +365,10 @@ main (int argc, char **argv)
       struct stat sb;
       if (stat (ref_file, &sb) != 0)
         error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
-      size = sb.st_size;
+      if (!got_size)
+        size = sb.st_size;
+      else
+        rsize = sb.st_size;
     }
 
   oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
@@ -397,7 +405,7 @@ main (int argc, char **argv)
 
       if (fd != -1)
         {
-          errors += do_ftruncate (fd, fname, size, rel_mode);
+          errors += do_ftruncate (fd, fname, size, rsize, rel_mode);
           if (close (fd) != 0)
             {
               error (0, errno, _("closing %s"), quote (fname));
index 6524070..c2f7019 100755 (executable)
@@ -30,7 +30,7 @@ truncate --size=0 && fail=1
 # must specify size. don't default to 0
 truncate file && fail=1
 
-# mixture of size & reference not allowed
+# mixture of absolute size & reference not allowed
 truncate --size=0 --reference=file file && fail=1
 
 # blocks without size is not valid
@@ -45,4 +45,14 @@ truncate --size=" >1" file || fail=1 #file now 1
 truncate --size=" +1" file || fail=1 #file now 2
 test $(stat --format %s file) = 2 || fail=1
 
+# reference allowed with relative size
+truncate --size=" +1" -r file file || fail=1 #file now 3
+test $(stat --format %s file) = 3 || fail=1
+
+# reference allowed alone also
+truncate -r file file || fail=1 #file still 3
+test $(stat --format %s file) = 3 || fail=1
+truncate -r file file2 || fail=1 #file2 now 3
+test $(stat --format %s file2) = 3 || fail=1
+
 Exit $fail