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