Mercurial > hg > RemoteEditor > vim7
comparison src/termlib.c @ 0:76efa0be13f1
Initial revision
author | atsuki |
---|---|
date | Sat, 10 Nov 2007 15:07:22 +0900 |
parents | |
children | e170173ecb68 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:76efa0be13f1 |
---|---|
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) && !defined(RISCOS) | |
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 mch_memmove(lbuf, lbuf + 2, strlen(lbuf + 2) + 1); | |
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 } |