7
|
1 /* vi:set ts=8 sts=4 sw=4: */
|
|
2 /*
|
|
3 * The following software is (C) 1984 Peter da Silva, the Mad Australian, in
|
|
4 * the public domain. It may be re-distributed for any purpose with the
|
|
5 * inclusion of this notice.
|
|
6 */
|
|
7
|
|
8 /* Modified by Bram Moolenaar for use with VIM - Vi Improved. */
|
|
9 /* A few bugs removed by Olaf 'Rhialto' Seibert. */
|
|
10
|
1214
|
11 /* TERMLIB: Terminal independent database. */
|
7
|
12
|
|
13 #include "vim.h"
|
|
14 #include "termlib.pro"
|
|
15
|
2823
|
16 #if !defined(AMIGA) && !defined(VMS) && !defined(MACOS)
|
7
|
17 # include <sgtty.h>
|
|
18 #endif
|
|
19
|
|
20 static int getent __ARGS((char *, char *, FILE *, int));
|
|
21 static int nextent __ARGS((char *, FILE *, int));
|
|
22 static int _match __ARGS((char *, char *));
|
|
23 static char *_addfmt __ARGS((char *, char *, int));
|
|
24 static char *_find __ARGS((char *, char *));
|
|
25
|
|
26 /*
|
|
27 * Global variables for termlib
|
|
28 */
|
|
29
|
|
30 char *tent; /* Pointer to terminal entry, set by tgetent */
|
|
31 char PC = 0; /* Pad character, default NULL */
|
|
32 char *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */
|
|
33 short ospeed; /* Baud rate (1-16, 1=300, 16=19200), as in stty */
|
|
34
|
|
35 /*
|
|
36 * Module: tgetent
|
|
37 *
|
|
38 * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
|
|
39 *
|
|
40 * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for terminal.
|
|
41 *
|
|
42 * Returned values: 1 = success, -1 = can't open file,
|
|
43 * 0 = can't find terminal.
|
|
44 *
|
|
45 * Notes:
|
|
46 * - Should probably supply static buffer.
|
|
47 * - Uses environment variables "TERM" and "TERMCAP". If TERM = term (that is,
|
|
48 * if the argument matches the environment) then it looks at TERMCAP.
|
|
49 * - If TERMCAP begins with a slash, then it assumes this is the file to
|
|
50 * search rather than /etc/termcap.
|
|
51 * - If TERMCAP does not begin with a slash, and it matches TERM, then this is
|
|
52 * used as the entry.
|
|
53 * - This could be simplified considerably for non-UNIX systems.
|
|
54 */
|
|
55
|
|
56 #ifndef TERMCAPFILE
|
|
57 # ifdef AMIGA
|
|
58 # define TERMCAPFILE "s:termcap"
|
|
59 # else
|
|
60 # ifdef VMS
|
|
61 # define TERMCAPFILE "VIMRUNTIME:termcap"
|
|
62 # else
|
|
63 # define TERMCAPFILE "/etc/termcap"
|
|
64 # endif
|
|
65 # endif
|
|
66 #endif
|
|
67
|
|
68 int
|
|
69 tgetent(tbuf, term)
|
|
70 char *tbuf; /* Buffer to hold termcap entry, TBUFSZ bytes max */
|
|
71 char *term; /* Name of terminal */
|
|
72 {
|
|
73 char tcbuf[32]; /* Temp buffer to handle */
|
|
74 char *tcptr = tcbuf; /* extended entries */
|
|
75 char *tcap = TERMCAPFILE; /* Default termcap file */
|
|
76 char *tmp;
|
|
77 FILE *termcap;
|
|
78 int retval = 0;
|
|
79 int len;
|
|
80
|
|
81 if ((tmp = (char *)mch_getenv((char_u *)"TERMCAP")) != NULL)
|
|
82 {
|
|
83 if (*tmp == '/') /* TERMCAP = name of termcap file */
|
|
84 {
|
|
85 tcap = tmp ;
|
|
86 #if defined(AMIGA)
|
|
87 /* Convert /usr/share/lib/termcap to usr:share/lib/termcap */
|
|
88 tcap++;
|
|
89 tmp = strchr(tcap, '/');
|
|
90 if (tmp)
|
|
91 *tmp = ':';
|
|
92 #endif
|
|
93 }
|
|
94 else /* TERMCAP = termcap entry itself */
|
|
95 {
|
|
96 int tlen = strlen(term);
|
|
97
|
|
98 while (*tmp && *tmp != ':') /* Check if TERM matches */
|
|
99 {
|
|
100 char *nexttmp;
|
|
101
|
|
102 while (*tmp == '|')
|
|
103 tmp++;
|
|
104 nexttmp = _find(tmp, ":|"); /* Rhialto */
|
|
105 if (tmp+tlen == nexttmp && _match(tmp, term) == tlen)
|
|
106 {
|
|
107 strcpy(tbuf, tmp);
|
|
108 tent = tbuf;
|
|
109 return 1;
|
|
110 }
|
|
111 else
|
|
112 tmp = nexttmp;
|
|
113 }
|
|
114 }
|
|
115 }
|
|
116 if (!(termcap = mch_fopen(tcap, "r")))
|
|
117 {
|
|
118 strcpy(tbuf, tcap);
|
|
119 return -1;
|
|
120 }
|
|
121
|
|
122 len = 0;
|
|
123 while (getent(tbuf + len, term, termcap, TBUFSZ - len))
|
|
124 {
|
|
125 tcptr = tcbuf; /* Rhialto */
|
|
126 if ((term = tgetstr("tc", &tcptr))) /* extended entry */
|
|
127 {
|
|
128 rewind(termcap);
|
|
129 len = strlen(tbuf);
|
|
130 }
|
|
131 else
|
|
132 {
|
|
133 retval = 1;
|
|
134 tent = tbuf; /* reset it back to the beginning */
|
|
135 break;
|
|
136 }
|
|
137 }
|
|
138 fclose(termcap);
|
|
139 return retval;
|
|
140 }
|
|
141
|
|
142 static int
|
|
143 getent(tbuf, term, termcap, buflen)
|
|
144 char *tbuf, *term;
|
|
145 FILE *termcap;
|
|
146 int buflen;
|
|
147 {
|
|
148 char *tptr;
|
|
149 int tlen = strlen(term);
|
|
150
|
|
151 while (nextent(tbuf, termcap, buflen)) /* For each possible entry */
|
|
152 {
|
|
153 tptr = tbuf;
|
|
154 while (*tptr && *tptr != ':') /* : terminates name field */
|
|
155 {
|
|
156 char *nexttptr;
|
|
157
|
1214
|
158 while (*tptr == '|') /* | separates names */
|
7
|
159 tptr++;
|
|
160 nexttptr = _find(tptr, ":|"); /* Rhialto */
|
|
161 if (tptr + tlen == nexttptr &&
|
|
162 _match(tptr, term) == tlen) /* FOUND! */
|
|
163 {
|
|
164 tent = tbuf;
|
|
165 return 1;
|
|
166 }
|
|
167 else /* Look for next name */
|
|
168 tptr = nexttptr;
|
|
169 }
|
|
170 }
|
|
171 return 0;
|
|
172 }
|
|
173
|
|
174 static int
|
|
175 nextent(tbuf, termcap, buflen) /* Read 1 entry from TERMCAP file */
|
|
176 char *tbuf;
|
|
177 FILE *termcap;
|
|
178 int buflen;
|
|
179 {
|
|
180 char *lbuf = tbuf; /* lbuf=line buffer */
|
|
181 /* read lines straight into buffer */
|
|
182
|
|
183 while (lbuf < tbuf+buflen && /* There's room and */
|
|
184 fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */
|
|
185 {
|
|
186 int llen = strlen(lbuf);
|
|
187
|
|
188 if (*lbuf == '#') /* eat comments */
|
|
189 continue;
|
|
190 if (lbuf[-1] == ':' && /* and whitespace */
|
|
191 lbuf[0] == '\t' &&
|
|
192 lbuf[1] == ':')
|
|
193 {
|
1621
|
194 STRMOVE(lbuf, lbuf + 2);
|
7
|
195 llen -= 2;
|
|
196 }
|
|
197 if (lbuf[llen-2] == '\\') /* and continuations */
|
|
198 lbuf += llen-2;
|
|
199 else
|
|
200 {
|
|
201 lbuf[llen-1]=0; /* no continuation, return */
|
|
202 return 1;
|
|
203 }
|
|
204 }
|
|
205
|
|
206 return 0; /* ran into end of file */
|
|
207 }
|
|
208
|
|
209 /*
|
|
210 * Module: tgetflag
|
|
211 *
|
|
212 * Purpose: returns flag true or false as to the existence of a given entry.
|
|
213 * used with 'bs', 'am', etc...
|
|
214 *
|
|
215 * Calling conventions: id is the 2 character capability id.
|
|
216 *
|
|
217 * Returned values: 1 for success, 0 for failure.
|
|
218 */
|
|
219
|
|
220 int
|
|
221 tgetflag(id)
|
|
222 char *id;
|
|
223 {
|
|
224 char buf[256], *ptr = buf;
|
|
225
|
|
226 return tgetstr(id, &ptr) ? 1 : 0;
|
|
227 }
|
|
228
|
|
229 /*
|
|
230 * Module: tgetnum
|
|
231 *
|
|
232 * Purpose: get numeric value such as 'li' or 'co' from termcap.
|
|
233 *
|
|
234 * Calling conventions: id = 2 character id.
|
|
235 *
|
|
236 * Returned values: -1 for failure, else numerical value.
|
|
237 */
|
|
238
|
|
239 int
|
|
240 tgetnum(id)
|
|
241 char *id;
|
|
242 {
|
|
243 char *ptr, buf[256];
|
|
244 ptr = buf;
|
|
245
|
|
246 if (tgetstr(id, &ptr))
|
|
247 return atoi(buf);
|
|
248 else
|
|
249 return 0;
|
|
250 }
|
|
251
|
|
252 /*
|
|
253 * Module: tgetstr
|
|
254 *
|
|
255 * Purpose: get terminal capability string from database.
|
|
256 *
|
|
257 * Calling conventions: id is the two character capability id.
|
|
258 * (*buf) points into a hold buffer for the
|
|
259 * id. the capability is copied into the buffer
|
|
260 * and (*buf) is advanced to point to the next
|
|
261 * free byte in the buffer.
|
|
262 *
|
|
263 * Returned values: 0 = no such entry, otherwise returns original
|
|
264 * (*buf) (now a pointer to the string).
|
|
265 *
|
|
266 * Notes
|
|
267 * It also decodes certain escape sequences in the buffer.
|
|
268 * they should be obvious from the code:
|
|
269 * \E = escape.
|
|
270 * \n, \r, \t, \f, \b match the 'c' escapes.
|
|
271 * ^x matches control-x (^@...^_).
|
|
272 * \nnn matches nnn octal.
|
|
273 * \x, where x is anything else, matches x. I differ
|
|
274 * from the standard library here, in that I allow ^: to match
|
|
275 * :.
|
|
276 *
|
|
277 */
|
|
278
|
|
279 char *
|
|
280 tgetstr(id, buf)
|
|
281 char *id, **buf;
|
|
282 {
|
|
283 int len = strlen(id);
|
|
284 char *tmp=tent;
|
|
285 char *hold;
|
|
286 int i;
|
|
287
|
|
288 do {
|
|
289 tmp = _find(tmp, ":"); /* For each field */
|
|
290 while (*tmp == ':') /* skip empty fields */
|
|
291 tmp++;
|
|
292 if (!*tmp)
|
|
293 break;
|
|
294
|
|
295 if (_match(id, tmp) == len) {
|
|
296 tmp += len; /* find '=' '@' or '#' */
|
|
297 if (*tmp == '@') /* :xx@: entry for tc */
|
|
298 return 0; /* deleted entry */
|
|
299 hold= *buf;
|
|
300 while (*++tmp && *tmp != ':') { /* not at end of field */
|
|
301 switch(*tmp) {
|
|
302 case '\\': /* Expand escapes here */
|
|
303 switch(*++tmp) {
|
|
304 case 0: /* ignore backslashes */
|
|
305 tmp--; /* at end of entry */
|
|
306 break; /* shouldn't happen */
|
|
307 case 'e':
|
|
308 case 'E': /* ESC */
|
|
309 *(*buf)++ = ESC;
|
|
310 break;
|
|
311 case 'n': /* \n */
|
|
312 *(*buf)++ = '\n';
|
|
313 break;
|
|
314 case 'r': /* \r */
|
|
315 *(*buf)++ = '\r';
|
|
316 break;
|
|
317 case 't': /* \t */
|
|
318 *(*buf)++ = '\t';
|
|
319 break;
|
|
320 case 'b': /* \b */
|
|
321 *(*buf)++ = '\b';
|
|
322 break;
|
|
323 case 'f': /* \f */
|
|
324 *(*buf)++ = '\f';
|
|
325 break;
|
|
326 case '0': /* \nnn */
|
|
327 case '1':
|
|
328 case '2':
|
|
329 case '3':
|
|
330 case '4':
|
|
331 case '5':
|
|
332 case '6':
|
|
333 case '7':
|
|
334 case '8':
|
|
335 case '9':
|
|
336 **buf = 0;
|
|
337 /* get up to three digits */
|
|
338 for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i)
|
|
339 **buf = **buf * 8 + *tmp++ - '0';
|
|
340 (*buf)++;
|
|
341 tmp--;
|
|
342 break;
|
|
343 default: /* \x, for all other x */
|
|
344 *(*buf)++= *tmp;
|
|
345 }
|
|
346 break;
|
|
347 case '^': /* control characters */
|
|
348 ++tmp;
|
|
349 *(*buf)++ = Ctrl_chr(*tmp);
|
|
350 break;
|
|
351 default:
|
|
352 *(*buf)++ = *tmp;
|
|
353 }
|
|
354 }
|
|
355 *(*buf)++ = 0;
|
|
356 return hold;
|
|
357 }
|
|
358 } while (*tmp);
|
|
359
|
|
360 return 0;
|
|
361 }
|
|
362
|
|
363 /*
|
|
364 * Module: tgoto
|
|
365 *
|
|
366 * Purpose: decode cm cursor motion string.
|
|
367 *
|
|
368 * Calling conventions: cm is cursor motion string. line, col, are the
|
|
369 * desired destination.
|
|
370 *
|
|
371 * Returned values: a string pointing to the decoded string, or "OOPS" if it
|
|
372 * cannot be decoded.
|
|
373 *
|
|
374 * Notes
|
|
375 * The accepted escapes are:
|
|
376 * %d as in printf, 0 origin.
|
|
377 * %2, %3 like %02d, %03d in printf.
|
|
378 * %. like %c
|
|
379 * %+x adds <x> to value, then %.
|
|
380 * %>xy if value>x, adds y. No output.
|
|
381 * %i increments line& col, no output.
|
|
382 * %r reverses order of line&col. No output.
|
|
383 * %% prints as a single %.
|
|
384 * %n exclusive or row & col with 0140.
|
|
385 * %B BCD, no output.
|
|
386 * %D reverse coding (x-2*(x%16)), no output.
|
|
387 */
|
|
388
|
|
389 char *
|
|
390 tgoto(cm, col, line)
|
|
391 char *cm; /* cm string, from termcap */
|
|
392 int col, /* column, x position */
|
|
393 line; /* line, y position */
|
|
394 {
|
|
395 char gx, gy, /* x, y */
|
|
396 *ptr, /* pointer in 'cm' */
|
|
397 reverse = 0, /* reverse flag */
|
|
398 *bufp, /* pointer in returned string */
|
|
399 addup = 0, /* add upline */
|
|
400 addbak = 0, /* add backup */
|
|
401 c;
|
|
402 static char buffer[32];
|
|
403
|
|
404 if (!cm)
|
|
405 return "OOPS"; /* Kludge, but standard */
|
|
406
|
|
407 bufp = buffer;
|
|
408 ptr = cm;
|
|
409
|
|
410 while (*ptr) {
|
|
411 if ((c = *ptr++) != '%') { /* normal char */
|
|
412 *bufp++ = c;
|
|
413 } else { /* % escape */
|
|
414 switch(c = *ptr++) {
|
|
415 case 'd': /* decimal */
|
|
416 bufp = _addfmt(bufp, "%d", line);
|
|
417 line = col;
|
|
418 break;
|
|
419 case '2': /* 2 digit decimal */
|
|
420 bufp = _addfmt(bufp, "%02d", line);
|
|
421 line = col;
|
|
422 break;
|
|
423 case '3': /* 3 digit decimal */
|
|
424 bufp = _addfmt(bufp, "%03d", line);
|
|
425 line = col;
|
|
426 break;
|
|
427 case '>': /* %>xy: if >x, add y */
|
|
428 gx = *ptr++;
|
|
429 gy = *ptr++;
|
|
430 if (col>gx) col += gy;
|
|
431 if (line>gx) line += gy;
|
|
432 break;
|
|
433 case '+': /* %+c: add c */
|
|
434 line += *ptr++;
|
|
435 case '.': /* print x/y */
|
|
436 if (line == '\t' || /* these are */
|
|
437 line == '\n' || /* chars that */
|
|
438 line == '\004' || /* UNIX hates */
|
|
439 line == '\0') {
|
|
440 line++; /* so go to next pos */
|
|
441 if (reverse == (line == col))
|
|
442 addup=1; /* and mark UP */
|
|
443 else
|
|
444 addbak=1; /* or BC */
|
|
445 }
|
|
446 *bufp++=line;
|
|
447 line = col;
|
|
448 break;
|
|
449 case 'r': /* r: reverse */
|
|
450 gx = line;
|
|
451 line = col;
|
|
452 col = gx;
|
|
453 reverse = 1;
|
|
454 break;
|
|
455 case 'i': /* increment (1-origin screen) */
|
|
456 col++;
|
|
457 line++;
|
|
458 break;
|
|
459 case '%': /* %%=% literally */
|
|
460 *bufp++='%';
|
|
461 break;
|
|
462 case 'n': /* magic DM2500 code */
|
|
463 line ^= 0140;
|
|
464 col ^= 0140;
|
|
465 break;
|
|
466 case 'B': /* bcd encoding */
|
|
467 line = line/10<<4+line%10;
|
|
468 col = col/10<<4+col%10;
|
|
469 break;
|
|
470 case 'D': /* magic Delta Data code */
|
|
471 line = line-2*(line&15);
|
|
472 col = col-2*(col&15);
|
|
473 break;
|
|
474 default: /* Unknown escape */
|
|
475 return "OOPS";
|
|
476 }
|
|
477 }
|
|
478 }
|
|
479
|
|
480 if (addup) /* add upline */
|
|
481 if (UP) {
|
|
482 ptr=UP;
|
|
483 while (VIM_ISDIGIT(*ptr) || *ptr == '.')
|
|
484 ptr++;
|
|
485 if (*ptr == '*')
|
|
486 ptr++;
|
|
487 while (*ptr)
|
|
488 *bufp++ = *ptr++;
|
|
489 }
|
|
490
|
|
491 if (addbak) /* add backspace */
|
|
492 if (BC) {
|
|
493 ptr=BC;
|
|
494 while (VIM_ISDIGIT(*ptr) || *ptr == '.')
|
|
495 ptr++;
|
|
496 if (*ptr == '*')
|
|
497 ptr++;
|
|
498 while (*ptr)
|
|
499 *bufp++ = *ptr++;
|
|
500 }
|
|
501 else
|
|
502 *bufp++='\b';
|
|
503
|
|
504 *bufp = 0;
|
|
505
|
|
506 return(buffer);
|
|
507 }
|
|
508
|
|
509 /*
|
|
510 * Module: tputs
|
|
511 *
|
|
512 * Purpose: decode padding information
|
|
513 *
|
|
514 * Calling conventions: cp = string to be padded, affcnt = # of items affected
|
|
515 * (lines, characters, whatever), outc = routine to output 1 character.
|
|
516 *
|
|
517 * Returned values: none
|
|
518 *
|
|
519 * Notes
|
|
520 * cp has padding information ahead of it, in the form
|
|
521 * nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
|
|
522 * and may be a decimal (nnn.mmm). If the asterisk is given, then
|
|
523 * the delay is multiplied by afcnt. The delay is produced by outputting
|
|
524 * a number of nulls (or other padding char) after printing the
|
|
525 * TEXT.
|
|
526 *
|
|
527 */
|
|
528
|
|
529 long _bauds[16]={
|
|
530 0, 50, 75, 110,
|
|
531 134, 150, 200, 300,
|
|
532 600, 1200, 1800, 2400,
|
|
533 4800, 9600, 19200, 19200 };
|
|
534
|
|
535 int
|
|
536 tputs(cp, affcnt, outc)
|
|
537 char *cp; /* string to print */
|
|
538 int affcnt; /* Number of lines affected */
|
|
539 void (*outc) __ARGS((unsigned int));/* routine to output 1 character */
|
|
540 {
|
|
541 long frac, /* 10^(#digits after decimal point) */
|
|
542 counter, /* digits */
|
|
543 atol __ARGS((const char *));
|
|
544
|
|
545 if (VIM_ISDIGIT(*cp)) {
|
|
546 counter = 0;
|
|
547 frac = 1000;
|
|
548 while (VIM_ISDIGIT(*cp))
|
|
549 counter = counter * 10L + (long)(*cp++ - '0');
|
|
550 if (*cp == '.')
|
|
551 while (VIM_ISDIGIT(*++cp)) {
|
|
552 counter = counter * 10L + (long)(*cp++ - '0');
|
|
553 frac = frac * 10;
|
|
554 }
|
|
555 if (*cp!='*') { /* multiply by affected lines */
|
|
556 if (affcnt>1) affcnt = 1;
|
|
557 }
|
|
558 else
|
|
559 cp++;
|
|
560
|
|
561 /* Calculate number of characters for padding counter/frac ms delay */
|
|
562 if (ospeed)
|
|
563 counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
|
|
564
|
|
565 while (*cp) /* output string */
|
|
566 (*outc)(*cp++);
|
|
567 if (ospeed)
|
|
568 while (counter--) /* followed by pad characters */
|
|
569 (*outc)(PC);
|
|
570 }
|
|
571 else
|
|
572 while (*cp)
|
|
573 (*outc)(*cp++);
|
|
574 return 0;
|
|
575 }
|
|
576
|
|
577 /*
|
|
578 * Module: tutil.c
|
|
579 *
|
|
580 * Purpose: Utility routines for TERMLIB functions.
|
|
581 *
|
|
582 */
|
|
583 static int
|
|
584 _match(s1, s2) /* returns length of text common to s1 and s2 */
|
|
585 char *s1, *s2;
|
|
586 {
|
|
587 int i = 0;
|
|
588
|
|
589 while (s1[i] && s1[i] == s2[i])
|
|
590 i++;
|
|
591
|
|
592 return i;
|
|
593 }
|
|
594
|
|
595 /*
|
|
596 * finds next c in s that's a member of set, returns pointer
|
|
597 */
|
|
598 static char *
|
|
599 _find(s, set)
|
|
600 char *s, *set;
|
|
601 {
|
|
602 for(; *s; s++)
|
|
603 {
|
|
604 char *ptr = set;
|
|
605
|
|
606 while (*ptr && *s != *ptr)
|
|
607 ptr++;
|
|
608
|
|
609 if (*ptr)
|
|
610 return s;
|
|
611 }
|
|
612
|
|
613 return s;
|
|
614 }
|
|
615
|
|
616 /*
|
|
617 * add val to buf according to format fmt
|
|
618 */
|
|
619 static char *
|
|
620 _addfmt(buf, fmt, val)
|
|
621 char *buf, *fmt;
|
|
622 int val;
|
|
623 {
|
|
624 sprintf(buf, fmt, val);
|
|
625 while (*buf)
|
|
626 buf++;
|
|
627 return buf;
|
|
628 }
|