From nobody Wed Jul 13 04:14:38 2022 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 36ACF17FC9B9; Wed, 13 Jul 2022 04:14:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4LjPQl1KP3z44Lv; Wed, 13 Jul 2022 04:14:39 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1657685679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=xOvTwlMUwFLDo+comRj16pdavf+8MiJo367y9o+oYkA=; b=pIyBArWVPOFEmY7ybsAzpVHLflTgIEQxkgWCN403CFv2oKr8ht1+7ZUDHsdwUOZcseAvtS kTZ/0s0kEOGARHMSqLQ1+4AS7I4ZjI9I3bBTCxPTriGpqqRL7cBuGtlJwmwsQ8RzT9uaov 6xSuX7LlAveC+uZhzfMpIdzyT1jO/NjkJQ2NJCIjGuYNewJlg6AiJuCp3xvDri+ZuNpFk2 AOn5qphZ6oLpFRBXPxkPPyBVOFtCD1t8Trb7t+7YUYI2QtucWMD07Je8VENTFcMSBVDw7x 7TC/Qiteb2PwleC1ufnrqFSi9ja09Q5j7NJXM/adIjhVclkfIupUisKAzebivA== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4LjPQl0MFNz18cY; Wed, 13 Jul 2022 04:14:39 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 26D4EcWL035976; Wed, 13 Jul 2022 04:14:38 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 26D4EcVP035975; Wed, 13 Jul 2022 04:14:38 GMT (envelope-from git) Date: Wed, 13 Jul 2022 04:14:38 GMT Message-Id: <202207130414.26D4EcVP035975@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Xin LI Subject: git: 643ac419fafb - main - Improve usability of head(1) and tail(1): List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: delphij X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 643ac419fafba89f5adda0e0ea75b538727453fb Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1657685679; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=xOvTwlMUwFLDo+comRj16pdavf+8MiJo367y9o+oYkA=; b=FXrbBzZhe8rv5tt7bQC4dIe5DeCl+ne5pfsQjHUyh1ucj6fFi7xuVB/Rowd9LRwlyxrnMv jn3/Dyfy3iQ5yMV1uiQ8emf7cefcV57LPu0Mx7OK3JynvFZqBZZGmCTfQSQxBuYKnrxMAj hYgLMVU0DG+ORnMnfT22jW32X9LtOGfGj4b4RUEVsIZR0lcip2A+qa8nvi+AMKfUZh0e2a Sj0EMw6wbmd68CThax97hONF5KGmbv32k5b4x/7tknkvBtMu28ryz2DQsOkLT2/ejEfov9 qtKkk8BwLBxfvpOPcpTH3NEuRNk+pb4Us4gPEn8Nb5qK1dD3Iw18fsHf2bSD7A== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1657685679; a=rsa-sha256; cv=none; b=yVPKOvLXx3Msw6Njn6wlk1EycctZb391AN1hgdXtQCeQIycrcMk4uXcytDEF6I7EDifSdx h6kMYPkKC5t/Tbns/xzqVbo+BbO+4Wdfh9dfhuWSQJX/sWTaHeMCQm3ObvkRPFvuuECSKc 7S/Drh2i/5axTt1fSc4U4h5X9c8m1zeGSw/dtsuDS8kYNjQNPFk6EEI2UCRqJzRUqrvCSd 1XzLb1sT29KvQVOSPPYGGMegaw6uqUDUeMSmY9osANJ3p9xYXn8FekVQAjIUzHdZDzaO0F UqMfu3L2M6LdzlqgFoZ9JHJTbK+CvJqaLYxFPRObEDB5DpHo6b1rl7sSigvLbQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch main has been updated by delphij: URL: https://cgit.FreeBSD.org/src/commit/?id=643ac419fafba89f5adda0e0ea75b538727453fb commit 643ac419fafba89f5adda0e0ea75b538727453fb Author: Xin LI AuthorDate: 2022-07-13 04:14:25 +0000 Commit: Xin LI CommitDate: 2022-07-13 04:14:25 +0000 Improve usability of head(1) and tail(1): - Consistently support -q (quiet) and -v (verbose) - Allow specifying numbers with SI prefixes supported by expand_number(3) - Remove 2^31 limit on lines for head(1) MFC after: 2 weeks Reviewed by: lwhsu, pauamma, gbe Relnotes: yes Differential Revision: https://reviews.freebsd.org/D35720 --- usr.bin/head/Makefile | 1 + usr.bin/head/head.1 | 24 +++++++++++++++++++--- usr.bin/head/head.c | 33 +++++++++++++++++++++--------- usr.bin/head/tests/head_test.sh | 45 +++++++++++++++++++++++++++++++++++++++++ usr.bin/tail/Makefile | 1 + usr.bin/tail/extern.h | 2 +- usr.bin/tail/forward.c | 6 +++--- usr.bin/tail/tail.1 | 25 ++++++++++++++++------- usr.bin/tail/tail.c | 22 ++++++++++++++------ usr.bin/tail/tests/tail_test.sh | 44 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 173 insertions(+), 30 deletions(-) diff --git a/usr.bin/head/Makefile b/usr.bin/head/Makefile index b966f8165795..0c9056924969 100644 --- a/usr.bin/head/Makefile +++ b/usr.bin/head/Makefile @@ -4,6 +4,7 @@ .include PROG= head +LIBADD= util HAS_TESTS= SUBDIR.${MK_TESTS}+= tests diff --git a/usr.bin/head/head.1 b/usr.bin/head/head.1 index c74983822916..3968e7283b17 100644 --- a/usr.bin/head/head.1 +++ b/usr.bin/head/head.1 @@ -28,7 +28,7 @@ .\" @(#)head.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd April 10, 2018 +.Dd June 12, 2022 .Dt HEAD 1 .Os .Sh NAME @@ -36,6 +36,7 @@ .Nd display first lines of a file .Sh SYNOPSIS .Nm +.Op Fl qv .Op Fl n Ar count | Fl c Ar bytes .Op Ar .Sh DESCRIPTION @@ -59,14 +60,30 @@ of each of the specified files. Print .Ar count lines of each of the specified files. +.Pp +Both +.Ar count +and +.Ar bytes +may also be specified with size suffixes supported by +.Xr expand_number 3 . +.It Fl q , Fl -quiet , Fl -silent +Suppresses printing of headers when multiple files are being examined. +.It Fl v , Fl -verbose +Prepend each file with a header. .El .Pp -If more than a single file is specified, each file is preceded by a +If more than a single file is specified, or if the +.Fl v +option is used, each file is preceded by a header consisting of the string .Dq ==> XXX <== where .Dq XXX is the name of the file. +The +.Fl q +flag disables the printing of the header in all cases. .Sh EXIT STATUS .Ex -std .Sh EXAMPLES @@ -83,7 +100,8 @@ in the following way to, for example, display only line 500 from the file .Pp .Dl $ head -n 500 foo | tail -n 1 .Sh SEE ALSO -.Xr tail 1 +.Xr tail 1 , +.Xr expand_number 3 .Sh HISTORY The .Nm diff --git a/usr.bin/head/head.c b/usr.bin/head/head.c index 0226dd96d9a6..1c6368823e7c 100644 --- a/usr.bin/head/head.c +++ b/usr.bin/head/head.c @@ -57,6 +57,8 @@ __FBSDID("$FreeBSD$"); #include #include +#include + #include #include @@ -66,7 +68,7 @@ __FBSDID("$FreeBSD$"); * Bill Joy UCB August 24, 1977 */ -static void head(FILE *, int); +static void head(FILE *, intmax_t); static void head_bytes(FILE *, off_t); static void obsolete(char *[]); static void usage(void); @@ -75,6 +77,9 @@ static const struct option long_opts[] = { {"bytes", required_argument, NULL, 'c'}, {"lines", required_argument, NULL, 'n'}, + {"quiet", no_argument, NULL, 'q'}, + {"silent", no_argument, NULL, 'q'}, + {"verbose", no_argument, NULL, 'v'}, {NULL, no_argument, NULL, 0} }; @@ -82,29 +87,37 @@ int main(int argc, char *argv[]) { FILE *fp; - char *ep; off_t bytecnt; - int ch, first, linecnt, eval; + intmax_t linecnt; + int ch, first, eval; fileargs_t *fa; cap_rights_t rights; + int qflag = 0; + int vflag = 0; linecnt = -1; eval = 0; bytecnt = -1; obsolete(argv); - while ((ch = getopt_long(argc, argv, "+n:c:", long_opts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "+n:c:qv", long_opts, NULL)) != -1) { switch(ch) { case 'c': - bytecnt = strtoimax(optarg, &ep, 10); - if (*ep || bytecnt <= 0) + if (expand_number(optarg, &bytecnt) || bytecnt <= 0) errx(1, "illegal byte count -- %s", optarg); break; case 'n': - linecnt = strtol(optarg, &ep, 10); - if (*ep || linecnt <= 0) + if (expand_number(optarg, &linecnt) || linecnt <= 0) errx(1, "illegal line count -- %s", optarg); break; + case 'q': + qflag = 1; + vflag = 0; + break; + case 'v': + qflag = 0; + vflag = 1; + break; case '?': default: usage(); @@ -134,7 +147,7 @@ main(int argc, char *argv[]) eval = 1; continue; } - if (argc > 1) { + if (vflag || (qflag == 0 && argc > 1)) { (void)printf("%s==> %s <==\n", first ? "" : "\n", *argv); first = 0; @@ -155,7 +168,7 @@ main(int argc, char *argv[]) } static void -head(FILE *fp, int cnt) +head(FILE *fp, intmax_t cnt) { char *cp; size_t error, readlen; diff --git a/usr.bin/head/tests/head_test.sh b/usr.bin/head/tests/head_test.sh index 1fc3cb10b455..9d1b97507c12 100755 --- a/usr.bin/head/tests/head_test.sh +++ b/usr.bin/head/tests/head_test.sh @@ -119,6 +119,48 @@ read_from_stdin_body() { atf_check cmp outfile expectfile } +atf_test_case silent_header +silent_header_head() { + atf_set "descr" "Test head(1)'s silent header feature" +} +silent_header_body() { + #head(1) defaults to head -n 10 if no args are given. + jot 11 1 11 > file1 + jot 11 2 12 > file2 + jot 10 1 10 > expectfile + jot 10 2 11 >> expectfile + head -q file1 file2 > outfile + atf_check cmp outfile expectfile +} + +atf_test_case verbose_header +verbose_header_head() { + atf_set "descr" "Test head(1)'s verbose header feature" +} +verbose_header_body() { + #head(1) defaults to head -n 10 if no args are given. + jot -b test 10 > file1 + echo '==> file1 <==' > expectfile + cat file1 >> expectfile + head -v file1 > outfile + atf_check cmp outfile expectfile +} + +atf_test_case si_number +si_number_head() { + atf_set "descr" "Test head(1)'s SI number feature" +} +si_number_body() { + jot -b aaaaaaa 129 > file1 + jot -b aaaaaaa 128 > expectfile + head -c 1k file1 > outfile + atf_check cmp outfile expectfile + jot 1025 1 1025 > file1 + jot 1024 1 1024 > expectfile + head -n 1k file1 > outfile + atf_check cmp outfile expectfile +} + atf_init_test_cases() { atf_add_test_case empty_file atf_add_test_case default_no_options @@ -129,4 +171,7 @@ atf_init_test_cases() { atf_add_test_case missing_line_count atf_add_test_case invalid_line_count atf_add_test_case read_from_stdin + atf_add_test_case silent_header + atf_add_test_case verbose_header + atf_add_test_case si_number } diff --git a/usr.bin/tail/Makefile b/usr.bin/tail/Makefile index 5d53b23957a7..b53d9d2c215f 100644 --- a/usr.bin/tail/Makefile +++ b/usr.bin/tail/Makefile @@ -5,6 +5,7 @@ PROG= tail SRCS= forward.c misc.c read.c reverse.c tail.c +LIBADD= util .if ${MK_CASPER} != "no" && !defined(RESCUE) LIBADD+= casper diff --git a/usr.bin/tail/extern.h b/usr.bin/tail/extern.h index 3d8c12629682..d78aa37dc2ff 100644 --- a/usr.bin/tail/extern.h +++ b/usr.bin/tail/extern.h @@ -77,5 +77,5 @@ int mapprint(struct mapinfo *, off_t, off_t); int maparound(struct mapinfo *, off_t); void printfn(const char *, int); -extern int Fflag, fflag, qflag, rflag, rval, no_files; +extern int Fflag, fflag, qflag, rflag, rval, no_files, vflag; extern fileargs_t *fa; diff --git a/usr.bin/tail/forward.c b/usr.bin/tail/forward.c index 878cb5a4550b..3897d115effd 100644 --- a/usr.bin/tail/forward.c +++ b/usr.bin/tail/forward.c @@ -246,8 +246,8 @@ show(file_info_t *file) int ch; while ((ch = getc(file->fp)) != EOF) { - if (last != file && no_files > 1) { - if (!qflag) + if (last != file) { + if (vflag || (qflag == 0 && no_files > 1)) printfn(file->file_name, 1); last = file; } @@ -325,7 +325,7 @@ follow(file_info_t *files, enum STYLE style, off_t off) if (file->fp) { active = 1; n++; - if (no_files > 1 && !qflag) + if (vflag || (qflag == 0 && no_files > 1)) printfn(file->file_name, 1); forward(file->fp, file->file_name, style, off, &file->st); if (Fflag && fileno(file->fp) != STDIN_FILENO) diff --git a/usr.bin/tail/tail.1 b/usr.bin/tail/tail.1 index 771d72c56989..59b632868dbd 100644 --- a/usr.bin/tail/tail.1 +++ b/usr.bin/tail/tail.1 @@ -31,7 +31,7 @@ .\" @(#)tail.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd March 22, 2020 +.Dd July 12, 2022 .Dt TAIL 1 .Os .Sh NAME @@ -40,7 +40,7 @@ .Sh SYNOPSIS .Nm .Op Fl F | f | r -.Op Fl q +.Op Fl qv .Oo .Fl b Ar number | Fl c Ar number | Fl n Ar number .Oc @@ -116,7 +116,7 @@ option if reading from standard input rather than a file. The location is .Ar number lines. -.It Fl q +.It Fl q, Fl -quiet, Fl -silent Suppresses printing of headers when multiple files are being examined. .It Fl r The @@ -135,16 +135,26 @@ from the beginning or end of the input from which to begin the display. The default for the .Fl r option is to display all of the input. +.It Fl v, Fl -verbose +Prepend each file with a header. .El .Pp -If more than a single file is specified, each file is preceded by a +If more than a single file is specified, or if the +.Fl v +option is used, each file is preceded by a header consisting of the string .Dq Li "==> " Ns Ar XXX Ns Li " <==" where .Ar XXX -is the name of the file unless +is the name of the file. +The .Fl q -flag is specified. +flag disables the printing of the header in all cases. +.Pp +All +.Ar number +arguments may also be specified with size suffixes supported by +.Xr expand_number 3 . .Sh EXIT STATUS .Ex -std .Sh EXAMPLES @@ -161,7 +171,8 @@ open, displaying to the standard output anything appended to the file: .Sh SEE ALSO .Xr cat 1 , .Xr head 1 , -.Xr sed 1 +.Xr sed 1 , +.Xr expand_number 3 .Sh STANDARDS The .Nm diff --git a/usr.bin/tail/tail.c b/usr.bin/tail/tail.c index 874557f105ec..51598d451bed 100644 --- a/usr.bin/tail/tail.c +++ b/usr.bin/tail/tail.c @@ -59,12 +59,14 @@ static const char sccsid[] = "@(#)tail.c 8.1 (Berkeley) 6/6/93"; #include #include +#include + #include #include #include "extern.h" -int Fflag, fflag, qflag, rflag, rval, no_files; +int Fflag, fflag, qflag, rflag, rval, no_files, vflag; fileargs_t *fa; static void obsolete(char **); @@ -75,6 +77,9 @@ static const struct option long_opts[] = {"blocks", required_argument, NULL, 'b'}, {"bytes", required_argument, NULL, 'c'}, {"lines", required_argument, NULL, 'n'}, + {"quiet", no_argument, NULL, 'q'}, + {"silent", no_argument, NULL, 'q'}, + {"verbose", no_argument, NULL, 'v'}, {NULL, no_argument, NULL, 0} }; @@ -88,7 +93,6 @@ main(int argc, char *argv[]) enum STYLE style; int ch, first; file_info_t file, *filep, *files; - char *p; cap_rights_t rights; /* @@ -106,8 +110,9 @@ main(int argc, char *argv[]) #define ARG(units, forward, backward) { \ if (style) \ usage(); \ - off = strtoll(optarg, &p, 10) * (units); \ - if (*p) \ + if (expand_number(optarg, &off)) \ + err(1, "illegal offset -- %s", optarg); \ + if (off > INT64_MAX / units || off < INT64_MIN / units ) \ errx(1, "illegal offset -- %s", optarg); \ switch(optarg[0]) { \ case '+': \ @@ -127,7 +132,7 @@ main(int argc, char *argv[]) obsolete(argv); style = NOTSET; off = 0; - while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qr", long_opts, NULL)) != + while ((ch = getopt_long(argc, argv, "+Fb:c:fn:qrv", long_opts, NULL)) != -1) switch(ch) { case 'F': /* -F is superset of (and implies) -f */ @@ -147,10 +152,15 @@ main(int argc, char *argv[]) break; case 'q': qflag = 1; + vflag = 0; break; case 'r': rflag = 1; break; + case 'v': + vflag = 1; + qflag = 0; + break; case '?': default: usage(); @@ -230,7 +240,7 @@ main(int argc, char *argv[]) ierr(fn); continue; } - if (argc > 1 && !qflag) { + if (vflag || (qflag == 0 && argc > 1)) { printfn(fn, !first); first = 0; } diff --git a/usr.bin/tail/tests/tail_test.sh b/usr.bin/tail/tests/tail_test.sh index 66d435f2cd02..e8263864f28c 100755 --- a/usr.bin/tail/tests/tail_test.sh +++ b/usr.bin/tail/tests/tail_test.sh @@ -352,6 +352,47 @@ follow_rename_body() atf_check kill $pid } +atf_test_case silent_header +silent_header_head() { + atf_set "descr" "Test tail(1)'s silent header feature" +} +silent_header_body() { + jot 11 1 11 > file1 + jot 11 2 12 > file2 + jot 10 2 11 > expectfile + jot 10 3 12 >> expectfile + tail -q file1 file2 > outfile + atf_check cmp outfile expectfile +} + +atf_test_case verbose_header +verbose_header_head() { + atf_set "descr" "Test tail(1)'s verbose header feature" +} +verbose_header_body() { + jot 11 1 11 > file1 + echo '==> file1 <==' > expectfile + jot 10 2 11 >> expectfile + tail -v file1 > outfile + atf_check cmp outfile expectfile +} + +atf_test_case si_number +si_number_head() { + atf_set "descr" "Test tail(1)'s SI number feature" +} +si_number_body() { + jot -b aaaaaaa 129 > file1 + jot -b aaaaaaa 128 > expectfile + tail -c 1k file1 > outfile + atf_check cmp outfile expectfile + jot 1025 1 1025 > file1 + jot 1024 2 1025 > expectfile + tail -n 1k file1 > outfile + atf_check cmp outfile expectfile +} + + atf_init_test_cases() { atf_add_test_case empty_r @@ -372,4 +413,7 @@ atf_init_test_cases() atf_add_test_case follow atf_add_test_case follow_stdin atf_add_test_case follow_rename + atf_add_test_case silent_header + atf_add_test_case verbose_header + atf_add_test_case si_number }