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