/* truncate -- truncate or extend the length of files.
- Copyright (C) 2008-2010 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 "posixver.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")
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);
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);
}
-/* return 1 on error, 0 on success */
-static int
+/* 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)
{
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)
{
" * %" 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 = 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 1;
+ 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 1;
+ 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)
{
error (0, 0, _("overflow rounding up size of file %s"),
quote (fname));
- return 1;
+ return false;
}
nsize = overflow;
}
{
error (0, 0, _("overflow extending size of file %s"),
quote (fname));
- return 1;
+ return false;
}
nsize = fsize + ssize;
}
error (0, errno,
_("failed to truncate %s at %" PRIdMAX " bytes"), quote (fname),
(intmax_t) nsize);
- return 1;
+ 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);
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"),
quote (fname));
- errors++;
+ errors = true;
}
continue;
}
if (fd != -1)
{
- errors += do_ftruncate (fd, fname, size, rsize, 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;
}
}
}