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