mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-25 11:37:56 +00:00
Add the -t option, which allows tab stop positions to be specified similarly
to the way expand(1) allows. This brings unexpand(1) up to SUSv3 conformance, and it now passes all relevant parts of the GNU textutils test suite. PR: 35621 Reviewed by: mike
This commit is contained in:
parent
92ab402302
commit
aca29625a8
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=95304
@ -32,7 +32,7 @@
|
|||||||
.\" @(#)expand.1 8.1 (Berkeley) 6/9/93
|
.\" @(#)expand.1 8.1 (Berkeley) 6/9/93
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd June 9, 1993
|
.Dd April 21, 2002
|
||||||
.Dt EXPAND 1
|
.Dt EXPAND 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -51,6 +51,12 @@
|
|||||||
.Op Ar
|
.Op Ar
|
||||||
.Nm unexpand
|
.Nm unexpand
|
||||||
.Op Fl a
|
.Op Fl a
|
||||||
|
.Oo
|
||||||
|
.Fl t
|
||||||
|
.Sm off
|
||||||
|
.Ar tab1 , tab2 , ... , tabn
|
||||||
|
.Sm on
|
||||||
|
.Oc
|
||||||
.Op Ar
|
.Op Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
@ -89,6 +95,13 @@ are reconverted to maximal strings of tabs. If the
|
|||||||
option is given, then tabs are inserted whenever they would compress the
|
option is given, then tabs are inserted whenever they would compress the
|
||||||
resultant file by replacing two or more characters.
|
resultant file by replacing two or more characters.
|
||||||
.El
|
.El
|
||||||
|
.Sh STANDARDS
|
||||||
|
The
|
||||||
|
.Nm expand
|
||||||
|
and
|
||||||
|
.Nm unexpand
|
||||||
|
utilities conform to
|
||||||
|
.St -p1003.1-2001 .
|
||||||
.Sh HISTORY
|
.Sh HISTORY
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
|
@ -49,45 +49,53 @@ static const char sccsid[] = "@(#)unexpand.c 8.1 (Berkeley) 6/6/93";
|
|||||||
* unexpand - put tabs into a file replacing blanks
|
* unexpand - put tabs into a file replacing blanks
|
||||||
*/
|
*/
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
char genbuf[BUFSIZ];
|
|
||||||
char linebuf[BUFSIZ];
|
|
||||||
int all;
|
int all;
|
||||||
|
int nstops;
|
||||||
|
int tabstops[100];
|
||||||
|
|
||||||
|
static void getstops(const char *);
|
||||||
static void usage(void);
|
static void usage(void);
|
||||||
void tabify(char);
|
static void tabify(void);
|
||||||
|
|
||||||
int
|
int
|
||||||
main(argc, argv)
|
main(argc, argv)
|
||||||
int argc;
|
int argc;
|
||||||
char *argv[];
|
char *argv[];
|
||||||
{
|
{
|
||||||
register char *cp;
|
int ch;
|
||||||
|
|
||||||
argc--, argv++;
|
nstops = 1;
|
||||||
if (argc > 0 && argv[0][0] == '-') {
|
tabstops[0] = 8;
|
||||||
if (strcmp(argv[0], "-a") != 0)
|
while ((ch = getopt(argc, argv, "at:")) != -1) {
|
||||||
|
switch (ch) {
|
||||||
|
case 'a': /* Un-expand all spaces, not just leading. */
|
||||||
|
all = 1;
|
||||||
|
break;
|
||||||
|
case 't': /* Specify tab list, implies -a. */
|
||||||
|
getstops(optarg);
|
||||||
|
all = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
usage();
|
usage();
|
||||||
all++;
|
/*NOTREACHED*/
|
||||||
argc--, argv++;
|
}
|
||||||
}
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (argc > 0) {
|
if (argc > 0) {
|
||||||
if (freopen(argv[0], "r", stdin) == NULL)
|
if (freopen(argv[0], "r", stdin) == NULL)
|
||||||
err(1, "%s", argv[0]);
|
err(1, "%s", argv[0]);
|
||||||
argc--, argv++;
|
argc--, argv++;
|
||||||
}
|
}
|
||||||
while (fgets(genbuf, BUFSIZ, stdin) != NULL) {
|
tabify();
|
||||||
for (cp = linebuf; *cp; cp++)
|
|
||||||
continue;
|
|
||||||
if (cp > linebuf)
|
|
||||||
cp[-1] = 0;
|
|
||||||
tabify(all);
|
|
||||||
printf("%s", linebuf);
|
|
||||||
}
|
|
||||||
} while (argc > 0);
|
} while (argc > 0);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
@ -95,52 +103,111 @@ main(argc, argv)
|
|||||||
static void
|
static void
|
||||||
usage()
|
usage()
|
||||||
{
|
{
|
||||||
fprintf(stderr, "usage: unexpand [-a] file ...\n");
|
fprintf(stderr, "usage: unexpand [-a] [-t tablist] [file ...]\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
tabify(c)
|
tabify()
|
||||||
char c;
|
|
||||||
{
|
{
|
||||||
register char *cp, *dp;
|
int ch, dcol, doneline, limit, n, ocol;
|
||||||
register int dcol;
|
|
||||||
int ocol;
|
|
||||||
|
|
||||||
ocol = 0;
|
limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
|
||||||
dcol = 0;
|
|
||||||
cp = genbuf, dp = linebuf;
|
|
||||||
for (;;) {
|
|
||||||
switch (*cp) {
|
|
||||||
|
|
||||||
case ' ':
|
doneline = ocol = dcol = 0;
|
||||||
dcol++;
|
while ((ch = getchar()) != EOF) {
|
||||||
break;
|
if (ch == '\n') {
|
||||||
|
putchar('\n');
|
||||||
|
doneline = ocol = dcol = 0;
|
||||||
|
continue;
|
||||||
|
} else if (ch == ' ' && !doneline) {
|
||||||
|
if (++dcol >= limit)
|
||||||
|
doneline = 1;
|
||||||
|
continue;
|
||||||
|
} else if (ch == '\b' && dcol > 0) {
|
||||||
|
dcol--;
|
||||||
|
} else if (ch == '\t') {
|
||||||
|
if (nstops == 1) {
|
||||||
|
dcol = (1 + dcol / tabstops[0]) *
|
||||||
|
tabstops[0];
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
for (n = 0; tabstops[n] - 1 < dcol &&
|
||||||
|
n < nstops; n++)
|
||||||
|
;
|
||||||
|
if (n < nstops - 1 && tabstops[n] - 1 < limit) {
|
||||||
|
dcol = tabstops[n];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
doneline = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case '\t':
|
/* Output maximal number of tabs. */
|
||||||
dcol += 8;
|
if (nstops == 1) {
|
||||||
dcol &= ~07;
|
while (((ocol + tabstops[0]) / tabstops[0])
|
||||||
break;
|
<= (dcol / tabstops[0])) {
|
||||||
|
if (dcol - ocol < 2)
|
||||||
default:
|
|
||||||
while (((ocol + 8) &~ 07) <= dcol) {
|
|
||||||
if (ocol + 1 == dcol)
|
|
||||||
break;
|
break;
|
||||||
*dp++ = '\t';
|
putchar('\t');
|
||||||
ocol += 8;
|
ocol = (1 + ocol / tabstops[0]) *
|
||||||
ocol &= ~07;
|
tabstops[0];
|
||||||
}
|
}
|
||||||
while (ocol < dcol) {
|
} else {
|
||||||
*dp++ = ' ';
|
for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
|
||||||
ocol++;
|
;
|
||||||
|
while (ocol < dcol && n < nstops && ocol < limit) {
|
||||||
|
putchar('\t');
|
||||||
|
ocol = tabstops[n++];
|
||||||
}
|
}
|
||||||
if (*cp == 0 || c == 0) {
|
}
|
||||||
strcpy(dp, cp);
|
|
||||||
return;
|
/* Then spaces. */
|
||||||
}
|
while (ocol < dcol && ocol < limit) {
|
||||||
*dp++ = *cp;
|
putchar(' ');
|
||||||
|
ocol++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch != ' ' || dcol > limit) {
|
||||||
|
putchar(ch);
|
||||||
ocol++, dcol++;
|
ocol++, dcol++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Only processing leading blanks or we've gone past the
|
||||||
|
* last tab stop. Emit remainder of this line unchanged.
|
||||||
|
*/
|
||||||
|
if (!all || dcol >= limit) {
|
||||||
|
while ((ch = getchar()) != '\n' && ch != EOF)
|
||||||
|
putchar(ch);
|
||||||
|
if (ch == '\n')
|
||||||
|
ungetc(ch, stdin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
getstops(cp)
|
||||||
|
const char *cp;
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nstops = 0;
|
||||||
|
for (;;) {
|
||||||
|
i = 0;
|
||||||
|
while (*cp >= '0' && *cp <= '9')
|
||||||
|
i = i * 10 + *cp++ - '0';
|
||||||
|
if (i <= 0)
|
||||||
|
errx(1, "bad tab stop spec");
|
||||||
|
if (nstops > 0 && i <= tabstops[nstops-1])
|
||||||
|
errx(1, "bad tab stop spec");
|
||||||
|
if (nstops == sizeof(tabstops) / sizeof(*tabstops))
|
||||||
|
errx(1, "too many tab stops");
|
||||||
|
tabstops[nstops++] = i;
|
||||||
|
if (*cp == 0)
|
||||||
|
break;
|
||||||
|
if (*cp != ',' && *cp != ' ')
|
||||||
|
errx(1, "bad tab stop spec");
|
||||||
cp++;
|
cp++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user