OpenSolaris_b135/cmd/newform/newform.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/*
 *	FUNCTION PAGE INDEX
 * Function	Page		Description
 * append	16	Append chars to end of line.
 * begtrunc	16	Truncate characters from beginning of line.
 * center	5	Center text in the work area.
 * cnvtspec	7	Convert tab spec to tab positions.
 * endtrunc	16	Truncate chars from end of line.
 * inputtabs	17	Expand according to input tab specs.
 * main		3	MAIN
 * inputn	5	Read a command line option number.
 * options	4	Process command line options.
 * outputtabs	19	Contract according to output tab specs.
 * prepend	16	Prepend chars to line.
 * process	15	Process one line of input.
 * readline	14	Read one line from the file.
 * readspec	12	Read a tabspec from a file.
 * sstrip	18	Strip SCCS SID char from beginning of line.
 * sadd		18	Add SCCS SID chars to end of line.
 * type		14	Determine type of a character.
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define	MAXOPTS	50
#define	NCOLS	512
#define	MAXLINE	512
#define	NUMBER	'0'
#define	LINELEN	80

static int tabtbl[500] = {		/* Table containing tab stops	*/
	1, 9, 17, 25, 33, 41, 49, 57, 65, 73, 0,
					/* Default tabs			*/
	1, 10, 16, 36, 72, 0,		/* IBM 370 Assembler		*/
	1, 10, 16, 40, 72, 0,		/* IBM 370 Assembler (alt.)	*/
	1, 8, 12, 16, 20, 55, 0,	/* COBOL			*/
	1, 6, 10, 14, 49, 0,		/* COBOL (crunched)		*/
	1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 67, 0,
					/* COBOL (crunched, many cols.)	*/
	1, 7, 11, 15, 19, 23, 0,	/* FORTRAN			*/
	1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
					/* PL/1				*/
	1, 10, 55, 0,			/* SNOBOL			*/
	1, 12, 20, 44, 0 },		/* UNIVAC Assembler		*/

	*nexttab = &tabtbl[87],		/* Pointer to next empty slot	*/

	*spectbl[40] = {	/* Table of pointers into tabtbl	*/
	&tabtbl[0],		/* Default specification		*/
	&tabtbl[11],		/* -a  specification			*/
	&tabtbl[17],		/* -a2 specification			*/
	&tabtbl[23],		/* -c  specification			*/
	&tabtbl[30],		/* -c2 specification			*/
	&tabtbl[36],		/* -c3 specification			*/
	&tabtbl[54],		/* -f  specification			*/
	&tabtbl[61],		/* -p  specification			*/
	&tabtbl[78],		/* -s  specification			*/
	&tabtbl[82] },		/* -u  specification			*/

	savek;		/* Stores char count stripped from front of line. */
static int nextspec = 10,	/* Index to next slot			*/
	sitabspec = -1,		/* Index to "standard input" spec.	*/
	effll	= 80,		/* Effective line length		*/
	optionf = 0,		/* 'f' option set			*/
	soption = 0,		/* 's' option used. */
	files	= 0,		/* Number of input files		*/
	kludge	= 0,		/* Kludge to allow reread of 1st line	*/
	okludge = 0,		/* Kludge to indicate reading "o" option */
	lock	= 0;		/* Lock to prevent file indirection	*/

static char pachar = ' ',	/* Prepend/append character		*/
	work[3*NCOLS+1],	/* Work area				*/
	*pfirst,		/* Pointer to beginning of line 	*/
	*plast,			/* Pointer to end of line		*/
	*wfirst = &work[0],	/* Pointer to beginning of work area	*/
	*wlast  = &work[3*NCOLS], /* Pointer to end of work area	*/
	siline[NCOLS],		/* First standard input line		*/
	savchr[8],		/* Holds char stripped from line start */
	format[80] = "-8";	/* Array to hold format line		*/

static struct f {
	char	option;
	int	param;
	}	optl[MAXOPTS],	/* List of command line options 	*/
		*flp = optl;	/* Pointer to next open slot		*/

