1 /*
2  * termcap.c - termcap manipulation through curses
3  *
4  * This file is part of zsh, the Z shell.
5  *
6  * Copyright (c) 1992-1997 Paul Falstad
7  * All rights reserved.
8  *
9  * Permission is hereby granted, without written agreement and without
10  * license or royalty fees, to use, copy, modify, and distribute this
11  * software and to distribute modified versions of this software for any
12  * purpose, provided that the above copyright notice and the following
13  * two paragraphs appear in all copies of this software.
14  *
15  * In no event shall Paul Falstad or the Zsh Development Group be liable
16  * to any party for direct, indirect, special, incidental, or consequential
17  * damages arising out of the use of this software and its documentation,
18  * even if Paul Falstad and the Zsh Development Group have been advised of
19  * the possibility of such damage.
20  *
21  * Paul Falstad and the Zsh Development Group specifically disclaim any
22  * warranties, including, but not limited to, the implied warranties of
23  * merchantability and fitness for a particular purpose.  The software
24  * provided hereunder is on an "as is" basis, and Paul Falstad and the
25  * Zsh Development Group have no obligation to provide maintenance,
26  * support, updates, enhancements, or modifications.
27  *
28  */
29 
30 /*
31  * We need to include the zsh headers later to avoid clashes with
32  * the definitions on some systems, however we need the configuration
33  * file to decide whether we should avoid curses.h, which clashes
34  * with several zsh constants on some systems (e.g. SunOS 4).
35  */
36 #include "../../config.h"
37 
38 #include "termcap.mdh"
39 #include "termcap.pro"
40 
41 /**/
42 #ifdef HAVE_TGETENT
43 
44 #ifndef HAVE_BOOLCODES
45 static char *boolcodes[] = {
46     "bw", "am", "ut", "cc", "xs", "YA", "YF", "YB", "xt", "xn", "eo",
47     "gn", "hc", "HC", "km", "YC", "hs", "hl", "in", "YG", "da", "db",
48     "mi", "ms", "nx", "xb", "NP", "ND", "NR", "os", "5i", "YD", "YE",
49     "es", "hz", "ul", "xo", NULL};
50 #endif
51 
52 /**/
53 static int
ztgetflag(char * s)54 ztgetflag(char *s)
55 {
56     char **b;
57 
58     /* ncurses can tell if an existing boolean capability is *
59      * off, but other curses variants can't, so we fudge it. *
60      * This feature of ncurses appears to have gone away as  *
61      * of NCURSES_MAJOR_VERSION == 5, so don't rely on it.   */
62     switch (tgetflag(s)) {
63     case -1:
64 	break;
65     case 0:
66 	for (b = (char **)boolcodes; *b; ++b)
67 	    if (s[0] == (*b)[0] && s[1] == (*b)[1])
68 		return 0;
69 	break;
70     default:
71 	return 1;
72     }
73     return -1;
74 }
75 
76 /* echotc: output a termcap */
77 
78 /**/
79 static int
bin_echotc(char * name,char ** argv,UNUSED (Options ops),UNUSED (int func))80 bin_echotc(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
81 {
82     char *s, buf[2048], *t, *u;
83     int num, argct;
84 
85     s = *argv++;
86     if (termflags & TERM_BAD)
87 	return 1;
88     if ((termflags & TERM_UNKNOWN) && (isset(INTERACTIVE) || !init_term()))
89 	return 1;
90     /* if the specified termcap has a numeric value, display it */
91     if ((num = tgetnum(s)) != -1) {
92 	printf("%d\n", num);
93 	return 0;
94     }
95     /* if the specified termcap is boolean, and set, say so  */
96     switch (ztgetflag(s)) {
97     case -1:
98 	break;
99     case 0:
100 	puts("no");
101 	return 0;
102     default:
103 	puts("yes");
104 	return 0;
105     }
106     /* get a string-type capability */
107     u = buf;
108     t = tgetstr(s, &u);
109     if (t == (char *)-1 || !t || !*t) {
110 	/* capability doesn't exist, or (if boolean) is off */
111 	zwarnnam(name, "no such capability: %s", s);
112 	return 1;
113     }
114     /* count the number of arguments required */
115     for (argct = 0, u = t; *u; u++)
116 	if (*u == '%') {
117 	    if (u++, (*u == 'd' || *u == '2' || *u == '3' || *u == '.' ||
118 		      *u == '+'))
119 		argct++;
120 	}
121     /* check that the number of arguments provided is correct */
122     if (arrlen(argv) != argct) {
123 	zwarnnam(name, (arrlen(argv) < argct) ? "not enough arguments" :
124 		 "too many arguments");
125 	return 1;
126     }
127     /* output string, through the proper termcap functions */
128     if (!argct)
129 	tputs(t, 1, putraw);
130     else {
131 	/* This assumes arguments of <lines> <columns> for cap 'cm' */
132 	num = (argv[1]) ? atoi(argv[1]) : atoi(*argv);
133 	tputs(tgoto(t, num, atoi(*argv)), 1, putraw);
134     }
135     return 0;
136 }
137 
138 static struct builtin bintab[] = {
139     BUILTIN("echotc", 0, bin_echotc, 1, -1, 0, NULL, NULL),
140 };
141 
142 /**/
143 static HashNode
gettermcap(UNUSED (HashTable ht),const char * name)144 gettermcap(UNUSED(HashTable ht), const char *name)
145 {
146     int len, num;
147     char *tcstr, buf[2048], *u, *nameu;
148     Param pm = NULL;
149 
150     /* This depends on the termcap stuff in init.c */
151     if (termflags & TERM_BAD)
152 	return NULL;
153     if ((termflags & TERM_UNKNOWN) && (isset(INTERACTIVE) || !init_term()))
154 	return NULL;
155 
156 
157     nameu = dupstring(name);
158     unmetafy(nameu, &len);
159 
160     pm = (Param) hcalloc(sizeof(struct param));
161     pm->node.nam = nameu;
162     pm->node.flags = PM_READONLY;
163     u = buf;
164 
165     /* logic in the following cascade copied from echotc, above */
166 
167     if ((num = tgetnum(nameu)) != -1) {
168 	pm->gsu.i = &nullsetinteger_gsu;
169 	pm->u.val = num;
170 	pm->node.flags |= PM_INTEGER;
171 	return &pm->node;
172     }
173 
174     pm->gsu.s = &nullsetscalar_gsu;
175     switch (ztgetflag(nameu)) {
176     case -1:
177 	break;
178     case 0:
179 	pm->u.str = dupstring("no");
180 	pm->node.flags |= PM_SCALAR;
181 	return &pm->node;
182     default:
183 	pm->u.str = dupstring("yes");
184 	pm->node.flags |= PM_SCALAR;
185 	return &pm->node;
186     }
187     if ((tcstr = tgetstr(nameu, &u)) != NULL && tcstr != (char *)-1) {
188 	pm->u.str = dupstring(tcstr);
189 	pm->node.flags |= PM_SCALAR;
190     } else {
191 	/* zwarn("no such capability: %s", name); */
192 	pm->u.str = dupstring("");
193 	pm->node.flags |= PM_UNSET;
194     }
195     return &pm->node;
196 }
197 
198 /**/
199 static void
scantermcap(UNUSED (HashTable ht),ScanFunc func,int flags)200 scantermcap(UNUSED(HashTable ht), ScanFunc func, int flags)
201 {
202     Param pm = NULL;
203     int num;
204     char **capcode, *tcstr, buf[2048], *u;
205 
206 #ifndef HAVE_NUMCODES
207     static char *numcodes[] = {
208 	"co", "it", "lh", "lw", "li", "lm", "sg", "ma", "Co", "pa", "MW",
209 	"NC", "Nl", "pb", "vt", "ws", "Yo", "Yp", "Ya", "BT", "Yc", "Yb",
210 	"Yd", "Ye", "Yf", "Yg", "Yh", "Yi", "Yk", "Yj", "Yl", "Ym", "Yn",
211 	NULL};
212 #endif
213 
214 #ifndef HAVE_STRCODES
215     static char *zstrcodes[] = {
216 	"ac", "bt", "bl", "cr", "ZA", "ZB", "ZC", "ZD", "cs", "rP", "ct",
217 	"MC", "cl", "cb", "ce", "cd", "ch", "CC", "CW", "cm", "do", "ho",
218 	"vi", "le", "CM", "ve", "nd", "ll", "up", "vs", "ZE", "dc", "dl",
219 	"DI", "ds", "DK", "hd", "eA", "as", "SA", "mb", "md", "ti", "dm",
220 	"mh", "ZF", "ZG", "im", "ZH", "ZI", "ZJ", "ZK", "ZL", "mp", "mr",
221 	"mk", "ZM", "so", "ZN", "ZO", "us", "ZP", "SX", "ec", "ae", "RA",
222 	"me", "te", "ed", "ZQ", "ei", "ZR", "ZS", "ZT", "ZU", "se", "ZV",
223 	"ZW", "ue", "ZX", "RX", "PA", "fh", "vb", "ff", "fs", "WG", "HU",
224 	"i1", "is", "i3", "if", "iP", "Ic", "Ip", "ic", "al", "ip", "K1",
225 	"K3", "K2", "kb", "@1", "kB", "K4", "K5", "@2", "ka", "kC", "@3",
226 	"@4", "@5", "@6", "kt", "kD", "kL", "kd", "kM", "@7", "@8", "kE",
227 	"kS", "@9", "k0", "k1", "k;", "F1", "F2", "F3", "F4", "F5", "F6",
228 	"F7", "F8", "F9", "k2", "FA", "FB", "FC", "FD", "FE", "FF", "FG",
229 	"FH", "FI", "FJ", "k3", "FK", "FL", "FM", "FN", "FO", "FP", "FQ",
230 	"FR", "FS", "FT", "k4", "FU", "FV", "FW", "FX", "FY", "FZ", "Fa",
231 	"Fb", "Fc", "Fd", "k5", "Fe", "Ff", "Fg", "Fh", "Fi", "Fj", "Fk",
232 	"Fl", "Fm", "Fn", "k6", "Fo", "Fp", "Fq", "Fr", "k7", "k8", "k9",
233 	"@0", "%1", "kh", "kI", "kA", "kl", "kH", "%2", "%3", "%4", "%5",
234 	"kN", "%6", "%7", "kP", "%8", "%9", "%0", "&1", "&2", "&3", "&4",
235 	"&5", "kr", "&6", "&9", "&0", "*1", "*2", "*3", "*4", "*5", "*6",
236 	"*7", "*8", "*9", "kF", "*0", "#1", "#2", "#3", "#4", "%a", "%b",
237 	"%c", "%d", "%e", "%f", "kR", "%g", "%h", "%i", "%j", "!1", "!2",
238 	"kT", "!3", "&7", "&8", "ku", "ke", "ks", "l0", "l1", "la", "l2",
239 	"l3", "l4", "l5", "l6", "l7", "l8", "l9", "Lf", "LF", "LO", "mo",
240 	"mm", "ZY", "ZZ", "Za", "Zb", "Zc", "Zd", "nw", "Ze", "oc", "op",
241 	"pc", "DC", "DL", "DO", "Zf", "IC", "SF", "AL", "LE", "Zg", "RI",
242 	"Zh", "SR", "UP", "Zi", "pk", "pl", "px", "pn", "ps", "pO", "pf",
243 	"po", "PU", "QD", "RC", "rp", "RF", "r1", "r2", "r3", "rf", "rc",
244 	"cv", "sc", "sf", "sr", "Zj", "sa", "Sb", "Zk", "Zl", "SC", "sp",
245 	"Sf", "ML", "Zm", "MR", "Zn", "st", "Zo", "Zp", "wi", "Zq", "Zr",
246 	"Zs", "Zt", "Zu", "Zv", "ta", "Zw", "ts", "TO", "uc", "hu", "u0",
247 	"u1", "u2", "u3", "u4", "u5", "u6", "u7", "u8", "u9", "WA", "XF",
248 	"XN", "Zx", "S8", "Yv", "Zz", "Xy", "Zy", "ci", "Yw", "Yx", "dv",
249 	"S1", "Yy", "S2", "S4", "S3", "S5", "Gm", "Km", "Mi", "S6", "xl",
250 	"RQ", "S7", "s0", "s1", "s2", "s3", "AB", "AF", "Yz", "ML", "YZ",
251 	"MT", "Xh", "Xl", "Xo", "Xr", "Xt", "Xv", "sA", "sL", NULL};
252 #endif
253 
254     pm = (Param) hcalloc(sizeof(struct param));
255     u = buf;
256 
257     pm->node.flags = PM_READONLY | PM_SCALAR;
258     pm->gsu.s = &nullsetscalar_gsu;
259 
260     for (capcode = (char **)boolcodes; *capcode; capcode++) {
261 	if ((num = ztgetflag(*capcode)) != -1) {
262 	    pm->u.str = num ? dupstring("yes") : dupstring("no");
263 	    pm->node.nam = dupstring(*capcode);
264 	    func(&pm->node, flags);
265 	}
266     }
267 
268     pm->node.flags = PM_READONLY | PM_INTEGER;
269     pm->gsu.i = &nullsetinteger_gsu;
270 
271     for (capcode = (char **)numcodes; *capcode; capcode++) {
272 	if ((num = tgetnum(*capcode)) != -1) {
273 	    pm->u.val = num;
274 	    pm->node.nam = dupstring(*capcode);
275 	    func(&pm->node, flags);
276 	}
277     }
278 
279     pm->node.flags = PM_READONLY | PM_SCALAR;
280     pm->gsu.s = &nullsetscalar_gsu;
281 
282     for (capcode = (char **)
283 #ifdef HAVE_STRCODES
284 	     strcodes
285 #else
286 	     zstrcodes
287 #endif
288 	     ; *capcode; capcode++) {
289 	if ((tcstr = (char *)tgetstr(*capcode,&u)) != NULL &&
290 	    tcstr != (char *)-1) {
291 	    pm->u.str = dupstring(tcstr);
292 	    pm->node.nam = dupstring(*capcode);
293 	    func(&pm->node, flags);
294 	}
295     }
296 }
297 
298 static struct paramdef partab[] = {
299     SPECIALPMDEF("termcap", PM_READONLY, NULL, gettermcap, scantermcap)
300 };
301 
302 /**/
303 #endif /* HAVE_TGETENT */
304 
305 static struct features module_features = {
306 #ifdef HAVE_TGETENT
307     bintab, sizeof(bintab)/sizeof(*bintab),
308 #else
309     NULL, 0,
310 #endif
311     NULL, 0,
312     NULL, 0,
313 #ifdef HAVE_TGETENT
314     partab, sizeof(partab)/sizeof(*partab),
315 #else
316     NULL, 0,
317 #endif
318     0
319 };
320 
321 /**/
322 int
setup_(UNUSED (Module m))323 setup_(UNUSED(Module m))
324 {
325     return 0;
326 }
327 
328 /**/
329 int
features_(Module m,char *** features)330 features_(Module m, char ***features)
331 {
332     *features = featuresarray(m, &module_features);
333     return 0;
334 }
335 
336 /**/
337 int
enables_(Module m,int ** enables)338 enables_(Module m, int **enables)
339 {
340     return handlefeatures(m, &module_features, enables);
341 }
342 
343 /**/
344 int
boot_(UNUSED (Module m))345 boot_(UNUSED(Module m))
346 {
347 #ifdef HAVE_TGETENT
348     zsetupterm();
349 #endif
350     return  0;
351 }
352 
353 /**/
354 int
cleanup_(Module m)355 cleanup_(Module m)
356 {
357 #ifdef HAVE_TGETENT
358     zdeleteterm();
359 #endif
360     return setfeatureenables(m, &module_features, NULL);
361 }
362 
363 /**/
364 int
finish_(UNUSED (Module m))365 finish_(UNUSED(Module m))
366 {
367     return 0;
368 }
369