1 |
/* $Header: /p/tcsh/cvsroot/tcsh/ed.screen.c,v 3.82 2016/11/24 15:04:14 christos Exp $ */ |
2 |
/* |
3 |
* ed.screen.c: Editor/termcap-curses interface |
4 |
*/ |
5 |
/*- |
6 |
* Copyright (c) 1980, 1991 The Regents of the University of California. |
7 |
* All rights reserved. |
8 |
* |
9 |
* Redistribution and use in source and binary forms, with or without |
10 |
* modification, are permitted provided that the following conditions |
11 |
* are met: |
12 |
* 1. Redistributions of source code must retain the above copyright |
13 |
* notice, this list of conditions and the following disclaimer. |
14 |
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
* notice, this list of conditions and the following disclaimer in the |
16 |
* documentation and/or other materials provided with the distribution. |
17 |
* 3. Neither the name of the University nor the names of its contributors |
18 |
* may be used to endorse or promote products derived from this software |
19 |
* without specific prior written permission. |
20 |
* |
21 |
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
22 |
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 |
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 |
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
25 |
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
26 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
27 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
30 |
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
31 |
* SUCH DAMAGE. |
32 |
*/ |
33 |
#include "sh.h" |
34 |
|
35 |
RCSID("$tcsh: ed.screen.c,v 3.82 2016/11/24 15:04:14 christos Exp $") |
36 |
|
37 |
#include "ed.h" |
38 |
#include "tc.h" |
39 |
#include "ed.defns.h" |
40 |
|
41 |
/* #define DEBUG_LITERAL */ |
42 |
|
43 |
/* |
44 |
* IMPORTANT NOTE: these routines are allowed to look at the current screen |
45 |
* and the current possition assuming that it is correct. If this is not |
46 |
* true, then the update will be WRONG! This is (should be) a valid |
47 |
* assumption... |
48 |
*/ |
49 |
|
50 |
#define TC_BUFSIZE 2048 |
51 |
|
52 |
#define GoodStr(a) (tstr[a].str != NULL && tstr[a].str[0] != '\0') |
53 |
#define Str(a) tstr[a].str |
54 |
#define Val(a) tval[a].val |
55 |
|
56 |
static const struct { |
57 |
const char *b_name; |
58 |
speed_t b_rate; |
59 |
} baud_rate[] = { |
60 |
|
61 |
#ifdef B0 |
62 |
{ "0", B0 }, |
63 |
#endif |
64 |
#ifdef B50 |
65 |
{ "50", B50 }, |
66 |
#endif |
67 |
#ifdef B75 |
68 |
{ "75", B75 }, |
69 |
#endif |
70 |
#ifdef B110 |
71 |
{ "110", B110 }, |
72 |
#endif |
73 |
#ifdef B134 |
74 |
{ "134", B134 }, |
75 |
#endif |
76 |
#ifdef B150 |
77 |
{ "150", B150 }, |
78 |
#endif |
79 |
#ifdef B200 |
80 |
{ "200", B200 }, |
81 |
#endif |
82 |
#ifdef B300 |
83 |
{ "300", B300 }, |
84 |
#endif |
85 |
#ifdef B600 |
86 |
{ "600", B600 }, |
87 |
#endif |
88 |
#ifdef B900 |
89 |
{ "900", B900 }, |
90 |
#endif |
91 |
#ifdef B1200 |
92 |
{ "1200", B1200 }, |
93 |
#endif |
94 |
#ifdef B1800 |
95 |
{ "1800", B1800 }, |
96 |
#endif |
97 |
#ifdef B2400 |
98 |
{ "2400", B2400 }, |
99 |
#endif |
100 |
#ifdef B3600 |
101 |
{ "3600", B3600 }, |
102 |
#endif |
103 |
#ifdef B4800 |
104 |
{ "4800", B4800 }, |
105 |
#endif |
106 |
#ifdef B7200 |
107 |
{ "7200", B7200 }, |
108 |
#endif |
109 |
#ifdef B9600 |
110 |
{ "9600", B9600 }, |
111 |
#endif |
112 |
#ifdef EXTA |
113 |
{ "19200", EXTA }, |
114 |
#endif |
115 |
#ifdef B19200 |
116 |
{ "19200", B19200 }, |
117 |
#endif |
118 |
#ifdef EXTB |
119 |
{ "38400", EXTB }, |
120 |
#endif |
121 |
#ifdef B38400 |
122 |
{ "38400", B38400 }, |
123 |
#endif |
124 |
{ NULL, 0 } |
125 |
}; |
126 |
|
127 |
#define T_at7 0 |
128 |
#define T_al 1 |
129 |
#define T_bl 2 |
130 |
#define T_cd 3 |
131 |
#define T_ce 4 |
132 |
#define T_ch 5 |
133 |
#define T_cl 6 |
134 |
#define T_dc 7 |
135 |
#define T_dl 8 |
136 |
#define T_dm 9 |
137 |
#define T_ed 10 |
138 |
#define T_ei 11 |
139 |
#define T_fs 12 |
140 |
#define T_ho 13 |
141 |
#define T_ic 14 |
142 |
#define T_im 15 |
143 |
#define T_ip 16 |
144 |
#define T_kd 17 |
145 |
#define T_kh 18 |
146 |
#define T_kl 19 |
147 |
#define T_kr 20 |
148 |
#define T_ku 21 |
149 |
#define T_md 22 |
150 |
#define T_me 23 |
151 |
#define T_mr 24 |
152 |
#define T_nd 25 |
153 |
#define T_se 26 |
154 |
#define T_so 27 |
155 |
#define T_ts 28 |
156 |
#define T_up 29 |
157 |
#define T_us 30 |
158 |
#define T_ue 31 |
159 |
#define T_vb 32 |
160 |
#define T_DC 33 |
161 |
#define T_DO 34 |
162 |
#define T_IC 35 |
163 |
#define T_LE 36 |
164 |
#define T_RI 37 |
165 |
#define T_UP 38 |
166 |
#define T_str 39 |
167 |
|
168 |
static struct termcapstr { |
169 |
const char *name; |
170 |
const char *long_name; |
171 |
char *str; |
172 |
} tstr[T_str + 1]; |
173 |
|
174 |
|
175 |
#define T_am 0 |
176 |
#define T_pt 1 |
177 |
#define T_li 2 |
178 |
#define T_co 3 |
179 |
#define T_km 4 |
180 |
#define T_xn 5 |
181 |
#define T_val 6 |
182 |
static struct termcapval { |
183 |
const char *name; |
184 |
const char *long_name; |
185 |
int val; |
186 |
} tval[T_val + 1]; |
187 |
|
188 |
void |
189 |
terminit(void) |
190 |
{ |
191 |
#ifdef NLS_CATALOGS |
192 |
int i; |
193 |
|
194 |
for (i = 0; i < T_str + 1; i++) |
195 |
xfree((ptr_t)(intptr_t)tstr[i].long_name); |
196 |
|
197 |
for (i = 0; i < T_val + 1; i++) |
198 |
xfree((ptr_t)(intptr_t)tval[i].long_name); |
199 |
#endif |
200 |
|
201 |
tstr[T_al].name = "al"; |
202 |
tstr[T_al].long_name = CSAVS(4, 1, "add new blank line"); |
203 |
|
204 |
tstr[T_bl].name = "bl"; |
205 |
tstr[T_bl].long_name = CSAVS(4, 2, "audible bell"); |
206 |
|
207 |
tstr[T_cd].name = "cd"; |
208 |
tstr[T_cd].long_name = CSAVS(4, 3, "clear to bottom"); |
209 |
|
210 |
tstr[T_ce].name = "ce"; |
211 |
tstr[T_ce].long_name = CSAVS(4, 4, "clear to end of line"); |
212 |
|
213 |
tstr[T_ch].name = "ch"; |
214 |
tstr[T_ch].long_name = CSAVS(4, 5, "cursor to horiz pos"); |
215 |
|
216 |
tstr[T_cl].name = "cl"; |
217 |
tstr[T_cl].long_name = CSAVS(4, 6, "clear screen"); |
218 |
|
219 |
tstr[T_dc].name = "dc"; |
220 |
tstr[T_dc].long_name = CSAVS(4, 7, "delete a character"); |
221 |
|
222 |
tstr[T_dl].name = "dl"; |
223 |
tstr[T_dl].long_name = CSAVS(4, 8, "delete a line"); |
224 |
|
225 |
tstr[T_dm].name = "dm"; |
226 |
tstr[T_dm].long_name = CSAVS(4, 9, "start delete mode"); |
227 |
|
228 |
tstr[T_ed].name = "ed"; |
229 |
tstr[T_ed].long_name = CSAVS(4, 10, "end delete mode"); |
230 |
|
231 |
tstr[T_ei].name = "ei"; |
232 |
tstr[T_ei].long_name = CSAVS(4, 11, "end insert mode"); |
233 |
|
234 |
tstr[T_fs].name = "fs"; |
235 |
tstr[T_fs].long_name = CSAVS(4, 12, "cursor from status line"); |
236 |
|
237 |
tstr[T_ho].name = "ho"; |
238 |
tstr[T_ho].long_name = CSAVS(4, 13, "home cursor"); |
239 |
|
240 |
tstr[T_ic].name = "ic"; |
241 |
tstr[T_ic].long_name = CSAVS(4, 14, "insert character"); |
242 |
|
243 |
tstr[T_im].name = "im"; |
244 |
tstr[T_im].long_name = CSAVS(4, 15, "start insert mode"); |
245 |
|
246 |
tstr[T_ip].name = "ip"; |
247 |
tstr[T_ip].long_name = CSAVS(4, 16, "insert padding"); |
248 |
|
249 |
tstr[T_kd].name = "kd"; |
250 |
tstr[T_kd].long_name = CSAVS(4, 17, "sends cursor down"); |
251 |
|
252 |
tstr[T_kl].name = "kl"; |
253 |
tstr[T_kl].long_name = CSAVS(4, 18, "sends cursor left"); |
254 |
|
255 |
tstr[T_kr].name = "kr"; |
256 |
tstr[T_kr].long_name = CSAVS(4, 19, "sends cursor right"); |
257 |
|
258 |
tstr[T_ku].name = "ku"; |
259 |
tstr[T_ku].long_name = CSAVS(4, 20, "sends cursor up"); |
260 |
|
261 |
tstr[T_md].name = "md"; |
262 |
tstr[T_md].long_name = CSAVS(4, 21, "begin bold"); |
263 |
|
264 |
tstr[T_me].name = "me"; |
265 |
tstr[T_me].long_name = CSAVS(4, 22, "end attributes"); |
266 |
|
267 |
tstr[T_nd].name = "nd"; |
268 |
tstr[T_nd].long_name = CSAVS(4, 23, "non destructive space"); |
269 |
|
270 |
tstr[T_se].name = "se"; |
271 |
tstr[T_se].long_name = CSAVS(4, 24, "end standout"); |
272 |
|
273 |
tstr[T_so].name = "so"; |
274 |
tstr[T_so].long_name = CSAVS(4, 25, "begin standout"); |
275 |
|
276 |
tstr[T_ts].name = "ts"; |
277 |
tstr[T_ts].long_name = CSAVS(4, 26, "cursor to status line"); |
278 |
|
279 |
tstr[T_up].name = "up"; |
280 |
tstr[T_up].long_name = CSAVS(4, 27, "cursor up one"); |
281 |
|
282 |
tstr[T_us].name = "us"; |
283 |
tstr[T_us].long_name = CSAVS(4, 28, "begin underline"); |
284 |
|
285 |
tstr[T_ue].name = "ue"; |
286 |
tstr[T_ue].long_name = CSAVS(4, 29, "end underline"); |
287 |
|
288 |
tstr[T_vb].name = "vb"; |
289 |
tstr[T_vb].long_name = CSAVS(4, 30, "visible bell"); |
290 |
|
291 |
tstr[T_DC].name = "DC"; |
292 |
tstr[T_DC].long_name = CSAVS(4, 31, "delete multiple chars"); |
293 |
|
294 |
tstr[T_DO].name = "DO"; |
295 |
tstr[T_DO].long_name = CSAVS(4, 32, "cursor down multiple"); |
296 |
|
297 |
tstr[T_IC].name = "IC"; |
298 |
tstr[T_IC].long_name = CSAVS(4, 33, "insert multiple chars"); |
299 |
|
300 |
tstr[T_LE].name = "LE"; |
301 |
tstr[T_LE].long_name = CSAVS(4, 34, "cursor left multiple"); |
302 |
|
303 |
tstr[T_RI].name = "RI"; |
304 |
tstr[T_RI].long_name = CSAVS(4, 35, "cursor right multiple"); |
305 |
|
306 |
tstr[T_UP].name = "UP"; |
307 |
tstr[T_UP].long_name = CSAVS(4, 36, "cursor up multiple"); |
308 |
|
309 |
tstr[T_kh].name = "kh"; |
310 |
tstr[T_kh].long_name = CSAVS(4, 43, "send cursor home"); |
311 |
|
312 |
tstr[T_at7].name = "@7"; |
313 |
tstr[T_at7].long_name = CSAVS(4, 44, "send cursor end"); |
314 |
|
315 |
tstr[T_mr].name = "mr"; |
316 |
tstr[T_mr].long_name = CSAVS(4, 45, "begin reverse video"); |
317 |
|
318 |
tstr[T_str].name = NULL; |
319 |
tstr[T_str].long_name = NULL; |
320 |
|
321 |
|
322 |
tval[T_am].name = "am"; |
323 |
tval[T_am].long_name = CSAVS(4, 37, "Has automatic margins"); |
324 |
|
325 |
tval[T_pt].name = "pt"; |
326 |
tval[T_pt].long_name = CSAVS(4, 38, "Can use physical tabs"); |
327 |
|
328 |
tval[T_li].name = "li"; |
329 |
tval[T_li].long_name = CSAVS(4, 39, "Number of lines"); |
330 |
|
331 |
tval[T_co].name = "co"; |
332 |
tval[T_co].long_name = CSAVS(4, 40, "Number of columns"); |
333 |
|
334 |
tval[T_km].name = "km"; |
335 |
tval[T_km].long_name = CSAVS(4, 41, "Has meta key"); |
336 |
|
337 |
tval[T_xn].name = "xn"; |
338 |
tval[T_xn].long_name = CSAVS(4, 42, "Newline ignored at right margin"); |
339 |
|
340 |
tval[T_val].name = NULL; |
341 |
tval[T_val].long_name = NULL; |
342 |
} |
343 |
|
344 |
/* |
345 |
* A very useful table from justin@crim.ca (Justin Bur) :-) |
346 |
* (Modified by per@erix.ericsson.se (Per Hedeland) |
347 |
* - first (and second:-) case fixed) |
348 |
* |
349 |
* Description Termcap variables tcsh behavior |
350 |
* am xn UseRightmost SendCRLF |
351 |
* -------------- ------- ------- ------------ ------------ |
352 |
* Automargins yes no yes no |
353 |
* Magic Margins yes yes yes no |
354 |
* No Wrap no -- yes yes |
355 |
*/ |
356 |
|
357 |
static int me_all = 0; /* does two or more of the attributes use me */ |
358 |
|
359 |
static void ReBufferDisplay (void); |
360 |
static void TCset (struct termcapstr *, const char *); |
361 |
|
362 |
|
363 |
static void |
364 |
TCset(struct termcapstr *t, const char *cap) |
365 |
{ |
366 |
if (cap == NULL || *cap == '\0') { |
367 |
xfree(t->str); |
368 |
t->str = NULL; |
369 |
} else { |
370 |
size_t size; |
371 |
|
372 |
size = strlen(cap) + 1; |
373 |
t->str = xrealloc(t->str, size); |
374 |
memcpy(t->str, cap, size); |
375 |
} |
376 |
} |
377 |
|
378 |
|
379 |
/*ARGSUSED*/ |
380 |
void |
381 |
TellTC(void) |
382 |
{ |
383 |
struct termcapstr *t; |
384 |
char *first, *s; |
385 |
|
386 |
xprintf("%s", CGETS(7, 1, "\n\tTcsh thinks your terminal has the\n")); |
387 |
xprintf("%s", CGETS(7, 2, "\tfollowing characteristics:\n\n")); |
388 |
xprintf(CGETS(7, 3, "\tIt has %d columns and %d lines\n"), |
389 |
Val(T_co), Val(T_li)); |
390 |
s = strsave(T_HasMeta ? CGETS(7, 5, "a") : CGETS(7, 6, "no")); |
391 |
cleanup_push(s, xfree); |
392 |
first = s; |
393 |
xprintf(CGETS(7, 4, "\tIt has %s meta key\n"), s); |
394 |
s = strsave(T_Tabs ? "" : CGETS(7, 8, " not")); |
395 |
cleanup_push(s, xfree); |
396 |
xprintf(CGETS(7, 7, "\tIt can%s use tabs\n"), s); |
397 |
s = strsave((T_Margin&MARGIN_AUTO) ? |
398 |
CGETS(7, 10, "has") : CGETS(7, 11, "does not have")); |
399 |
cleanup_push(s, xfree); |
400 |
xprintf(CGETS(7, 9, "\tIt %s automatic margins\n"), s); |
401 |
if (T_Margin & MARGIN_AUTO) { |
402 |
s = strsave((T_Margin & MARGIN_MAGIC) ? |
403 |
CGETS(7, 10, "has") : CGETS(7, 11, "does not have")); |
404 |
cleanup_push(s, xfree); |
405 |
xprintf(CGETS(7, 12, "\tIt %s magic margins\n"), s); |
406 |
} |
407 |
for (t = tstr; t->name != NULL; t++) { |
408 |
s = strsave(t->str && *t->str ? t->str : CGETS(7, 13, "(empty)")); |
409 |
cleanup_push(s, xfree); |
410 |
xprintf("\t%36s (%s) == %s\n", t->long_name, t->name, s); |
411 |
cleanup_until(s); |
412 |
} |
413 |
xputchar('\n'); |
414 |
cleanup_until(first); |
415 |
} |
416 |
|
417 |
|
418 |
static void |
419 |
ReBufferDisplay(void) |
420 |
{ |
421 |
int i; |
422 |
Char **b; |
423 |
|
424 |
b = Display; |
425 |
Display = NULL; |
426 |
blkfree(b); |
427 |
b = Vdisplay; |
428 |
Vdisplay = NULL; |
429 |
blkfree(b); |
430 |
TermH = Val(T_co); |
431 |
TermV = (INBUFSIZE * 4) / TermH + 1;/*FIXBUF*/ |
432 |
b = xmalloc(sizeof(*b) * (TermV + 1)); |
433 |
for (i = 0; i < TermV; i++) |
434 |
b[i] = xmalloc(sizeof(*b[i]) * (TermH + 1)); |
435 |
b[TermV] = NULL; |
436 |
Display = b; |
437 |
b = xmalloc(sizeof(*b) * (TermV + 1)); |
438 |
for (i = 0; i < TermV; i++) |
439 |
b[i] = xmalloc(sizeof(*b[i]) * (TermH + 1)); |
440 |
b[TermV] = NULL; |
441 |
Vdisplay = b; |
442 |
} |
443 |
|
444 |
void |
445 |
SetTC(char *what, char *how) |
446 |
{ |
447 |
struct termcapstr *ts; |
448 |
struct termcapval *tv; |
449 |
|
450 |
/* |
451 |
* Do the strings first |
452 |
*/ |
453 |
setname("settc"); |
454 |
for (ts = tstr; ts->name != NULL; ts++) |
455 |
if (strcmp(ts->name, what) == 0) |
456 |
break; |
457 |
if (ts->name != NULL) { |
458 |
TCset(ts, how); |
459 |
/* |
460 |
* Reset variables |
461 |
*/ |
462 |
if (GoodStr(T_me) && GoodStr(T_ue)) |
463 |
me_all = (strcmp(Str(T_me), Str(T_ue)) == 0); |
464 |
else |
465 |
me_all = 0; |
466 |
if (GoodStr(T_me) && GoodStr(T_se)) |
467 |
me_all |= (strcmp(Str(T_me), Str(T_se)) == 0); |
468 |
|
469 |
T_CanCEOL = GoodStr(T_ce); |
470 |
T_CanDel = GoodStr(T_dc) || GoodStr(T_DC); |
471 |
T_CanIns = GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC); |
472 |
T_CanUP = GoodStr(T_up) || GoodStr(T_UP); |
473 |
return; |
474 |
} |
475 |
|
476 |
/* |
477 |
* Do the numeric ones second |
478 |
*/ |
479 |
for (tv = tval; tv->name != NULL; tv++) |
480 |
if (strcmp(tv->name, what) == 0) |
481 |
break; |
482 |
|
483 |
if (tv->name != NULL) { |
484 |
if (tv == &tval[T_pt] || tv == &tval[T_km] || |
485 |
tv == &tval[T_am] || tv == &tval[T_xn]) { |
486 |
if (strcmp(how, "yes") == 0) |
487 |
tv->val = 1; |
488 |
else if (strcmp(how, "no") == 0) |
489 |
tv->val = 0; |
490 |
else { |
491 |
stderror(ERR_SETTCUS, tv->name); |
492 |
return; |
493 |
} |
494 |
T_Tabs = Val(T_pt); |
495 |
T_HasMeta = Val(T_km); |
496 |
T_Margin = Val(T_am) ? MARGIN_AUTO : 0; |
497 |
T_Margin |= Val(T_xn) ? MARGIN_MAGIC : 0; |
498 |
if (tv == &tval[T_am] || tv == &tval[T_xn]) |
499 |
ChangeSize(Val(T_li), Val(T_co)); |
500 |
return; |
501 |
} |
502 |
else { |
503 |
tv->val = atoi(how); |
504 |
T_Cols = (Char) Val(T_co); |
505 |
T_Lines = (Char) Val(T_li); |
506 |
if (tv == &tval[T_co] || tv == &tval[T_li]) |
507 |
ChangeSize(Val(T_li), Val(T_co)); |
508 |
return; |
509 |
} |
510 |
} |
511 |
stderror(ERR_NAME | ERR_TCCAP, what); |
512 |
return; |
513 |
} |
514 |
|
515 |
|
516 |
/* |
517 |
* Print the termcap string out with variable substitution |
518 |
*/ |
519 |
void |
520 |
EchoTC(Char **v) |
521 |
{ |
522 |
char *cap, *scap, *cv; |
523 |
int arg_need, arg_cols, arg_rows; |
524 |
int verbose = 0, silent = 0; |
525 |
char *area; |
526 |
static const char fmts[] = "%s\n", fmtd[] = "%d\n"; |
527 |
struct termcapstr *t; |
528 |
char buf[TC_BUFSIZE]; |
529 |
Char **globbed; |
530 |
|
531 |
area = buf; |
532 |
|
533 |
setname("echotc"); |
534 |
|
535 |
v = glob_all_or_error(v); |
536 |
globbed = v; |
537 |
cleanup_push(globbed, blk_cleanup); |
538 |
|
539 |
if (!*v || *v[0] == '\0') |
540 |
goto end; |
541 |
if (v[0][0] == '-') { |
542 |
switch (v[0][1]) { |
543 |
case 'v': |
544 |
verbose = 1; |
545 |
break; |
546 |
case 's': |
547 |
silent = 1; |
548 |
break; |
549 |
default: |
550 |
stderror(ERR_NAME | ERR_TCUSAGE); |
551 |
break; |
552 |
} |
553 |
v++; |
554 |
} |
555 |
if (!*v || *v[0] == '\0') |
556 |
goto end; |
557 |
cv = strsave(short2str(*v)); |
558 |
cleanup_push(cv, xfree); |
559 |
if (strcmp(cv, "tabs") == 0) { |
560 |
xprintf(fmts, T_Tabs ? CGETS(7, 14, "yes") : |
561 |
CGETS(7, 15, "no")); |
562 |
goto end_flush; |
563 |
} |
564 |
else if (strcmp(cv, "meta") == 0) { |
565 |
xprintf(fmts, Val(T_km) ? CGETS(7, 14, "yes") : |
566 |
CGETS(7, 15, "no")); |
567 |
goto end_flush; |
568 |
} |
569 |
else if (strcmp(cv, "xn") == 0) { |
570 |
xprintf(fmts, T_Margin & MARGIN_MAGIC ? CGETS(7, 14, "yes") : |
571 |
CGETS(7, 15, "no")); |
572 |
goto end_flush; |
573 |
} |
574 |
else if (strcmp(cv, "am") == 0) { |
575 |
xprintf(fmts, T_Margin & MARGIN_AUTO ? CGETS(7, 14, "yes") : |
576 |
CGETS(7, 15, "no")); |
577 |
goto end_flush; |
578 |
} |
579 |
else if (strcmp(cv, "baud") == 0) { |
580 |
int i; |
581 |
|
582 |
for (i = 0; baud_rate[i].b_name != NULL; i++) |
583 |
if (T_Speed == baud_rate[i].b_rate) { |
584 |
xprintf(fmts, baud_rate[i].b_name); |
585 |
goto end_flush; |
586 |
} |
587 |
xprintf(fmtd, 0); |
588 |
goto end_flush; |
589 |
} |
590 |
else if (strcmp(cv, "rows") == 0 || strcmp(cv, "lines") == 0 || |
591 |
strcmp(cv, "li") == 0) { |
592 |
xprintf(fmtd, Val(T_li)); |
593 |
goto end_flush; |
594 |
} |
595 |
else if (strcmp(cv, "cols") == 0 || strcmp(cv, "co") == 0) { |
596 |
xprintf(fmtd, Val(T_co)); |
597 |
goto end_flush; |
598 |
} |
599 |
|
600 |
/* |
601 |
* Try to use our local definition first |
602 |
*/ |
603 |
scap = NULL; |
604 |
for (t = tstr; t->name != NULL; t++) |
605 |
if (strcmp(t->name, cv) == 0) { |
606 |
scap = t->str; |
607 |
break; |
608 |
} |
609 |
if (t->name == NULL) |
610 |
scap = tgetstr(cv, &area); |
611 |
if (!scap || scap[0] == '\0') { |
612 |
if (tgetflag(cv)) { |
613 |
xprintf("%s", CGETS(7, 14, "yes\n")); |
614 |
goto end; |
615 |
} |
616 |
if (silent) |
617 |
goto end; |
618 |
else |
619 |
stderror(ERR_NAME | ERR_TCCAP, cv); |
620 |
} |
621 |
|
622 |
/* |
623 |
* Count home many values we need for this capability. |
624 |
*/ |
625 |
for (cap = scap, arg_need = 0; *cap; cap++) |
626 |
if (*cap == '%') |
627 |
switch (*++cap) { |
628 |
case 'd': |
629 |
case '2': |
630 |
case '3': |
631 |
case '.': |
632 |
case '+': |
633 |
arg_need++; |
634 |
break; |
635 |
case '%': |
636 |
case '>': |
637 |
case 'i': |
638 |
case 'r': |
639 |
case 'n': |
640 |
case 'B': |
641 |
case 'D': |
642 |
break; |
643 |
default: |
644 |
/* |
645 |
* hpux has lot's of them... |
646 |
*/ |
647 |
if (verbose) |
648 |
stderror(ERR_NAME | ERR_TCPARM, *cap); |
649 |
/* This is bad, but I won't complain */ |
650 |
break; |
651 |
} |
652 |
|
653 |
switch (arg_need) { |
654 |
case 0: |
655 |
v++; |
656 |
if (*v && *v[0]) { |
657 |
if (silent) |
658 |
goto end; |
659 |
else |
660 |
stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); |
661 |
} |
662 |
(void) tputs(scap, 1, PUTRAW); |
663 |
break; |
664 |
case 1: |
665 |
v++; |
666 |
if (!*v || *v[0] == '\0') |
667 |
stderror(ERR_NAME | ERR_TCNARGS, cv, 1); |
668 |
arg_cols = 0; |
669 |
arg_rows = atoi(short2str(*v)); |
670 |
v++; |
671 |
if (*v && *v[0]) { |
672 |
if (silent) |
673 |
goto end; |
674 |
else |
675 |
stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); |
676 |
} |
677 |
(void) tputs(tgoto(scap, arg_cols, arg_rows), 1, PUTRAW); |
678 |
break; |
679 |
default: |
680 |
/* This is wrong, but I will ignore it... */ |
681 |
if (verbose) |
682 |
stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); |
683 |
/*FALLTHROUGH*/ |
684 |
case 2: |
685 |
v++; |
686 |
if (!*v || *v[0] == '\0') { |
687 |
if (silent) |
688 |
goto end; |
689 |
else |
690 |
stderror(ERR_NAME | ERR_TCNARGS, cv, 2); |
691 |
} |
692 |
arg_cols = atoi(short2str(*v)); |
693 |
v++; |
694 |
if (!*v || *v[0] == '\0') { |
695 |
if (silent) |
696 |
goto end; |
697 |
else |
698 |
stderror(ERR_NAME | ERR_TCNARGS, cv, 2); |
699 |
} |
700 |
arg_rows = atoi(short2str(*v)); |
701 |
v++; |
702 |
if (*v && *v[0]) { |
703 |
if (silent) |
704 |
goto end; |
705 |
else |
706 |
stderror(ERR_NAME | ERR_TCARGS, cv, arg_need); |
707 |
} |
708 |
(void) tputs(tgoto(scap, arg_cols, arg_rows), arg_rows, PUTRAW); |
709 |
break; |
710 |
} |
711 |
end_flush: |
712 |
flush(); |
713 |
end: |
714 |
cleanup_until(globbed); |
715 |
} |
716 |
|
717 |
int GotTermCaps = 0; |
718 |
|
719 |
static struct { |
720 |
Char *name; |
721 |
int key; |
722 |
XmapVal fun; |
723 |
int type; |
724 |
} arrow[] = { |
725 |
#define A_K_DN 0 |
726 |
{ STRdown, T_kd, { 0 }, 0 }, |
727 |
#define A_K_UP 1 |
728 |
{ STRup, T_ku, { 0 }, 0 }, |
729 |
#define A_K_LT 2 |
730 |
{ STRleft, T_kl, { 0 }, 0 }, |
731 |
#define A_K_RT 3 |
732 |
{ STRright, T_kr, { 0 }, 0 }, |
733 |
#define A_K_HO 4 |
734 |
{ STRhome, T_kh, { 0 }, 0 }, |
735 |
#define A_K_EN 5 |
736 |
{ STRend, T_at7, { 0 }, 0} |
737 |
}; |
738 |
#define A_K_NKEYS 6 |
739 |
|
740 |
void |
741 |
ResetArrowKeys(void) |
742 |
{ |
743 |
arrow[A_K_DN].fun.cmd = F_DOWN_HIST; |
744 |
arrow[A_K_DN].type = XK_CMD; |
745 |
|
746 |
arrow[A_K_UP].fun.cmd = F_UP_HIST; |
747 |
arrow[A_K_UP].type = XK_CMD; |
748 |
|
749 |
arrow[A_K_LT].fun.cmd = F_CHARBACK; |
750 |
arrow[A_K_LT].type = XK_CMD; |
751 |
|
752 |
arrow[A_K_RT].fun.cmd = F_CHARFWD; |
753 |
arrow[A_K_RT].type = XK_CMD; |
754 |
|
755 |
arrow[A_K_HO].fun.cmd = F_TOBEG; |
756 |
arrow[A_K_HO].type = XK_CMD; |
757 |
|
758 |
arrow[A_K_EN].fun.cmd = F_TOEND; |
759 |
arrow[A_K_EN].type = XK_CMD; |
760 |
} |
761 |
|
762 |
void |
763 |
DefaultArrowKeys(void) |
764 |
{ |
765 |
static Char strA[] = {033, '[', 'A', '\0'}; |
766 |
static Char strB[] = {033, '[', 'B', '\0'}; |
767 |
static Char strC[] = {033, '[', 'C', '\0'}; |
768 |
static Char strD[] = {033, '[', 'D', '\0'}; |
769 |
static Char strH[] = {033, '[', 'H', '\0'}; |
770 |
static Char strF[] = {033, '[', 'F', '\0'}; |
771 |
static Char stOA[] = {033, 'O', 'A', '\0'}; |
772 |
static Char stOB[] = {033, 'O', 'B', '\0'}; |
773 |
static Char stOC[] = {033, 'O', 'C', '\0'}; |
774 |
static Char stOD[] = {033, 'O', 'D', '\0'}; |
775 |
static Char stOH[] = {033, 'O', 'H', '\0'}; |
776 |
static Char stOF[] = {033, 'O', 'F', '\0'}; |
777 |
|
778 |
CStr cs; |
779 |
#ifndef IS_ASCII |
780 |
if (strA[0] == 033) |
781 |
{ |
782 |
strA[0] = CTL_ESC('\033'); |
783 |
strB[0] = CTL_ESC('\033'); |
784 |
strC[0] = CTL_ESC('\033'); |
785 |
strD[0] = CTL_ESC('\033'); |
786 |
strH[0] = CTL_ESC('\033'); |
787 |
strF[0] = CTL_ESC('\033'); |
788 |
stOA[0] = CTL_ESC('\033'); |
789 |
stOB[0] = CTL_ESC('\033'); |
790 |
stOC[0] = CTL_ESC('\033'); |
791 |
stOD[0] = CTL_ESC('\033'); |
792 |
stOH[0] = CTL_ESC('\033'); |
793 |
stOF[0] = CTL_ESC('\033'); |
794 |
} |
795 |
#endif |
796 |
|
797 |
cs.len = 3; |
798 |
|
799 |
cs.buf = strA; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); |
800 |
cs.buf = strB; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); |
801 |
cs.buf = strC; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); |
802 |
cs.buf = strD; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); |
803 |
cs.buf = strH; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); |
804 |
cs.buf = strF; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); |
805 |
cs.buf = stOA; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); |
806 |
cs.buf = stOB; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); |
807 |
cs.buf = stOC; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); |
808 |
cs.buf = stOD; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); |
809 |
cs.buf = stOH; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); |
810 |
cs.buf = stOF; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); |
811 |
|
812 |
if (VImode) { |
813 |
cs.len = 2; |
814 |
cs.buf = &strA[1]; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); |
815 |
cs.buf = &strB[1]; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); |
816 |
cs.buf = &strC[1]; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); |
817 |
cs.buf = &strD[1]; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); |
818 |
cs.buf = &strH[1]; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); |
819 |
cs.buf = &strF[1]; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); |
820 |
cs.buf = &stOA[1]; AddXkey(&cs, &arrow[A_K_UP].fun, arrow[A_K_UP].type); |
821 |
cs.buf = &stOB[1]; AddXkey(&cs, &arrow[A_K_DN].fun, arrow[A_K_DN].type); |
822 |
cs.buf = &stOC[1]; AddXkey(&cs, &arrow[A_K_RT].fun, arrow[A_K_RT].type); |
823 |
cs.buf = &stOD[1]; AddXkey(&cs, &arrow[A_K_LT].fun, arrow[A_K_LT].type); |
824 |
cs.buf = &stOH[1]; AddXkey(&cs, &arrow[A_K_HO].fun, arrow[A_K_HO].type); |
825 |
cs.buf = &stOF[1]; AddXkey(&cs, &arrow[A_K_EN].fun, arrow[A_K_EN].type); |
826 |
} |
827 |
} |
828 |
|
829 |
|
830 |
int |
831 |
SetArrowKeys(const CStr *name, XmapVal *fun, int type) |
832 |
{ |
833 |
int i; |
834 |
for (i = 0; i < A_K_NKEYS; i++) |
835 |
if (Strcmp(name->buf, arrow[i].name) == 0) { |
836 |
arrow[i].fun = *fun; |
837 |
arrow[i].type = type; |
838 |
return 0; |
839 |
} |
840 |
return -1; |
841 |
} |
842 |
|
843 |
int |
844 |
IsArrowKey(Char *name) |
845 |
{ |
846 |
int i; |
847 |
for (i = 0; i < A_K_NKEYS; i++) |
848 |
if (Strcmp(name, arrow[i].name) == 0) |
849 |
return 1; |
850 |
return 0; |
851 |
} |
852 |
|
853 |
int |
854 |
ClearArrowKeys(const CStr *name) |
855 |
{ |
856 |
int i; |
857 |
for (i = 0; i < A_K_NKEYS; i++) |
858 |
if (Strcmp(name->buf, arrow[i].name) == 0) { |
859 |
arrow[i].type = XK_NOD; |
860 |
return 0; |
861 |
} |
862 |
return -1; |
863 |
} |
864 |
|
865 |
void |
866 |
PrintArrowKeys(const CStr *name) |
867 |
{ |
868 |
int i; |
869 |
|
870 |
for (i = 0; i < A_K_NKEYS; i++) |
871 |
if (name->len == 0 || Strcmp(name->buf, arrow[i].name) == 0) |
872 |
if (arrow[i].type != XK_NOD) |
873 |
printOne(arrow[i].name, &arrow[i].fun, arrow[i].type); |
874 |
} |
875 |
|
876 |
|
877 |
void |
878 |
BindArrowKeys(void) |
879 |
{ |
880 |
KEYCMD *map, *dmap; |
881 |
int i, j; |
882 |
char *p; |
883 |
CStr cs; |
884 |
|
885 |
if (!GotTermCaps) |
886 |
return; |
887 |
map = VImode ? CcAltMap : CcKeyMap; |
888 |
dmap = VImode ? CcViCmdMap : CcEmacsMap; |
889 |
|
890 |
DefaultArrowKeys(); |
891 |
|
892 |
for (i = 0; i < A_K_NKEYS; i++) { |
893 |
p = tstr[arrow[i].key].str; |
894 |
if (p && *p) { |
895 |
j = (unsigned char) *p; |
896 |
cs.buf = str2short(p); |
897 |
cs.len = Strlen(cs.buf); |
898 |
/* |
899 |
* Assign the arrow keys only if: |
900 |
* |
901 |
* 1. They are multi-character arrow keys and the user |
902 |
* has not re-assigned the leading character, or |
903 |
* has re-assigned the leading character to be F_XKEY |
904 |
* 2. They are single arrow keys pointing to an unassigned key. |
905 |
*/ |
906 |
if (arrow[i].type == XK_NOD) { |
907 |
ClearXkey(map, &cs); |
908 |
} |
909 |
else { |
910 |
if (p[1] && (dmap[j] == map[j] || map[j] == F_XKEY)) { |
911 |
AddXkey(&cs, &arrow[i].fun, arrow[i].type); |
912 |
map[j] = F_XKEY; |
913 |
} |
914 |
else if (map[j] == F_UNASSIGNED) { |
915 |
ClearXkey(map, &cs); |
916 |
if (arrow[i].type == XK_CMD) |
917 |
map[j] = arrow[i].fun.cmd; |
918 |
else |
919 |
AddXkey(&cs, &arrow[i].fun, arrow[i].type); |
920 |
} |
921 |
} |
922 |
} |
923 |
} |
924 |
} |
925 |
|
926 |
static Char cur_atr = 0; /* current attributes */ |
927 |
|
928 |
void |
929 |
SetAttributes(Char atr) |
930 |
{ |
931 |
atr &= ATTRIBUTES; |
932 |
if (atr != cur_atr) { |
933 |
if (me_all && GoodStr(T_me)) { |
934 |
if (((cur_atr & BOLD) && !(atr & BOLD)) || |
935 |
((cur_atr & UNDER) && !(atr & UNDER)) || |
936 |
((cur_atr & STANDOUT) && !(atr & STANDOUT))) { |
937 |
(void) tputs(Str(T_me), 1, PUTPURE); |
938 |
cur_atr = 0; |
939 |
} |
940 |
} |
941 |
if ((atr & BOLD) != (cur_atr & BOLD)) { |
942 |
if (atr & BOLD) { |
943 |
if (GoodStr(T_md) && GoodStr(T_me)) { |
944 |
(void) tputs(Str(T_md), 1, PUTPURE); |
945 |
cur_atr |= BOLD; |
946 |
} |
947 |
} |
948 |
else { |
949 |
if (GoodStr(T_md) && GoodStr(T_me)) { |
950 |
(void) tputs(Str(T_me), 1, PUTPURE); |
951 |
if ((cur_atr & STANDOUT) && GoodStr(T_se)) { |
952 |
(void) tputs(Str(T_se), 1, PUTPURE); |
953 |
cur_atr &= ~STANDOUT; |
954 |
} |
955 |
if ((cur_atr & UNDER) && GoodStr(T_ue)) { |
956 |
(void) tputs(Str(T_ue), 1, PUTPURE); |
957 |
cur_atr &= ~UNDER; |
958 |
} |
959 |
cur_atr &= ~BOLD; |
960 |
} |
961 |
} |
962 |
} |
963 |
if ((atr & STANDOUT) != (cur_atr & STANDOUT)) { |
964 |
if (atr & STANDOUT) { |
965 |
if (GoodStr(T_so) && GoodStr(T_se)) { |
966 |
(void) tputs(Str(T_so), 1, PUTPURE); |
967 |
cur_atr |= STANDOUT; |
968 |
} |
969 |
} |
970 |
else { |
971 |
if (GoodStr(T_se)) { |
972 |
(void) tputs(Str(T_se), 1, PUTPURE); |
973 |
cur_atr &= ~STANDOUT; |
974 |
} |
975 |
} |
976 |
} |
977 |
if ((atr & UNDER) != (cur_atr & UNDER)) { |
978 |
if (atr & UNDER) { |
979 |
if (GoodStr(T_us) && GoodStr(T_ue)) { |
980 |
(void) tputs(Str(T_us), 1, PUTPURE); |
981 |
cur_atr |= UNDER; |
982 |
} |
983 |
} |
984 |
else { |
985 |
if (GoodStr(T_ue)) { |
986 |
(void) tputs(Str(T_ue), 1, PUTPURE); |
987 |
cur_atr &= ~UNDER; |
988 |
} |
989 |
} |
990 |
} |
991 |
} |
992 |
} |
993 |
|
994 |
int highlighting = 0; |
995 |
|
996 |
void |
997 |
StartHighlight(void) |
998 |
{ |
999 |
(void) tputs(Str(T_mr), 1, PUTPURE); |
1000 |
highlighting = 1; |
1001 |
} |
1002 |
|
1003 |
void |
1004 |
StopHighlight(void) |
1005 |
{ |
1006 |
(void) tputs(Str(T_me), 1, PUTPURE); |
1007 |
highlighting = 0; |
1008 |
} |
1009 |
|
1010 |
/* PWP 6-27-88 -- if the tty driver thinks that we can tab, we ask termcap */ |
1011 |
int |
1012 |
CanWeTab(void) |
1013 |
{ |
1014 |
return (Val(T_pt)); |
1015 |
} |
1016 |
|
1017 |
/* move to line <where> (first line == 0) as efficiently as possible; */ |
1018 |
void |
1019 |
MoveToLine(int where) |
1020 |
{ |
1021 |
int del; |
1022 |
|
1023 |
if (where == CursorV) |
1024 |
return; |
1025 |
|
1026 |
if (where > TermV) { |
1027 |
#ifdef DEBUG_SCREEN |
1028 |
xprintf("MoveToLine: where is ridiculous: %d\r\n", where); |
1029 |
flush(); |
1030 |
#endif /* DEBUG_SCREEN */ |
1031 |
return; |
1032 |
} |
1033 |
|
1034 |
del = where - CursorV; |
1035 |
|
1036 |
if (del > 0) { |
1037 |
while (del > 0) { |
1038 |
if ((T_Margin & MARGIN_AUTO) && Display[CursorV][0] != '\0') { |
1039 |
size_t h; |
1040 |
|
1041 |
for (h = TermH - 1; h > 0 && Display[CursorV][h] == CHAR_DBWIDTH; |
1042 |
h--) |
1043 |
; |
1044 |
/* move without newline */ |
1045 |
MoveToChar(h); |
1046 |
so_write(&Display[CursorV][CursorH], TermH - CursorH); /* updates CursorH/V*/ |
1047 |
del--; |
1048 |
} |
1049 |
else { |
1050 |
if ((del > 1) && GoodStr(T_DO)) { |
1051 |
(void) tputs(tgoto(Str(T_DO), del, del), del, PUTPURE); |
1052 |
del = 0; |
1053 |
} |
1054 |
else { |
1055 |
for ( ; del > 0; del--) |
1056 |
(void) putraw('\n'); |
1057 |
CursorH = 0; /* because the \n will become \r\n */ |
1058 |
} |
1059 |
} |
1060 |
} |
1061 |
} |
1062 |
else { /* del < 0 */ |
1063 |
if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up))) |
1064 |
(void) tputs(tgoto(Str(T_UP), -del, -del), -del, PUTPURE); |
1065 |
else { |
1066 |
int i; |
1067 |
if (GoodStr(T_up)) |
1068 |
for (i = 0; i < -del; i++) |
1069 |
(void) tputs(Str(T_up), 1, PUTPURE); |
1070 |
} |
1071 |
} |
1072 |
CursorV = where; /* now where is here */ |
1073 |
} |
1074 |
|
1075 |
void |
1076 |
MoveToChar(int where) /* move to character position (where) */ |
1077 |
{ /* as efficiently as possible */ |
1078 |
int del; |
1079 |
|
1080 |
mc_again: |
1081 |
if (where == CursorH) |
1082 |
return; |
1083 |
|
1084 |
if (where >= TermH) { |
1085 |
#ifdef DEBUG_SCREEN |
1086 |
xprintf("MoveToChar: where is riduculous: %d\r\n", where); |
1087 |
flush(); |
1088 |
#endif /* DEBUG_SCREEN */ |
1089 |
return; |
1090 |
} |
1091 |
|
1092 |
if (!where) { /* if where is first column */ |
1093 |
(void) putraw('\r'); /* do a CR */ |
1094 |
CursorH = 0; |
1095 |
return; |
1096 |
} |
1097 |
|
1098 |
del = where - CursorH; |
1099 |
|
1100 |
if ((del < -4 || del > 4) && GoodStr(T_ch)) |
1101 |
/* go there directly */ |
1102 |
(void) tputs(tgoto(Str(T_ch), where, where), where, PUTPURE); |
1103 |
else { |
1104 |
int i; |
1105 |
if (del > 0) { /* moving forward */ |
1106 |
if ((del > 4) && GoodStr(T_RI)) |
1107 |
(void) tputs(tgoto(Str(T_RI), del, del), del, PUTPURE); |
1108 |
else { |
1109 |
/* if I can do tabs, use them */ |
1110 |
if (T_Tabs) { |
1111 |
if ((CursorH & 0370) != (where & ~0x7) |
1112 |
&& Display[CursorV][where & ~0x7] != CHAR_DBWIDTH) { |
1113 |
/* if not within tab stop */ |
1114 |
for (i = (CursorH & 0370); i < (where & ~0x7); i += 8) |
1115 |
(void) putraw('\t'); /* then tab over */ |
1116 |
CursorH = where & ~0x7; |
1117 |
/* Note: considering that we often want to go to |
1118 |
TermH - 1 for the wrapping, it would be nice to |
1119 |
optimize this case by tabbing to the last column |
1120 |
- but this doesn't work for all terminals! */ |
1121 |
} |
1122 |
} |
1123 |
/* it's usually cheaper to just write the chars, so we do. */ |
1124 |
|
1125 |
/* NOTE THAT so_write() WILL CHANGE CursorH!!! */ |
1126 |
so_write(&Display[CursorV][CursorH], where - CursorH); |
1127 |
|
1128 |
} |
1129 |
} |
1130 |
else { /* del < 0 := moving backward */ |
1131 |
if ((-del > 4) && GoodStr(T_LE)) |
1132 |
(void) tputs(tgoto(Str(T_LE), -del, -del), -del, PUTPURE); |
1133 |
else { /* can't go directly there */ |
1134 |
/* if the "cost" is greater than the "cost" from col 0 */ |
1135 |
if (T_Tabs ? (-del > ((where >> 3) + (where & 07))) |
1136 |
: (-del > where)) { |
1137 |
(void) putraw('\r'); /* do a CR */ |
1138 |
CursorH = 0; |
1139 |
goto mc_again; /* and try again */ |
1140 |
} |
1141 |
for (i = 0; i < -del; i++) |
1142 |
(void) putraw('\b'); |
1143 |
} |
1144 |
} |
1145 |
} |
1146 |
CursorH = where; /* now where is here */ |
1147 |
} |
1148 |
|
1149 |
void |
1150 |
so_write(Char *cp, int n) |
1151 |
{ |
1152 |
int cur_pos, prompt_len = 0, region_start = 0, region_end = 0; |
1153 |
|
1154 |
if (n <= 0) |
1155 |
return; /* catch bugs */ |
1156 |
|
1157 |
if (n > TermH) { |
1158 |
#ifdef DEBUG_SCREEN |
1159 |
xprintf("so_write: n is riduculous: %d\r\n", n); |
1160 |
flush(); |
1161 |
#endif /* DEBUG_SCREEN */ |
1162 |
return; |
1163 |
} |
1164 |
|
1165 |
if (adrof(STRhighlight)) { |
1166 |
/* find length of prompt */ |
1167 |
Char *promptc; |
1168 |
for (promptc = Prompt; *promptc; promptc++); |
1169 |
prompt_len = promptc - Prompt; |
1170 |
|
1171 |
/* find region start and end points */ |
1172 |
if (IncMatchLen) { |
1173 |
region_start = (Cursor - InputBuf) + prompt_len; |
1174 |
region_end = region_start + IncMatchLen; |
1175 |
} else if (MarkIsSet) { |
1176 |
region_start = (min(Cursor, Mark) - InputBuf) + prompt_len; |
1177 |
region_end = (max(Cursor, Mark) - InputBuf) + prompt_len; |
1178 |
} |
1179 |
} |
1180 |
|
1181 |
do { |
1182 |
if (adrof(STRhighlight)) { |
1183 |
cur_pos = CursorV * TermH + CursorH; |
1184 |
if (!highlighting && |
1185 |
cur_pos >= region_start && cur_pos < region_end) |
1186 |
StartHighlight(); |
1187 |
else if (highlighting && cur_pos >= region_end) |
1188 |
StopHighlight(); |
1189 |
|
1190 |
/* don't highlight over the cursor. the highlighting's reverse |
1191 |
* video would cancel it out. :P */ |
1192 |
if (highlighting && cur_pos == (Cursor - InputBuf) + prompt_len) |
1193 |
StopHighlight(); |
1194 |
} |
1195 |
|
1196 |
if (*cp != CHAR_DBWIDTH) { |
1197 |
if (*cp & LITERAL) { |
1198 |
Char *d; |
1199 |
#ifdef DEBUG_LITERAL |
1200 |
xprintf("so: litnum %d\r\n", (int)(*cp & ~LITERAL)); |
1201 |
#endif /* DEBUG_LITERAL */ |
1202 |
for (d = litptr + (*cp & ~LITERAL) * LIT_FACTOR; *d; d++) |
1203 |
(void) putwraw(*d); |
1204 |
} |
1205 |
else |
1206 |
(void) putwraw(*cp); |
1207 |
} |
1208 |
cp++; |
1209 |
CursorH++; |
1210 |
} while (--n); |
1211 |
|
1212 |
if (adrof(STRhighlight) && highlighting) |
1213 |
StopHighlight(); |
1214 |
|
1215 |
if (CursorH >= TermH) { /* wrap? */ |
1216 |
if (T_Margin & MARGIN_AUTO) { /* yes */ |
1217 |
CursorH = 0; |
1218 |
CursorV++; |
1219 |
if (T_Margin & MARGIN_MAGIC) { |
1220 |
/* force the wrap to avoid the "magic" situation */ |
1221 |
Char xc; |
1222 |
if ((xc = Display[CursorV][CursorH]) != '\0') { |
1223 |
so_write(&xc, 1); |
1224 |
while(Display[CursorV][CursorH] == CHAR_DBWIDTH) |
1225 |
CursorH++; |
1226 |
} |
1227 |
else { |
1228 |
(void) putraw(' '); |
1229 |
CursorH = 1; |
1230 |
} |
1231 |
} |
1232 |
} |
1233 |
else /* no wrap, but cursor stays on screen */ |
1234 |
CursorH = TermH - 1; |
1235 |
} |
1236 |
} |
1237 |
|
1238 |
|
1239 |
void |
1240 |
DeleteChars(int num) /* deletes <num> characters */ |
1241 |
{ |
1242 |
if (num <= 0) |
1243 |
return; |
1244 |
|
1245 |
if (!T_CanDel) { |
1246 |
#ifdef DEBUG_EDIT |
1247 |
xprintf(CGETS(7, 16, "ERROR: cannot delete\r\n")); |
1248 |
#endif /* DEBUG_EDIT */ |
1249 |
flush(); |
1250 |
return; |
1251 |
} |
1252 |
|
1253 |
if (num > TermH) { |
1254 |
#ifdef DEBUG_SCREEN |
1255 |
xprintf(CGETS(7, 17, "DeleteChars: num is riduculous: %d\r\n"), num); |
1256 |
flush(); |
1257 |
#endif /* DEBUG_SCREEN */ |
1258 |
return; |
1259 |
} |
1260 |
|
1261 |
if (GoodStr(T_DC)) /* if I have multiple delete */ |
1262 |
if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more expen. */ |
1263 |
(void) tputs(tgoto(Str(T_DC), num, num), num, PUTPURE); |
1264 |
return; |
1265 |
} |
1266 |
|
1267 |
if (GoodStr(T_dm)) /* if I have delete mode */ |
1268 |
(void) tputs(Str(T_dm), 1, PUTPURE); |
1269 |
|
1270 |
if (GoodStr(T_dc)) /* else do one at a time */ |
1271 |
while (num--) |
1272 |
(void) tputs(Str(T_dc), 1, PUTPURE); |
1273 |
|
1274 |
if (GoodStr(T_ed)) /* if I have delete mode */ |
1275 |
(void) tputs(Str(T_ed), 1, PUTPURE); |
1276 |
} |
1277 |
|
1278 |
/* Puts terminal in insert character mode, or inserts num characters in the |
1279 |
line */ |
1280 |
void |
1281 |
Insert_write(Char *cp, int num) |
1282 |
{ |
1283 |
if (num <= 0) |
1284 |
return; |
1285 |
if (!T_CanIns) { |
1286 |
#ifdef DEBUG_EDIT |
1287 |
xprintf(CGETS(7, 18, "ERROR: cannot insert\r\n")); |
1288 |
#endif /* DEBUG_EDIT */ |
1289 |
flush(); |
1290 |
return; |
1291 |
} |
1292 |
|
1293 |
if (num > TermH) { |
1294 |
#ifdef DEBUG_SCREEN |
1295 |
xprintf(CGETS(7, 19, "StartInsert: num is riduculous: %d\r\n"), num); |
1296 |
flush(); |
1297 |
#endif /* DEBUG_SCREEN */ |
1298 |
return; |
1299 |
} |
1300 |
|
1301 |
if (GoodStr(T_IC)) /* if I have multiple insert */ |
1302 |
if ((num > 1) || !GoodStr(T_ic)) { /* if ic would be more expen. */ |
1303 |
(void) tputs(tgoto(Str(T_IC), num, num), num, PUTPURE); |
1304 |
so_write(cp, num); /* this updates CursorH/V */ |
1305 |
return; |
1306 |
} |
1307 |
|
1308 |
if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */ |
1309 |
(void) tputs(Str(T_im), 1, PUTPURE); |
1310 |
|
1311 |
so_write(cp, num); /* this updates CursorH/V */ |
1312 |
|
1313 |
if (GoodStr(T_ip)) /* have to make num chars insert */ |
1314 |
(void) tputs(Str(T_ip), 1, PUTPURE); |
1315 |
|
1316 |
(void) tputs(Str(T_ei), 1, PUTPURE); |
1317 |
return; |
1318 |
} |
1319 |
|
1320 |
do { |
1321 |
if (GoodStr(T_ic)) /* have to make num chars insert */ |
1322 |
(void) tputs(Str(T_ic), 1, PUTPURE); /* insert a char */ |
1323 |
|
1324 |
so_write(cp++, 1); /* this updates CursorH/V */ |
1325 |
|
1326 |
if (GoodStr(T_ip)) /* have to make num chars insert */ |
1327 |
(void) tputs(Str(T_ip), 1, PUTPURE);/* pad the inserted char */ |
1328 |
|
1329 |
} while (--num); |
1330 |
|
1331 |
} |
1332 |
|
1333 |
/* clear to end of line. There are num characters to clear */ |
1334 |
void |
1335 |
ClearEOL(int num) |
1336 |
{ |
1337 |
int i; |
1338 |
|
1339 |
if (num <= 0) |
1340 |
return; |
1341 |
|
1342 |
if (T_CanCEOL && GoodStr(T_ce)) |
1343 |
(void) tputs(Str(T_ce), 1, PUTPURE); |
1344 |
else { |
1345 |
for (i = 0; i < num; i++) |
1346 |
(void) putraw(' '); |
1347 |
CursorH += num; /* have written num spaces */ |
1348 |
} |
1349 |
} |
1350 |
|
1351 |
void |
1352 |
ClearScreen(void) |
1353 |
{ /* clear the whole screen and home */ |
1354 |
if (GoodStr(T_cl)) |
1355 |
/* send the clear screen code */ |
1356 |
(void) tputs(Str(T_cl), Val(T_li), PUTPURE); |
1357 |
else if (GoodStr(T_ho) && GoodStr(T_cd)) { |
1358 |
(void) tputs(Str(T_ho), Val(T_li), PUTPURE); /* home */ |
1359 |
/* clear to bottom of screen */ |
1360 |
(void) tputs(Str(T_cd), Val(T_li), PUTPURE); |
1361 |
} |
1362 |
else { |
1363 |
(void) putraw('\r'); |
1364 |
(void) putraw('\n'); |
1365 |
} |
1366 |
} |
1367 |
|
1368 |
void |
1369 |
SoundBeep(void) |
1370 |
{ /* produce a sound */ |
1371 |
beep_cmd (); |
1372 |
if (adrof(STRnobeep)) |
1373 |
return; |
1374 |
|
1375 |
if (GoodStr(T_vb) && adrof(STRvisiblebell)) |
1376 |
(void) tputs(Str(T_vb), 1, PUTPURE); /* visible bell */ |
1377 |
else if (GoodStr(T_bl)) |
1378 |
/* what termcap says we should use */ |
1379 |
(void) tputs(Str(T_bl), 1, PUTPURE); |
1380 |
else |
1381 |
(void) putraw(CTL_ESC('\007')); /* an ASCII bell; ^G */ |
1382 |
} |
1383 |
|
1384 |
void |
1385 |
ClearToBottom(void) |
1386 |
{ /* clear to the bottom of the screen */ |
1387 |
if (GoodStr(T_cd)) |
1388 |
(void) tputs(Str(T_cd), Val(T_li), PUTPURE); |
1389 |
else if (GoodStr(T_ce)) |
1390 |
(void) tputs(Str(T_ce), Val(T_li), PUTPURE); |
1391 |
} |
1392 |
|
1393 |
void |
1394 |
GetTermCaps(void) |
1395 |
{ /* read in the needed terminal capabilites */ |
1396 |
int i; |
1397 |
const char *ptr; |
1398 |
char buf[TC_BUFSIZE]; |
1399 |
static char bp[TC_BUFSIZE]; |
1400 |
char *area; |
1401 |
struct termcapstr *t; |
1402 |
|
1403 |
|
1404 |
#ifdef SIG_WINDOW |
1405 |
sigset_t oset, set; |
1406 |
int lins, cols; |
1407 |
|
1408 |
/* don't want to confuse things here */ |
1409 |
sigemptyset(&set); |
1410 |
sigaddset(&set, SIG_WINDOW); |
1411 |
(void)sigprocmask(SIG_BLOCK, &set, &oset); |
1412 |
cleanup_push(&oset, sigprocmask_cleanup); |
1413 |
#endif /* SIG_WINDOW */ |
1414 |
area = buf; |
1415 |
|
1416 |
GotTermCaps = 1; |
1417 |
|
1418 |
setname("gettermcaps"); |
1419 |
ptr = getenv("TERM"); |
1420 |
|
1421 |
#ifdef apollo |
1422 |
/* |
1423 |
* If we are on a pad, we pretend that we are dumb. Otherwise the termcap |
1424 |
* library will put us in a weird screen mode, thinking that we are going |
1425 |
* to use curses |
1426 |
*/ |
1427 |
if (isapad()) |
1428 |
ptr = "dumb"; |
1429 |
#endif /* apollo */ |
1430 |
|
1431 |
if (!ptr || !ptr[0] || !strcmp(ptr, "wm") || !strcmp(ptr,"dmx")) |
1432 |
ptr = "dumb"; |
1433 |
|
1434 |
setzero(bp, TC_BUFSIZE); |
1435 |
|
1436 |
i = tgetent(bp, ptr); |
1437 |
if (i <= 0) { |
1438 |
if (i == -1) { |
1439 |
#if (SYSVREL == 0) || defined(IRIS3D) |
1440 |
xprintf(CGETS(7, 20, |
1441 |
"%s: The terminal database could not be opened.\n"), progname); |
1442 |
} |
1443 |
else if (i == 0) { |
1444 |
#endif /* SYSVREL */ |
1445 |
xprintf(CGETS(7, 21, |
1446 |
"%s: No entry for terminal type \"%s\"\n"), progname, |
1447 |
getenv("TERM")); |
1448 |
} |
1449 |
xprintf(CGETS(7, 22, "%s: using dumb terminal settings.\n"), progname); |
1450 |
Val(T_co) = 80; /* do a dumb terminal */ |
1451 |
Val(T_pt) = Val(T_km) = Val(T_li) = 0; |
1452 |
for (t = tstr; t->name != NULL; t++) |
1453 |
TCset(t, NULL); |
1454 |
} |
1455 |
else { |
1456 |
/* Can we tab */ |
1457 |
Val(T_pt) = tgetflag("pt") && !tgetflag("xt"); |
1458 |
/* do we have a meta? */ |
1459 |
Val(T_km) = (tgetflag("km") || tgetflag("MT")); |
1460 |
Val(T_am) = tgetflag("am"); |
1461 |
Val(T_xn) = tgetflag("xn"); |
1462 |
Val(T_co) = tgetnum("co"); |
1463 |
Val(T_li) = tgetnum("li"); |
1464 |
for (t = tstr; t->name != NULL; t++) |
1465 |
TCset(t, tgetstr(t->name, &area)); |
1466 |
} |
1467 |
if (Val(T_co) < 2) |
1468 |
Val(T_co) = 80; /* just in case */ |
1469 |
if (Val(T_li) < 1) |
1470 |
Val(T_li) = 24; |
1471 |
|
1472 |
T_Cols = (Char) Val(T_co); |
1473 |
T_Lines = (Char) Val(T_li); |
1474 |
if (T_Tabs) |
1475 |
T_Tabs = Val(T_pt); |
1476 |
T_HasMeta = Val(T_km); |
1477 |
T_Margin = Val(T_am) ? MARGIN_AUTO : 0; |
1478 |
T_Margin |= Val(T_xn) ? MARGIN_MAGIC : 0; |
1479 |
T_CanCEOL = GoodStr(T_ce); |
1480 |
T_CanDel = GoodStr(T_dc) || GoodStr(T_DC); |
1481 |
T_CanIns = GoodStr(T_im) || GoodStr(T_ic) || GoodStr(T_IC); |
1482 |
T_CanUP = GoodStr(T_up) || GoodStr(T_UP); |
1483 |
if (GoodStr(T_me) && GoodStr(T_ue)) |
1484 |
me_all = (strcmp(Str(T_me), Str(T_ue)) == 0); |
1485 |
else |
1486 |
me_all = 0; |
1487 |
if (GoodStr(T_me) && GoodStr(T_se)) |
1488 |
me_all |= (strcmp(Str(T_me), Str(T_se)) == 0); |
1489 |
|
1490 |
|
1491 |
#ifdef DEBUG_SCREEN |
1492 |
if (!T_CanUP) { |
1493 |
xprintf(CGETS(7, 23, "%s: WARNING: Your terminal cannot move up.\n", |
1494 |
progname)); |
1495 |
xprintf(CGETS(7, 24, "Editing may be odd for long lines.\n")); |
1496 |
} |
1497 |
if (!T_CanCEOL) |
1498 |
xprintf(CGETS(7, 25, "no clear EOL capability.\n")); |
1499 |
if (!T_CanDel) |
1500 |
xprintf(CGETS(7, 26, "no delete char capability.\n")); |
1501 |
if (!T_CanIns) |
1502 |
xprintf(CGETS(7, 27, "no insert char capability.\n")); |
1503 |
#endif /* DEBUG_SCREEN */ |
1504 |
|
1505 |
|
1506 |
|
1507 |
#ifdef SIG_WINDOW |
1508 |
(void) GetSize(&lins, &cols); /* get the correct window size */ |
1509 |
ChangeSize(lins, cols); |
1510 |
|
1511 |
cleanup_until(&oset); /* can change it again */ |
1512 |
#else /* SIG_WINDOW */ |
1513 |
ChangeSize(Val(T_li), Val(T_co)); |
1514 |
#endif /* SIG_WINDOW */ |
1515 |
|
1516 |
BindArrowKeys(); |
1517 |
} |
1518 |
|
1519 |
#ifdef SIG_WINDOW |
1520 |
/* GetSize(): |
1521 |
* Return the new window size in lines and cols, and |
1522 |
* true if the size was changed. This can fail if SHIN |
1523 |
* is not a tty, but it will work in most cases. |
1524 |
*/ |
1525 |
int |
1526 |
GetSize(int *lins, int *cols) |
1527 |
{ |
1528 |
*cols = Val(T_co); |
1529 |
*lins = Val(T_li); |
1530 |
|
1531 |
#ifdef TIOCGWINSZ |
1532 |
# define KNOWsize |
1533 |
# ifndef lint |
1534 |
{ |
1535 |
struct winsize ws; /* from 4.3 */ |
1536 |
|
1537 |
if (ioctl(SHIN, TIOCGWINSZ, (ioctl_t) &ws) != -1) { |
1538 |
if (ws.ws_col) |
1539 |
*cols = ws.ws_col; |
1540 |
if (ws.ws_row) |
1541 |
*lins = ws.ws_row; |
1542 |
} |
1543 |
} |
1544 |
# endif /* !lint */ |
1545 |
#else /* TIOCGWINSZ */ |
1546 |
# ifdef TIOCGSIZE |
1547 |
# define KNOWsize |
1548 |
{ |
1549 |
struct ttysize ts; /* from Sun */ |
1550 |
|
1551 |
if (ioctl(SHIN, TIOCGSIZE, (ioctl_t) &ts) != -1) { |
1552 |
if (ts.ts_cols) |
1553 |
*cols = ts.ts_cols; |
1554 |
if (ts.ts_lines) |
1555 |
*lins = ts.ts_lines; |
1556 |
} |
1557 |
} |
1558 |
# endif /* TIOCGSIZE */ |
1559 |
#endif /* TIOCGWINSZ */ |
1560 |
|
1561 |
return (Val(T_co) != *cols || Val(T_li) != *lins); |
1562 |
} |
1563 |
|
1564 |
#endif /* SIG_WINDOW */ |
1565 |
|
1566 |
#ifdef KNOWsize |
1567 |
static int |
1568 |
UpdateVal(const Char *tag, int value, Char *termcap, Char *backup) |
1569 |
{ |
1570 |
Char *ptr, *p; |
1571 |
if ((ptr = Strstr(termcap, tag)) == NULL) { |
1572 |
(void)Strcpy(backup, termcap); |
1573 |
return 0; |
1574 |
} else { |
1575 |
size_t len = (ptr - termcap) + Strlen(tag); |
1576 |
(void)Strncpy(backup, termcap, len); |
1577 |
backup[len] = '\0'; |
1578 |
p = Itoa(value, 0, 0); |
1579 |
(void) Strcat(backup + len, p); |
1580 |
xfree(p); |
1581 |
ptr = Strchr(ptr, ':'); |
1582 |
if (ptr) |
1583 |
(void) Strcat(backup, ptr); |
1584 |
return 1; |
1585 |
} |
1586 |
} |
1587 |
#endif |
1588 |
|
1589 |
void |
1590 |
ChangeSize(int lins, int cols) |
1591 |
{ |
1592 |
/* |
1593 |
* Just in case |
1594 |
*/ |
1595 |
Val(T_co) = (cols < 2) ? 80 : cols; |
1596 |
Val(T_li) = (lins < 1) ? 24 : lins; |
1597 |
|
1598 |
#ifdef KNOWsize |
1599 |
/* |
1600 |
* We want to affect the environment only when we have a valid |
1601 |
* setup, not when we get bad settings. Consider the following scenario: |
1602 |
* We just logged in, and we have not initialized the editor yet. |
1603 |
* We reset termcap with tset, and not $TERMCAP has the right |
1604 |
* terminal size. But since the editor is not initialized yet, and |
1605 |
* the kernel's notion of the terminal size might be wrong we arrive |
1606 |
* here with lines = columns = 0. If we reset the environment we lose |
1607 |
* our only chance to get the window size right. |
1608 |
*/ |
1609 |
if (Val(T_co) == cols && Val(T_li) == lins) { |
1610 |
Char *p; |
1611 |
char *tptr; |
1612 |
|
1613 |
if (getenv("COLUMNS")) { |
1614 |
p = Itoa(Val(T_co), 0, 0); |
1615 |
cleanup_push(p, xfree); |
1616 |
tsetenv(STRCOLUMNS, p); |
1617 |
cleanup_until(p); |
1618 |
} |
1619 |
|
1620 |
if (getenv("LINES")) { |
1621 |
p = Itoa(Val(T_li), 0, 0); |
1622 |
cleanup_push(p, xfree); |
1623 |
tsetenv(STRLINES, p); |
1624 |
cleanup_until(p); |
1625 |
} |
1626 |
|
1627 |
if ((tptr = getenv("TERMCAP")) != NULL) { |
1628 |
/* Leave 64 characters slop in case we enlarge the termcap string */ |
1629 |
Char termcap[TC_BUFSIZE+64], backup[TC_BUFSIZE+64], *ptr; |
1630 |
int changed; |
1631 |
|
1632 |
ptr = str2short(tptr); |
1633 |
(void) Strncpy(termcap, ptr, TC_BUFSIZE); |
1634 |
termcap[TC_BUFSIZE-1] = '\0'; |
1635 |
|
1636 |
changed = UpdateVal(STRco, Val(T_co), termcap, backup); |
1637 |
changed |= UpdateVal(STRli, Val(T_li), termcap, backup); |
1638 |
|
1639 |
if (changed) { |
1640 |
/* |
1641 |
* Chop the termcap string at TC_BUFSIZE-1 characters to avoid |
1642 |
* core-dumps in the termcap routines |
1643 |
*/ |
1644 |
termcap[TC_BUFSIZE - 1] = '\0'; |
1645 |
tsetenv(STRTERMCAP, termcap); |
1646 |
} |
1647 |
} |
1648 |
} |
1649 |
#endif /* KNOWsize */ |
1650 |
|
1651 |
ReBufferDisplay(); /* re-make display buffers */ |
1652 |
ClearDisp(); |
1653 |
} |