|
|
1.1 root 1: /*
2: * tabs [tabspec] [+mn] [-Ttype]
3: * set tabs (and margin, if +mn), for terminal type
4: */
5:
6: static char tabsvers[] = "@(#)tabs.c 1.1";
7:
8: #include <stdio.h>
9: #include <signal.h>
10: #include <sys/types.h>
11: #include <sys/stat.h>
12: #include <sys/ttyio.h>
13: #define EQ(a,b) (strcmp(a, b) == 0)
14: /* max # columns used (needed for GSI) */
15: #define NCOLS 158
16: #define NTABS 41 /* max # tabs +1 (to be set) */
17: #define NTABSCL 21 /* max # tabs + 1 that will be cleared */
18: #define ESC 033
19: #define CLEAR '2'
20: #define SET '1'
21: #define TAB '\t'
22: #define CR '\r'
23: #define NMG 0 /* no margin setting */
24: #define GMG 1 /* DTC300s margin */
25: #define TMG 2 /* TERMINET margin */
26: #define DMG 3 /* DASI450 margin */
27: #define FMG 4 /* TTY 43 margin */
28: #define TRMG 5 /* Trendata 4000a */
29:
30: #define TCLRLN 0 /* long, repetitive, general tab clear */
31:
32: char tclrhp[] = {ESC,'3',CR,0}; /* short for HP44,45,etc */
33: char tclrsh[] = {ESC,CLEAR,CR,0}; /* short sequence for many terminals */
34: char tclrgs[] = {ESC,TAB,CR,0}; /* short, for 300s */
35: char tclr40[] = {ESC,'R',CR,0}; /* TTY 40/2 */
36:
37: struct ttab {
38: char *ttype; /* -Tttype */
39: char *tclr; /* char sequence to clear tabs and return carriage */
40: int tmaxtab; /* maximum allowed position */
41: int tmarg; /* type of margin setting allowed */
42: } *tt;
43:
44: struct ttab termtab[] = {
45: "", tclrsh, 132, NMG,
46: "1620", tclrsh, 132, DMG,
47: "1620-12", tclrsh, 158, DMG,
48: "1620-12-8", tclrsh, 158, DMG,
49: "1700", tclrsh, 132, DMG,
50: "1700-12", tclrsh, 132, DMG,
51: "1700-12-8", tclrsh, 158, DMG,
52: "2640", TCLRLN, 80, NMG, /* hp 2640a & b */
53: "2645", tclrhp, 80, NMG, /* all hp 40 series except 2640's */
54: "2621", tclrhp, 80, NMG, /* hp2621a and p */
55: "hp", tclrhp, 80, NMG, /* hp default */
56: "300", TCLRLN, 132, NMG,
57: "300-12", TCLRLN, 158, NMG,
58: "300s", tclrgs, 132, GMG,
59: "300s-12", tclrgs, 158, GMG,
60: "40-2", tclr40, 80, NMG,
61: "4000a", tclrsh, 132, TRMG,
62: "4000a-12", tclrsh, 158, TRMG,
63: "43", "", 0, FMG,
64: "450", tclrsh, 132, DMG,
65: "450-12", tclrsh, 158, DMG,
66: "450-12-8", tclrsh, 158, DMG,
67: "tn1200", tclrsh, 118, TMG,
68: "tn300", tclrsh, 118, TMG,
69: "1520", "", 0, NMG,
70: "3045", "", 0, NMG,
71: 0
72: };
73:
74:
75: int maxtab; /* max tab for repetitive spec */
76: int margin;
77: int margflg; /* >0 ==> +m option used, 0 ==> not */
78: char *terminal = "";
79: char *tabspec = "-8"; /* default tab specification */
80:
81: struct sgttyb ttyold; /* tty table */
82: int ttysave; /* save for modes */
83: int istty; /* 1 ==> is actual tty */
84:
85: struct stat statbuf;
86: char *devtty;
87:
88: int endup();
89: char *getenv();
90: struct ttab *termadj();
91:
92: main(argc, argv)
93: char **argv;
94: {
95: int tabvect[NTABS]; /* build tab list here */
96: char *ttyname();
97: char *scan; /* scan pointer to next char */
98: int endup();
99:
100: signal(SIGINT, endup);
101: if (ioctl(1, TIOCGETP, &ttyold) == 0) {
102: ttysave = ttyold.sg_flags;
103: fstat(1, &statbuf);
104: devtty = ttyname(1);
105: if (devtty && *devtty)
106: chmod(devtty, 0000); /* nobody, not even us */
107: istty++;
108: }
109: tabvect[0] = 0; /* mark as not yet filled in */
110: while (--argc > 0) {
111: scan = *++argv;
112: if (*scan == '+')
113: switch (*++scan) {
114: case 'm':
115: margflg++;
116: if (*++scan)
117: margin = getnum(&scan);
118: else
119: margin = 10;
120: break;
121: }
122: else if (*scan == '-' && *(scan+1) == 'T')
123: terminal = scan+2;
124: else
125: tabspec = scan; /* save tab specification */
126: }
127: if (*terminal == '\0') {
128: terminal = getenv("TERM");
129: if (terminal == NULL)
130: terminal = "";
131: }
132: tt = termadj();
133: maxtab = tt->tmaxtab;
134: scantab(tabspec,tabvect,0);
135: if (!tabvect[0])
136: repetab("8",tabvect);
137: settabs(tabvect);
138: endup();
139: exit(0);
140: }
141:
142: /* scantab: scan 1 tabspec & return tab list for it */
143:
144: scantab(scan,tabvect,level)
145: char *scan;
146: int tabvect[NTABS], level;
147: {
148: register char c;
149: if (*scan == '-')
150: if ((c = *++scan) == '-')
151: filetab(++scan,tabvect,level);
152: else if (c >= '0' && c <= '9')
153: repetab(scan,tabvect);
154: else if (stdtab(scan,tabvect))
155: error("unknown tab code");
156: else;
157: else
158: arbitab(scan,tabvect);
159: }
160:
161: /* repetab: scan and set repetitve tabs, 1+n, 1+2*n, etc */
162:
163: repetab(scan,tabvect)
164: char *scan;
165: int tabvect[NTABS];
166: {
167: register incr, i, tabn;
168: int limit;
169: incr = getnum(&scan);
170: tabn = 1;
171: limit = (maxtab-1)/(incr?incr:1)-1; /* # last actual tab */
172: if (limit>NTABS-2)
173: limit = NTABS-2;
174: for (i = 0; i<=limit; i++)
175: tabvect[i] = tabn += incr;
176: tabvect[i] = 0;
177: }
178:
179: /* arbitab: handle list of arbitrary tabs */
180:
181: arbitab(scan,tabvect)
182: char *scan;
183: int tabvect[NTABS];
184: {
185: register i, t, last;
186: last = 0;
187: for (i = 0; i<NTABS-1;) {
188: if (*scan == '+') {
189: scan++; /* +n ==> increment, not absolute */
190: if (t = getnum(&scan))
191: tabvect[i++] = last += t;
192: else error("illegal increment");
193: }
194: else {
195: if ((t = getnum(&scan)) > last)
196: tabvect[i++] = last = t;
197: else error("illegal tabs");
198: }
199: if (*scan++ != ',') break;
200: }
201: if (last > NCOLS)
202: error("illegal tabs");
203: tabvect[i] = 0;
204: }
205:
206: /* filetab: copy tabspec from existing file */
207: #define CARDSIZ 132
208: filetab(scan,tabvect,level)
209: char *scan;
210: int tabvect[NTABS];
211: {
212: register length, i;
213: register char c;
214: int fildes;
215: char card[CARDSIZ]; /* buffer area for 1st card in file */
216: char state, found;
217: char *temp;
218: if (level)
219: error("file indirection");
220: if ((fildes = open(scan,0)) < 0)
221: error("can't open");
222: length = read(fildes,card,CARDSIZ);
223: close(fildes);
224: found = state = 0;
225: scan = 0;
226: for (i = 0; i<length && (c = card[i]) != '\n'; i++) {
227: switch (state) {
228: case 0:
229: state = (c == '<'); break;
230: case 1:
231: state = (c == ':')?2:0; break;
232: case 2:
233: if (c == 't')
234: state = 3;
235: else if (c == ':')
236: state = 6;
237: else if (c != ' ')
238: state = 5;
239: break;
240: case 3:
241: if (c == ' ')
242: state = 2;
243: else {
244: scan = &card[i];
245: state = 4;
246: }
247: break;
248: case 4:
249: if (c == ' ') {
250: card[i] = '\0';
251: state = 5;
252: }
253: else if (c == ':') {
254: card[i] = '\0';
255: state = 6;
256: }
257: break;
258: case 5:
259: if (c == ' ')
260: state = 2;
261: else if (c == ':')
262: state = 6;
263: break;
264: case 6:
265: if (c == '>') {
266: found = 1;
267: goto done;
268: }
269: else state = 5;
270: break;
271: }
272: }
273: done:
274: if (found && scan != 0) {
275: scantab(scan,tabvect,1);
276: temp = scan;
277: while (*++temp);
278: *temp = '\n';
279: }
280: else scantab("-8",tabvect,1);
281: }
282:
283: struct ttab *
284: termadj()
285: {
286: register struct ttab *t;
287:
288: for (t = termtab; t->ttype; t++) {
289: if (EQ(terminal, t->ttype))
290: return(t);
291: }
292: /* should have message */
293: return(termtab);
294: }
295:
296: char *cleartabs();
297: /* settabs: set actual tabs at terminal */
298: /* note: this code caters to necessities of handling GSI and
299: other terminals in a consistent way. */
300:
301: settabs(tabvect)
302: int tabvect[NTABS];
303: {
304: char setbuf[400]; /* 2+3*NTABS+2+NCOLS+NTABS (+ some extra) */
305: register char *p; /* ptr for assembly in setbuf */
306: register *curtab; /* ptr to tabvect item */
307: int i, previous, nblanks;
308: if (istty) {
309: ttyold.sg_flags &= ~(CRMOD);
310: ioctl(1, TIOCSETN, &ttyold); /* turn off cr-lf map */
311: }
312: p = setbuf;
313: *p++ = CR;
314: p = cleartabs(p, tt->tclr);
315:
316: if (margflg)
317: switch(tt->tmarg) {
318: case GMG: /* GSI300S */
319: /* NOTE: the 300S appears somewhat odd, in that there is
320: a column 0, but there is no way to do a direct tab to it.
321: The sequence ESC 'T' '\0' jumps to column 27 and prints
322: a '0', without changing the margin. */
323: *p++ = ESC;
324: *p++ = 'T'; /* setup for direct tab */
325: if (margin &= 0177) /* normal case */
326: *p++ = margin;
327: else { /* +m0 case */
328: *p++ = 1; /* column 1 */
329: *p++ = '\b'; /* column 0 */
330: }
331: *p++ = margin; /* direct horizontal tab */
332: *p++ = ESC;
333: *p++ = '0'; /* actual margin set */
334: break;
335: case TMG: /* TERMINET 300 & 1200 */
336: while (margin--)
337: *p++ = ' ';
338: break;
339: case DMG: /* DASI450/DIABLO 1620 */
340: *p++ = ESC; /* direct tab ignores margin */
341: *p++ = '\t';
342: if (margin == 3){
343: *p++ = (margin & 0177);
344: *p++ = ' ';
345: }
346: else
347: *p++ = (margin & 0177) + 1;
348: *p++ = ESC;
349: *p++ = '9';
350: break;
351: case FMG: /* TTY 43 */
352: p--;
353: *p++ = ESC;
354: *p++ = 'x';
355: *p++ = CR;
356: while (margin--)
357: *p++ = ' ';
358: *p++ = ESC;
359: *p++ = 'l';
360: *p++ = CR;
361: write(1, setbuf, p - setbuf);
362: return;
363: case TRMG:
364: p--;
365: *p++ = ESC;
366: *p++ = 'N';
367: while (margin--)
368: *p++ = ' ';
369: *p++ = ESC;
370: *p++ = 'F';
371: break;
372: }
373:
374: /*
375: * actual setting: at least terminals do this consistently!
376: */
377: previous = 1; curtab = tabvect;
378: while ((nblanks = *curtab-previous) >= 0 &&
379: previous + nblanks <= maxtab) {
380: for (i = 1; i <= nblanks; i++) *p++ = ' ';
381: previous = *curtab++;
382: *p++ =ESC;
383: *p++ = SET;
384: }
385: *p++ = CR;
386: if (tt->tclr && EQ(tt->tclr, tclr40))
387: *p++ = '\n'; /* TTY40/2 needs LF, not just CR */
388: write(1, setbuf, p - setbuf);
389: }
390:
391: /* cleartabs(pointer to buffer, pointer to clear sequence */
392: char *cleartabs(p, qq)
393: register char *p;
394: char *qq;
395: {
396: register i;
397: register char *q;
398: q = qq;
399: if (q == TCLRLN) { /* if repetitive sequence */
400: *p++ = CR;
401: for(i = 0; i < NTABSCL - 1; i++) {
402: *p++ = TAB;
403: *p++ = ESC;
404: *p++ = CLEAR;
405: }
406: *p++ = CR;
407: }
408: else {
409: while(*p++ = *q++); /* copy table sequence */
410: p--; /* adjust for null */
411: if (qq == tclr40) { /* TTY40 extra delays needed */
412: *p++ = '\0';
413: *p++ = '\0';
414: *p++ = '\0';
415: *p++ = '\0';
416: }
417: }
418: return(p);
419: }
420: /* getnum: scan and convert number, return zero if none found */
421: /* set scan ptr to addr of ending delimeter */
422: getnum(scan1)
423: char **scan1;
424: {
425: register n;
426: register char c, *scan;
427: n = 0;
428: scan = *scan1;
429: while ((c = *scan++) >= '0' && c <= '9') n = n * 10 + c -'0';
430: *scan1 = --scan;
431: return(n);
432: }
433:
434: /* error: terminate processing with message to terminal */
435: error(arg)
436: char *arg;
437: {
438: register char *temp;
439: temp = arg;
440: while (*++temp); /* get length */
441: *temp = '\n';
442: endup();
443: write(2, arg, temp+1-arg);
444: exit(1);
445: }
446:
447: /* endup: make sure tty mode reset & exit */
448: endup()
449: {
450: if (istty) {
451: ttyold.sg_flags = ttysave;
452: ioctl(1, TIOCSETN, &ttyold); /* reset cr-lf to previous */
453: if (devtty && *devtty)
454: chmod(devtty, statbuf.st_mode & 0777);
455: }
456: }
457:
458: /* stdtabs: standard tabs table
459: format: option code letter(s), null, tabs, null */
460: char stdtabs[] = {
461: 'a', 0,1,10,16,36,72,0, /* IBM 370 Assembler */
462: 'a','2',0,1,10,16,40,72,0, /* IBM Assembler alternative*/
463: 'c', 0,1,8,12,16,20,55,0, /* COBOL, normal */
464: 'c','2',0,1,6,10,14,49,0, /* COBOL, crunched*/
465: 'c','3',0,1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67,0,
466: /* crunched COBOL, many tabs */
467: 'f', 0,1,7,11,15,19,23,0, /* FORTRAN */
468: 'p', 0,1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,0, /* PL/I */
469: 's', 0,1,10,55,0, /* SNOBOL */
470: 'u', 0,1,12,20,44,0, /* UNIVAC ASM */
471: 0};
472:
473: /* stdtab: return tab list for any "canned" tab option.
474: entry: option points to null-terminated option string
475: tabvect points to vector to be filled in
476: exit: return(0) if legal, tabvect filled, ending with zero
477: return(-1) if unknown option
478: */
479: stdtab(option,tabvect)
480: char option[];
481: int tabvect[];
482: {
483: register char *sp;
484: tabvect[0] = 0;
485: sp = stdtabs;
486: while (*sp) {
487: if (EQ(option,sp)) {
488: while (*sp++); /* skip to 1st tab value */
489: while (*tabvect++ = *sp++); /* copy, make int */
490: return(0);
491: }
492: while(*sp++); /* skip to 1st tab value */
493: while(*sp++); /* skip over tab list */
494: }
495: return(-1);
496: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.