blob: 9488a320e438075a5dc5eb9055d2dafc7aa84cb1 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* 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
Bram Moolenaarccc18222007-05-10 18:25:20 +000011/* TERMLIB: Terminal independent database. */
Bram Moolenaar071d4272004-06-13 20:20:40 +000012
13#include "vim.h"
14#include "termlib.pro"
15
Bram Moolenaare60acc12011-05-10 16:41:25 +020016#if !defined(AMIGA) && !defined(VMS) && !defined(MACOS)
Bram Moolenaar071d4272004-06-13 20:20:40 +000017# include <sgtty.h>
18#endif
19
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +010020static int getent(char *, char *, FILE *, int);
21static int nextent(char *, FILE *, int);
22static int _match(char *, char *);
23static char *_addfmt(char *, char *, int);
24static char *_find(char *, char *);
Bram Moolenaar071d4272004-06-13 20:20:40 +000025
26/*
27 * Global variables for termlib
28 */
29
30char *tent; /* Pointer to terminal entry, set by tgetent */
31char PC = 0; /* Pad character, default NULL */
32char *UP = 0, *BC = 0; /* Pointers to UP and BC strings from database */
33short 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
Bram Moolenaar764b23c2016-01-30 21:10:09 +010069tgetent(
70 char *tbuf, /* Buffer to hold termcap entry, TBUFSZ bytes max */
71 char *term) /* Name of terminal */
Bram Moolenaar071d4272004-06-13 20:20:40 +000072{
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
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100143getent(char *tbuf, *term, FILE *termcap, int buflen)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000144{
145 char *tptr;
146 int tlen = strlen(term);
147
148 while (nextent(tbuf, termcap, buflen)) /* For each possible entry */
149 {
150 tptr = tbuf;
151 while (*tptr && *tptr != ':') /* : terminates name field */
152 {
153 char *nexttptr;
154
Bram Moolenaarccc18222007-05-10 18:25:20 +0000155 while (*tptr == '|') /* | separates names */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000156 tptr++;
157 nexttptr = _find(tptr, ":|"); /* Rhialto */
158 if (tptr + tlen == nexttptr &&
159 _match(tptr, term) == tlen) /* FOUND! */
160 {
161 tent = tbuf;
162 return 1;
163 }
164 else /* Look for next name */
165 tptr = nexttptr;
166 }
167 }
168 return 0;
169}
170
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100171/*
172 * Read 1 entry from TERMCAP file.
173 */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000174 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100175nextent(char *tbuf, FILE *termcap, int buflen)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000176{
177 char *lbuf = tbuf; /* lbuf=line buffer */
178 /* read lines straight into buffer */
179
180 while (lbuf < tbuf+buflen && /* There's room and */
181 fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap)) /* another line */
182 {
183 int llen = strlen(lbuf);
184
185 if (*lbuf == '#') /* eat comments */
186 continue;
187 if (lbuf[-1] == ':' && /* and whitespace */
188 lbuf[0] == '\t' &&
189 lbuf[1] == ':')
190 {
Bram Moolenaar446cb832008-06-24 21:56:24 +0000191 STRMOVE(lbuf, lbuf + 2);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000192 llen -= 2;
193 }
194 if (lbuf[llen-2] == '\\') /* and continuations */
195 lbuf += llen-2;
196 else
197 {
198 lbuf[llen-1]=0; /* no continuation, return */
199 return 1;
200 }
201 }
202
203 return 0; /* ran into end of file */
204}
205
206/*
207 * Module: tgetflag
208 *
209 * Purpose: returns flag true or false as to the existence of a given entry.
210 * used with 'bs', 'am', etc...
211 *
212 * Calling conventions: id is the 2 character capability id.
213 *
214 * Returned values: 1 for success, 0 for failure.
215 */
216
217 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100218tgetflag(char *id)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000219{
220 char buf[256], *ptr = buf;
221
222 return tgetstr(id, &ptr) ? 1 : 0;
223}
224
225/*
226 * Module: tgetnum
227 *
228 * Purpose: get numeric value such as 'li' or 'co' from termcap.
229 *
230 * Calling conventions: id = 2 character id.
231 *
232 * Returned values: -1 for failure, else numerical value.
233 */
234
235 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100236tgetnum(char *id)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237{
238 char *ptr, buf[256];
239 ptr = buf;
240
241 if (tgetstr(id, &ptr))
242 return atoi(buf);
243 else
244 return 0;
245}
246
247/*
248 * Module: tgetstr
249 *
250 * Purpose: get terminal capability string from database.
251 *
252 * Calling conventions: id is the two character capability id.
253 * (*buf) points into a hold buffer for the
254 * id. the capability is copied into the buffer
255 * and (*buf) is advanced to point to the next
256 * free byte in the buffer.
257 *
258 * Returned values: 0 = no such entry, otherwise returns original
259 * (*buf) (now a pointer to the string).
260 *
261 * Notes
262 * It also decodes certain escape sequences in the buffer.
263 * they should be obvious from the code:
264 * \E = escape.
265 * \n, \r, \t, \f, \b match the 'c' escapes.
266 * ^x matches control-x (^@...^_).
267 * \nnn matches nnn octal.
268 * \x, where x is anything else, matches x. I differ
269 * from the standard library here, in that I allow ^: to match
270 * :.
271 *
272 */
273
274 char *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100275tgetstr(char *id, char **buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000276{
277 int len = strlen(id);
278 char *tmp=tent;
279 char *hold;
280 int i;
281
282 do {
283 tmp = _find(tmp, ":"); /* For each field */
284 while (*tmp == ':') /* skip empty fields */
285 tmp++;
286 if (!*tmp)
287 break;
288
289 if (_match(id, tmp) == len) {
290 tmp += len; /* find '=' '@' or '#' */
291 if (*tmp == '@') /* :xx@: entry for tc */
292 return 0; /* deleted entry */
293 hold= *buf;
294 while (*++tmp && *tmp != ':') { /* not at end of field */
295 switch(*tmp) {
296 case '\\': /* Expand escapes here */
297 switch(*++tmp) {
298 case 0: /* ignore backslashes */
299 tmp--; /* at end of entry */
300 break; /* shouldn't happen */
301 case 'e':
302 case 'E': /* ESC */
303 *(*buf)++ = ESC;
304 break;
305 case 'n': /* \n */
306 *(*buf)++ = '\n';
307 break;
308 case 'r': /* \r */
309 *(*buf)++ = '\r';
310 break;
311 case 't': /* \t */
312 *(*buf)++ = '\t';
313 break;
314 case 'b': /* \b */
315 *(*buf)++ = '\b';
316 break;
317 case 'f': /* \f */
318 *(*buf)++ = '\f';
319 break;
320 case '0': /* \nnn */
321 case '1':
322 case '2':
323 case '3':
324 case '4':
325 case '5':
326 case '6':
327 case '7':
328 case '8':
329 case '9':
330 **buf = 0;
331 /* get up to three digits */
332 for (i = 0; i < 3 && VIM_ISDIGIT(*tmp); ++i)
333 **buf = **buf * 8 + *tmp++ - '0';
334 (*buf)++;
335 tmp--;
336 break;
337 default: /* \x, for all other x */
338 *(*buf)++= *tmp;
339 }
340 break;
341 case '^': /* control characters */
342 ++tmp;
343 *(*buf)++ = Ctrl_chr(*tmp);
344 break;
345 default:
346 *(*buf)++ = *tmp;
347 }
348 }
349 *(*buf)++ = 0;
350 return hold;
351 }
352 } while (*tmp);
353
354 return 0;
355}
356
357/*
358 * Module: tgoto
359 *
360 * Purpose: decode cm cursor motion string.
361 *
362 * Calling conventions: cm is cursor motion string. line, col, are the
363 * desired destination.
364 *
365 * Returned values: a string pointing to the decoded string, or "OOPS" if it
366 * cannot be decoded.
367 *
368 * Notes
369 * The accepted escapes are:
370 * %d as in printf, 0 origin.
371 * %2, %3 like %02d, %03d in printf.
372 * %. like %c
373 * %+x adds <x> to value, then %.
374 * %>xy if value>x, adds y. No output.
375 * %i increments line& col, no output.
376 * %r reverses order of line&col. No output.
377 * %% prints as a single %.
378 * %n exclusive or row & col with 0140.
379 * %B BCD, no output.
380 * %D reverse coding (x-2*(x%16)), no output.
381 */
382
383 char *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100384tgoto(
385 char *cm, /* cm string, from termcap */
386 int col, /* column, x position */
387 int line) /* line, y position */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388{
389 char gx, gy, /* x, y */
390 *ptr, /* pointer in 'cm' */
391 reverse = 0, /* reverse flag */
392 *bufp, /* pointer in returned string */
393 addup = 0, /* add upline */
394 addbak = 0, /* add backup */
395 c;
396 static char buffer[32];
397
398 if (!cm)
399 return "OOPS"; /* Kludge, but standard */
400
401 bufp = buffer;
402 ptr = cm;
403
404 while (*ptr) {
405 if ((c = *ptr++) != '%') { /* normal char */
406 *bufp++ = c;
407 } else { /* % escape */
408 switch(c = *ptr++) {
409 case 'd': /* decimal */
410 bufp = _addfmt(bufp, "%d", line);
411 line = col;
412 break;
413 case '2': /* 2 digit decimal */
414 bufp = _addfmt(bufp, "%02d", line);
415 line = col;
416 break;
417 case '3': /* 3 digit decimal */
418 bufp = _addfmt(bufp, "%03d", line);
419 line = col;
420 break;
421 case '>': /* %>xy: if >x, add y */
422 gx = *ptr++;
423 gy = *ptr++;
424 if (col>gx) col += gy;
425 if (line>gx) line += gy;
426 break;
427 case '+': /* %+c: add c */
428 line += *ptr++;
429 case '.': /* print x/y */
430 if (line == '\t' || /* these are */
431 line == '\n' || /* chars that */
432 line == '\004' || /* UNIX hates */
433 line == '\0') {
434 line++; /* so go to next pos */
435 if (reverse == (line == col))
436 addup=1; /* and mark UP */
437 else
438 addbak=1; /* or BC */
439 }
440 *bufp++=line;
441 line = col;
442 break;
443 case 'r': /* r: reverse */
444 gx = line;
445 line = col;
446 col = gx;
447 reverse = 1;
448 break;
449 case 'i': /* increment (1-origin screen) */
450 col++;
451 line++;
452 break;
453 case '%': /* %%=% literally */
454 *bufp++='%';
455 break;
456 case 'n': /* magic DM2500 code */
457 line ^= 0140;
458 col ^= 0140;
459 break;
460 case 'B': /* bcd encoding */
461 line = line/10<<4+line%10;
462 col = col/10<<4+col%10;
463 break;
464 case 'D': /* magic Delta Data code */
465 line = line-2*(line&15);
466 col = col-2*(col&15);
467 break;
468 default: /* Unknown escape */
469 return "OOPS";
470 }
471 }
472 }
473
474 if (addup) /* add upline */
475 if (UP) {
476 ptr=UP;
477 while (VIM_ISDIGIT(*ptr) || *ptr == '.')
478 ptr++;
479 if (*ptr == '*')
480 ptr++;
481 while (*ptr)
482 *bufp++ = *ptr++;
483 }
484
485 if (addbak) /* add backspace */
486 if (BC) {
487 ptr=BC;
488 while (VIM_ISDIGIT(*ptr) || *ptr == '.')
489 ptr++;
490 if (*ptr == '*')
491 ptr++;
492 while (*ptr)
493 *bufp++ = *ptr++;
494 }
495 else
496 *bufp++='\b';
497
498 *bufp = 0;
499
500 return(buffer);
501}
502
503/*
504 * Module: tputs
505 *
506 * Purpose: decode padding information
507 *
508 * Calling conventions: cp = string to be padded, affcnt = # of items affected
509 * (lines, characters, whatever), outc = routine to output 1 character.
510 *
511 * Returned values: none
512 *
513 * Notes
514 * cp has padding information ahead of it, in the form
515 * nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
516 * and may be a decimal (nnn.mmm). If the asterisk is given, then
517 * the delay is multiplied by afcnt. The delay is produced by outputting
518 * a number of nulls (or other padding char) after printing the
519 * TEXT.
520 *
521 */
522
523long _bauds[16]={
524 0, 50, 75, 110,
525 134, 150, 200, 300,
526 600, 1200, 1800, 2400,
527 4800, 9600, 19200, 19200 };
528
529 int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100530tputs(
531 char *cp, /* string to print */
532 int affcnt, /* Number of lines affected */
533 void (*outc)(unsigned int)) /* routine to output 1 character */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000534{
535 long frac, /* 10^(#digits after decimal point) */
536 counter, /* digits */
Bram Moolenaarbaaa7e92016-01-29 22:47:03 +0100537 atol(const char *);
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538
539 if (VIM_ISDIGIT(*cp)) {
540 counter = 0;
541 frac = 1000;
542 while (VIM_ISDIGIT(*cp))
543 counter = counter * 10L + (long)(*cp++ - '0');
544 if (*cp == '.')
545 while (VIM_ISDIGIT(*++cp)) {
546 counter = counter * 10L + (long)(*cp++ - '0');
547 frac = frac * 10;
548 }
549 if (*cp!='*') { /* multiply by affected lines */
550 if (affcnt>1) affcnt = 1;
551 }
552 else
553 cp++;
554
555 /* Calculate number of characters for padding counter/frac ms delay */
556 if (ospeed)
557 counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
558
559 while (*cp) /* output string */
560 (*outc)(*cp++);
561 if (ospeed)
562 while (counter--) /* followed by pad characters */
563 (*outc)(PC);
564 }
565 else
566 while (*cp)
567 (*outc)(*cp++);
568 return 0;
569}
570
571/*
572 * Module: tutil.c
573 *
574 * Purpose: Utility routines for TERMLIB functions.
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100575 * Returns length of text common to s1 and s2.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000576 */
577 static int
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100578_match(char *s1, char *s2)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000579{
580 int i = 0;
581
582 while (s1[i] && s1[i] == s2[i])
583 i++;
584
585 return i;
586}
587
588/*
589 * finds next c in s that's a member of set, returns pointer
590 */
591 static char *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100592_find(char *s, char *set)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000593{
594 for(; *s; s++)
595 {
596 char *ptr = set;
597
598 while (*ptr && *s != *ptr)
599 ptr++;
600
601 if (*ptr)
602 return s;
603 }
604
605 return s;
606}
607
608/*
609 * add val to buf according to format fmt
610 */
611 static char *
Bram Moolenaar764b23c2016-01-30 21:10:09 +0100612_addfmt(char *buf, char *fmt, int val)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000613{
614 sprintf(buf, fmt, val);
615 while (*buf)
616 buf++;
617 return buf;
618}