truncate: Add support for -s % and /

% round up to the multiple size and / round down
This is compatible with gnu truncate.
Add tests and document in the man page.
This commit is contained in:
manu 2018-07-09 19:03:30 +00:00
parent 930c5c942c
commit 76772b9dc3
3 changed files with 110 additions and 9 deletions

View File

@ -57,7 +57,7 @@ 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 ..."
}
@ -378,6 +378,66 @@ negative_body()
[ ${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
@ -396,4 +456,7 @@ atf_init_test_cases()
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
}

View File

@ -37,7 +37,7 @@
.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
@ -69,7 +69,7 @@ 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
@ -85,6 +85,17 @@ 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

View File

@ -56,13 +56,15 @@ main(int argc, char **argv)
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)
@ -75,13 +77,19 @@ main(int argc, char **argv)
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:
@ -103,7 +111,7 @@ main(int argc, char **argv)
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;
@ -139,6 +147,25 @@ main(int argc, char **argv)
}
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;
@ -158,7 +185,7 @@ 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);
}