unexpand.c revision 95304
11553Srgrimes/*-
21553Srgrimes * Copyright (c) 1980, 1993
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#include <sys/cdefs.h>
3530113Sjkh
361553Srgrimes__FBSDID("$FreeBSD: head/usr.bin/unexpand/unexpand.c 95304 2002-04-23 07:15:09Z tjr $");
371553Srgrimes
381553Srgrimes#ifndef lint
391553Srgrimesstatic const char copyright[] =
401553Srgrimes"@(#) Copyright (c) 1980, 1993\n\
411553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
421553Srgrimes#endif
431553Srgrimes
441553Srgrimes#ifndef lint
451553Srgrimesstatic const char sccsid[] = "@(#)unexpand.c	8.1 (Berkeley) 6/6/93";
461553Srgrimes#endif
471553Srgrimes
481553Srgrimes/*
491553Srgrimes * unexpand - put tabs into a file replacing blanks
501553Srgrimes */
511553Srgrimes#include <err.h>
521553Srgrimes#include <limits.h>
531553Srgrimes#include <stdio.h>
541553Srgrimes#include <stdlib.h>
551553Srgrimes#include <string.h>
561553Srgrimes#include <unistd.h>
571553Srgrimes
581553Srgrimesint	all;
591553Srgrimesint	nstops;
601553Srgrimesint	tabstops[100];
611553Srgrimes
621553Srgrimesstatic void getstops(const char *);
636972Sachestatic void usage(void);
646972Sachestatic void tabify(void);
651553Srgrimes
661553Srgrimesint
676972Sachemain(argc, argv)
686972Sache	int argc;
696972Sache	char *argv[];
706972Sache{
716972Sache	int ch;
726972Sache
736972Sache	nstops = 1;
746972Sache	tabstops[0] = 8;
756972Sache	while ((ch = getopt(argc, argv, "at:")) != -1) {
761553Srgrimes		switch (ch) {
771553Srgrimes		case 'a':	/* Un-expand all spaces, not just leading. */
781553Srgrimes			all = 1;
791553Srgrimes			break;
801553Srgrimes		case 't':	/* Specify tab list, implies -a. */
811553Srgrimes			getstops(optarg);
821553Srgrimes			all = 1;
831553Srgrimes			break;
841553Srgrimes		default:
851553Srgrimes			usage();
861553Srgrimes			/*NOTREACHED*/
871553Srgrimes		}
881553Srgrimes	}
891553Srgrimes	argc -= optind;
901553Srgrimes	argv += optind;
911553Srgrimes
921553Srgrimes	do {
931553Srgrimes		if (argc > 0) {
941553Srgrimes			if (freopen(argv[0], "r", stdin) == NULL)
951553Srgrimes				err(1, "%s", argv[0]);
961553Srgrimes			argc--, argv++;
971553Srgrimes		}
981553Srgrimes		tabify();
996972Sache	} while (argc > 0);
1001553Srgrimes	exit(0);
1011553Srgrimes}
1021553Srgrimes
1031553Srgrimesstatic void
1041553Srgrimesusage()
1051553Srgrimes{
1061553Srgrimes	fprintf(stderr, "usage: unexpand [-a] [-t tablist] [file ...]\n");
1071553Srgrimes	exit(1);
1088857Srgrimes}
1091553Srgrimes
1101553Srgrimesstatic void
1111553Srgrimestabify()
1121553Srgrimes{
1131553Srgrimes	int ch, dcol, doneline, limit, n, ocol;
1141553Srgrimes
1151553Srgrimes	limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
1161553Srgrimes
1171553Srgrimes	doneline = ocol = dcol = 0;
1181553Srgrimes	while ((ch = getchar()) != EOF) {
1191553Srgrimes		if (ch == '\n') {
1201553Srgrimes			putchar('\n');
1211553Srgrimes			doneline = ocol = dcol = 0;
1221553Srgrimes			continue;
1231553Srgrimes		} else if (ch == ' ' && !doneline) {
1241553Srgrimes			if (++dcol >= limit)
1251553Srgrimes				doneline = 1;
1261553Srgrimes			continue;
1271553Srgrimes		} else if (ch == '\b' && dcol > 0) {
1281553Srgrimes			dcol--;
12930113Sjkh		} else if (ch == '\t') {
1301553Srgrimes			if (nstops == 1) {
1311553Srgrimes				dcol = (1 + dcol / tabstops[0]) *
1321553Srgrimes				    tabstops[0];
1331553Srgrimes				continue;
1341553Srgrimes			} else {
1351553Srgrimes				for (n = 0; tabstops[n] - 1 < dcol &&
1361553Srgrimes				    n < nstops; n++)
1371553Srgrimes					;
1381553Srgrimes				if (n < nstops - 1 && tabstops[n] - 1 < limit) {
1391553Srgrimes					dcol = tabstops[n];
1401553Srgrimes					continue;
14116876Sguido				}
14216876Sguido				doneline = 1;
1431553Srgrimes			}
1441553Srgrimes		}
1451553Srgrimes
1461553Srgrimes		/* Output maximal number of tabs. */
1471553Srgrimes		if (nstops == 1) {
1481553Srgrimes			while (((ocol + tabstops[0]) / tabstops[0])
14916876Sguido			    <= (dcol / tabstops[0])) {
15028662Sjoerg				if (dcol - ocol < 2)
15116876Sguido					break;
15216876Sguido				putchar('\t');
15328662Sjoerg				ocol = (1 + ocol / tabstops[0]) *
15416876Sguido				    tabstops[0];
15516876Sguido			}
15616876Sguido		} else {
1571553Srgrimes			for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
1581553Srgrimes				;
1591553Srgrimes			while (ocol < dcol && n < nstops && ocol < limit) {
1601553Srgrimes				putchar('\t');
1611553Srgrimes				ocol = tabstops[n++];
1621553Srgrimes			}
1631553Srgrimes		}
1641553Srgrimes
1651553Srgrimes		/* Then spaces. */
1661553Srgrimes		while (ocol < dcol && ocol < limit) {
1671553Srgrimes			putchar(' ');
1681553Srgrimes			ocol++;
1691553Srgrimes		}
1701553Srgrimes
1711553Srgrimes		if (ch != ' ' || dcol > limit) {
1721553Srgrimes			putchar(ch);
1731553Srgrimes			ocol++, dcol++;
1741553Srgrimes		}
17530113Sjkh
1761553Srgrimes		/*
1778857Srgrimes		 * Only processing leading blanks or we've gone past the
1781553Srgrimes		 * last tab stop. Emit remainder of this line unchanged.
1791553Srgrimes		 */
1806972Sache		if (!all || dcol >= limit) {
1811553Srgrimes			while ((ch = getchar()) != '\n' && ch != EOF)
1821553Srgrimes				putchar(ch);
1831553Srgrimes			if (ch == '\n')
1841553Srgrimes				ungetc(ch, stdin);
1851553Srgrimes		}
1861553Srgrimes	}
1871553Srgrimes}
1886972Sache
1896972Sachestatic void
1906972Sachegetstops(cp)
1916972Sache	const char *cp;
1926972Sache{
1936972Sache	int i;
1946972Sache
1956972Sache	nstops = 0;
1966972Sache	for (;;) {
1976972Sache		i = 0;
1986972Sache		while (*cp >= '0' && *cp <= '9')
1996972Sache			i = i * 10 + *cp++ - '0';
2001553Srgrimes		if (i <= 0)
2011553Srgrimes			errx(1, "bad tab stop spec");
2021553Srgrimes		if (nstops > 0 && i <= tabstops[nstops-1])
2031553Srgrimes			errx(1, "bad tab stop spec");
2041553Srgrimes		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
20529980Swosch			errx(1, "too many tab stops");
2061553Srgrimes		tabstops[nstops++] = i;
2071553Srgrimes		if (*cp == 0)
2081553Srgrimes			break;
20929980Swosch		if (*cp != ',' && *cp != ' ')
21029980Swosch			errx(1, "bad tab stop spec");
21129980Swosch		cp++;
21229980Swosch	}
2131553Srgrimes}
2141553Srgrimes