Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 18 Feb 2002 14:14:15 +1100
From:      Tim Robbins <tim@robbins.dropbear.id.au>
To:        freebsd-standards@FreeBSD.ORG
Subject:   unexpand -t option
Message-ID:  <20020218141415.A33138@descent.robbins.dropbear.id.au>

next in thread | raw e-mail | index | archive | help
This patch adds the -t option to unexpand, makes it use getopt instead of
rolling its own option parser, and just generally cleans it up and simplifies
it.

Now that it no longer buffers output itself, it should be trivial to convert
to support multibyte encoding: make `ch' in tabify() a rune_t, use
fgetrune() and fputrune() instead of getchar()/putchar(). Should I make
this change? (The standard suggests it should handle multibyte encodings
when LC_CTYPE specifies one)

Comments?


Index: unexpand/unexpand.c
===================================================================
RCS file: /home/ncvs/src/usr.bin/unexpand/unexpand.c,v
retrieving revision 1.7
diff -u -r1.7 unexpand.c
--- unexpand/unexpand.c	2001/12/11 23:18:25	1.7
+++ unexpand/unexpand.c	2002/02/18 03:07:21
@@ -52,13 +52,16 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
 
-char	genbuf[BUFSIZ];
-char	linebuf[BUFSIZ];
 int	all;
+int	nstops;
+int	tabstops[100];
 
+static void getstops __P((char *));
 static void usage __P((void));
-void tabify __P((char));
+void tabify __P((void));
 
 int
 main(argc, argv)
@@ -66,28 +69,34 @@
 	char *argv[];
 {
 	register char *cp;
+	int ch;
 
-	argc--, argv++;
-	if (argc > 0 && argv[0][0] == '-') {
-		if (strcmp(argv[0], "-a") != 0)
+	nstops = 1;
+	tabstops[0] = 8;
+	while ((ch = getopt(argc, argv, "at:")) != -1) {
+		switch (ch) {
+		case 'a':
+			all = 1;
+			break;
+		case 't':
+			getstops(optarg);
+			all = 1;
+			break;
+		default:
 			usage();
-		all++;
-		argc--, argv++;
+			/*NOTREACHED*/
+		}
 	}
+	argc -= optind;
+	argv += optind;
+
 	do {
 		if (argc > 0) {
 			if (freopen(argv[0], "r", stdin) == NULL)
-				err(1, "%s", argv[0]);
+				err(EX_NOINPUT, "%s", argv[0]);
 			argc--, argv++;
 		}
-		while (fgets(genbuf, BUFSIZ, stdin) != NULL) {
-			for (cp = linebuf; *cp; cp++)
-				continue;
-			if (cp > linebuf)
-				cp[-1] = 0;
-			tabify(all);
-			printf("%s", linebuf);
-		}
+		tabify();
 	} while (argc > 0);
 	exit(0);
 }
@@ -95,52 +104,98 @@
 static void
 usage()
 {
-	fprintf(stderr, "usage: unexpand [-a] file ...\n");
-	exit(1);
+	fprintf(stderr, "usage: unexpand [-a] [-t tablist] [file ...]\n");
+	exit(EX_USAGE);
 }
 
 void
-tabify(c)
-	char c;
+tabify()
 {
-	register char *cp, *dp;
 	register int dcol;
-	int ocol;
+	int ch, n, ocol;
 
 	ocol = 0;
 	dcol = 0;
-	cp = genbuf, dp = linebuf;
-	for (;;) {
-		switch (*cp) {
-
+	while ((ch = getchar()) != EOF) {
+top:		switch (ch) {
+		case EOF:
+			return;
+		case '\n':
+			putchar('\n');
+			ocol = dcol = 0;
+			break;
 		case ' ':
 			dcol++;
 			break;
-
 		case '\t':
 			dcol += 8;
 			dcol &= ~07;
 			break;
-
+		case '\b':
+			if (dcol > 0)
+				dcol--;
+			/*FALLTHROUGH*/
 		default:
-			while (((ocol + 8) &~ 07) <= dcol) {
-				if (ocol + 1 == dcol)
-					break;
-				*dp++ = '\t';
-				ocol += 8;
-				ocol &= ~07;
+			if (nstops == 1) {
+				while (((ocol + tabstops[0]) / tabstops[0])
+				    <= (dcol / tabstops[0])) {
+					if (dcol - ocol < 2)
+						break;
+					putchar('\t');
+					ocol = (1 + ocol / tabstops[0]) *
+						tabstops[0];
+				}
+			} else {
+				for (n = 0; n < nstops; n++)
+					if (tabstops[n] > ocol)
+						break;
+				for (; n != nstops && tabstops[n] <= dcol;
+				    n++) {
+					putchar('\t');
+					if (n == 0)
+						ocol = tabstops[n];
+					else
+						ocol = tabstops[n] +
+						    (ocol - tabstops[n - 1]);
+				}
 			}
 			while (ocol < dcol) {
-				*dp++ = ' ';
+				putchar(' ');
 				ocol++;
 			}
-			if (*cp == 0 || c == 0) {
-				strcpy(dp, cp);
-				return;
+			putchar(ch);
+			if (!all) {
+				while ((ch = getchar()) != EOF && ch != '\n')
+					putchar(ch);
+				goto top;
 			}
-			*dp++ = *cp;
 			ocol++, dcol++;
 		}
+	}
+}
+
+static void
+getstops(cp)
+	register char *cp;
+{
+	register 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 tabstops");
+		tabstops[nstops++] = i;
+		if (*cp == 0)
+			break;
+		if (*cp != ',' && *cp != ' ')
+			errx(1, "bad tab stop spec");
 		cp++;
 	}
 }



Ugh! The expand(1) and unexpand(1) manual pages are stuck together.

Index: expand/expand.1
===================================================================
RCS file: /home/ncvs/src/usr.bin/expand/expand.1,v
retrieving revision 1.7
diff -u -r1.7 expand.1
--- expand/expand.1	2001/07/10 14:15:57	1.7
+++ expand/expand.1	2002/02/18 02:16:14
@@ -51,6 +51,12 @@
 .Op Ar
 .Nm unexpand
 .Op Fl a
+.Oo
+.Fl t
+.Sm off
+.Ar tab1 , tab2 , ... , tabn
+.Sm on
+.Oc
 .Op Ar
 .Sh DESCRIPTION
 .Nm Expand
@@ -91,3 +97,10 @@
 .Nm
 command appeared in
 .Bx 3.0 .
+.Pp
+The
+.Nm expand
+and
+.Nm unexpand
+utilities are expected to conform to
+.St -p1003.1-2001 .

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-standards" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020218141415.A33138>