unexpand.c revision 102944
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 1980, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
3487697Smarkm#include <sys/cdefs.h>
3587697Smarkm
3687697Smarkm__FBSDID("$FreeBSD: head/usr.bin/unexpand/unexpand.c 102944 2002-09-04 23:29:10Z dwmalone $");
3787697Smarkm
381590Srgrimes#ifndef lint
3928456Scharnierstatic const char copyright[] =
401590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
411590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
4287697Smarkm#endif
431590Srgrimes
441590Srgrimes#ifndef lint
4587697Smarkmstatic const char sccsid[] = "@(#)unexpand.c	8.1 (Berkeley) 6/6/93";
4628456Scharnier#endif
471590Srgrimes
481590Srgrimes/*
491590Srgrimes * unexpand - put tabs into a file replacing blanks
501590Srgrimes */
5198251Stjr#include <ctype.h>
5228456Scharnier#include <err.h>
5395304Stjr#include <limits.h>
5498251Stjr#include <locale.h>
551590Srgrimes#include <stdio.h>
5678717Sdd#include <stdlib.h>
5733645Sjb#include <string.h>
5895304Stjr#include <unistd.h>
591590Srgrimes
601590Srgrimesint	all;
6195304Stjrint	nstops;
6295304Stjrint	tabstops[100];
631590Srgrimes
6495304Stjrstatic void getstops(const char *);
6592922Simpstatic void usage(void);
6695304Stjrstatic void tabify(void);
6728456Scharnier
6828789Scharnierint
69102944Sdwmalonemain(int argc, char *argv[])
701590Srgrimes{
7195474Stjr	int ch, failed;
7295474Stjr	char *filename;
731590Srgrimes
7498251Stjr	setlocale(LC_CTYPE, "");
7598251Stjr
7695304Stjr	nstops = 1;
7795304Stjr	tabstops[0] = 8;
7895304Stjr	while ((ch = getopt(argc, argv, "at:")) != -1) {
7995304Stjr		switch (ch) {
8095304Stjr		case 'a':	/* Un-expand all spaces, not just leading. */
8195304Stjr			all = 1;
8295304Stjr			break;
8395304Stjr		case 't':	/* Specify tab list, implies -a. */
8495304Stjr			getstops(optarg);
8595304Stjr			all = 1;
8695304Stjr			break;
8795304Stjr		default:
8828456Scharnier			usage();
8995304Stjr			/*NOTREACHED*/
9095304Stjr		}
911590Srgrimes	}
9295304Stjr	argc -= optind;
9395304Stjr	argv += optind;
9495304Stjr
9595474Stjr	failed = 0;
9695474Stjr	if (argc == 0)
9795474Stjr		tabify();
9895474Stjr	else {
9995474Stjr		while ((filename = *argv++) != NULL) {
10095474Stjr			if (freopen(filename, "r", stdin) == NULL) {
10195474Stjr				warn("%s", filename);
10295474Stjr				failed++;
10395474Stjr			} else
10495474Stjr				tabify();
1051590Srgrimes		}
10695474Stjr	}
10795474Stjr	exit(failed != 0);
1081590Srgrimes}
1091590Srgrimes
11028456Scharnierstatic void
111102944Sdwmaloneusage(void)
11228456Scharnier{
11395304Stjr	fprintf(stderr, "usage: unexpand [-a] [-t tablist] [file ...]\n");
11428456Scharnier	exit(1);
11528456Scharnier}
11628456Scharnier
11795304Stjrstatic void
118102944Sdwmalonetabify(void)
1191590Srgrimes{
12095304Stjr	int ch, dcol, doneline, limit, n, ocol;
1211590Srgrimes
12295304Stjr	limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
1231590Srgrimes
12495304Stjr	doneline = ocol = dcol = 0;
12595304Stjr	while ((ch = getchar()) != EOF) {
12695474Stjr		if (ch == ' ' && !doneline) {
12795304Stjr			if (++dcol >= limit)
12895304Stjr				doneline = 1;
12995304Stjr			continue;
13095304Stjr		} else if (ch == '\t') {
13195304Stjr			if (nstops == 1) {
13295304Stjr				dcol = (1 + dcol / tabstops[0]) *
13395304Stjr				    tabstops[0];
13495304Stjr				continue;
13595304Stjr			} else {
13695304Stjr				for (n = 0; tabstops[n] - 1 < dcol &&
13795304Stjr				    n < nstops; n++)
13895304Stjr					;
13995304Stjr				if (n < nstops - 1 && tabstops[n] - 1 < limit) {
14095304Stjr					dcol = tabstops[n];
14195304Stjr					continue;
14295304Stjr				}
14395304Stjr				doneline = 1;
14495304Stjr			}
14595304Stjr		}
1461590Srgrimes
14795304Stjr		/* Output maximal number of tabs. */
14895304Stjr		if (nstops == 1) {
14995304Stjr			while (((ocol + tabstops[0]) / tabstops[0])
15095304Stjr			    <= (dcol / tabstops[0])) {
15195304Stjr				if (dcol - ocol < 2)
1521590Srgrimes					break;
15395304Stjr				putchar('\t');
15495304Stjr				ocol = (1 + ocol / tabstops[0]) *
15595304Stjr				    tabstops[0];
1561590Srgrimes			}
15795304Stjr		} else {
15895304Stjr			for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
15995304Stjr				;
16095304Stjr			while (ocol < dcol && n < nstops && ocol < limit) {
16195304Stjr				putchar('\t');
16295304Stjr				ocol = tabstops[n++];
1631590Srgrimes			}
16495304Stjr		}
16595304Stjr
16695304Stjr		/* Then spaces. */
16795304Stjr		while (ocol < dcol && ocol < limit) {
16895304Stjr			putchar(' ');
16995304Stjr			ocol++;
17095304Stjr		}
17195304Stjr
17295474Stjr		if (ch == '\b') {
17395474Stjr			putchar('\b');
17495474Stjr			if (ocol > 0)
17595474Stjr				ocol--, dcol--;
17695474Stjr		} else if (ch == '\n') {
17795474Stjr			putchar('\n');
17895474Stjr			doneline = ocol = dcol = 0;
17995474Stjr		} else if (ch != ' ' || dcol > limit) {
18095304Stjr			putchar(ch);
18198251Stjr			if (isprint(ch))
18298251Stjr				ocol++, dcol++;
1831590Srgrimes		}
18495304Stjr
18595304Stjr		/*
18695304Stjr		 * Only processing leading blanks or we've gone past the
18795304Stjr		 * last tab stop. Emit remainder of this line unchanged.
18895304Stjr		 */
18995304Stjr		if (!all || dcol >= limit) {
19095304Stjr			while ((ch = getchar()) != '\n' && ch != EOF)
19195304Stjr				putchar(ch);
19295304Stjr			if (ch == '\n')
19395474Stjr				putchar('\n');
19495474Stjr			doneline = ocol = dcol = 0;
19595304Stjr		}
19695304Stjr	}
19795304Stjr}
19895304Stjr
19995304Stjrstatic void
200102944Sdwmalonegetstops(const char *cp)
20195304Stjr{
20295304Stjr	int i;
20395304Stjr
20495304Stjr	nstops = 0;
20595304Stjr	for (;;) {
20695304Stjr		i = 0;
20795304Stjr		while (*cp >= '0' && *cp <= '9')
20895304Stjr			i = i * 10 + *cp++ - '0';
20995304Stjr		if (i <= 0)
21095304Stjr			errx(1, "bad tab stop spec");
21195304Stjr		if (nstops > 0 && i <= tabstops[nstops-1])
21295304Stjr			errx(1, "bad tab stop spec");
21395304Stjr		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
21495304Stjr			errx(1, "too many tab stops");
21595304Stjr		tabstops[nstops++] = i;
21695304Stjr		if (*cp == 0)
21795304Stjr			break;
21898251Stjr		if (*cp != ',' && !isblank((unsigned char)*cp))
21995304Stjr			errx(1, "bad tab stop spec");
2201590Srgrimes		cp++;
2211590Srgrimes	}
2221590Srgrimes}
223