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
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.
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]
23 * Copyright 1996 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
30 #pragma ident "%Z%%M% %I% %E% SMI"
33 * tabs [tabspec] [+mn] [-Ttype]
34 * set tabs (and margin, if +mn), for terminal type
40 #include <sys/types.h>
53 #define EQ(a, b) (strcmp(a, b) == 0)
54 /* max # columns used (needed for GSI) */
56 #define NTABS 65 /* max # tabs +1 (to be set) */
57 #define NTABSCL 21 /* max # tabs + 1 that will be cleared */
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 */
70 #define TCLRLN 0 /* long, repetitive, general tab clear */
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 */
82 char *ttype
; /* -Tttype */
83 char *tclr
; /* char sequence to clear tabs and return carriage */
84 int tmaxtab
; /* maximum allowed position */
87 static struct ttab termtab
[] = {
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,
97 "4000a-12", tclrsh
, 158,
98 "450-12", tclrsh
, 158,
99 "450-12-8", tclrsh
, 158,
101 "2631-c", tclrhp
, 240,
108 static char settab
[32], clear_tabs
[32];
110 static int maxtab
; /* max tab for repetitive spec */
112 static int margflg
; /* >0 ==> +m option used, 0 ==> not */
113 static char *terminal
= "";
114 static char *tabspec
= "-8"; /* default tab specification */
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 */
121 static struct stat statbuf
;
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
);
134 static int stdtab(char option
[], int tabvect
[]);
136 static int chk_codes(char *codes
);
139 main(int argc
, char **argv
)
141 int tabvect
[NTABS
]; /* build tab list here */
142 char *scan
; /* scan pointer to next char */
143 char operand
[LINE_MAX
];
146 (void) setlocale(LC_ALL
, "");
148 #if !defined(TEXT_DOMAIN)
149 #define TEXT_DOMAIN "SYS_TEST"
151 (void) textdomain(TEXT_DOMAIN
);
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
);
159 (void) chmod(devtty
, 0000); /* nobody, not even us */
162 tabvect
[0] = 0; /* mark as not yet filled in */
167 if (*++scan
== 'm') {
170 margin
= getnum(&scan
);
174 (void) fprintf(stderr
, gettext(
175 "tabs: %s: invalid tab spec\n"), scan
-1);
180 * only n1[,n2,...] operand can follow
181 * end of options delimiter "--"
183 (void) fprintf(stderr
, gettext(
184 "tabs: %s: invalid tab stop operand\n"), scan
);
187 } else if (*scan
== '-') {
189 if (*(scan
+1) == 'T') {
190 /* allow space or no space after -T */
191 if (*(scan
+2) == '\0') {
198 } else if (*(scan
+1) == '-')
199 if (*(scan
+2) == '\0')
202 tabspec
= scan
; /* --file */
203 else if (strcmp(scan
+1, "code") == 0) {
205 /* skip to next argument */
206 } else if (chk_codes(scan
+1) ||
207 (isdigit(*(scan
+1)) && *(scan
+2) == '\0')) {
209 * valid code or single digit decimal
214 (void) fprintf(stderr
, gettext(
215 "tabs: %s: invalid tab spec\n"), scan
);
220 * only n1[,n2,...] operand can follow
221 * end of options delimiter "--"
223 (void) fprintf(stderr
, gettext(
224 "tabs: %s: invalid tab stop operand\n"), scan
);
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.
236 if (strrchr(*argv
, '-') == (char *)NULL
) {
237 (void) strcat(operand
, *argv
);
239 (void) strcat(operand
, ",");
243 (void) fprintf(stderr
, gettext(
244 "tabs: %s: tab stop values must be positive integers\n"),
249 tabspec
= operand
; /* save tab specification */
252 if (*terminal
== '\0') {
253 if ((terminal
= getenv("TERM")) == (char *)NULL
||
256 * Use tab setting and clearing sequences specified
257 * by the ANSI standard.
259 terminal
= "ansi+tabs";
262 if (setupterm(terminal
, 1, &err
) == ERR
) {
263 (void) fprintf(stderr
, gettext(
264 "tabs: %s: terminfo file not found\n"), terminal
);
266 } else if (!tigetstr("hts")) {
267 (void) fprintf(stderr
, gettext(
268 "tabs: cannot set tabs on terminal type %s\n"), terminal
);
271 if (err
<= 0 || columns
<= 0 || set_tab
== 0) {
273 if (strcmp(terminal
, "ibm") == 0)
274 (void) strcpy(settab
, tsetibm
);
276 (void) strcpy(settab
, tsethp
);
277 (void) strcpy(clear_tabs
, tt
->tclr
);
278 maxtab
= tt
->tmaxtab
;
281 (void) strcpy(settab
, set_tab
);
282 (void) strcpy(clear_tabs
, clear_all_tabs
);
284 scantab(tabspec
, tabvect
, 0);
286 repetab("8", tabvect
);
293 * return 1 if code option is valid, otherwise return 0
296 chk_codes(char *code
)
298 if (*(code
+1) == '\0' && (*code
== 'a' || *code
== 'c' ||
299 *code
== 'f' || *code
== 'p' || *code
== 's' || *code
== 'u'))
301 else if (*(code
+1) == '2' && *(code
+2) == '\0' &&
302 (*code
== 'a' || *code
== 'c'))
304 else if (*code
== 'c' && *(code
+1) == '3' && *(code
+2) == '\0')
309 /* scantab: scan 1 tabspec & return tab list for it */
311 scantab(char *scan
, int tabvect
[NTABS
], int level
)
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
)) {
321 (void) fprintf(stderr
, gettext(
322 "tabs: %s: unknown tab code\n"), scan
);
326 arbitab(scan
, tabvect
);
330 /* repetab: scan and set repetitve tabs, 1+n, 1+2*n, etc */
333 repetab(char *scan
, int tabvect
[NTABS
])
337 incr
= getnum(&scan
);
339 limit
= (maxtab
-1)/(incr
?incr
:1)-1; /* # last actual tab */
342 for (i
= 0; i
<= limit
; i
++)
343 tabvect
[i
] = tabn
+= incr
;
347 /* arbitab: handle list of arbitrary tabs */
350 arbitab(char *scan
, int tabvect
[NTABS
])
357 for (i
= 0; i
< NTABS
-1; ) {
359 scan
++; /* +n ==> increment, not absolute */
360 if (t
= getnum(&scan
))
361 tabvect
[i
++] = last
+= t
;
364 (void) fprintf(stderr
, gettext(
365 "tabs: %s: invalid increment\n"), scan_save
);
369 if ((t
= getnum(&scan
)) > last
)
370 tabvect
[i
++] = last
= t
;
373 (void) fprintf(stderr
, gettext(
374 "tabs: %s: invalid tab stop\n"), scan_save
);
378 if (*scan
++ != ',') break;
382 (void) fprintf(stderr
, gettext(
383 "tabs: %s: last tab stop would be set at a column greater than %d\n"),
390 /* filetab: copy tabspec from existing file */
394 filetab(char *scan
, int tabvect
[NTABS
], int level
)
399 char card
[CARDSIZ
]; /* buffer area for 1st card in file */
404 (void) fprintf(stderr
, gettext(
405 "tabs: %s points to another file: invalid file indirection\n"),
409 if ((fildes
= open(scan
, O_RDONLY
)) < 0) {
411 (void) fprintf(stderr
, gettext("tabs: %s: "), scan
);
415 length
= read(fildes
, card
, CARDSIZ
);
416 (void) close(fildes
);
419 for (i
= 0; i
< length
&& (c
= card
[i
]) != '\n'; i
++) {
422 state
= (c
== '<'); break;
424 state
= (c
== ':')?2:0; break;
445 } else if (c
== ':') {
465 if (found
&& scan
!= 0) {
466 scantab(scan
, tabvect
, 1);
473 scantab("-8", tabvect
, 1);
479 if (strncmp(term
, "1620", 4) == 0 ||
480 strncmp(term
, "1700", 4) == 0 || strncmp(term
, "450", 3) == 0)
482 else if (strncmp(term
, "300s", 4) == 0)
484 else if (strncmp(term
, "4000a", 5) == 0)
486 else if (strcmp(term
, "43") == 0)
488 else if (strcmp(term
, "tn300") == 0 || strcmp(term
, "tn1200") == 0)
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");
508 for (t
= termtab
; t
->ttype
; t
++) {
509 if (EQ(terminal
, t
->ttype
))
512 /* should have message */
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.
524 settabs(int tabvect
[NTABS
])
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
;
531 ttyold
.c_iflag
&= ~ICRNL
;
532 ttyold
.c_oflag
&= ~(ONLCR
|OCRNL
|ONOCR
|ONLRET
);
533 (void) ioctl(1, TCSETAW
, &ttyold
); /* turn off cr-lf map */
537 p
= cleartabs(p
, clear_tabs
);
540 tmarg
= getmarg(terminal
);
542 case GMG
: /* GSI300S */
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.
550 *p
++ = 'T'; /* setup for direct tab */
551 if (margin
&= 0177) /* normal case */
553 else { /* +m0 case */
554 *p
++ = 1; /* column 1 */
555 *p
++ = '\b'; /* column 0 */
557 *p
++ = margin
; /* direct horizontal tab */
559 *p
++ = '0'; /* actual margin set */
561 case TMG
: /* TERMINET 300 & 1200 */
565 case DMG
: /* DASI450/DIABLO 1620 */
566 *p
++ = ESC
; /* direct tab ignores margin */
569 *p
++ = (margin
& 0177);
573 *p
++ = (margin
& 0177) + 1;
577 case FMG
: /* TTY 43 */
587 (void) write(1, setbuf
, p
- setbuf
);
602 * actual setting: at least terminals do this consistently!
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
);
613 if (EQ(terminal
, "4424"))
614 *p
++ = '\n'; /* TTY40/2 needs LF, not just CR */
615 (void) write(1, setbuf
, p
- setbuf
);
620 * Set software tabs. This only works on UNIX/370 using a series/1
621 * front-end processor.
625 /* cleartabs(pointer to buffer, pointer to clear sequence) */
627 cleartabs(register char *p
, char *qq
)
632 if (clear_tabs
== 0) { /* if repetitive sequence */
634 for (i
= 0; i
< NTABSCL
- 1; i
++) {
641 while (*p
++ = *q
++) /* copy table sequence */
643 p
--; /* adjust for null */
644 if (EQ(terminal
, "4424")) { /* TTY40 extra delays needed */
653 /* getnum: scan and convert number, return zero if none found */
654 /* set scan ptr to addr of ending delimeter */
662 while ((c
= *scan
++) >= '0' && c
<= '9') n
= n
* 10 + c
-'0';
667 /* usage: terminate processing with usage message */
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"));
675 (void) fprintf(stderr
, gettext(
676 " tabs [-T type][+m[n]] n1[,n2,...]\n"));
682 /* endup: make sure tty mode reset & exit */
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
);
700 * stdtabs: standard tabs table
701 * format: option code letter(s), null, tabs, null
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,
713 's', 0, 1, 10, 55, 0, /* SNOBOL */
714 'u', 0, 1, 12, 20, 44, 0, /* UNIVAC ASM */
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
725 stdtab(char option
[], int tabvect
[])
731 if (EQ(option
, sp
)) {
732 while (*sp
++) /* skip to 1st tab value */
734 while (*tabvect
++ = *sp
++) /* copy, make int */
738 while (*sp
++) /* skip to 1st tab value */
740 while (*sp
++) /* skip over tab list */