Index: usr.bin/uniq/uniq.1 =================================================================== --- usr.bin/uniq/uniq.1 +++ usr.bin/uniq/uniq.1 @@ -31,7 +31,7 @@ .\" From: @(#)uniq.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd May 1, 2018 +.Dd November 6, 2019 .Dt UNIQ 1 .Os .Sh NAME @@ -39,8 +39,7 @@ .Nd report or filter out repeated lines in a file .Sh SYNOPSIS .Nm -.Op Fl c -.Op Fl d | Fl u +.Op Fl c | Fl d | Fl D | Fl u .Op Fl i .Op Fl f Ar num .Op Fl s Ar chars @@ -75,7 +74,24 @@ Precede each output line with the count of the number of times the line occurred in the input, followed by a single space. .It Fl d , Fl -repeated -Only output lines that are repeated in the input. +Output a single copy of each line that is repeated in the input. +.It Fl D , Fl -all-repeated Op Ar septype +Output all lines that are repeated (like +.Fl d , +but each copy of the repeated line is written). +The optional +.Ar septype +argument controls how to separate groups of repeated lines in the output; +it must be one of the following values: +.Pp +.Bl -tag -compact -width separate +.It none +Do not separate groups of lines (this is the default). +.It prepend +Output an empty line before each group of lines. +.It separate +Output an empty line after each group of lines. +.El .It Fl f Ar num , Fl -skip-fields Ar num Ignore the first .Ar num Index: usr.bin/uniq/uniq.c =================================================================== --- usr.bin/uniq/uniq.c +++ usr.bin/uniq/uniq.c @@ -65,11 +65,18 @@ #include #include -static int cflag, dflag, uflag, iflag; +static int Dflag, cflag, dflag, uflag, iflag; static int numchars, numfields, repeats; +/* Dflag values */ +#define DF_NONE 0 +#define DF_NOSEP 1 +#define DF_PRESEP 2 +#define DF_POSTSEP 3 + static const struct option long_opts[] = { + {"all-repeated",optional_argument, NULL, 'D'}, {"count", no_argument, NULL, 'c'}, {"repeated", no_argument, NULL, 'd'}, {"skip-fields", required_argument, NULL, 'f'}, @@ -101,9 +108,19 @@ (void) setlocale(LC_ALL, ""); obsolete(argv); - while ((ch = getopt_long(argc, argv, "+cdif:s:u", long_opts, + while ((ch = getopt_long(argc, argv, "+D::cdif:s:u", long_opts, NULL)) != -1) switch (ch) { + case 'D': + if (optarg == NULL || strcasecmp(optarg, "none") == 0) + Dflag = DF_NOSEP; + else if (strcasecmp(optarg, "prepend") == 0) + Dflag = DF_PRESEP; + else if (strcasecmp(optarg, "separate") == 0) + Dflag = DF_POSTSEP; + else + usage(); + break; case 'c': cflag = 1; break; @@ -194,7 +211,10 @@ if (comp) { /* If different, print; set previous to new value. */ - show(ofp, prevline); + if (Dflag == DF_POSTSEP && repeats > 0) + fputc('\n', ofp); + if (!Dflag) + show(ofp, prevline); p = prevline; b1 = prevbuflen; prevline = thisline; @@ -206,12 +226,22 @@ thisbuflen = b1; tthis = NULL; repeats = 0; - } else + } else { + if (Dflag) { + if (repeats == 0) { + if (Dflag == DF_PRESEP) + fputc('\n', ofp); + show(ofp, prevline); + } + show(ofp, thisline); + } ++repeats; + } } if (ferror(ifp)) err(1, "%s", ifn); - show(ofp, prevline); + if (!Dflag) + show(ofp, prevline); exit(0); } @@ -276,7 +306,7 @@ show(FILE *ofp, const char *str) { - if ((dflag && repeats == 0) || (uflag && repeats > 0)) + if ((!Dflag && dflag && repeats == 0) || (uflag && repeats > 0)) return; if (cflag) (void)fprintf(ofp, "%4d %s", repeats + 1, str); @@ -343,6 +373,6 @@ usage(void) { (void)fprintf(stderr, -"usage: uniq [-c] [-d | -u] [-i] [-f fields] [-s chars] [input [output]]\n"); +"usage: uniq [-c | -d | -D | -u] [-i] [-f fields] [-s chars] [input [output]]\n"); exit(1); }