Tizen 2.0 Release
[external/vim.git] / src / termlib.c
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
11 /* TERMLIB: Terminal independent database. */
12
13 #include "vim.h"
14 #include "termlib.pro"
15
16 #if !defined(AMIGA) && !defined(VMS) && !defined(MACOS)
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
158             while (*tptr == '|')                /* | separates names */
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         {
194             STRMOVE(lbuf, lbuf + 2);
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 }