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 * 4. Neither the name of the University nor the names of its contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
3087697Smarkm#include <sys/cdefs.h>
3187697Smarkm
3287697Smarkm__FBSDID("$FreeBSD$");
3387697Smarkm
341590Srgrimes#ifndef lint
3528456Scharnierstatic const char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1980, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
3887697Smarkm#endif
391590Srgrimes
401590Srgrimes#ifndef lint
4187697Smarkmstatic const char sccsid[] = "@(#)unexpand.c	8.1 (Berkeley) 6/6/93";
4228456Scharnier#endif
431590Srgrimes
441590Srgrimes/*
451590Srgrimes * unexpand - put tabs into a file replacing blanks
461590Srgrimes */
4798251Stjr#include <ctype.h>
4828456Scharnier#include <err.h>
4995304Stjr#include <limits.h>
5098251Stjr#include <locale.h>
511590Srgrimes#include <stdio.h>
5278717Sdd#include <stdlib.h>
53200462Sdelphij#include <string.h>
5495304Stjr#include <unistd.h>
55131054Stjr#include <wchar.h>
56200462Sdelphij#include <wctype.h>
571590Srgrimes
58227192Sedstatic int	all;
59227192Sedstatic int	nstops;
60227192Sedstatic int	tabstops[100];
611590Srgrimes
6295304Stjrstatic void getstops(const char *);
6392922Simpstatic void usage(void);
64131054Stjrstatic int tabify(const char *);
6528456Scharnier
6628789Scharnierint
67102944Sdwmalonemain(int argc, char *argv[])
681590Srgrimes{
6995474Stjr	int ch, failed;
7095474Stjr	char *filename;
711590Srgrimes
7298251Stjr	setlocale(LC_CTYPE, "");
7398251Stjr
7495304Stjr	nstops = 1;
7595304Stjr	tabstops[0] = 8;
7695304Stjr	while ((ch = getopt(argc, argv, "at:")) != -1) {
7795304Stjr		switch (ch) {
7895304Stjr		case 'a':	/* Un-expand all spaces, not just leading. */
7995304Stjr			all = 1;
8095304Stjr			break;
8195304Stjr		case 't':	/* Specify tab list, implies -a. */
8295304Stjr			getstops(optarg);
8395304Stjr			all = 1;
8495304Stjr			break;
8595304Stjr		default:
8628456Scharnier			usage();
8795304Stjr			/*NOTREACHED*/
8895304Stjr		}
891590Srgrimes	}
9095304Stjr	argc -= optind;
9195304Stjr	argv += optind;
9295304Stjr
9395474Stjr	failed = 0;
9495474Stjr	if (argc == 0)
95131054Stjr		failed |= tabify("stdin");
9695474Stjr	else {
9795474Stjr		while ((filename = *argv++) != NULL) {
9895474Stjr			if (freopen(filename, "r", stdin) == NULL) {
9995474Stjr				warn("%s", filename);
100131054Stjr				failed = 1;
10195474Stjr			} else
102131054Stjr				failed |= tabify(filename);
1031590Srgrimes		}
10495474Stjr	}
10595474Stjr	exit(failed != 0);
1061590Srgrimes}
1071590Srgrimes
10828456Scharnierstatic void
109102944Sdwmaloneusage(void)
11028456Scharnier{
111163316Sru	fprintf(stderr, "usage: unexpand [-a | -t tablist] [file ...]\n");
11228456Scharnier	exit(1);
11328456Scharnier}
11428456Scharnier
115131054Stjrstatic int
116131054Stjrtabify(const char *curfile)
1171590Srgrimes{
118131054Stjr	int dcol, doneline, limit, n, ocol, width;
119131054Stjr	wint_t ch;
1201590Srgrimes
12195304Stjr	limit = nstops == 1 ? INT_MAX : tabstops[nstops - 1] - 1;
1221590Srgrimes
12395304Stjr	doneline = ocol = dcol = 0;
124131054Stjr	while ((ch = getwchar()) != WEOF) {
12595474Stjr		if (ch == ' ' && !doneline) {
12695304Stjr			if (++dcol >= limit)
12795304Stjr				doneline = 1;
12895304Stjr			continue;
12995304Stjr		} else if (ch == '\t') {
13095304Stjr			if (nstops == 1) {
13195304Stjr				dcol = (1 + dcol / tabstops[0]) *
13295304Stjr				    tabstops[0];
13395304Stjr				continue;
13495304Stjr			} else {
13595304Stjr				for (n = 0; tabstops[n] - 1 < dcol &&
13695304Stjr				    n < nstops; n++)
13795304Stjr					;
13895304Stjr				if (n < nstops - 1 && tabstops[n] - 1 < limit) {
13995304Stjr					dcol = tabstops[n];
14095304Stjr					continue;
14195304Stjr				}
14295304Stjr				doneline = 1;
14395304Stjr			}
14495304Stjr		}
1451590Srgrimes
14695304Stjr		/* Output maximal number of tabs. */
14795304Stjr		if (nstops == 1) {
14895304Stjr			while (((ocol + tabstops[0]) / tabstops[0])
14995304Stjr			    <= (dcol / tabstops[0])) {
15095304Stjr				if (dcol - ocol < 2)
1511590Srgrimes					break;
152131054Stjr				putwchar('\t');
15395304Stjr				ocol = (1 + ocol / tabstops[0]) *
15495304Stjr				    tabstops[0];
1551590Srgrimes			}
15695304Stjr		} else {
15795304Stjr			for (n = 0; tabstops[n] - 1 < ocol && n < nstops; n++)
15895304Stjr				;
15995304Stjr			while (ocol < dcol && n < nstops && ocol < limit) {
160131054Stjr				putwchar('\t');
16195304Stjr				ocol = tabstops[n++];
1621590Srgrimes			}
16395304Stjr		}
16495304Stjr
16595304Stjr		/* Then spaces. */
16695304Stjr		while (ocol < dcol && ocol < limit) {
167131054Stjr			putwchar(' ');
16895304Stjr			ocol++;
16995304Stjr		}
17095304Stjr
17195474Stjr		if (ch == '\b') {
172131054Stjr			putwchar('\b');
17395474Stjr			if (ocol > 0)
17495474Stjr				ocol--, dcol--;
17595474Stjr		} else if (ch == '\n') {
176131054Stjr			putwchar('\n');
17795474Stjr			doneline = ocol = dcol = 0;
178104466Stjr			continue;
17995474Stjr		} else if (ch != ' ' || dcol > limit) {
180131054Stjr			putwchar(ch);
181131054Stjr			if ((width = wcwidth(ch)) > 0)
182131054Stjr				ocol += width, dcol += width;
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) {
190131054Stjr			while ((ch = getwchar()) != '\n' && ch != WEOF)
191131054Stjr				putwchar(ch);
19295304Stjr			if (ch == '\n')
193131054Stjr				putwchar('\n');
19495474Stjr			doneline = ocol = dcol = 0;
19595304Stjr		}
19695304Stjr	}
197131054Stjr	if (ferror(stdin)) {
198131054Stjr		warn("%s", curfile);
199131054Stjr		return (1);
200131054Stjr	}
201131054Stjr	return (0);
20295304Stjr}
20395304Stjr
20495304Stjrstatic void
205102944Sdwmalonegetstops(const char *cp)
20695304Stjr{
20795304Stjr	int i;
20895304Stjr
20995304Stjr	nstops = 0;
21095304Stjr	for (;;) {
21195304Stjr		i = 0;
21295304Stjr		while (*cp >= '0' && *cp <= '9')
21395304Stjr			i = i * 10 + *cp++ - '0';
21495304Stjr		if (i <= 0)
21595304Stjr			errx(1, "bad tab stop spec");
21695304Stjr		if (nstops > 0 && i <= tabstops[nstops-1])
21795304Stjr			errx(1, "bad tab stop spec");
21895304Stjr		if (nstops == sizeof(tabstops) / sizeof(*tabstops))
21995304Stjr			errx(1, "too many tab stops");
22095304Stjr		tabstops[nstops++] = i;
22195304Stjr		if (*cp == 0)
22295304Stjr			break;
22398251Stjr		if (*cp != ',' && !isblank((unsigned char)*cp))
22495304Stjr			errx(1, "bad tab stop spec");
2251590Srgrimes		cp++;
2261590Srgrimes	}
2271590Srgrimes}
228