1 /*
2 * int CapInit (buf)
3 * char buf [2048];
4 *
5 * - get termcap entry into buffer buf.
6 * Return 1 if ok, else 0.
7 *
8 * CapGet (tab)
9 * struct CapTab *tab;
10 *
11 * - read terminal properties, described in table tab.
12 * Buffer used as argument to CapInit must be available.
13 *
14 * char *CapGoto (movestr, col, line)
15 * char *movestr;
16 *
17 * - expand string movestr by column and line numbers.
18 */
19 /* #define DEBUG */
20
21 #include <stdlib.h>
22 #if HAVE_UNISTD_H
23 # include <unistd.h>
24 #endif
25 #if HAVE_FCNTL_H
26 # include <fcntl.h>
27 #endif
28 #if HAVE_TERMCAP_H
29 # include <termcap.h>
30 #else
31 int tgetent (char*, const char*);
32 int tgetflag (const char*);
33 int tgetnum (const char*);
34 char *tgetstr (const char*, char**);
35 #endif
36 #include "deco.h"
37 #include "scr.h"
38 #include "env.h"
39
40 #define BUFSIZ 2048 /* max length of termcap entry */
41 #define MAXRETURNSIZE 64 /* max length of CapGoto string */
42 #define ERR(c) (c)
43 #define TPARMERR(c) { strcpy (outp, (c)); return; }
44
45 static char *tname; /* terminal name */
46 static char *tbuf; /* terminal entry buffer */
47
48 static void sysvgoto (char *outp, char *cp, int p1, int p2);
49
50 /*
51 * Get an entry for terminal name in buffer bp,
52 * from the termcap file. Parse is very rudimentary;
53 * we just notice escaped newlines.
54 */
CapInit(char * bp)55 int CapInit (char *bp)
56 {
57 if (tbuf)
58 return (1);
59 if (! (tname = EnvGet ("TERM")))
60 tname = "unknown";
61 if (tgetent (bp, tname) <= 0)
62 return (0);
63 tbuf = bp;
64 return (1);
65 }
66
CapGet(struct CapTab * t)67 void CapGet (struct CapTab *t)
68 {
69 char *area, *begarea, *bp, *val;
70 struct CapTab *p;
71
72 if (! tbuf)
73 return;
74 area = begarea = malloc (BUFSIZ);
75 for (p=t; p->tname[0]; ++p)
76 switch (p->ttype) {
77 case CAPNUM:
78 *(p->ti) = tgetnum (p->tname);
79 if (*(p->ti) < 0)
80 *(p->ti) = 0;
81 break;
82 case CAPFLG:
83 *(p->tc) = tgetflag (p->tname);
84 break;
85 case CAPSTR:
86 val = tgetstr (p->tname, &area);
87 if (val) {
88 *(p->ts) = val;
89 }
90 break;
91 }
92 bp = malloc (area - begarea);
93 if (! bp) {
94 free (begarea);
95 return;
96 }
97 memcpy (bp, begarea, area - begarea);
98 for (p=t; p->tname[0]; ++p)
99 if (p->ttype == CAPSTR && *(p->ts) >= begarea &&
100 *(p->ts) < area)
101 *(p->ts) += bp - begarea;
102 free (begarea);
103 #ifdef DEBUG
104 for (p=t; p->tname[0]; ++p) {
105 printf ("%c%c", p->tname[0], p->tname[1]);
106 switch (p->ttype) {
107 case CAPNUM:
108 printf ("#%d\n", *(p->ti));
109 break;
110 case CAPFLG:
111 printf (" %s\n", *(p->tc) ? "on" : "off");
112 break;
113 case CAPSTR:
114 if (*(p->ts))
115 printf ("='%s'\n", *(p->ts));
116 else
117 printf ("=NULL\n");
118 break;
119 }
120 }
121 #endif
122 }
123
124 /*
125 * Routine to perform cursor addressing.
126 * CM is a string containing printf type escapes to allow
127 * cursor addressing. We start out ready to print the destination
128 * line, and switch each time we print row or column.
129 * The following escapes are defined for substituting row/column:
130 *
131 * %d as in printf
132 * %2 like %2d
133 * %3 like %3d
134 * %. gives %c hacking special case characters
135 * %+x like %c but adding x first
136 *
137 * The codes below affect the state but don't use up a value.
138 *
139 * %>xy if value > x add y
140 * %r reverses row/column
141 * %i increments row/column (for one origin indexing)
142 * %% gives %
143 * %B BCD (2 decimal digits encoded in one byte)
144 * %D Delta Data (backwards bcd)
145 *
146 * all other characters are ``self-inserting''.
147 */
CapGoto(char * CM,int destcol,int destline)148 char *CapGoto (char *CM, int destcol, int destline)
149 {
150 register char *cp, *dp;
151 register c, which, oncol;
152 static char result [MAXRETURNSIZE];
153
154 #ifdef DEBUG
155 printf ("CM='%s'\n", CM);
156 #endif
157 cp = CM;
158 if (! cp)
159 return "";
160 dp = result;
161 oncol = 0;
162 which = destline;
163 while ((c = *cp++)) {
164 if (c != '%') {
165 *dp++ = c;
166 continue;
167 }
168 switch (c = *cp++) {
169 case 'n':
170 destcol ^= 0140;
171 destline ^= 0140;
172 goto setwhich;
173 case 'd':
174 if (which < 10)
175 goto one;
176 if (which < 100)
177 goto two;
178 /* fall into... */
179 case '3':
180 *dp++ = (which / 100) | '0';
181 which %= 100;
182 /* fall into... */
183 case '2':
184 two: *dp++ = which / 10 | '0';
185 one: *dp++ = which % 10 | '0';
186 swap: oncol = 1 - oncol;
187 setwhich: which = oncol ? destcol : destline;
188 continue;
189 case '>':
190 if (which > *cp++)
191 which += *cp++;
192 else
193 cp++;
194 continue;
195 case '+':
196 which += *cp++;
197 /* fall into... */
198 case '.':
199 *dp++ = which;
200 goto swap;
201 case 'r':
202 oncol = 1;
203 goto setwhich;
204 case 'i':
205 destcol++;
206 destline++;
207 which++;
208 continue;
209 case '%':
210 *dp++ = c;
211 continue;
212 case '/':
213 c = *cp;
214 *dp++ = which / c | '0';
215 which %= *cp++;
216 continue;
217 case 'B':
218 which = (which/10 << 4) + which%10;
219 continue;
220 case 'D':
221 which = which - 2 * (which%16);
222 continue;
223 case 'p':
224 case 'P':
225 case '\'':
226 case '{':
227 case '?':
228 /* string is in tparm format... */
229 sysvgoto (dp, cp-2, destline, destcol);
230 return (result);
231 default:
232 return "bad capgoto";
233 }
234 }
235 *dp = 0;
236 return (result);
237 }
238
branchto(register char * cp,int to)239 static char *branchto (register char *cp, int to)
240 {
241 register c, level;
242
243 level = 0;
244 while ((c = *cp++)) {
245 if (c == '%') {
246 if ((c = *cp++) == to || c == ';') {
247 if (level == 0) {
248 return cp;
249 }
250 }
251 if (c == '?')
252 level++;
253 if (c == ';')
254 level--;
255 }
256 }
257 return ERR ("no matching ENDIF");
258 }
259
260 /*
261 * Routine to perform parameter substitution.
262 * instring is a string containing printf type escapes.
263 * The whole thing uses a stack, much like an HP 35.
264 * The following escapes are defined for substituting row/column:
265 *
266 * %d print pop() as in printf
267 * %[0]2d print pop() like %2d
268 * %[0]3d print pop() like %3d
269 * %c print pop() like %c
270 *
271 * %p[1-0] push ith parm
272 * %P[a-z] set variable
273 * %g[a-z] get variable
274 * %'c' char constant c
275 * %{nn} integer constant nn
276 *
277 * %+ %- %* %/ %m arithmetic (%m is mod): push(pop() op pop())
278 * %& %| %^ bit operations: push(pop() op pop())
279 * %= %> %< logical operations: push(pop() op pop())
280 * %! %~ unary operations push(op pop())
281 * %b unary BCD conversion
282 * %d unary Delta Data conversion
283 *
284 * %? expr %t thenpart %e elsepart %;
285 * if-then-else, %e elsepart is optional.
286 * else-if's are possible ala Algol 68:
287 * %? c1 %t %e c2 %t %e c3 %t %e c4 %t %e %;
288 *
289 * all other characters are ``self-inserting''. %% gets % output.
290 */
sysvgoto(register char * outp,register char * cp,int p1,int p2)291 static void sysvgoto (register char *outp, register char *cp, int p1, int p2)
292 {
293 register c, op;
294 int vars [26], stack [10], top, sign;
295
296 #define PUSH(i) (stack [++top] = (i))
297 #define POP() (stack [top--])
298
299 if (! cp)
300 TPARMERR ("null arg");
301 top = 0;
302 while ((c = *cp++)) {
303 if (c != '%') {
304 *outp++ = c;
305 continue;
306 }
307 op = stack [top];
308 if (*cp == '0')
309 ++cp;
310 switch (c = *cp++) {
311 case 'd': /* PRINTING CASES */
312 if (op < 10)
313 goto one;
314 if (op < 100)
315 goto two;
316 /* fall into... */
317 case '3':
318 three:
319 if (c == '3' && *cp++ != 'd')
320 TPARMERR ("bad char after %3");
321 *outp++ = (op / 100) | '0';
322 op %= 100;
323 /* fall into... */
324 case '2':
325 if (op >= 100)
326 goto three;
327 if (c == '2' && *cp++ != 'd')
328 TPARMERR ("bad char after %2");
329 two:
330 *outp++ = op / 10 | '0';
331 one:
332 *outp++ = op % 10 | '0';
333 (void) POP ();
334 continue;
335 case 'c':
336 *outp++ = op;
337 (void) POP ();
338 break;
339 case '%':
340 *outp++ = c;
341 break;
342 /*
343 * %i: shorthand for increment first two parms.
344 * Useful for terminals that start numbering from
345 * one instead of zero (like ANSI terminals).
346 */
347 case 'i':
348 p1++; p2++;
349 break;
350 case 'p': /* %pi: push the ith parameter */
351 switch (c = *cp++) {
352 case '1': PUSH (p1); break;
353 case '2': PUSH (p2); break;
354 default: TPARMERR ("bad parm number");
355 }
356 break;
357 case 'P': /* %Pi: pop from stack into variable i (a-z) */
358 vars [*cp++ - 'a'] = POP ();
359 break;
360 case 'g': /* %gi: push variable i (a-z) */
361 PUSH (vars [*cp++ - 'a']);
362 break;
363 case '\'': /* %'c' : character constant */
364 PUSH (*cp++);
365 if (*cp++ != '\'')
366 TPARMERR ("missing closing quote");
367 break;
368 case '{': /* %{nn} : integer constant. */
369 op = 0; sign = 1;
370 if (*cp == '-') {
371 sign = -1;
372 cp++;
373 } else if (*cp == '+')
374 cp++;
375 while ((c = *cp++) >= '0' && c <= '9') {
376 op = 10*op + c - '0';
377 }
378 if (c != '}')
379 TPARMERR ("missing closing brace");
380 PUSH (sign * op);
381 break;
382 /* binary operators */
383 case '+': c = POP (); op = POP (); PUSH (op + c); break;
384 case '-': c = POP (); op = POP (); PUSH (op - c); break;
385 case '*': c = POP (); op = POP (); PUSH (op * c); break;
386 case '/': c = POP (); op = POP (); PUSH (op / c); break;
387 case 'm': c = POP (); op = POP (); PUSH (op % c); break;
388 case '&': c = POP (); op = POP (); PUSH (op & c); break;
389 case '|': c = POP (); op = POP (); PUSH (op | c); break;
390 case '^': c = POP (); op = POP (); PUSH (op ^ c); break;
391 case '=': c = POP (); op = POP (); PUSH (op = c); break;
392 case '>': c = POP (); op = POP (); PUSH (op > c); break;
393 case '<': c = POP (); op = POP (); PUSH (op < c); break;
394 /* Unary operators. */
395 case '!': stack [top] = ! stack [top]; break;
396 case '~': stack [top] = ~ stack [top]; break;
397 /* Sorry, no unary minus, because minus is binary. */
398 /*
399 * If-then-else. Implemented by a low level hack of
400 * skipping forward until the match is found, counting
401 * nested if-then-elses.
402 */
403 case '?': /* IF - just a marker */
404 break;
405 case 't': /* THEN - branch if false */
406 if (! POP ())
407 cp = branchto (cp, 'e');
408 break;
409 case 'e': /* ELSE - branch to ENDIF */
410 cp = branchto (cp, ';');
411 break;
412 case ';': /* ENDIF - just a marker */
413 break;
414 default:
415 TPARMERR ("bad % sequence");
416 }
417 }
418 *outp = 0;
419 }
420