sort: fix hang with sort --compress
[platform/upstream/coreutils.git] / src / truncate.c
index 5085d2c..faa83f1 100644 (file)
 
    This is backwards compatible with the FreeBSD utility, but is more
    flexible wrt the size specifications and the use of long options,
-   to better fit the "GNU" environment.
-
-   Note if !defined(HAVE_FTRUNCATE) then the --skip-ftruncate configure flag
-   was specified or we're in a mingw environment. In these cases gnulib
-   emulation will be used and GNULIB_FTRUNCATE is defined. Note if emulation
-   can't even be provided ftruncate() will return EIO.  */
+   to better fit the "GNU" environment.  */
 
 #include <config.h>             /* sets _FILE_OFFSET_BITS=64 etc. */
 #include <stdio.h>
@@ -117,11 +112,11 @@ Mandatory arguments to long options are mandatory for short options too.\n\
   -c, --no-create        do not create any files\n\
 "), stdout);
       fputs (_("\
-  -o, --io-blocks        Treat SIZE as number of IO blocks instead of bytes\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 ();
@@ -129,26 +124,23 @@ 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);
 }
 
-/* return 1 on error, 0 on success */
-static int
-do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
+/* return true on success, false on error.  */
+static bool
+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;
+      return false;
     }
   if (block_mode)
     {
@@ -160,29 +152,29 @@ do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
                    " * %" PRIdMAX " byte blocks for file %s"),
                  (intmax_t) ssize, (intmax_t) blksize,
                  quote (fname));
-          return 1;
+          return false;
         }
       ssize *= blksize;
     }
   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) /* fstat used above to get size.  */
         {
-          /* Complain only for a regular file, a directory,
-             or a shared memory object, as POSIX 1003.1-2004 specifies
-             ftruncate's behavior only for these file types.  */
-          if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
-              || S_TYPEISSHM (&sb))
+          if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb))
             {
-              /* overflow is the only reason I can think
-                 this would ever go negative for the above types */
+              error (0, 0, _("cannot get the size of %s"), quote (fname));
+              return false;
+            }
+          if (sb.st_size < 0)
+            {
+              /* Sanity check. Overflow is the only reason I can think
+                 this would ever go negative. */
               error (0, 0, _("%s has unusable, apparently negative size"),
                      quote (fname));
-              return 1;
+              return false;
             }
-          return 0;
         }
 
       if (rel_mode == rm_min)
@@ -201,7 +193,7 @@ do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
             {
               error (0, 0, _("overflow rounding up size of file %s"),
                      quote (fname));
-              return 1;
+              return false;
             }
           nsize = overflow;
         }
@@ -211,7 +203,7 @@ do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
             {
               error (0, 0, _("overflow extending size of file %s"),
                      quote (fname));
-              return 1;
+              return false;
             }
           nsize = fsize + ssize;
         }
@@ -223,39 +215,25 @@ do_ftruncate (int fd, char const *fname, off_t ssize, rel_mode_t rel_mode)
 
   if (ftruncate (fd, nsize) == -1)      /* note updates mtime & ctime */
     {
-      /* Complain only when ftruncate fails on a regular file, a
-         directory, or a shared memory object, as POSIX 1003.1-2004
-         specifies ftruncate's behavior only for these file types.
-         For example, do not complain when Linux kernel 2.4 ftruncate
-         fails on /dev/fd0.  */
-      int const ftruncate_errno = errno;
-      if (fstat (fd, &sb) != 0)
-        {
-          error (0, errno, _("cannot fstat %s"), quote (fname));
-          return 1;
-        }
-      else if (S_ISREG (sb.st_mode) || S_ISDIR (sb.st_mode)
-               || S_TYPEISSHM (&sb))
-        {
-          error (0, ftruncate_errno,
-                 _("truncating %s at %" PRIdMAX " bytes"), quote (fname),
-                 (intmax_t) nsize);
-          return 1;
-        }
-      return 0;
+      error (0, errno,
+             _("failed to truncate %s at %" PRIdMAX " bytes"), quote (fname),
+             (intmax_t) nsize);
+      return false;
     }
 
-  return 0;
+  return true;
 }
 
 int
 main (int argc, char **argv)
 {
   bool got_size = false;
-  off_t size IF_LINT (= 0);
+  bool errors = 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;
+  int c, fd = -1, oflags;
   char const *fname;
 
   initialize_main (&argc, &argv);
@@ -340,9 +318,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 one of %s or %s"),
+      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 a relative %s with %s"),
              quote_n (0, "--size"), quote_n (1, "--reference"));
       usage (EXIT_FAILURE);
     }
@@ -362,10 +347,17 @@ main (int argc, char **argv)
 
   if (ref_file)
     {
+      /* FIXME: Maybe support getting size of block devices.  */
       struct stat sb;
       if (stat (ref_file, &sb) != 0)
         error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
-      size = sb.st_size;
+      if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb))
+        error (EXIT_FAILURE, 0, _("cannot get the size of %s"),
+               quote (ref_file));
+      if (!got_size)
+        size = sb.st_size;
+      else
+        rsize = sb.st_size;
     }
 
   oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
@@ -381,20 +373,9 @@ main (int argc, char **argv)
              `truncate -s0 .` should gen EISDIR error */
           if (!(no_create && errno == ENOENT))
             {
-              int const open_errno = errno;
-              struct stat sb;
-              if (stat (fname, &sb) == 0)
-                {
-                  /* Complain only for a regular file, a directory,
-                     or a shared memory object, as POSIX 1003.1-2004 specifies
-                     ftruncate's behavior only for these file types.  */
-                  if (!S_ISREG (sb.st_mode) && !S_ISDIR (sb.st_mode)
-                      && !S_TYPEISSHM (&sb))
-                    continue;
-                }
-              error (0, open_errno, _("cannot open %s for writing"),
+              error (0, errno, _("cannot open %s for writing"),
                      quote (fname));
-              errors++;
+              errors = true;
             }
           continue;
         }
@@ -402,11 +383,11 @@ 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));
-              errors++;
+              errors = true;
             }
         }
     }