xref: /illumos-gate/usr/src/cmd/tabs/tabs.c (revision 48edc7cf07b5dccc3ad84bf2dafe4150bd666d60)
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 1996 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  *	tabs [tabspec] [+mn] [-Ttype]
34  *	set tabs (and margin, if +mn), for terminal type
35  */
36 
37 
38 #include <stdio.h>
39 #include <signal.h>
40 #include <sys/types.h>
41 #include <stdlib.h>
42 #include <fcntl.h>
43 #include <sys/stat.h>
44 #include <curses.h>
45 #include <term.h>
46 #include <locale.h>
47 #include <unistd.h>
48 #include <string.h>
49 #include <ctype.h>
50 #include <limits.h>
51 #include <signal.h>
52 
53 #define	EQ(a, b)	(strcmp(a, b) == 0)
54 /*	max # columns used (needed for GSI) */
55 #define	NCOLS	256
56 #define	NTABS	65	/* max # tabs +1 (to be set) */
57 #define	NTABSCL	21	/* max # tabs + 1 that will be cleared */
58 #define	ESC	033
59 #define	CLEAR	'2'
60 #define	SET	'1'
61 #define	TAB	'\t'
62 #define	CR	'\r'
63 #define	NMG	0	/* no margin setting */
64 #define	GMG	1	/* DTC300s margin */
65 #define	TMG	2	/* TERMINET margin */
66 #define	DMG	3	/* DASI450 margin */
67 #define	FMG	4	/* TTY 43 margin */
68 #define	TRMG	5	/* Trendata 4000a */
69 
70 #define	TCLRLN	0	/* long, repetitive, general tab clear */
71 
72 static char tsethp[] = {ESC,  '1', 0};		/* (default) */
73 static char tsetibm[] = {ESC, '0', 0};		/* ibm */
74 static char tclrhp[] = {ESC, '3', CR, 0};	/* hp terminals */
75 /* short sequence for many terminals */
76 static char tclrsh[] = {ESC, CLEAR, CR, 0};
77 static char tclrgs[] = {ESC, TAB, CR, 0};	/* short, for 300s */
78 static char tclr40[] = {ESC, 'R', CR, 0};	/* TTY 40/2, 4424 */
79 static char tclribm[] = {ESC, '1', CR, 0};	/* ibm */
80 
81 static struct ttab {
82 	char *ttype;	/* -Tttype */
83 	char *tclr;	/* char sequence to clear tabs and return carriage */
84 	int tmaxtab;	/* maximum allowed position */
85 } *tt;
86 
87 static struct ttab termtab[] = {
88 	"",		tclrsh,	132,
89 	"1620-12",	tclrsh,	158,
90 	"1620-12-8",	tclrsh,	158,
91 	"1700-12",	tclrsh,	132,
92 	"1700-12-8",	tclrsh,	158,
93 	"300-12",	TCLRLN,	158,
94 	"300s-12",	tclrgs,	158,
95 	"4424",		tclr40,	 80,
96 	"4000a",	tclrsh,	132,
97 	"4000a-12",	tclrsh,	158,
98 	"450-12",	tclrsh,	158,
99 	"450-12-8",	tclrsh,	158,
100 	"2631",		tclrhp, 240,
101 	"2631-c",	tclrhp, 240,
102 	"ibm",		tclribm, 80,
103 	0
104 };
105 
106 static int	err;
107 static int 	tmarg;
108 static char	settab[32], clear_tabs[32];
109 
110 static int	maxtab;		/* max tab for repetitive spec */
111 static int	margin;
112 static int	margflg;	/* >0 ==> +m option used, 0 ==> not */
113 static char	*terminal = "";
114 static char	*tabspec = "-8";	/* default tab specification */
115 
116 static struct termio ttyold;	/* tty table */
117 static int	ttyisave;	/* save for input modes */
118 static int	ttyosave;	/* save for output modes */
119 static int	istty;		/* 1 ==> is actual tty */
120 
121 static struct	stat	statbuf;
122 static char	*devtty;
123 
124 static void scantab(char *scan, int tabvect[NTABS], int level);
125 static void repetab(char *scan, int tabvect[NTABS]);
126 static void arbitab(char *scan, int tabvect[NTABS]);
127 static void filetab(char *scan, int tabvect[NTABS], int level);
128 static int getmarg(char *term);
129 static struct ttab *termadj();
130 static void settabs(int tabvect[NTABS]);
131 static char *cleartabs(register char *p, char *qq);
132 static int getnum(char **scan1);
133 static void endup();
134 static int stdtab(char option[], int tabvect[]);
135 static void usage();
136 static int chk_codes(char *codes);
137 
138 int
139 main(int argc, char **argv)
140 {
141 	int tabvect[NTABS];	/* build tab list here */
142 	char *scan;	/* scan pointer to next char */
143 	char operand[LINE_MAX];
144 	int option_end = 0;
145 
146 	(void) setlocale(LC_ALL, "");
147 
148 #if !defined(TEXT_DOMAIN)
149 #define	TEXT_DOMAIN "SYS_TEST"
150 #endif
151 	(void) textdomain(TEXT_DOMAIN);
152 
153 	(void) signal(SIGINT, endup);
154 	if (ioctl(1, TCGETA, &ttyold) == 0) {
155 		ttyisave = ttyold.c_iflag;
156 		ttyosave = ttyold.c_oflag;
157 		(void) fstat(1, &statbuf);
158 		devtty = ttyname(1);
159 		(void) chmod(devtty, 0000);	/* nobody, not even us */
160 		istty++;
161 	}
162 	tabvect[0] = 0;	/* mark as not yet filled in */
163 	while (--argc > 0) {
164 		scan = *++argv;
165 		if (*scan == '+') {
166 			if (!option_end) {
167 				if (*++scan == 'm') {
168 					margflg++;
169 					if (*++scan)
170 						margin = getnum(&scan);
171 					else
172 						margin = 10;
173 				} else {
174 					(void) fprintf(stderr, gettext(
175 				"tabs: %s: invalid tab spec\n"), scan-1);
176 					usage();
177 				}
178 			} else {
179 				/*
180 				 * only n1[,n2,...] operand can follow
181 				 * end of options delimiter "--"
182 				 */
183 				(void) fprintf(stderr, gettext(
184 				"tabs: %s: invalid tab stop operand\n"), scan);
185 				usage();
186 			}
187 		} else if (*scan == '-') {
188 			if (!option_end) {
189 				if (*(scan+1) == 'T') {
190 					/* allow space or no space after -T */
191 					if (*(scan+2) == '\0') {
192 						if (--argc > 0)
193 							terminal = *++argv;
194 						else
195 							usage();
196 					} else
197 						terminal = scan+2;
198 				} else if (*(scan+1) == '-')
199 					if (*(scan+2) == '\0')
200 						option_end = 1;
201 					else
202 						tabspec = scan; /* --file */
203 				else if (strcmp(scan+1, "code") == 0) {
204 					/* EMPTY */
205 					/* skip to next argument */
206 				} else if (chk_codes(scan+1) ||
207 				    (isdigit(*(scan+1)) && *(scan+2) == '\0')) {
208 					/*
209 					 * valid code or single digit decimal
210 					 * number
211 					 */
212 					tabspec = scan;
213 				} else {
214 					(void) fprintf(stderr, gettext(
215 					"tabs: %s: invalid tab spec\n"), scan);
216 					usage();
217 				}
218 			} else {
219 				/*
220 				 * only n1[,n2,...] operand can follow
221 				 * end of options delimiter "--"
222 				 */
223 				(void) fprintf(stderr, gettext(
224 				"tabs: %s: invalid tab stop operand\n"), scan);
225 				usage();
226 			}
227 		} else {
228 			/*
229 			 * Tab-stop values separated using either commas
230 			 * or blanks.  If any number (except the first one)
231 			 * is preceded by a plus sign, it is taken as an
232 			 * increment to be added to the previous value.
233 			 */
234 			operand[0] = '\0';
235 			while (argc > 0) {
236 				if (strrchr(*argv, '-') == (char *)NULL) {
237 					(void) strcat(operand, *argv);
238 					if (argc > 1)
239 						(void) strcat(operand, ",");
240 					--argc;
241 					++argv;
242 				} else {
243 					(void) fprintf(stderr, gettext(
244 		"tabs: %s: tab stop values must be positive integers\n"),
245 					    *argv);
246 					usage();
247 				}
248 			}
249 			tabspec = operand;	/* save tab specification */
250 		}
251 	}
252 	if (*terminal == '\0') {
253 		if ((terminal = getenv("TERM")) == (char *)NULL ||
254 		    *terminal == '\0') {
255 			/*
256 			 * Use tab setting and clearing sequences specified
257 			 * by the ANSI standard.
258 			 */
259 			terminal = "ansi+tabs";
260 		}
261 	}
262 	if (setupterm(terminal, 1, &err) == ERR) {
263 		(void) fprintf(stderr, gettext(
264 		"tabs: %s: terminfo file not found\n"), terminal);
265 		usage();
266 	} else if (!tigetstr("hts")) {
267 		(void) fprintf(stderr, gettext(
268 		"tabs: cannot set tabs on terminal type %s\n"), terminal);
269 		usage();
270 	}
271 	if (err <= 0 || columns <= 0 || set_tab == 0) {
272 		tt = termadj();
273 		if (strcmp(terminal, "ibm") == 0)
274 			(void) strcpy(settab, tsetibm);
275 		else
276 			(void) strcpy(settab, tsethp);
277 		(void) strcpy(clear_tabs, tt->tclr);
278 		maxtab = tt->tmaxtab;
279 	} else {
280 		maxtab = columns;
281 		(void) strcpy(settab, set_tab);
282 		(void) strcpy(clear_tabs, clear_all_tabs);
283 	}
284 	scantab(tabspec, tabvect, 0);
285 	if (!tabvect[0])
286 		repetab("8", tabvect);
287 	settabs(tabvect);
288 	endup();
289 	return (0);
290 }
291 
292 /*
293  * return 1 if code option is valid, otherwise return 0
294  */
295 int
296 chk_codes(char *code)
297 {
298 	if (*(code+1) == '\0' && (*code == 'a' || *code == 'c' ||
299 	    *code == 'f' || *code == 'p' || *code == 's' || *code == 'u'))
300 			return (1);
301 	else if (*(code+1) == '2' && *(code+2) == '\0' &&
302 	    (*code == 'a' || *code == 'c'))
303 			return (1);
304 	else if (*code == 'c' && *(code+1) == '3' && *(code+2) == '\0')
305 		return (1);
306 	return (0);
307 }
308 
309 /*	scantab: scan 1 tabspec & return tab list for it */
310 void
311 scantab(char *scan, int tabvect[NTABS], int level)
312 {
313 	char c;
314 	if (*scan == '-') {
315 		if ((c = *++scan) == '-')
316 			filetab(++scan, tabvect, level);
317 		else if (c >= '0' && c <= '9')
318 			repetab(scan, tabvect);
319 		else if (stdtab(scan, tabvect)) {
320 			endup();
321 			(void) fprintf(stderr, gettext(
322 			"tabs: %s: unknown tab code\n"), scan);
323 			usage();
324 		}
325 	} else {
326 		arbitab(scan, tabvect);
327 	}
328 }
329 
330 /*	repetab: scan and set repetitve tabs, 1+n, 1+2*n, etc */
331 
332 void
333 repetab(char *scan, int tabvect[NTABS])
334 {
335 	int incr, i, tabn;
336 	int limit;
337 	incr = getnum(&scan);
338 	tabn = 1;
339 	limit = (maxtab-1)/(incr?incr:1)-1; /* # last actual tab */
340 	if (limit > NTABS-2)
341 		limit = NTABS-2;
342 	for (i = 0; i <= limit; i++)
343 		tabvect[i] = tabn += incr;
344 	tabvect[i] = 0;
345 }
346 
347 /*	arbitab: handle list of arbitrary tabs */
348 
349 void
350 arbitab(char *scan, int tabvect[NTABS])
351 {
352 	char *scan_save;
353 	int i, t, last;
354 
355 	scan_save = scan;
356 	last = 0;
357 	for (i = 0; i < NTABS-1; ) {
358 		if (*scan == '+') {
359 			scan++;		/* +n ==> increment, not absolute */
360 			if (t = getnum(&scan))
361 				tabvect[i++] = last += t;
362 			else {
363 				endup();
364 				(void) fprintf(stderr, gettext(
365 				"tabs: %s: invalid increment\n"), scan_save);
366 				usage();
367 			}
368 		} else {
369 			if ((t = getnum(&scan)) > last)
370 				tabvect[i++] = last = t;
371 			else {
372 				endup();
373 				(void) fprintf(stderr, gettext(
374 				"tabs: %s: invalid tab stop\n"), scan_save);
375 				usage();
376 			}
377 		}
378 		if (*scan++ != ',') break;
379 	}
380 	if (last > NCOLS) {
381 		endup();
382 		(void) fprintf(stderr, gettext(
383 	"tabs: %s: last tab stop would be set at a column greater than %d\n"),
384 		    scan_save, NCOLS);
385 		usage();
386 	}
387 	tabvect[i] = 0;
388 }
389 
390 /*	filetab: copy tabspec from existing file */
391 #define	CARDSIZ	132
392 
393 void
394 filetab(char *scan, int tabvect[NTABS], int level)
395 {
396 	int length, i;
397 	char c;
398 	int fildes;
399 	char card[CARDSIZ];	/* buffer area for 1st card in file */
400 	char state, found;
401 	char *temp;
402 	if (level) {
403 		endup();
404 		(void) fprintf(stderr, gettext(
405 		"tabs: %s points to another file: invalid file indirection\n"),
406 		    scan);
407 		exit(1);
408 	}
409 	if ((fildes = open(scan, O_RDONLY)) < 0) {
410 		endup();
411 		(void) fprintf(stderr, gettext("tabs: %s: "), scan);
412 		perror("");
413 		exit(1);
414 	}
415 	length = read(fildes, card, CARDSIZ);
416 	(void) close(fildes);
417 	found = state = 0;
418 	scan = 0;
419 	for (i = 0; i < length && (c = card[i]) != '\n'; i++) {
420 		switch (state) {
421 		case 0:
422 			state = (c == '<'); break;
423 		case 1:
424 			state = (c == ':')?2:0; break;
425 		case 2:
426 			if (c == 't')
427 				state = 3;
428 			else if (c == ':')
429 				state = 6;
430 			else if (c != ' ')
431 				state = 5;
432 			break;
433 		case 3:
434 			if (c == ' ')
435 				state = 2;
436 			else {
437 				scan = &card[i];
438 				state = 4;
439 			}
440 			break;
441 		case 4:
442 			if (c == ' ') {
443 				card[i] = '\0';
444 				state = 5;
445 			} else if (c == ':') {
446 				card[i] = '\0';
447 				state = 6;
448 			}
449 			break;
450 		case 5:
451 			if (c == ' ')
452 				state = 2;
453 			else if (c == ':')
454 				state = 6;
455 			break;
456 		case 6:
457 			if (c == '>') {
458 				found = 1;
459 				goto done;
460 			} else state = 5;
461 			break;
462 		}
463 	}
464 done:
465 	if (found && scan != 0) {
466 		scantab(scan, tabvect, 1);
467 		temp = scan;
468 		while (*++temp)
469 			;
470 		*temp = '\n';
471 	}
472 	else
473 		scantab("-8", tabvect, 1);
474 }
475 
476 int
477 getmarg(char *term)
478 {
479 	if (strncmp(term, "1620", 4) == 0 ||
480 	    strncmp(term, "1700", 4) == 0 || strncmp(term, "450", 3) == 0)
481 		return (DMG);
482 	else if (strncmp(term, "300s", 4) == 0)
483 		return (GMG);
484 	else if (strncmp(term, "4000a", 5) == 0)
485 		return (TRMG);
486 	else if (strcmp(term, "43") == 0)
487 		return (FMG);
488 	else if (strcmp(term, "tn300") == 0 || strcmp(term, "tn1200") == 0)
489 		return (TMG);
490 	else
491 		return (NMG);
492 }
493 
494 
495 
496 struct ttab *
497 termadj(void)
498 {
499 	struct ttab *t;
500 
501 	if (strncmp(terminal, "40-2", 4) == 0 || strncmp(terminal,
502 	    "40/2", 4) == 0 || strncmp(terminal, "4420", 4) == 0)
503 		(void) strcpy(terminal, "4424");
504 	else if (strncmp(terminal, "ibm", 3) == 0 || strcmp(terminal,
505 	    "3101") == 0 || strcmp(terminal, "system1") == 0)
506 		(void) strcpy(terminal, "ibm");
507 
508 	for (t = termtab; t->ttype; t++) {
509 		if (EQ(terminal, t->ttype))
510 			return (t);
511 	}
512 /* should have message */
513 	return (termtab);
514 }
515 
516 char	*cleartabs();
517 /*
518  *	settabs: set actual tabs at terminal
519  *	note: this code caters to necessities of handling GSI and
520  *	other terminals in a consistent way.
521  */
522 
523 void
524 settabs(int tabvect[NTABS])
525 {
526 	char setbuf[512];	/* 2+3*NTABS+2+NCOLS+NTABS (+ some extra) */
527 	char *p;		/* ptr for assembly in setbuf */
528 	int *curtab;		/* ptr to tabvect item */
529 	int i, previous, nblanks;
530 	if (istty) {
531 		ttyold.c_iflag &= ~ICRNL;
532 		ttyold.c_oflag &= ~(ONLCR|OCRNL|ONOCR|ONLRET);
533 		(void) ioctl(1, TCSETAW, &ttyold);	/* turn off cr-lf map */
534 	}
535 	p = setbuf;
536 	*p++ = CR;
537 	p = cleartabs(p, clear_tabs);
538 
539 	if (margflg) {
540 		tmarg = getmarg(terminal);
541 		switch (tmarg) {
542 		case GMG:	/* GSI300S */
543 		/*
544 		 * NOTE: the 300S appears somewhat odd, in that there is
545 		 * a column 0, but there is no way to do a direct tab to it.
546 		 * The sequence ESC 'T' '\0' jumps to column 27 and prints
547 		 * a '0', without changing the margin.
548 		 */
549 			*p++ = ESC;
550 			*p++ = 'T';	/* setup for direct tab */
551 			if (margin &= 0177)	/* normal case */
552 				*p++ = margin;
553 			else {			/* +m0 case */
554 				*p++ = 1;	/* column 1 */
555 				*p++ = '\b';	/* column 0 */
556 			}
557 			*p++ = margin;	/* direct horizontal tab */
558 			*p++ = ESC;
559 			*p++ = '0';	/* actual margin set */
560 			break;
561 		case TMG:	/* TERMINET 300 & 1200 */
562 			while (margin--)
563 				*p++ = ' ';
564 			break;
565 		case DMG:	/* DASI450/DIABLO 1620 */
566 			*p++ = ESC;	/* direct tab ignores margin */
567 			*p++ = '\t';
568 			if (margin == 3) {
569 				*p++ = (margin & 0177);
570 				*p++ = ' ';
571 			}
572 			else
573 				*p++ = (margin & 0177) + 1;
574 			*p++ = ESC;
575 			*p++ = '9';
576 			break;
577 		case FMG:	/* TTY 43 */
578 			p--;
579 			*p++ = ESC;
580 			*p++ = 'x';
581 			*p++ = CR;
582 			while (margin--)
583 				*p++ = ' ';
584 			*p++ = ESC;
585 			*p++ = 'l';
586 			*p++ = CR;
587 			(void) write(1, setbuf, p - setbuf);
588 			return;
589 		case TRMG:
590 			p--;
591 			*p++ = ESC;
592 			*p++ = 'N';
593 			while (margin--)
594 				*p++ = ' ';
595 			*p++ = ESC;
596 			*p++ = 'F';
597 			break;
598 		}
599 	}
600 
601 /*
602  *	actual setting: at least terminals do this consistently!
603  */
604 	previous = 1; curtab = tabvect;
605 	while ((nblanks = *curtab-previous) >= 0 &&
606 	    previous + nblanks <= maxtab) {
607 		for (i = 1; i <= nblanks; i++) *p++ = ' ';
608 		previous = *curtab++;
609 		(void) strcpy(p, settab);
610 		p += strlen(settab);
611 	}
612 	*p++ = CR;
613 	if (EQ(terminal, "4424"))
614 		*p++ = '\n';	/* TTY40/2 needs LF, not just CR */
615 	(void) write(1, setbuf, p - setbuf);
616 }
617 
618 
619 /*
620  *	Set software tabs.  This only works on UNIX/370 using a series/1
621  *	front-end processor.
622  */
623 
624 
625 /*	cleartabs(pointer to buffer, pointer to clear sequence) */
626 char *
627 cleartabs(register char *p, char *qq)
628 {
629 	int i;
630 	char *q;
631 	q = qq;
632 	if (clear_tabs == 0) {		/* if repetitive sequence */
633 		*p++ = CR;
634 		for (i = 0; i < NTABSCL - 1; i++) {
635 			*p++ = TAB;
636 			*p++ = ESC;
637 			*p++ = CLEAR;
638 		}
639 		*p++ = CR;
640 	} else {
641 		while (*p++ = *q++)	/* copy table sequence */
642 			;
643 		p--;			/* adjust for null */
644 		if (EQ(terminal, "4424")) {	/* TTY40 extra delays needed */
645 			*p++ = '\0';
646 			*p++ = '\0';
647 			*p++ = '\0';
648 			*p++ = '\0';
649 		}
650 	}
651 	return (p);
652 }
653 /*	getnum: scan and convert number, return zero if none found */
654 /*	set scan ptr to addr of ending delimeter */
655 int
656 getnum(char **scan1)
657 {
658 	int n;
659 	char c, *scan;
660 	n = 0;
661 	scan = *scan1;
662 	while ((c = *scan++) >= '0' && c <= '9') n = n * 10 + c -'0';
663 	*scan1 = --scan;
664 	return (n);
665 }
666 
667 /*	usage: terminate processing with usage message */
668 void
669 usage(void)
670 {
671 	(void) fprintf(stderr, gettext(
672 "usage: tabs [ -n| --file| [[-code] -a| -a2| -c| -c2| -c3| -f| -p| -s| -u]] \
673 [+m[n]] [-T type]\n"));
674 
675 	(void) fprintf(stderr, gettext(
676 "       tabs [-T type][+m[n]] n1[,n2,...]\n"));
677 
678 	endup();
679 	exit(1);
680 }
681 
682 /*	endup: make sure tty mode reset & exit */
683 void
684 endup(void)
685 {
686 
687 	if (istty) {
688 		ttyold.c_iflag = ttyisave;
689 		ttyold.c_oflag = ttyosave;
690 		/* reset cr-lf to previous */
691 		(void) ioctl(1, TCSETAW, &ttyold);
692 		(void) chmod(devtty, statbuf.st_mode);
693 	}
694 	if (err > 0) {
695 		(void) resetterm();
696 	}
697 }
698 
699 /*
700  *	stdtabs: standard tabs table
701  *	format: option code letter(s), null, tabs, null
702  */
703 static char stdtabs[] = {
704 'a',	0, 1, 10, 16, 36, 72, 0,		/* IBM 370 Assembler */
705 'a', '2', 0, 1, 10, 16, 40, 72, 0,		/* IBM Assembler alternative */
706 'c',	0, 1, 8, 12, 16, 20, 55, 0,		/* COBOL, normal */
707 'c', '2', 0, 1, 6, 10, 14, 49, 0,		/* COBOL, crunched */
708 'c', '3', 0, 1, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62, 67,
709 	0,					/* crunched COBOL, many tabs */
710 'f',	0, 1, 7, 11, 15, 19, 23, 0,		/* FORTRAN */
711 'p',	0, 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 0,
712 						/* PL/I */
713 's',	0, 1, 10, 55, 0, 			/* SNOBOL */
714 'u',	0, 1, 12, 20, 44, 0,			/* UNIVAC ASM */
715 0};
716 
717 /*
718  *	stdtab: return tab list for any "canned" tab option.
719  *	entry: option points to null-terminated option string
720  *		tabvect points to vector to be filled in
721  *	exit: return (0) if legal, tabvect filled, ending with zero
722  *		return (-1) if unknown option
723  */
724 int
725 stdtab(char option[], int tabvect[])
726 {
727 	char *sp;
728 	tabvect[0] = 0;
729 	sp = stdtabs;
730 	while (*sp) {
731 		if (EQ(option, sp)) {
732 			while (*sp++)		/* skip to 1st tab value */
733 				;
734 			while (*tabvect++ = *sp++)	/* copy, make int */
735 				;
736 			return (0);
737 		}
738 		while (*sp++)	/* skip to 1st tab value */
739 			;
740 		while (*sp++)		/* skip over tab list */
741 			;
742 	}
743 	return (-1);
744 }
745