static void append(int);
static void begtrunc(int);
static void center(void);
static int cnvtspec(char *);
static void endtrunc(int);
static int inputn(char *);
static void inputtabs(int);
static void options(int, char **);
static void outputtabs(int);
static void prepend(int);
static void process(FILE *);
static char *readline(FILE *, char *);
static int readspec(char *);
static void sadd(void);
static void sstrip(void);
static char type(char);

int
main(int argc, char **argv)
{
	char	*scan;		/* String scan pointer			*/
	FILE	*fp;		/* Pointer to current file		*/

	options(argc, argv);
	if (optionf) {		/* Write tab spec format line. */
		(void) fputs("<:t", stdout);
		(void) fputs(format, stdout);
		(void) fputs(" d:>\n", stdout);
	}
	if (files) {
		while (--argc) {
			scan = *++argv;
			if (*scan != '-') {
				if ((fp = fopen(scan, "r")) == NULL) {
					(void) fprintf(stderr,
					    "newform: can't open %s\n", scan);
					exit(1);
				}
				process(fp);
				(void) fclose(fp);
			}
		}
	} else {
		process(stdin);
	}
	return (0);
}


static void
options(int argc, char **argv)		/* Process command line options	*/
{
	int	n;		/* Temporary number holder		*/
	char	*scan;		/* Pointer to individual option strings	*/
	char	c;		/* Option character			*/

/*	changes to option parsing includes checks for exceeding	*/
/*	initial buffer sizes					*/

	while (--argc > 0) {
		scan = *++argv;
		if (*scan++ == '-') {
			switch (c = *scan++) {
			case 'a':
				flp->option = 'a';
				flp->param = inputn(scan);
				if (flp->param <= NCOLS)
					flp++;
				else {
					(void) fprintf(stderr, "newform: "
					    "prefix request larger than "
					    "buffer, %d\n", NCOLS);
					exit(1);
				}
				break;
			case 'b':
			case 'e':
				flp->option = c;
				flp->param = inputn(scan);
				flp++;
				break;
			case 'p':
				flp->option = 'p';
				flp->param = inputn(scan);
				if (flp->param <= NCOLS)
					flp++;
				else {
					(void) fprintf(stderr, "newform: "
					    "prefix request larger than "
					    "buffer, %d\n", NCOLS);
					exit(1);
				}
				break;
			case 'c':
				flp->option = 'c';
				flp->param = *scan ? *scan : ' ';
				flp++;
				break;
			case 'f':
				flp->option = 'f';
				optionf++;
				flp++;
				break;
			case 'i':
				flp->option = 'i';
				flp->param = cnvtspec(scan);
				flp++;
				break;
			case 'o':
				if (*scan == '-' && *(scan+1) == '0' &&
				    *(scan+2) == '\0')
					break;
			/* Above allows the -o-0 option to be ignored. */
				flp->option = 'o';
				(void) strcpy(format, scan);
				okludge++;
				flp->param = cnvtspec(scan);
				okludge--;
				if (flp->param == 0)
					(void) strcpy(format, "-8");
				flp++;
				break;
			case 'l':
				flp->option = 'l';
				flp->param = ((n = inputn(scan)) ? n : 72);
				if (flp->param <= (3*NCOLS))
					flp++;
				else {
					(void) fprintf(stderr, "newform: "
					    "line length request larger "
					    "than buffer, %d \n", (3*NCOLS));
					exit(1);
				}
				break;
			case 's':
				flp->option = 's';
				flp++;
				soption++;
				break;
			default:
				goto usageerr;
				}
			}
		else
			files++;
		}
	return;
usageerr:
	(void) fprintf(stderr, "usage: newform  [-s] [-itabspec] [-otabspec] ");
	(void) fprintf(stderr, "[-pn] [-en] [-an] [-f] [-cchar]\n\t\t");
	(void) fprintf(stderr, "[-ln] [-bn] [file ...]\n");
	exit(1);
}
/* _________________________________________________________________ */

