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