Index: head/usr.bin/truncate/tests/truncate_test.sh =================================================================== --- head/usr.bin/truncate/tests/truncate_test.sh (revision 336139) +++ head/usr.bin/truncate/tests/truncate_test.sh (revision 336140) @@ -1,399 +1,462 @@ # # Copyright 2014, Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # * Neither the name of Google Inc. nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # $FreeBSD$ # # Helper function that is always used to create and fill stderr.txt for these # tests. _custom_create_file() { # The first argument is a command. # The second is just a string. case "${1}" in creat) > stderr.txt ;; print) [ "${2}" ] && \ printf "%s\n" "${2}" >> stderr.txt ;; esac } # Helper function that create the file stderr.txt that contains the string # passed in as the first argument. create_stderr_file() { _custom_create_file creat _custom_create_file print "${1}" } # Helper function that create the file stderr.txt that contains the expected # truncate utility usage message. create_stderr_usage_file() { _custom_create_file creat _custom_create_file print "${1}" _custom_create_file print \ - "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g|T|t] file ..." + "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ..." _custom_create_file print " truncate [-c] -r rfile file ..." } atf_test_case illegal_option illegal_option_head() { atf_set "descr" "Verifies that truncate exits >0 when passed an" \ "invalid command line option" } illegal_option_body() { create_stderr_usage_file 'truncate: illegal option -- 7' # We expect the error message, with no new files. atf_check -s not-exit:0 -e file:stderr.txt truncate -7 -s0 output.txt [ ! -e output.txt ] || atf_fail "output.txt should not exist" } atf_test_case illegal_size illegal_size_head() { atf_set "descr" "Verifies that truncate exits >0 when passed an" \ "invalid power of two convention" } illegal_size_body() { create_stderr_file "truncate: invalid size argument \`+1L'" # We expect the error message, with no new files. atf_check -s not-exit:0 -e file:stderr.txt truncate -s+1L output.txt [ ! -e output.txt ] || atf_fail "output.txt should not exist" } atf_test_case too_large_size too_large_size_head() { atf_set "descr" "Verifies that truncate exits >0 when passed an" \ "a size that is INT64_MAX < size <= UINT64_MAX" } too_large_size_body() { create_stderr_file "truncate: invalid size argument \`8388608t'" # We expect the error message, with no new files. atf_check -s not-exit:0 -e file:stderr.txt \ truncate -s8388608t output.txt [ ! -e output.txt ] || atf_fail "output.txt should not exist" } atf_test_case opt_c opt_c_head() { atf_set "descr" "Verifies that -c prevents creation of new files" } opt_c_body() { # No new files and truncate returns 0 as if this is a success. atf_check truncate -c -s 0 doesnotexist.txt [ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist" > reference atf_check truncate -c -r reference doesnotexist.txt [ ! -e output.txt ] || atf_fail "doesnotexist.txt should not exist" create_stderr_file # The existing file will be altered by truncate. > exists.txt atf_check -e file:stderr.txt truncate -c -s1 exists.txt [ -s exists.txt ] || atf_fail "exists.txt be larger than zero bytes" } atf_test_case opt_rs opt_rs_head() { atf_set "descr" "Verifies that truncate command line flags" \ "-s and -r cannot be specifed together" } opt_rs_body() { create_stderr_usage_file # Force an error due to the use of both -s and -r. > afile atf_check -s not-exit:0 -e file:stderr.txt truncate -s0 -r afile afile } atf_test_case no_files no_files_head() { atf_set "descr" "Verifies that truncate needs a list of files on" \ "the command line" } no_files_body() { create_stderr_usage_file # A list of files must be present on the command line. atf_check -s not-exit:0 -e file:stderr.txt truncate -s1 } atf_test_case bad_refer bad_refer_head() { atf_set "descr" "Verifies that truncate detects a non-existent" \ "reference file" } bad_refer_body() { create_stderr_file "truncate: afile: No such file or directory" # The reference file must exist before you try to use it. atf_check -s not-exit:0 -e file:stderr.txt truncate -r afile afile [ ! -e afile ] || atf_fail "afile should not exist" } atf_test_case bad_truncate bad_truncate_head() { atf_set "descr" "Verifies that truncate reports an error during" \ "truncation" atf_set "require.user" "unprivileged" } bad_truncate_body() { create_stderr_file "truncate: exists.txt: Permission denied" # Trying to get the ftruncate() call to return -1. > exists.txt atf_check chmod 444 exists.txt atf_check -s not-exit:0 -e file:stderr.txt truncate -s1 exists.txt } atf_test_case new_absolute_grow new_absolute_grow_head() { atf_set "descr" "Verifies truncate can make and grow a new 1m file" } new_absolute_grow_body() { create_stderr_file # Create a new file and grow it to 1024 bytes. atf_check -s exit:0 -e file:stderr.txt truncate -s1k output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" create_stderr_file # Grow the existing file to 1M. We are using absolute sizes. atf_check -s exit:0 -e file:stderr.txt truncate -c -s1M output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" } atf_test_case new_absolute_shrink new_absolute_shrink_head() { atf_set "descr" "Verifies that truncate can make and" \ "shrink a new 1m file" } new_absolute_shrink_body() { create_stderr_file # Create a new file and grow it to 1048576 bytes. atf_check -s exit:0 -e file:stderr.txt truncate -s1M output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" create_stderr_file # Shrink the existing file to 1k. We are using absolute sizes. atf_check -s exit:0 -e file:stderr.txt truncate -s1k output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" } atf_test_case new_relative_grow new_relative_grow_head() { atf_set "descr" "Verifies truncate can make and grow a new 1m file" \ "using relative sizes" } new_relative_grow_body() { create_stderr_file # Create a new file and grow it to 1024 bytes. atf_check -s exit:0 -e file:stderr.txt truncate -s+1k output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" create_stderr_file # Grow the existing file to 1M. We are using relative sizes. atf_check -s exit:0 -e file:stderr.txt truncate -s+1047552 output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1048576 ] || atf_fail "expected file size of 1m" } atf_test_case new_relative_shrink new_relative_shrink_head() { atf_set "descr" "Verifies truncate can make and shrink a new 1m file" \ "using relative sizes" } new_relative_shrink_body() { create_stderr_file # Create a new file and grow it to 1049600 bytes. atf_check -s exit:0 -e file:stderr.txt truncate -s+1049600 output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1049600 ] || atf_fail "expected file size of 1m" create_stderr_file # Shrink the existing file to 1k. We are using relative sizes. atf_check -s exit:0 -e file:stderr.txt truncate -s-1M output.txt atf_check -s exit:1 cmp -s output.txt /dev/zero eval $(stat -s output.txt) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" } atf_test_case cannot_open cannot_open_head() { atf_set "descr" "Verifies truncate handles open failures correctly" \ "in a list of files" atf_set "require.user" "unprivileged" } cannot_open_body() { # Create three files -- the middle file cannot allow writes. > before > 0000 > after atf_check chmod 0000 0000 create_stderr_file "truncate: 0000: Permission denied" # Create a new file and grow it to 1024 bytes. atf_check -s not-exit:0 -e file:stderr.txt \ truncate -c -s1k before 0000 after eval $(stat -s before) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" eval $(stat -s after) [ ${st_size} -eq 1024 ] || atf_fail "expected file size of 1k" eval $(stat -s 0000) [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" } atf_test_case reference reference_head() { atf_set "descr" "Verifies that truncate can use a reference file" } reference_body() { # Create a 4 byte reference file. printf "123\n" > reference eval $(stat -s reference) [ ${st_size} -eq 4 ] || atf_fail "reference file should be 4 bytes" create_stderr_file # Create a new file and grow it to 4 bytes. atf_check -e file:stderr.txt truncate -r reference afile eval $(stat -s afile) [ ${st_size} -eq 4 ] || atf_fail "new file should also be 4 bytes" } atf_test_case new_zero new_zero_head() { atf_set "descr" "Verifies truncate can make and grow zero byte file" } new_zero_body() { create_stderr_file # Create a new file and grow it to zero bytes. atf_check -s exit:0 -e file:stderr.txt truncate -s0 output.txt eval $(stat -s output.txt) [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" # Pretend to grow the file. atf_check -s exit:0 -e file:stderr.txt truncate -s+0 output.txt eval $(stat -s output.txt) [ ${st_size} -eq 0 ] || atf_fail "expected file size of zero" } atf_test_case negative negative_head() { atf_set "descr" "Verifies truncate treats negative sizes as zero" } negative_body() { # Create a 5 byte file. printf "abcd\n" > afile eval $(stat -s afile) [ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes" create_stderr_file # Create a new file and do a 100 byte negative relative shrink. atf_check -e file:stderr.txt truncate -s-100 afile eval $(stat -s afile) [ ${st_size} -eq 0 ] || atf_fail "new file should now be zero bytes" } +atf_test_case roundup +roundup_head() +{ + atf_set "descr" "Verifies truncate round up" +} +roundup_body() +{ + # Create a 5 byte file. + printf "abcd\n" > afile + eval $(stat -s afile) + [ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes" + + create_stderr_file + + # Create a new file and do a 100 byte roundup. + atf_check -e file:stderr.txt truncate -s%100 afile + eval $(stat -s afile) + [ ${st_size} -eq 100 ] || atf_fail "new file should now be 100 bytes" +} + +atf_test_case rounddown +rounddown_head() +{ + atf_set "descr" "Verifies truncate round down" +} +rounddown_body() +{ + # Create a 5 byte file. + printf "abcd\n" > afile + eval $(stat -s afile) + [ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes" + + create_stderr_file + + # Create a new file and do a 2 byte roundup. + atf_check -e file:stderr.txt truncate -s/2 afile + eval $(stat -s afile) + [ ${st_size} -eq 4 ] || atf_fail "new file should now be 4 bytes" +} + +atf_test_case rounddown_zero +rounddown_zero_head() +{ + atf_set "descr" "Verifies truncate round down to zero" +} +rounddown_zero_body() +{ + # Create a 5 byte file. + printf "abcd\n" > afile + eval $(stat -s afile) + [ ${st_size} -eq 5 ] || atf_fail "afile file should be 5 bytes" + + create_stderr_file + + # Create a new file and do a 10 byte roundup. + atf_check -e file:stderr.txt truncate -s/10 afile + eval $(stat -s afile) + [ ${st_size} -eq 0 ] || atf_fail "new file should now be 0 bytes" +} + atf_init_test_cases() { atf_add_test_case illegal_option atf_add_test_case illegal_size atf_add_test_case too_large_size atf_add_test_case opt_c atf_add_test_case opt_rs atf_add_test_case no_files atf_add_test_case bad_refer atf_add_test_case bad_truncate atf_add_test_case cannot_open atf_add_test_case new_absolute_grow atf_add_test_case new_absolute_shrink atf_add_test_case new_relative_grow atf_add_test_case new_relative_shrink atf_add_test_case reference atf_add_test_case new_zero atf_add_test_case negative + atf_add_test_case roundup + atf_add_test_case rounddown + atf_add_test_case rounddown_zero } Index: head/usr.bin/truncate/truncate.1 =================================================================== --- head/usr.bin/truncate/truncate.1 (revision 336139) +++ head/usr.bin/truncate/truncate.1 (revision 336140) @@ -1,152 +1,163 @@ .\" .\" Copyright (c) 2000 Sheldon Hearn . .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" .Dd December 19, 2006 .Dt TRUNCATE 1 .Os .Sh NAME .Nm truncate .Nd truncate or extend the length of files .Sh SYNOPSIS .Nm .Op Fl c .Bk -words .Fl s Xo .Sm off -.Op Cm + | - +.Op Cm + | - | % | / .Ar size .Op Cm K | k | M | m | G | g | T | t .Sm on .Xc .Ek .Ar .Nm .Op Fl c .Bk -words .Fl r Ar rfile .Ek .Ar .Sh DESCRIPTION The .Nm utility adjusts the length of each regular file given on the command-line. .Pp The following options are available: .Bl -tag -width indent .It Fl c Do not create files if they do not exist. The .Nm utility does not treat this as an error. No error messages are displayed and the exit value is not affected. .It Fl r Ar rfile Truncate or extend files to the length of the file .Ar rfile . .It Fl s Xo .Sm off -.Op Cm + | - +.Op Cm + | - | % | / .Ar size .Op Cm K | k | M | m | G | g | T | t .Sm on .Xc If the .Ar size argument is preceded by a plus sign .Pq Cm + , files will be extended by this number of bytes. If the .Ar size argument is preceded by a dash .Pq Cm - , file lengths will be reduced by no more than this number of bytes, +to a minimum length of zero bytes. +If the +.Ar size +argument is preceded by a percent sign +.Pq Cm % , +files will be round up to a multiple of this number of bytes. +If the +.Ar size +argument is preceded by a slash sign +.Pq Cm / , +files will be round down to a multiple of this number of bytes, to a minimum length of zero bytes. Otherwise, the .Ar size argument specifies an absolute length to which all files should be extended or reduced as appropriate. .Pp The .Ar size argument may be suffixed with one of .Cm K , .Cm M , .Cm G or .Cm T (either upper or lower case) to indicate a multiple of Kilobytes, Megabytes, Gigabytes or Terabytes respectively. .El .Pp Exactly one of the .Fl r and .Fl s options must be specified. .Pp If a file is made smaller, its extra data is lost. If a file is made larger, it will be extended as if by writing bytes with the value zero. If the file does not exist, it is created unless the .Fl c option is specified. .Pp Note that, while truncating a file causes space on disk to be freed, extending a file does not cause space to be allocated. To extend a file and actually allocate the space, it is necessary to explicitly write data to it, using (for example) the shell's .Ql >> redirection syntax, or .Xr dd 1 . .Sh EXIT STATUS .Ex -std If the operation fails for an argument, .Nm will issue a diagnostic and continue processing the remaining arguments. .Sh SEE ALSO .Xr dd 1 , .Xr touch 1 , .Xr truncate 2 .Sh STANDARDS The .Nm utility conforms to no known standards. .Sh HISTORY The .Nm utility first appeared in .Fx 4.2 . .Sh AUTHORS The .Nm utility was written by .An Sheldon Hearn Aq Mt sheldonh@starjuice.net . Index: head/usr.bin/truncate/truncate.c =================================================================== --- head/usr.bin/truncate/truncate.c (revision 336139) +++ head/usr.bin/truncate/truncate.c (revision 336140) @@ -1,164 +1,191 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2000 Sheldon Hearn . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif #include #include #include #include #include #include #include #include #include static void usage(void); int main(int argc, char **argv) { struct stat sb; mode_t omode; off_t oflow, rsize, sz, tsize; uint64_t usz; int ch, error, fd, oflags; int no_create; int do_relative; + int do_round; int do_refer; int got_size; + int round; char *fname, *rname; fd = -1; rsize = tsize = sz = 0; - no_create = do_relative = do_refer = got_size = 0; + no_create = do_relative = do_round = do_refer = got_size = 0; error = 0; rname = NULL; while ((ch = getopt(argc, argv, "cr:s:")) != -1) switch (ch) { case 'c': no_create = 1; break; case 'r': do_refer = 1; rname = optarg; break; case 's': - do_relative = *optarg == '+' || *optarg == '-'; - if (expand_number(do_relative ? optarg + 1 : optarg, + if (*optarg == '+' || *optarg == '-') { + do_relative = 1; + } else if (*optarg == '%' || *optarg == '/') { + do_round = 1; + } + if (expand_number(do_relative || do_round ? + optarg + 1 : optarg, &usz) == -1 || (off_t)usz < 0) errx(EXIT_FAILURE, "invalid size argument `%s'", optarg); - sz = (*optarg == '-') ? -(off_t)usz : (off_t)usz; + sz = (*optarg == '-' || *optarg == '/') ? + -(off_t)usz : (off_t)usz; got_size = 1; break; default: usage(); /* NOTREACHED */ } argv += optind; argc -= optind; /* * Exactly one of do_refer or got_size must be specified. Since * do_relative implies got_size, do_relative and do_refer are * also mutually exclusive. See usage() for allowed invocations. */ if (do_refer + got_size != 1 || argc < 1) usage(); if (do_refer) { if (stat(rname, &sb) == -1) err(EXIT_FAILURE, "%s", rname); tsize = sb.st_size; - } else if (do_relative) + } else if (do_relative || do_round) rsize = sz; else tsize = sz; if (no_create) oflags = O_WRONLY; else oflags = O_WRONLY | O_CREAT; omode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; while ((fname = *argv++) != NULL) { if (fd != -1) close(fd); if ((fd = open(fname, oflags, omode)) == -1) { if (errno != ENOENT) { warn("%s", fname); error++; } continue; } if (do_relative) { if (fstat(fd, &sb) == -1) { warn("%s", fname); error++; continue; } oflow = sb.st_size + rsize; if (oflow < (sb.st_size + rsize)) { errno = EFBIG; warn("%s", fname); error++; continue; } tsize = oflow; } + if (do_round) { + if (fstat(fd, &sb) == -1) { + warn("%s", fname); + error++; + continue; + } + sz = rsize; + if (sz < 0) + sz = -sz; + if (sb.st_size % sz) { + round = sb.st_size / sz; + if (round != sz && rsize < 0) + round--; + else if (rsize > 0) + round++; + tsize = (round < 0 ? 0 : round) * sz; + } else + tsize = sb.st_size; + } if (tsize < 0) tsize = 0; if (ftruncate(fd, tsize) == -1) { warn("%s", fname); error++; continue; } } if (fd != -1) close(fd); return error ? EXIT_FAILURE : EXIT_SUCCESS; } static void usage(void) { fprintf(stderr, "%s\n%s\n", - "usage: truncate [-c] -s [+|-]size[K|k|M|m|G|g|T|t] file ...", + "usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...", " truncate [-c] -r rfile file ..."); exit(EXIT_FAILURE); }