maint: update all copyright year number ranges
[platform/upstream/coreutils.git] / src / truncate.c
index b6c7762..bb095fe 100644 (file)
@@ -1,5 +1,5 @@
 /* truncate -- truncate or extend the length of files.
-   Copyright (C) 2008-2011 Free Software Foundation, Inc.
+   Copyright (C) 2008-2013 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
 #include "system.h"
 #include "error.h"
 #include "quote.h"
+#include "stat-size.h"
 #include "xstrtol.h"
 
-/* The official name of this program (e.g., no `g' prefix).  */
+/* The official name of this program (e.g., no 'g' prefix).  */
 #define PROGRAM_NAME "truncate"
 
 #define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
@@ -89,8 +90,7 @@ void
 usage (int status)
 {
   if (status != EXIT_SUCCESS)
-    fprintf (stderr, _("Try `%s --help' for more information.\n"),
-             program_name);
+    emit_try_help ();
   else
     {
       printf (_("Usage: %s OPTION... FILE...\n"), program_name);
@@ -121,8 +121,8 @@ Mandatory arguments to long options are mandatory for short options too.\n\
       emit_size_note ();
       fputs (_("\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);
+'+' extend by, '-' reduce by, '<' at most, '>' at least,\n\
+'/' round down to multiple of, '%' round up to multiple of.\n"), stdout);
       emit_ancillary_info ();
     }
   exit (status);
@@ -157,23 +157,36 @@ do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize,
     }
   if (rel_mode)
     {
-      uintmax_t const fsize = rsize < 0 ? sb.st_size : rsize;
+      uintmax_t fsize;
 
-      if (rsize < 0) /* fstat used above to get size.  */
+      if (0 <= rsize)
+        fsize = rsize;
+      else
         {
-          if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb))
+          off_t file_size;
+          if (usable_st_size (&sb))
             {
-              error (0, 0, _("cannot get the size of %s"), quote (fname));
-              return false;
+              file_size = sb.st_size;
+              if (file_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 false;
+                }
             }
-          if (sb.st_size < 0)
+          else
             {
-              /* 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 false;
+              file_size = lseek (fd, 0, SEEK_END);
+              if (file_size < 0)
+                {
+                  error (0, errno, _("cannot get the size of %s"),
+                         quote (fname));
+                  return false;
+                }
             }
+          fsize = file_size;
         }
 
       if (rel_mode == rm_min)
@@ -231,7 +244,6 @@ main (int argc, char **argv)
   off_t size IF_LINT ( = 0);
   off_t rsize = -1;
   rel_mode_t rel_mode = rm_abs;
-  mode_t omode;
   int c, fd = -1, oflags;
   char const *fname;
 
@@ -346,30 +358,48 @@ main (int argc, char **argv)
 
   if (ref_file)
     {
-      /* FIXME: Maybe support getting size of block devices.  */
       struct stat sb;
+      off_t file_size = -1;
       if (stat (ref_file, &sb) != 0)
         error (EXIT_FAILURE, errno, _("cannot stat %s"), quote (ref_file));
-      if (!S_ISREG (sb.st_mode) && !S_TYPEISSHM (&sb))
-        error (EXIT_FAILURE, 0, _("cannot get the size of %s"),
+      if (usable_st_size (&sb))
+        file_size = sb.st_size;
+      else
+        {
+          int ref_fd = open (ref_file, O_RDONLY);
+          if (0 <= ref_fd)
+            {
+              off_t file_end = lseek (ref_fd, 0, SEEK_END);
+              int saved_errno = errno;
+              close (ref_fd); /* ignore failure */
+              if (0 <= file_end)
+                file_size = file_end;
+              else
+                {
+                  /* restore, in case close clobbered it. */
+                  errno = saved_errno;
+                }
+            }
+        }
+      if (file_size < 0)
+        error (EXIT_FAILURE, errno, _("cannot get the size of %s"),
                quote (ref_file));
       if (!got_size)
-        size = sb.st_size;
+        size = file_size;
       else
-        rsize = sb.st_size;
+        rsize = file_size;
     }
 
   oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK;
-  omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
 
   while ((fname = *argv++) != NULL)
     {
-      if ((fd = open (fname, oflags, omode)) == -1)
+      if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1)
         {
-          /* `truncate -s0 -c no-such-file`  shouldn't gen error
-             `truncate -s0 no-such-dir/file` should gen ENOENT error
-             `truncate -s0 no-such-dir/` should gen EISDIR error
-             `truncate -s0 .` should gen EISDIR error */
+          /* 'truncate -s0 -c no-such-file'  shouldn't gen error
+             'truncate -s0 no-such-dir/file' should gen ENOENT error
+             'truncate -s0 no-such-dir/' should gen EISDIR error
+             'truncate -s0 .' should gen EISDIR error */
           if (!(no_create && errno == ENOENT))
             {
               error (0, errno, _("cannot open %s for writing"),