xref: /titanic_44/usr/src/cmd/newform/newform.c (revision b7f45089ccbe01bab3d7c7377b49d80d2ae18a69)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 #ident	"%Z%%M%	%I%	%E% SMI"	/* SVr4.0 1.14	*/
27 
28 /*	FUNCTION PAGE INDEX
29 Function	Page		Description
30 append		16	Append chars to end of line.
31 begtrunc	16	Truncate characters from beginning of line.
32 center		5	Center text in the work area.
33 cnvtspec	7	Convert tab spec to tab positions.
34 endtrunc	16	Truncate chars from end of line.
35 inputtabs	17	Expand according to input tab specs.
36 main		3	MAIN
37 inputn		5	Read a command line option number.
38 options		4	Process command line options.
39 outputtabs	19	Contract according to output tab specs.
40 prepend		16	Prepend chars to line.
41 process		15	Process one line of input.
42 readline	14	Read one line from the file.
43 readspec	12	Read a tabspec from a file.
44 sstrip		18	Strip SCCS SID char from beginning of line.
45 sadd		18	Add SCCS SID chars to end of line.
46 type		14	Determine type of a character.	*/
47 
48 #include	<stdio.h>
49 
50 #define	MAXOPTS	50
51 #define	NCOLS	512
52 #define	MAXLINE	512
53 #define	NUMBER	'0'
54 #define	LINELEN	80
55 
56 int	tabtbl[500] = {		/* Table containing tab stops		*/
57 	1,9,17,25,33,41,49,57,65,73,0,
58 				/* Default tabs				*/
59 	1,10,16,36,72,0,	/* IBM 370 Assembler			*/
60 	1,10,16,40,72,0,	/* IBM 370 Assembler (alt.)		*/
61 	1,8,12,16,20,55,0,	/* COBOL				*/
62 	1,6,10,14,49,0,		/* COBOL (crunched)			*/
63 	1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67,0,
64 				/* COBOL (crunched, many cols.)		*/
65 	1,7,11,15,19,23,0,	/* FORTRAN				*/
66 	1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,0,
67 				/* PL/1					*/
68 	1,10,55,0,		/* SNOBOL				*/
69 	1,12,20,44,0 },		/* UNIVAC Assembler			*/
70 
71 	*nexttab = &tabtbl[87],	/* Pointer to next empty slot		*/
72 
73 	*spectbl[40] = {	/* Table of pointers into tabtbl	*/
74 	&tabtbl[0],		/* Default specification		*/
75 	&tabtbl[11],		/* -a  specification			*/
76 	&tabtbl[17],		/* -a2 specification			*/
77 	&tabtbl[23],		/* -c  specification			*/
78 	&tabtbl[30],		/* -c2 specification			*/
79 	&tabtbl[36],		/* -c3 specification			*/
80 	&tabtbl[54],		/* -f  specification			*/
81 	&tabtbl[61],		/* -p  specification			*/
82 	&tabtbl[78],		/* -s  specification			*/
83 	&tabtbl[82] },		/* -u  specification			*/
84 
85 	savek;		/* Stores char count stripped from front of line. */
86 int	nextspec = 10,		/* Index to next slot			*/
87 	sitabspec = -1,		/* Index to "standard input" spec.	*/
88 	effll	= 80,		/* Effective line length		*/
89 	optionf = 0,		/* 'f' option set			*/
90 	soption = 0,		/* 's' option used. */
91 	files	= 0,		/* Number of input files		*/
92 	kludge	= 0,		/* Kludge to allow reread of 1st line	*/
93 	okludge = 0,		/* Kludge to indicate reading "o" option*/
94 	lock	= 0;		/* Lock to prevent file indirection	*/
95 
96 char	pachar = ' ',		/* Prepend/append character		*/
97 	work[3*NCOLS+1],	/* Work area				*/
98 	*pfirst,		/* Pointer to beginning of line 	*/
99 	*plast,			/* Pointer to end of line		*/
100 	*wfirst = &work[0],	/* Pointer to beginning of work area	*/
101 	*wlast  = &work[3*NCOLS], /* Pointer to end of work area	*/
102 	siline[NCOLS],		/* First standard input line		*/
103 	savchr[8],		/* Holds char stripped from line start */
104 	format[80] = "-8",	/* Array to hold format line		*/
105 	*strcpy();		/* Eliminates a warning by 'lint'. */
106 
107 struct f {
108 	char	option;
109 	int	param;
110 	}	optl[MAXOPTS],	/* List of command line options 	*/
111 		*flp = optl;	/* Pointer to next open slot		*/
112 main(argc,argv)			/* Main procedure	 		*/
113 int	argc;			/* Count of command line arguments	*/
114 char	**argv;			/* Array of pointers to arguments	*/
115 {
116 	char	*scan;		/* String scan pointer			*/
117 	FILE	*fp;		/* Pointer to current file		*/
118 
119 	options(argc,argv);
120 	if (optionf)		/* Write tab spec format line. */
121 		{fputs("<:t",stdout);
122 		fputs(format,stdout);
123 		fputs(" d:>\n",stdout);
124 		}
125 	if (files) {
126 		while (--argc) {
127 			scan = *++argv;
128 			if (*scan != '-') {
129 				if ((fp = fopen(scan,"r")) == NULL) {
130 					fprintf(stderr,
131 					"newform: can't open %s\n",scan);
132 					exit(1);
133 					}
134 				process(fp);
135 				fclose(fp);
136 				}
137 			}
138 		}
139 	else {
140 		process(stdin);
141 		}
142 	exit(0);
143 }
144 
145 
146 options(argc, argv)		/* Process command line options		*/
147 register int	argc;		/* Count of arguments			*/
148 register char	**argv;		/* Array of pointers to arguments	*/
149 {
150 	int	n;		/* Temporary number holder		*/
151 	register char	*scan,	/* Pointer to individual option strings	*/
152 			c;	/* Option character			*/
153 
154 /*	changes to option parsing includes checks for exceeding	*/
155 /*	initial buffer sizes					*/
156 
157 	while (--argc > 0) {
158 		scan = *++argv;
159 		if (*scan++ == '-') {
160 			switch (c = *scan++) {
161 			case 'a':
162 				flp->option = 'a';
163 				flp->param = inputn(scan);
164 				if (flp->param <=NCOLS)
165 					flp++;
166 				else {
167 					fprintf(stderr,"newform: prefix request larger than buffer, %d\n", NCOLS);
168 					exit(1);
169 				}
170 				break;
171 			case 'b':
172 			case 'e':
173 				flp->option = c;
174 				flp->param = inputn(scan);
175 				flp++;
176 				break;
177 			case 'p':
178 				flp->option = 'p';
179 				flp->param = inputn(scan);
180 				if (flp->param <=NCOLS)
181 					flp++;
182 				else {
183 					fprintf(stderr,"newform: prefix request larger than buffer, %d\n", NCOLS);
184 					exit(1);
185 				}
186 				break;
187 			case 'c':
188 				flp->option = 'c';
189 				flp->param = *scan ? *scan : ' ';
190 				flp++;
191 				break;
192 			case 'f':
193 				flp->option = 'f';
194 				optionf++;
195 				flp++;
196 				break;
197 			case 'i':
198 				flp->option = 'i';
199 				flp->param = cnvtspec(scan);
200 				flp++;
201 				break;
202 			case 'o':
203  				if(*scan=='-' && *(scan+1)=='0' && *(scan+2)=='\0')break;
204  		/* Above allows the -o-0 option to be ignored. */
205 				flp->option = 'o';
206 				strcpy(format,scan);
207 				okludge++;
208 				flp->param = cnvtspec(scan);
209 				okludge--;
210 				if(flp->param == 0) strcpy(format,"-8");
211 				flp++;
212 				break;
213 			case 'l':
214 				flp->option = 'l';
215 				flp->param = ((n = inputn(scan)) ? n : 72);
216 				if (flp->param <= (3*NCOLS))
217 					flp++;
218 				else {
219 					fprintf(stderr, "newform: line length request larger than buffer, %d \n", (3*NCOLS));
220 					exit(1);
221 				}
222 				break;
223 			case 's':
224 				flp->option = 's';
225 				flp++;
226 				soption++;
227 				break;
228 			default:
229 				goto usageerr;
230 				}
231 			}
232 		else
233 			files++;
234 		}
235 	return;
236 usageerr:
237 	fprintf(stderr,"usage: newform  [-s] [-itabspec] [-otabspec] ");
238 	fprintf(stderr,"[-pn] [-en] [-an] [-f] [-cchar]\n\t\t");
239 	fprintf(stderr,"[-ln] [-bn] [file ...]\n");
240 	exit(1);
241 }
242 /* _________________________________________________________________ */
243 
244 int inputn(scan)		/* Read a command option number		*/
245 register char	*scan;		/* Pointer to string of digits		*/
246 {
247 	int	n;		/* Number				*/
248 	char	c;		/* Character being scanned		*/
249 
250 	n = 0;
251 	while ((c = *scan++) >= '0' && c <= '9')
252 		n = n * 10 + c - '0';
253 	return(n);
254 }
255 /* _________________________________________________________________ */
256 
257 center()			/* Center the text in the work area.	*/
258 {
259 	char	*tfirst,	/* Pointer for moving buffer down	*/
260 		*tlast,		/* Pointer for moving buffer up		*/
261 		*tptr;		/* Temporary				*/
262 
263 	if (plast - pfirst > MAXLINE) {
264 		fprintf(stderr,"newform: internal line too long\n");
265 		exit(1);
266 		}
267 	if (pfirst < &work[NCOLS]) {
268 		tlast = plast + (&work[NCOLS] - pfirst);
269 		tptr = tlast;
270 		while (plast >= pfirst) *tlast-- = *plast--;
271 		pfirst = ++tlast;
272 		plast = tptr;
273 		}
274 	else {
275 		tfirst = &work[NCOLS];
276 		tptr = tfirst;
277 		while (pfirst <= plast) *tfirst++ = *pfirst++;
278 		plast = --tfirst;
279 		pfirst = tptr;
280 		}
281 }
282 int cnvtspec(p)		/* Convert tab specification to tab positions.	*/
283 register char	*p;		/* Pointer to spec string.		*/
284 {
285 	int	state,		/* DFA state				*/
286 		spectype,	/* Specification type			*/
287 		number[40],	/* Array of read-in numbers		*/
288 		tp,		/* Pointer to last number		*/
289 		ix;		/* Temporary				*/
290 	int	tspec=0;	/* Tab spec pointer			*/
291 	char	c,		/* Temporary				*/
292 		*stptr,		/* Pointer to stdin 			*/
293 		*filep,		/* Pointer to file name			*/
294 		type(),		/* Function				*/
295 		*readline();	/* Function				*/
296 	FILE	*fopen(),	/* File open routine			*/
297 		*fp;		/* File pointer				*/
298 
299 	state = 0;
300 	while (state >= 0) {
301 		c = *p++;
302 		switch (state) {
303 		case 0:
304 			switch (type(c)) {
305 			case '\0':
306 				spectype = 0;
307 				state = -1;
308 				break;
309 			case NUMBER:
310 				state = 1;
311 				tp = 0;
312 				number[tp] = c - '0';
313 				break;
314 			case '-':
315 				state = 3;
316 				break;
317 			default:
318 				goto tabspecerr;
319 				}
320 			break;
321 		case 1:
322 			switch (type(c)) {
323 			case '\0':
324 				spectype = 11;
325 				state = -1;
326 				break;
327 			case NUMBER:
328 				state = 1;
329 				number[tp] = number[tp] * 10 + c - '0';
330 				break;
331 			case ',':
332 				state = 2;
333 				break;
334 			default:
335 				goto tabspecerr;
336 				}
337 			break;
338 		case 2:
339 			if (type(c) == NUMBER) {
340 				state = 1;
341 				number[++tp] = c - '0';
342 				}
343 			else
344 				goto tabspecerr;
345 
346 			break;
347 		case 3:
348 			switch (type(c)) {
349 			case '-':
350 				state = 4;
351 				break;
352 			case 'a':
353 				state = 5;
354 				break;
355 			case 'c':
356 				state = 7;
357 				break;
358 			case 'f':
359 				state = 10;
360 				break;
361 			case 'p':
362 				state = 11;
363 				break;
364 			case 's':
365 				state = 12;
366 				break;
367 			case 'u':
368 				state = 13;
369 				break;
370 			case NUMBER:
371 				state = 14;
372 				number[0] = c - '0';
373 				break;
374 			default:
375 				goto tabspecerr;
376 				}
377 			break;
378 		case 4:
379 			if (c == '\0') {
380 				spectype = 12;
381 				state = -1;
382 				}
383 			else {
384 				filep = --p;
385 				spectype = 13;
386 				state = -1;
387 				}
388 			break;
389 		case 5:
390 			if (c == '\0') {
391 				spectype = 1;
392 				state = -1;
393 				}
394 			else if (c == '2')
395 				state = 6;
396 			else
397 				goto tabspecerr;
398 			break;
399 		case 6:
400 			if (c == '\0') {
401 				spectype = 2;
402 				state = -1;
403 				}
404 			else
405 				goto tabspecerr;
406 			break;
407 		case 7:
408 			switch (c) {
409 			case '\0':
410 				spectype = 3;
411 				state = -1;
412 				break;
413 			case '2':
414 				state = 8;
415 				break;
416 			case '3':
417 				state = 9;
418 				break;
419 			default:
420 				goto tabspecerr;
421 				}
422 			break;
423 		case 8:
424 			if (c == '\0') {
425 				spectype = 4;
426 				state = -1;
427 				}
428 			else
429 				goto tabspecerr;
430 			break;
431 		case 9:
432 			if (c == '\0') {
433 				spectype = 5;
434 				state = -1;
435 				}
436 			else
437 				goto tabspecerr;
438 			break;
439 		case 10:
440 			if (c == '\0') {
441 				spectype = 6;
442 				state = -1;
443 				}
444 			else
445 				goto tabspecerr;
446 			break;
447 		case 11:
448 			if (c == '\0') {
449 				spectype = 7;
450 				state = -1;
451 				}
452 			else
453 				goto tabspecerr;
454 			break;
455 		case 12:
456 			if (c == '\0') {
457 				spectype = 8;
458 				state = -1;
459 				}
460 			else
461 				goto tabspecerr;
462 			break;
463 		case 13:
464 			if (c == '\0') {
465 				spectype = 9;
466 				state = -1;
467 				}
468 			else
469 				goto tabspecerr;
470 			break;
471 		case 14:
472 			if (type(c) == NUMBER) {
473 				state = 14;
474 				number[0] = number[0] * 10 + c - '0';
475 				}
476 			else if (c == '\0') {
477 				spectype = 10;
478 				state = -1;
479 				}
480 			else
481 				goto tabspecerr;
482 			break;
483 			}
484 		}
485 	if (spectype <= 9) return(spectype);
486 	if (spectype == 10) {
487 		spectype = nextspec++;
488 		spectbl[spectype] = nexttab;
489 		*nexttab = 1;
490 	 	if(number[0] == 0)number[0] = 1; /* Prevent infinite loop. */
491 		while (*nexttab < LINELEN) {
492 			*(nexttab + 1) = *nexttab;
493 			*++nexttab += number[0];
494 			}
495 		*nexttab++ = '\0';
496 		return(spectype);
497 		}
498 	if (spectype == 11) {
499 		spectype = nextspec++;
500 		spectbl[spectype] = nexttab;
501 		*nexttab++ = 1;
502 		for (ix = 0; ix <= tp; ix++) {
503 			*nexttab++ = number[ix];
504 			if ((number[ix] >= number[ix+1]) && (ix != tp))
505 				goto tabspecerr;
506 			}
507 		*nexttab++ = '\0';
508 		return(spectype);
509 		}
510 	if (lock == 1) {
511 		fprintf(stderr,"newform: tabspec indirection illegal\n");
512 		exit(1);
513 		}
514 	lock = 1;
515 	if (spectype == 12) {
516 		if (sitabspec >= 0) {
517 			tspec = sitabspec;
518 			}
519 		else {
520 			if ( (stptr=readline(stdin,siline)) != NULL){
521 				kludge = 1;
522 				tspec = readspec(siline);
523 				sitabspec = tspec;
524 			}
525 		}
526 	}
527 	if (spectype == 13) {
528 		if ((fp = fopen(filep,"r")) == NULL) {
529 			fprintf(stderr,"newform: can't open %s\n", filep);
530 			exit(1);
531 			}
532 		readline(fp,work);
533 		fclose(fp);
534 		tspec = readspec(work);
535 		}
536 	lock = 0;
537 	return(tspec);
538 tabspecerr:
539 	fprintf(stderr,"newform: tabspec in error\n");
540 	fprintf(stderr,"tabspec is \t-a\t-a2\t-c\t-c2\t-c3\t-f\t-p\t-s\n");
541 	fprintf(stderr,"\t\t-u\t--\t--file\t-number\tnumber,..,number\n");
542 	exit(1);
543 return(-1);	/* Stops 'lint's complaint about return(e) VS return. */
544 }
545 int readspec(p)			/* Read a tabspec from a file		*/
546 
547 register char	*p;		/* Pointer to buffer to process		*/
548 {
549 	int	state,		/* Current state			*/
550 		firsttime,	/* Flag to indicate spec found		*/
551 		value;		/* Function value			*/
552 	char	c,		/* Char being looked at			*/
553 		*tabspecp,	/* Pointer to spec string		*/
554 		*restore = " ",	/* Character to be restored		*/
555 		repch;		/* Character to replace with		*/
556 
557 	state = 0;
558 	firsttime = 1;
559 	while (state >= 0) {
560 		c = *p++;
561 		switch (state) {
562 		case 0:
563 			state = (c == '<') ? 1 : 0;
564 			break;
565 		case 1:
566 			state = (c == ':') ? 2 : 0;
567 			break;
568 		case 2:
569 			state = (c == 't') ? 4
570 				: ((c == ' ') || (c == '\t')) ? 2 : 3;
571 			break;
572 		case 3:
573 			state = ((c == ' ') || (c == '\t')) ? 2 : 3;
574 			break;
575 		case 4:
576 			if (firsttime) {
577 				tabspecp = --p;
578 				p++;
579 				firsttime = 0;
580 				}
581 			if ((c == ' ') || (c == '\t') || (c == ':')) {
582 				repch = *(restore = p - 1);
583 				*restore = '\0';
584 				}
585 			state = (c == ':') ? 6
586 				: ((c == ' ') || (c == '\t')) ? 5 : 4;
587 			break;
588 		case 5:
589 			state = (c == ':') ? 6 : 5;
590 			break;
591 		case 6:
592 			state = (c == '>') ? -2 : 5;
593 			break;
594 			}
595 		if (c == '\n') state = -1;
596 		}
597 	if (okludge) strcpy(format,tabspecp);
598 	value = (state == -1) ? 0 : cnvtspec(tabspecp);
599 	*restore = repch;
600 	return(value);
601 }
602 char *readline(fp,area)		/* Read one line from the file.	*/
603 FILE	*fp;			/* File to read from			*/
604 char	*area;			/* Array of characters to read into	*/
605 {
606 	int	c;		/* Current character			*/
607 	char	*xarea,		/* Temporary pointer to character array	*/
608 		UNDEF = '\377',		/* Undefined char for return */
609 		*temp;		/* Array pointer			*/
610 
611 
612 
613 /* check for existence of stdin before attempting to read 		*/
614 /* kludge refers to reading from stdin to get tabspecs for option -i--	*/
615 
616 	xarea = area;
617 	if (kludge && (fp == stdin)) {
618 		if (fp != NULL) {
619 			temp = siline;
620 			while ((*area++ = *temp++) != '\n') ;
621 			kludge = 0;
622 			return(xarea);
623 		}
624 		else
625 			return(NULL);
626 	}
627 	else {
628 
629 /* check for exceeding size of buffer when reading valid input */
630 
631 		while ( wlast - area ) {
632 			switch(c = getc(fp)){
633 			case EOF:
634 				if (area == xarea)
635 					return(NULL);
636 			case '\n':	/*EOF falls through to here*/
637 				*area = '\n';
638 				return(xarea);
639 			}
640 			*area=c;
641 			*area++;
642 		}
643 		printf("newform: input line larger than buffer area \n");
644 		exit(1);
645 	}
646 return(&UNDEF);	/* Stops 'lint's complaint about return(e) VS return. */
647 }
648 /* _________________________________________________________________ */
649 
650 char type(c)			/* Determine type of a character	*/
651 char c;				/* Character to check			*/
652 {
653 	return((c >= '0') && (c <= '9') ? NUMBER : c);
654 }
655 process(fp)			/* Process one line of input		*/
656 FILE	*fp;			/* File pointer for current input	*/
657 {
658 	struct	f	*lp;	/* Pointer to structs			*/
659 	char	*readline();	/* Function				*/
660 	char	chrnow;		/* For int to char conversion. */
661 
662 	while (readline(fp,&work[NCOLS]) != NULL) {
663 		effll = 80;
664 		pachar = ' ';
665 		pfirst = plast = &work[NCOLS];
666 		while (*plast != '\n') plast++;
667 
668 /*	changes to line parsing includes checks for exceeding	*/
669 /*	line size when modifying text				*/
670 
671 		for (lp = optl ; lp < flp ; lp++) {
672 			switch (lp->option) {
673 			case 'a':
674 				append(lp->param);
675 				break;
676 			case 'b':
677 				if (lp->param <= (plast - pfirst))
678 					begtrunc(lp->param);
679 				else
680 					fprintf(stderr,"newform: truncate request larger than line, %d \n", (plast - pfirst));
681 				break;
682 			case 'c':
683 				chrnow = lp->param;
684 				pachar = chrnow ? chrnow : ' ';
685 				break;
686 			case 'e':
687 				if (lp->param <= (plast - pfirst))
688 					endtrunc(lp->param);
689 				else
690 					fprintf(stderr,"newform: truncate request larger than line, %d \n", (plast - pfirst));
691 				break;
692 			case 'f':
693 				/* Ignored */
694 				break;
695 			case 'i':
696 				inputtabs(lp->param);
697 				break;
698 			case 'l':	/* New eff line length */
699 				effll = lp->param ? lp->param : 72;
700 				break;
701 			case 's':
702 				sstrip();
703 				break;
704 			case 'o':
705 				outputtabs(lp->param);
706 				break;
707 			case 'p':
708 				prepend(lp->param);
709 				break;
710 				}
711 			}
712 		if(soption)sadd();
713 		*++plast = '\0';
714 		fputs(pfirst,stdout);
715 		}
716 }
717 append(n)			/* Append characters to end of line.	*/
718 int	n;			/* Number of characters to append.	*/
719 {
720 	if (plast - pfirst < effll) {
721 		n = n ? n : effll - (plast - pfirst);
722 		if (plast + n > wlast) center();
723 		while (n--) *plast++ = pachar;
724 		*plast = '\n';
725 		}
726 }
727 /* _________________________________________________________________ */
728 
729 prepend(n)			/* Prepend characters to line.		*/
730 int	n;			/* Number of characters to prepend.	*/
731 {
732 	if (plast - pfirst < effll) {
733 		n = n ? n : effll - (plast - pfirst);
734 		if (pfirst - n < wfirst) center();
735 		while (n--) *--pfirst = pachar;
736 		}
737 }
738 /* _________________________________________________________________ */
739 
740 begtrunc(n)		/* Truncate characters from beginning of line.	*/
741 int	n;			/* Number of characters to truncate.	*/
742 {
743 	if (plast - pfirst > effll) {
744 		n = n ? n : plast - pfirst - effll;
745 		pfirst += n;
746 		if (pfirst >= plast)
747 			*(pfirst = plast = &work[NCOLS]) = '\n';
748 		}
749 }
750 /* _________________________________________________________________ */
751 
752 endtrunc(n)			/* Truncate characters from end of line.*/
753 int	n;			/* Number of characters to truncate.	*/
754 {
755 	if (plast - pfirst > effll) {
756 		n = n ? n : plast - pfirst - effll;
757 		plast -= n;
758 		if (pfirst >= plast)
759 			*(pfirst = plast = &work[NCOLS]) = '\n';
760 		else
761 			*plast = '\n';
762 		}
763 }
764 inputtabs(p)		/* Expand according to input tab specifications.*/
765 int	p;			/* Pointer to tab specification.	*/
766 {
767 	int	*tabs;		/* Pointer to tabs			*/
768 	char	*tfirst,	/* Pointer to new buffer start		*/
769 		*tlast;		/* Pointer to new buffer end		*/
770 	register char c;	/* Character being scanned		*/
771 	int	logcol;		/* Logical column			*/
772 
773 	tabs = spectbl[p];
774 	tfirst = tlast = work;
775 	logcol = 1;
776 	center();
777 	while (pfirst <= plast) {
778 		if (logcol >= *tabs) tabs++;
779 		switch (c = *pfirst++) {
780 		case '\b':
781 			if (logcol > 1) logcol--;
782 			*tlast++ = c;
783 			if (logcol < *tabs) tabs--;
784 			break;
785 		case '\t':
786 			while (logcol < *tabs) {
787 				*tlast++ = ' ';
788 				logcol++;
789 				}
790 			tabs++;
791 			break;
792 		default:
793 			*tlast++ = c;
794 			logcol++;
795 			break;
796 			}
797 		}
798 	pfirst = tfirst;
799 	plast = --tlast;
800 }
801 /* Add SCCS SID (generated by a "get -m" command) to the end of each line.
802 Sequence is as follows for EACH line:
803 	Check for at least 1 tab.  Err if none.
804 	Strip off all char up to & including first tab.
805 	If more than 8 char were stripped, the 8 th is replaced by
806 		a '*' & the remainder are discarded.
807 	Unless user specified an "a", append blanks to fill
808 		out line to eff. line length (default= 72 char).
809 	Truncate lines > eff. line length (default=72).
810 	Add stripped char to end of line.	*/
811 sstrip()
812 {
813 	register int i, k;
814 	char *c,*savec;
815 
816 	k = -1;
817 	c = pfirst;
818 	while(*c != '\t' && *c != '\n'){k++; c++;}
819 	if(*c != '\t'){fprintf(stderr,"not -s format\r\n"); exit(1);}
820 
821 	savec = c;
822 	c = pfirst;
823 	savek = (k > 7) ? 7 : k;
824 	for(i=0; i <= savek; i++)savchr[i] = *c++;	/* Tab not saved */
825 	if(k > 7)savchr[7] = '*';
826 
827 	pfirst = ++savec;		/* Point pfirst to char after tab */
828 }
829 /* ================================================================= */
830 
831 sadd()
832 {
833 	register int i;
834 
835 	for(i=0; i <= savek; i++)*plast++ = savchr[i];
836 	*plast = '\n';
837 }
838 outputtabs(p)	/* Contract according to output tab specifications.	*/
839 int	p;			/* Pointer to tab specification.	*/
840 {
841 	int	*tabs;		/* Pointer to tabs			*/
842 	char	*tfirst,	/* Pointer to new buffer start		*/
843 		*tlast,		/* Pointer to new buffer end		*/
844 		*mark;		/* Marker pointer			*/
845 	register char c;	/* Character being scanned		*/
846 	int	logcol;		/* Logical column			*/
847 
848 	tabs = spectbl[p];
849 	tfirst = tlast = pfirst;
850 	logcol = 1;
851 	while (pfirst <= plast) {
852 		if (logcol == *tabs) tabs++;
853 		switch (c = *pfirst++) {
854 		case '\b':
855 			if (logcol > 1) logcol--;
856 			*tlast++ = c;
857 			if (logcol < *tabs) tabs--;
858 			break;
859 		case ' ':
860 			mark = tlast;
861 			do {
862 				*tlast++ = ' ';
863 				logcol++;
864 				if (logcol == *tabs) {
865 					*mark++ = '\t';
866 					tlast = mark;
867 					tabs++;
868 					}
869 				} while (*pfirst++ == ' ');
870 			pfirst--;
871 			break;
872 		default:
873 			logcol++;
874 			*tlast++ = c;
875 			break;
876 			}
877 		}
878 	pfirst = tfirst;
879 	plast = --tlast;
880 }
881