/* truncate -- truncate or extend the length of files. This is the truncate utility
Copyright (C) 2008-2018 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 <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Written by Pádraig Brady
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. */
#include <config.h> /* sets _FILE_OFFSET_BITS=64 etc. */ Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include "system.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "quote.h" ...!includes auto-comment...
#include "stat-size.h" ...!includes auto-comment...
#include "xdectoint.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "truncate" Line 36
#define AUTHORS proper_name ("Padraig Brady") Line 38
/* (-c) If true, don't create if not already there */
static bool no_create; Line 41
/* (-o) If true, --size refers to blocks not bytes */
static bool block_mode; Line 44
/* (-r) Reference file to use size from */
static char const *ref_file; Line 47
static struct option const longopts[] = Line 49
{
{"no-create", no_argument, NULL, 'c'}, Line 51
{"io-blocks", no_argument, NULL, 'o'}, Line 52
{"reference", required_argument, NULL, 'r'}, Line 53
{"size", required_argument, NULL, 's'}, Line 54
{GETOPT_HELP_OPTION_DECL}, Line 55
{GETOPT_VERSION_OPTION_DECL}, Line 56
{NULL, 0, NULL, 0} Line 57
}; Block 1
typedef enum Line 60
{ rm_abs = 0, rm_rel, rm_min, rm_max, rm_rdn, rm_rup } rel_mode_t; Line 61
void Line 63
usage (int status) Line 64
{
if (status != EXIT_SUCCESS) Line 66
emit_try_help (); ...!common auto-comment...
else Line 68
{
printf (_("Usage: %s OPTION... FILE...\n"), program_name); Line 70
fputs (_("\ Line 71
Shrink or extend the size of each FILE to the specified size\n\ Line 72
\n\
A FILE argument that does not exist is created.\n\ Line 74
\n\
If a FILE is larger than the specified size, the extra data is lost.\n\ Line 76
If a FILE is shorter, it is extended and the extended part (hole)\n\ Line 77
reads as zero bytes.\n\ Line 78
"), stdout); Line 79
emit_mandatory_arg_note (); ...!common auto-comment...
fputs (_("\ Line 83
-c, --no-create do not create any files\n\ Line 84
"), stdout); Line 85
fputs (_("\ Line 86
-o, --io-blocks treat SIZE as number of IO blocks instead of bytes\n\ Line 87
"), stdout); Line 88
fputs (_("\ Line 89
-r, --reference=RFILE base size on RFILE\n\ Line 90
-s, --size=SIZE set or adjust the file size by SIZE bytes\n"), stdout);Line 91
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 92
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 93
emit_size_note (); Line 94
fputs (_("\n\ Line 95
SIZE may also be prefixed by one of the following modifying characters:\n\ Line 96
'+' extend by, '-' reduce by, '<' at most, '>' at least,\n\ Line 97
'/' round down to multiple of, '%' round up to multiple of.\n"), stdout); Line 98
emit_ancillary_info (PROGRAM_NAME); Line 99
}
exit (status); Line 101
} Block 3
/* return true on success, false on error. */
static bool Line 105
do_ftruncate (int fd, char const *fname, off_t ssize, off_t rsize, Line 106...!syscalls auto-comment...
rel_mode_t rel_mode) Line 107
{
struct stat sb; Line 109
off_t nsize; Line 110
if ((block_mode || (rel_mode && rsize < 0)) && fstat (fd, &sb) != 0) Line 112...!syscalls auto-comment......!syscalls auto-comment...
{
error (0, errno, _("cannot fstat %s"), quoteaf (fname)); Line 114
return false; Line 115
}
if (block_mode) Line 117
{
off_t const blksize = ST_BLKSIZE (sb); Line 119
if (ssize < OFF_T_MIN / blksize || ssize > OFF_T_MAX / blksize) Line 120
{
error (0, 0, Line 122
_("overflow in %" PRIdMAX Line 123
" * %" PRIdMAX " byte blocks for file %s"), Line 124
(intmax_t) ssize, (intmax_t) blksize, Line 125
quoteaf (fname)); Line 126
return false; Line 127
}
ssize *= blksize; Line 129
}
if (rel_mode) Line 131
{
uintmax_t fsize; Line 133
if (0 <= rsize) Line 135
fsize = rsize; Line 136
else Line 137
{
off_t file_size; Line 139
if (usable_st_size (&sb)) Line 140
{
file_size = sb.st_size; Line 142
if (file_size < 0) Line 143
{
/* Sanity check. Overflow is the only reason I can think
this would ever go negative. */
error (0, 0, _("%s has unusable, apparently negative size"), Line 147
quoteaf (fname)); Line 148
return false; Line 149
}
}
else Line 152
{
file_size = lseek (fd, 0, SEEK_END); Line 154
if (file_size < 0) Line 155
{
error (0, errno, _("cannot get the size of %s"), Line 157
quoteaf (fname)); Line 158
return false; Line 159
}
}
fsize = file_size; Line 162
}
if (rel_mode == rm_min) Line 165
nsize = MAX (fsize, (uintmax_t) ssize); Line 166
else if (rel_mode == rm_max) Line 167
nsize = MIN (fsize, (uintmax_t) ssize); Line 168
else if (rel_mode == rm_rdn) Line 169
/* 0..ssize-1 -> 0 */
nsize = (fsize / ssize) * ssize; Line 171
else if (rel_mode == rm_rup) Line 172
/* 1..ssize -> ssize */
{
/* Here ssize>=1 && fsize>=0 */
uintmax_t const overflow = ((fsize + ssize - 1) / ssize) * ssize; Line 176
if (overflow > OFF_T_MAX) Line 177
{
error (0, 0, _("overflow rounding up size of file %s"), Line 179
quoteaf (fname)); Line 180
return false; Line 181
}
nsize = overflow; Line 183
}
else Line 185
{
if (ssize > OFF_T_MAX - (off_t)fsize) Line 187
{
error (0, 0, _("overflow extending size of file %s"), Line 189
quoteaf (fname)); Line 190
return false; Line 191
}
nsize = fsize + ssize; Line 193
}
}
else Line 196
nsize = ssize; Line 197
if (nsize < 0) Line 198
nsize = 0; Line 199
if (ftruncate (fd, nsize) == -1) /* note updates mtime & ctime */ Line 201...!syscalls auto-comment...
{
error (0, errno, Line 203
_("failed to truncate %s at %" PRIdMAX " bytes"), quoteaf (fname), Line 204
(intmax_t) nsize); Line 205
return false; Line 206
}
return true; Line 209
} Block 4
int
main (int argc, char **argv) Line 213
{
bool got_size = false; Line 215
bool errors = false; Line 216
off_t size IF_LINT ( = 0); Line 217
off_t rsize = -1; Line 218
rel_mode_t rel_mode = rm_abs; Line 219
int c, fd = -1, oflags; Line 220
char const *fname; Line 221
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdout); Close stdout on exit (see gnulib)
while ((c = getopt_long (argc, argv, "cor:s:", longopts, NULL)) != -1) Line 231
{
switch (c) Line 233
{
case 'c': Line 235
no_create = true; Line 236
break; Line 237
case 'o': Line 239
block_mode = true; Line 240
break; Line 241
case 'r': Line 243
ref_file = optarg; Line 244
break; Line 245
case 's': Line 247
/* skip any whitespace */
while (isspace (to_uchar (*optarg))) Line 249
optarg++; Line 250
switch (*optarg) Line 251
{
case '<': Line 253
rel_mode = rm_max; Line 254
optarg++; Line 255
break; Line 256
case '>': Line 257
rel_mode = rm_min; Line 258
optarg++; Line 259
break; Line 260
case '/': Line 261
rel_mode = rm_rdn; Line 262
optarg++; Line 263
break; Line 264
case '%': Line 265
rel_mode = rm_rup; Line 266
optarg++; Line 267
break; Line 268
}
/* skip any whitespace */
while (isspace (to_uchar (*optarg))) Line 271
optarg++; Line 272
if (*optarg == '+' || *optarg == '-') Line 273
{
if (rel_mode) Line 275
{
error (0, 0, _("multiple relative modifiers specified")); Line 277
/* Note other combinations are flagged as invalid numbers */
usage (EXIT_FAILURE); Line 279
}
rel_mode = rm_rel; Line 281
}
/* Support dd BLOCK size suffixes + lowercase g,t,m for bsd compat.
Note we don't support dd's b=512, c=1, w=2 or 21x512MiB formats. */
size = xdectoimax (optarg, OFF_T_MIN, OFF_T_MAX, "EgGkKmMPtTYZ0", Line 285
_("Invalid number"), 0); Line 286
/* Rounding to multiple of 0 is nonsensical */
if ((rel_mode == rm_rup || rel_mode == rm_rdn) && size == 0) Line 288
die (EXIT_FAILURE, 0, _("division by zero")); Line 289
got_size = true; Line 290
break; Line 291
case_GETOPT_HELP_CHAR; Line 293
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 295
default: Line 297
usage (EXIT_FAILURE); Line 298
}
}
argv += optind; Line 302
argc -= optind; Line 303
/* must specify either size or reference file */
if (!ref_file && !got_size) Line 306
{
error (0, 0, _("you must specify either %s or %s"), Line 308
quote_n (0, "--size"), quote_n (1, "--reference")); Line 309
usage (EXIT_FAILURE); Line 310
}
/* must specify a relative size with a reference file */
if (ref_file && got_size && !rel_mode) Line 313
{
error (0, 0, _("you must specify a relative %s with %s"), Line 315
quote_n (0, "--size"), quote_n (1, "--reference")); Line 316
usage (EXIT_FAILURE); Line 317
}
/* block_mode without size is not valid */
if (block_mode && !got_size) Line 320
{
error (0, 0, _("%s was specified but %s was not"), Line 322
quote_n (0, "--io-blocks"), quote_n (1, "--size")); Line 323
usage (EXIT_FAILURE); Line 324
}
/* must specify at least 1 file */
if (argc < 1) Line 327
{
error (0, 0, _("missing file operand")); Line 329
usage (EXIT_FAILURE); Line 330
}
if (ref_file) Line 333
{
struct stat sb; Line 335
off_t file_size = -1; Line 336
if (stat (ref_file, &sb) != 0) Line 337...!syscalls auto-comment...
die (EXIT_FAILURE, errno, _("cannot stat %s"), quoteaf (ref_file)); Line 338
if (usable_st_size (&sb)) Line 339
file_size = sb.st_size; Line 340
else Line 341
{
int ref_fd = open (ref_file, O_RDONLY); Line 343...!syscalls auto-comment...
if (0 <= ref_fd) Line 344
{
off_t file_end = lseek (ref_fd, 0, SEEK_END); Line 346
int saved_errno = errno; Line 347
close (ref_fd); /* ignore failure */ Line 348...!syscalls auto-comment...
if (0 <= file_end) Line 349
file_size = file_end; Line 350
else Line 351
{
/* restore, in case close clobbered it. */
errno = saved_errno; Line 354
}
}
}
if (file_size < 0) Line 358
die (EXIT_FAILURE, errno, _("cannot get the size of %s"), Line 359
quoteaf (ref_file)); Line 360
if (!got_size) Line 361
size = file_size; Line 362
else Line 363
rsize = file_size; Line 364
}
oflags = O_WRONLY | (no_create ? 0 : O_CREAT) | O_NONBLOCK; Line 367
while ((fname = *argv++) != NULL) Line 369
{
if ((fd = open (fname, oflags, MODE_RW_UGO)) == -1) Line 371...!syscalls auto-comment...
{
/* '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)) Line 377
{
error (0, errno, _("cannot open %s for writing"), Line 379
quoteaf (fname)); Line 380
errors = true; Line 381
}
continue; Line 383
}
if (fd != -1) Line 387
{
errors |= !do_ftruncate (fd, fname, size, rsize, rel_mode); Line 389...!syscalls auto-comment...
if (close (fd) != 0) Line 390...!syscalls auto-comment...
{
error (0, errno, _("failed to close %s"), quoteaf (fname)); Line 392
errors = true; Line 393
}
}
}
return errors ? EXIT_FAILURE : EXIT_SUCCESS; Line 398
} Block 5