static int
inputn(char *scan)		/* Read a command option number		*/
	/* Pointer to string of digits */
{
	int	n;		/* Number				*/
	char	c;		/* Character being scanned		*/

	n = 0;
	while ((c = *scan++) >= '0' && c <= '9')
		n = n * 10 + c - '0';
	return (n);
}
/* _________________________________________________________________ */

static void
center(void)			/* Center the text in the work area.	*/
{
	char	*tfirst;	/* Pointer for moving buffer down	*/
	char	*tlast;		/* Pointer for moving buffer up		*/
	char	*tptr;		/* Temporary				*/

	if (plast - pfirst > MAXLINE) {
		(void) fprintf(stderr, "newform: internal line too long\n");
		exit(1);
	}
	if (pfirst < &work[NCOLS]) {
		tlast = plast + (&work[NCOLS] - pfirst);
		tptr = tlast;
		while (plast >= pfirst) *tlast-- = *plast--;
		pfirst = ++tlast;
		plast = tptr;
	} else {
		tfirst = &work[NCOLS];
		tptr = tfirst;
		while (pfirst <= plast) *tfirst++ = *pfirst++;
		plast = --tfirst;
		pfirst = tptr;
	}
}

static int
cnvtspec(char *p)	/* Convert tab specification to tab positions.	*/
	/* Pointer to spec string. */
{
	int	state,		/* DFA state				*/
		spectype,	/* Specification type			*/
		number[40],	/* Array of read-in numbers		*/
		tp,		/* Pointer to last number		*/
		ix;		/* Temporary				*/
	int	tspec = 0;	/* Tab spec pointer			*/
	char	c,		/* Temporary				*/
		*filep;		/* Pointer to file name			*/
	FILE	*fp;		/* File pointer				*/

	state = 0;
	while (state >= 0) {
		c = *p++;
		switch (state) {
		case 0:
			switch (type(c)) {
			case '\0':
				spectype = 0;
				state = -1;
				break;
			case NUMBER:
				state = 1;
				tp = 0;
				number[tp] = c - '0';
				break;
			case '-':
				state = 3;
				break;
			default:
				goto tabspecerr;
				}
			break;
		case 1:
			switch (type(c)) {
			case '\0':
				spectype = 11;
				state = -1;
				break;
			case NUMBER:
				state = 1;
				number[tp] = number[tp] * 10 + c - '0';
				break;
			case ',':
				state = 2;
				break;
			default:
				goto tabspecerr;
				}
			break;
		case 2:
			if (type(c) == NUMBER) {
				state = 1;
				number[++tp] = c - '0';
				}
			else
				goto tabspecerr;

			break;
		case 3:
			switch (type(c)) {
			case '-':
				state = 4;
				break;
			case 'a':
				state = 5;
				break;
			case 'c':
				state = 7;
				break;
			case 'f':
				state = 10;
				break;
			case 'p':
				state = 11;
				break;
			case 's':
				state = 12;
				break;
			case 'u':
				state = 13;
				break;
			case NUMBER:
				state = 14;
				number[0] = c - '0';
				break;
			default:
				goto tabspecerr;
				}
			break;
		case 4:
			if (c == '\0') {
				spectype = 12;
				state = -1;
			} else {
				filep = --p;
				spectype = 13;
				state = -1;
			}
			break;
		case 5:
			if (c == '\0') {
				spectype = 1;
				state = -1;
			} else if (c == '2')
				state = 6;
			else
				goto tabspecerr;
			break;
		case 6:
			if (c == '\0') {
				spectype = 2;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 7:
			switch (c) {
			case '\0':
				spectype = 3;
				state = -1;
				break;
			case '2':
				state = 8;
				break;
			case '3':
				state = 9;
				break;
			default:
				goto tabspecerr;
				}
			break;
		case 8:
			if (c == '\0') {
				spectype = 4;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 9:
			if (c == '\0') {
				spectype = 5;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 10:
			if (c == '\0') {
				spectype = 6;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 11:
			if (c == '\0') {
				spectype = 7;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 12:
			if (c == '\0') {
				spectype = 8;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 13:
			if (c == '\0') {
				spectype = 9;
				state = -1;
				}
			else
				goto tabspecerr;
			break;
		case 14:
			if (type(c) == NUMBER) {
				state = 14;
				number[0] = number[0] * 10 + c - '0';
			} else if (c == '\0') {
				spectype = 10;
				state = -1;
			} else
				goto tabspecerr;
			break;
		}
	}
	if (spectype <= 9)
		return (spectype);
	if (spectype == 10) {
		spectype = nextspec++;
		spectbl[spectype] = nexttab;
		*nexttab = 1;
		if (number[0] == 0) number[0] = 1; /* Prevent infinite loop. */
		while (*nexttab < LINELEN) {
			*(nexttab + 1) = *nexttab;
			*++nexttab += number[0];
			}
		*nexttab++ = '\0';
		return (spectype);
	}
	if (spectype == 11) {
		spectype = nextspec++;
		spectbl[spectype] = nexttab;
		*nexttab++ = 1;
		for (ix = 0; ix <= tp; ix++) {
			*nexttab++ = number[ix];
			if ((number[ix] >= number[ix+1]) && (ix != tp))
				goto tabspecerr;
			}
		*nexttab++ = '\0';
		return (spectype);
	}
	if (lock == 1) {
		(void) fprintf(stderr,
		    "newform: tabspec indirection illegal\n");
		exit(1);
	}
	lock = 1;
	if (spectype == 12) {
		if (sitabspec >= 0) {
			tspec = sitabspec;
		} else {
			if (readline(stdin, siline) != NULL) {
				kludge = 1;
				tspec = readspec(siline);
				sitabspec = tspec;
			}
		}
	}
	if (spectype == 13) {
		if ((fp = fopen(filep, "r")) == NULL) {
			(void) fprintf(stderr,
			    "newform: can't open %s\n", filep);
			exit(1);
		}
		(void) readline(fp, work);
		(void) fclose(fp);
		tspec = readspec(work);
	}
	lock = 0;
	return (tspec);
tabspecerr:
	(void) fprintf(stderr, "newform: tabspec in error\n");
	(void) fprintf(stderr,
	    "tabspec is \t-a\t-a2\t-c\t-c2\t-c3\t-f\t-p\t-s\n");
	(void) fprintf(stderr,
	    "\t\t-u\t--\t--file\t-number\tnumber,..,number\n");
	exit(1);
	/* NOTREACHED */
}

static int
readspec(char *p)		/* Read a tabspec from a file		*/
	/* Pointer to buffer to process */
{
	int	state,		/* Current state			*/
		firsttime,	/* Flag to indicate spec found		*/
		value;		/* Function value			*/
	char	c,		/* Char being looked at			*/
		*tabspecp,	/* Pointer to spec string		*/
		*restore = " ",	/* Character to be restored		*/
		repch;		/* Character to replace with		*/

	state = 0;
	firsttime = 1;
	while (state >= 0) {
		c = *p++;
		switch (state) {
		case 0:
			state = (c == '<') ? 1 : 0;
			break;
		case 1:
			state = (c == ':') ? 2 : 0;
			break;
		case 2:
			state = (c == 't') ? 4
				: ((c == ' ') || (c == '\t')) ? 2 : 3;
			break;
		case 3:
			state = ((c == ' ') || (c == '\t')) ? 2 : 3;
			break;
		case 4:
			if (firsttime) {
				tabspecp = --p;
				p++;
				firsttime = 0;
				}
			if ((c == ' ') || (c == '\t') || (c == ':')) {
				repch = *(restore = p - 1);
				*restore = '\0';
				}
			state = (c == ':') ? 6
				: ((c == ' ') || (c == '\t')) ? 5 : 4;
			break;
		case 5:
			state = (c == ':') ? 6 : 5;
			break;
		case 6:
			state = (c == '>') ? -2 : 5;
			break;
			}
		if (c == '\n') state = -1;
		}
	if (okludge)
		(void) strcpy(format, tabspecp);
	value = (state == -1) ? 0 : cnvtspec(tabspecp);
	*restore = repch;
	return (value);
}

static char *
readline(FILE *fp, char *area)		/* Read one line from the file.	*/
	/* fp - File to read from */
	/* area - Array of characters to read into */
{
	int	c;		/* Current character			*/
	char	*xarea,		/* Temporary pointer to character array	*/
		*temp;		/* Array pointer			*/



/* check for existence of stdin before attempting to read 		*/
/* kludge refers to reading from stdin to get tabspecs for option -i--	*/

	xarea = area;
	if (kludge && (fp == stdin)) {
		if (fp != NULL) {
			temp = siline;
			while ((*area++ = *temp++) != '\n')
				;
			kludge = 0;
			return (xarea);
		} else
			return (NULL);
	} else {

/* check for exceeding size of buffer when reading valid input */

		while (wlast - area) {
			switch (c = getc(fp)) {
			case EOF:
				if (area == xarea)
					return (NULL);
				/* FALLTHROUGH */
			case '\n':	/* EOF falls through to here */
				*area = '\n';
				return (xarea);
			}
			*area = c;
			area++;
		}
		(void) printf("newform: input line larger than buffer area \n");
		exit(1);
	}
	/* NOTREACHED */
}
/* _________________________________________________________________ */

static char
type(char c)			/* Determine type of a character	*/
	/* Character to check */
{
	return ((c >= '0') && (c <= '9') ? NUMBER : c);
}

static void
process(FILE *fp)		/* Process one line of input		*/
	/* File pointer for current input */
{
	struct	f	*lp;	/* Pointer to structs			*/
	char	chrnow;		/* For int to char conversion. */

	while (readline(fp, &work[NCOLS]) != NULL) {
		effll = 80;
		pachar = ' ';
		pfirst = plast = &work[NCOLS];
		while (*plast != '\n') plast++;

/*	changes to line parsing includes checks for exceeding	*/
/*	line size when modifying text				*/

		for (lp = optl; lp < flp; lp++) {
			switch (lp->option) {
			case 'a':
				append(lp->param);
				break;
			case 'b':
				if (lp->param <= (plast - pfirst))
					begtrunc(lp->param);
				else
					(void) fprintf(stderr,
					    "newform: truncate "
					    "request larger than line, %d \n",
					    (plast - pfirst));
				break;
			case 'c':
				chrnow = lp->param;
				pachar = chrnow ? chrnow : ' ';
				break;
			case 'e':
				if (lp->param <= (plast - pfirst))
					endtrunc(lp->param);
				else
					(void) fprintf(stderr,
					    "newform: truncate "
					    "request larger than line, %d \n",
					    (plast - pfirst));
				break;
			case 'f':
				/* Ignored */
				break;
			case 'i':
				inputtabs(lp->param);
				break;
			case 'l':	/* New eff line length */
				effll = lp->param ? lp->param : 72;
				break;
			case 's':
				sstrip();
				break;
			case 'o':
				outputtabs(lp->param);
				break;
			case 'p':
				prepend(lp->param);
				break;
			}
		}
		if (soption) sadd();
		*++plast = '\0';
		(void) fputs(pfirst, stdout);
	}
}

static void
append(int n)			/* Append characters to end of line.	*/
	/* Number of characters to append. */
{
	if (plast - pfirst < effll) {
		n = n ? n : effll - (plast - pfirst);
		if (plast + n > wlast) center();
		while (n--) *plast++ = pachar;
		*plast = '\n';
		}
}
/* _________________________________________________________________ */

static void
prepend(int n)			/* Prepend characters to line.		*/
	/* Number of characters to prepend. */
{
	if (plast - pfirst < effll) {
		n = n ? n : effll - (plast - pfirst);
		if (pfirst - n < wfirst) center();
		while (n--) *--pfirst = pachar;
		}
}
/* _________________________________________________________________ */

static void
begtrunc(int n)		/* Truncate characters from beginning of line.	*/
	/* Number of characters to truncate. */
{
	if (plast - pfirst > effll) {
		n = n ? n : plast - pfirst - effll;
		pfirst += n;
		if (pfirst >= plast)
			*(pfirst = plast = &work[NCOLS]) = '\n';
		}
}
/* _________________________________________________________________ */

static void
endtrunc(int n)			/* Truncate characters from end of line. */
	/* Number of characters to truncate. */
{
	if (plast - pfirst > effll) {
		n = n ? n : plast - pfirst - effll;
		plast -= n;
		if (pfirst >= plast)
			*(pfirst = plast = &work[NCOLS]) = '\n';
		else
			*plast = '\n';
		}
}

static void
inputtabs(int p)	/* Expand according to input tab specifications. */
	/* Pointer to tab specification. */
{
	int	*tabs;		/* Pointer to tabs			*/
	char	*tfirst,	/* Pointer to new buffer start		*/
		*tlast;		/* Pointer to new buffer end		*/
	char	c;		/* Character being scanned		*/
	int	logcol;		/* Logical column			*/

	tabs = spectbl[p];
	tfirst = tlast = work;
	logcol = 1;
	center();
	while (pfirst <= plast) {
		if (logcol >= *tabs) tabs++;
		switch (c = *pfirst++) {
		case '\b':
			if (logcol > 1) logcol--;
			*tlast++ = c;
			if (logcol < *tabs) tabs--;
			break;
		case '\t':
			while (logcol < *tabs) {
				*tlast++ = ' ';
				logcol++;
				}
			tabs++;
			break;
		default:
			*tlast++ = c;
			logcol++;
			break;
			}
		}
	pfirst = tfirst;
	plast = --tlast;
}
/*
 * Add SCCS SID (generated by a "get -m" command) to the end of each line.
 * Sequence is as follows for EACH line:
 *	Check for at least 1 tab.  Err if none.
 *	Strip off all char up to & including first tab.
 *	If more than 8 char were stripped, the 8 th is replaced by
 *		a '*' & the remainder are discarded.
 *	Unless user specified an "a", append blanks to fill
 *		out line to eff. line length (default= 72 char).
 *	Truncate lines > eff. line length (default=72).
 *	Add stripped char to end of line.
 */
static void
sstrip(void)
{
	int i, k;
	char *c, *savec;

	k = -1;
	c = pfirst;
	while (*c != '\t' && *c != '\n') {
		k++;
		c++;
	}
	if (*c != '\t') {
		(void) fprintf(stderr, "not -s format\r\n");
		exit(1);
	}

	savec = c;
	c = pfirst;
	savek = (k > 7) ? 7 : k;
	for (i = 0; i <= savek; i++) savchr[i] = *c++;	/* Tab not saved */
	if (k > 7) savchr[7] = '*';

	pfirst = ++savec;		/* Point pfirst to char after tab */
}
/* ================================================================= */

static void
sadd(void)
{
	int i;

	for (i = 0; i <= savek; i++) *plast++ = savchr[i];
	*plast = '\n';
}

static void
outputtabs(int p)	/* Contract according to output tab specifications. */
	/* Pointer to tab specification. */
{
	int	*tabs;		/* Pointer to tabs			*/
	char	*tfirst,	/* Pointer to new buffer start		*/
		*tlast,		/* Pointer to new buffer end		*/
		*mark;		/* Marker pointer			*/
	char c;			/* Character being scanned		*/
	int	logcol;		/* Logical column			*/

	tabs = spectbl[p];
	tfirst = tlast = pfirst;
	logcol = 1;
	while (pfirst <= plast) {
		if (logcol == *tabs) tabs++;
		switch (c = *pfirst++) {
		case '\b':
			if (logcol > 1) logcol--;
			*tlast++ = c;
			if (logcol < *tabs) tabs--;
			break;
		case ' ':
			mark = tlast;
			do {
				*tlast++ = ' ';
				logcol++;
				if (logcol == *tabs) {
					*mark++ = '\t';
					tlast = mark;
					tabs++;
					}
				} while (*pfirst++ == ' ');
			pfirst--;
			break;
		default:
			logcol++;
			*tlast++ = c;
			break;
			}
		}
	pfirst = tfirst;
	plast = --tlast;
}