1 #include "config.h"
2 
3 #include <slang.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <sys/signal.h>
8 #include <sys/time.h>
9 #include <sys/types.h>
10 #include <termios.h>
11 #include <unistd.h>
12 #include <wchar.h>
13 
14 #ifdef HAVE_ALLOCA_H
15 #include <alloca.h>
16 #endif
17 
18 #include "newt.h"
19 #include "newt_pr.h"
20 
21 struct Window {
22     int height, width, top, left;
23     SLsmg_Char_Type * buffer;
24     char * title;
25 };
26 
27 struct keymap {
28     char * str;
29     int code;
30     char * tc;
31 };
32 
33 static struct Window windowStack[20];
34 static struct Window * currentWindow = NULL;
35 
36 static char * helplineStack[20];
37 static char ** currentHelpline = NULL;
38 
39 static int cursorRow, cursorCol;
40 static int cursorOn = 1;
41 static int noFlowCtrl = 0;
42 static int trashScreen = 0;
43 extern int needResize;
44 
45 static const char * const defaultHelpLine =
46 "  <Tab>/<Alt-Tab> between elements   |  <Space> selects   |  <F12> next screen"
47 ;
48 
49 const struct newtColors newtDefaultColorPalette = {
50 	"white", "blue", 			/* root fg, bg */
51 	"black", "lightgray",			/* border fg, bg */
52 	"black", "lightgray",			/* window fg, bg */
53 	"white", "black",			/* shadow fg, bg */
54 	"red", "lightgray",			/* title fg, bg */
55 	"lightgray", "red",			/* button fg, bg */
56 	"red", "lightgray",			/* active button fg, bg */
57 	"lightgray", "blue",			/* checkbox fg, bg */
58 	"lightgray", "red",			/* active checkbox fg, bg */
59 	"lightgray", "blue",			/* entry box fg, bg */
60 	"blue", "lightgray",			/* label fg, bg */
61 	"black", "lightgray",			/* listbox fg, bg */
62 	"lightgray", "blue",			/* active listbox fg, bg */
63 	"black", "lightgray",			/* textbox fg, bg */
64 	"lightgray", "red",			/* active textbox fg, bg */
65 	"white", "blue",			/* help line */
66 	"lightgray", "blue",			/* root text */
67 	"blue",					/* scale full */
68 	"red",					/* scale empty */
69 	"blue", "lightgray",			/* disabled entry fg, bg */
70 	"black", "lightgray",			/* compact button fg, bg */
71 	"lightgray", "red",			/* active & sel listbox */
72 	"black", "brown"			/* selected listbox */
73 };
74 
75 static const struct keymap keymap[] = {
76 	{ "\033OA", 		NEWT_KEY_UP, 		"ku" },
77 	{ "\020", 		NEWT_KEY_UP, 		NULL }, /* emacs ^P */
78 	{ "\033OB", 		NEWT_KEY_DOWN, 		"kd" },
79 	{ "\016", 		NEWT_KEY_DOWN, 		NULL }, /* emacs ^N */
80 	{ "\033OC", 		NEWT_KEY_RIGHT, 	"kr" },
81 	{ "\006", 		NEWT_KEY_RIGHT, 	NULL }, /* emacs ^F */
82 	{ "\033OD", 		NEWT_KEY_LEFT, 		"kl" },
83 	{ "\002", 		NEWT_KEY_LEFT, 		NULL }, /* emacs ^B */
84 	{ "\033OH",		NEWT_KEY_HOME, 		"kh" },
85 	{ "\033[1~",		NEWT_KEY_HOME, 		NULL },
86 	{ "\001",		NEWT_KEY_HOME, 		NULL }, /* emacs ^A */
87 	{ "\033Ow",		NEWT_KEY_END, 		"kH" },
88         { "\033[4~",		NEWT_KEY_END, 		"@7" },
89         { "\005",		NEWT_KEY_END, 		NULL }, /* emacs ^E */
90 
91 	{ "\033[3~",		NEWT_KEY_DELETE,	"kD" },
92 	{ "\004",		NEWT_KEY_DELETE,	NULL }, /* emacs ^D */
93 	{ "\033[2~", 		NEWT_KEY_INSERT,        "kI" },
94 
95 	{ "\033\t",		NEWT_KEY_UNTAB,		"kB" },
96 	{ "\033[Z",		NEWT_KEY_UNTAB,		NULL },
97 
98 	{ "\033[5~",		NEWT_KEY_PGUP,		"kP" },
99 	{ "\033[6~",		NEWT_KEY_PGDN,		"kN" },
100 	{ "\033V",		NEWT_KEY_PGUP, 		NULL },
101 	{ "\033v",		NEWT_KEY_PGUP, 		NULL },
102 	{ "\033[G",             NEWT_KEY_PGDN,          NULL },
103 	{ "\033[I",             NEWT_KEY_PGUP,          NULL },
104         { "\026",		NEWT_KEY_PGDN,		NULL },
105 
106 	{ "\033[[A",		NEWT_KEY_F1,		NULL },
107 	{ "\033[[B",		NEWT_KEY_F2,		NULL },
108 	{ "\033[[C",		NEWT_KEY_F3,		NULL },
109 	{ "\033[[D",		NEWT_KEY_F4,		NULL },
110 	{ "\033[[E",		NEWT_KEY_F5,		NULL },
111 
112 	{ "\033OP",		NEWT_KEY_F1,		NULL },
113 	{ "\033OQ",		NEWT_KEY_F2,		NULL },
114 	{ "\033OR",		NEWT_KEY_F3,		NULL },
115 	{ "\033OS",		NEWT_KEY_F4,		NULL },
116 
117 	{ "\033[11~",		NEWT_KEY_F1,		"k1" },
118 	{ "\033[12~",		NEWT_KEY_F2,		"k2" },
119 	{ "\033[13~",		NEWT_KEY_F3,		"k3" },
120 	{ "\033[14~",		NEWT_KEY_F4,		"k4" },
121 	{ "\033[15~",		NEWT_KEY_F5,		"k5" },
122 	{ "\033[17~",		NEWT_KEY_F6,		"k6" },
123 	{ "\033[18~",		NEWT_KEY_F7,		"k7" },
124 	{ "\033[19~",		NEWT_KEY_F8,		"k8" },
125 	{ "\033[20~",		NEWT_KEY_F9,            "k9" },
126 	{ "\033[21~",		NEWT_KEY_F10,		"k;" },
127 	{ "\033[23~",		NEWT_KEY_F11,		"F1" },
128 	{ "\033[24~",		NEWT_KEY_F12,		"F2" },
129 	{ "\033",		NEWT_KEY_ESCAPE,	"@2" },
130         { "\033",		NEWT_KEY_ESCAPE,	"@9" },
131 
132         { "\177",		NEWT_KEY_BKSPC,		NULL },
133         { "\010",		NEWT_KEY_BKSPC,		NULL },
134 
135 	{ 0 },	/* LEAVE this one */
136 };
137 static void initKeymap();
138 static void freeKeymap();
139 
140 static const char ident[] = // ident friendly
141     "$Version: Newt windowing library v" VERSION " $"
142     "$Copyright: (C) 1996-2003 Red Hat, Inc. Written by Erik Troan $"
143     "$License: Lesser GNU Public License. $";
144 
145 static newtSuspendCallback suspendCallback = NULL;
146 static void * suspendCallbackData = NULL;
147 
newtSetSuspendCallback(newtSuspendCallback cb,void * data)148 void newtSetSuspendCallback(newtSuspendCallback cb, void * data) {
149     suspendCallback = cb;
150     suspendCallbackData = data;
151 }
152 
handleSigwinch(int signum)153 static void handleSigwinch(int signum) {
154     needResize = 1;
155 }
156 
getkeyInterruptHook(void)157 static int getkeyInterruptHook(void) {
158     return -1;
159 }
160 
_newt_wstrlen(const char * str,int len)161 int _newt_wstrlen(const char *str, int len) {
162 	mbstate_t ps;
163 	wchar_t tmp;
164 	int nchars = 0;
165 
166 	if (!str) return 0;
167 	if (!len) return 0;
168 	if (len < 0) len = strlen(str);
169 	memset(&ps,0,sizeof(mbstate_t));
170 	while (len > 0) {
171 		int x,y;
172 
173 		x = mbrtowc(&tmp,str,len,&ps);
174 		if (x >0) {
175 		    	str += x;
176 			len -= x;
177 			y = wcwidth(tmp);
178 			if (y>0)
179 			  nchars+=y;
180 		} else break;
181 	}
182 	return nchars;
183 }
184 
185 /** Trim a string to fit
186  * @param title - string. NULL will be inserted if necessary
187  * @param chrs  - available space. (character cells)
188  */
trim_string(char * title,int chrs)189 void trim_string(char *title, int chrs)
190 {
191 	char *p = title;
192 	int ln;
193 	int x = 0,y = 0;
194 	wchar_t tmp;
195 	mbstate_t ps;
196 
197 	memset(&ps, 0, sizeof(ps));
198 	ln = strlen(title);
199 
200 	while (*p) {
201 		x = mbrtowc(&tmp, p, ln, &ps);
202 		if (x < 0) { // error
203 			*p = '\0';
204 			return;
205 		}
206 		y = wcwidth(tmp);
207 		if (y > chrs) {
208 			*p = '\0';
209 			return;
210 		} else {
211 			p += x;
212 			ln -= x;
213 			chrs -= y;
214 		}
215 	}
216 }
217 
getkey()218 static int getkey() {
219     int c;
220 
221     while ((c = SLang_getkey()) == '\xC') { /* if Ctrl-L redraw whole screen */
222         SLsmg_touch_lines(0, SLtt_Screen_Rows);
223         SLsmg_refresh();
224     }
225     return c;
226 
227 }
228 
updateColorset(char * fg,char * bg,char ** fg_p,char ** bg_p)229 static void updateColorset(char *fg, char *bg, char **fg_p, char **bg_p)
230 {
231     if (*fg && fg_p)
232 	*fg_p = fg;
233     if (*bg && bg_p)
234 	*bg_p = bg;
235 }
236 
237 /* parse color specifications (e.g. root=,black:border=red,blue)
238  * and update the palette
239  */
parseColors(char * s,struct newtColors * palette)240 static void parseColors(char *s, struct newtColors *palette)
241 {
242     char *name, *str, *fg, *bg;
243 
244     for (str = s; (s = strtok(str, ";:\n\r\t ")); str = NULL) {
245 	name = s;
246 	if (!(s = strchr(s, '=')) || !*s)
247 	    continue;
248 	*s = '\0';
249 	fg = ++s;
250 	if (!(s = strchr(s, ',')) || !*s)
251 	    continue;
252 	*s = '\0';
253 	bg = ++s;
254 
255 	if (!strcmp(name, "root"))
256 	    updateColorset(fg, bg, &palette->rootFg, &palette->rootBg);
257 	else if (!strcmp(name, "border"))
258 	    updateColorset(fg, bg, &palette->borderFg, &palette->borderBg);
259 	else if (!strcmp(name, "window"))
260 	    updateColorset(fg, bg, &palette->windowFg, &palette->windowBg);
261 	else if (!strcmp(name, "shadow"))
262 	    updateColorset(fg, bg, &palette->shadowFg, &palette->shadowBg);
263 	else if (!strcmp(name, "title"))
264 	    updateColorset(fg, bg, &palette->titleFg, &palette->titleBg);
265 	else if (!strcmp(name, "button"))
266 	    updateColorset(fg, bg, &palette->buttonFg, &palette->buttonBg);
267 	else if (!strcmp(name, "actbutton"))
268 	    updateColorset(fg, bg, &palette->actButtonFg, &palette->actButtonBg);
269 	else if (!strcmp(name, "checkbox"))
270 	    updateColorset(fg, bg, &palette->checkboxFg, &palette->checkboxBg);
271 	else if (!strcmp(name, "actcheckbox"))
272 	    updateColorset(fg, bg, &palette->actCheckboxFg, &palette->actCheckboxBg);
273 	else if (!strcmp(name, "entry"))
274 	    updateColorset(fg, bg, &palette->entryFg, &palette->entryBg);
275 	else if (!strcmp(name, "label"))
276 	    updateColorset(fg, bg, &palette->labelFg, &palette->labelBg);
277 	else if (!strcmp(name, "listbox"))
278 	    updateColorset(fg, bg, &palette->listboxFg, &palette->listboxBg);
279 	else if (!strcmp(name, "actlistbox"))
280 	    updateColorset(fg, bg, &palette->actListboxFg, &palette->actListboxBg);
281 	else if (!strcmp(name, "textbox"))
282 	    updateColorset(fg, bg, &palette->textboxFg, &palette->textboxBg);
283 	else if (!strcmp(name, "acttextbox"))
284 	    updateColorset(fg, bg, &palette->actTextboxFg, &palette->actTextboxBg);
285 	else if (!strcmp(name, "helpline"))
286 	    updateColorset(fg, bg, &palette->helpLineFg, &palette->helpLineBg);
287 	else if (!strcmp(name, "roottext"))
288 	    updateColorset(fg, bg, &palette->rootTextFg, &palette->rootTextBg);
289 	else if (!strcmp(name, "emptyscale"))
290 	    updateColorset(fg, bg, NULL, &palette->emptyScale);
291 	else if (!strcmp(name, "fullscale"))
292 	    updateColorset(fg, bg, NULL, &palette->fullScale);
293 	else if (!strcmp(name, "disentry"))
294 	    updateColorset(fg, bg, &palette->disabledEntryFg, &palette->disabledEntryBg);
295 	else if (!strcmp(name, "compactbutton"))
296 	    updateColorset(fg, bg, &palette->compactButtonFg, &palette->compactButtonBg);
297 	else if (!strcmp(name, "actsellistbox"))
298 	    updateColorset(fg, bg, &palette->actSelListboxFg, &palette->actSelListboxBg);
299 	else if (!strcmp(name, "sellistbox"))
300 	    updateColorset(fg, bg, &palette->selListboxFg, &palette->selListboxBg);
301     }
302 }
303 
initColors(void)304 static void initColors(void)
305 {
306     char *colors, *colors_file, buf[16384];
307     FILE *f;
308     struct newtColors palette;
309 
310     palette = newtDefaultColorPalette;
311 
312     colors_file = getenv("NEWT_COLORS_FILE");
313 #ifdef NEWT_COLORS_FILE
314     if (colors_file == NULL)
315 	colors_file = NEWT_COLORS_FILE;
316 #endif
317 
318     if ((colors = getenv("NEWT_COLORS"))) {
319 	strncpy(buf, colors, sizeof (buf));
320 	buf[sizeof (buf) - 1] = '\0';
321 	parseColors(buf, &palette);
322     } else if (colors_file && *colors_file && (f = fopen(colors_file, "r"))) {
323 	size_t r;
324 	if ((r = fread(buf, 1, sizeof (buf) - 1, f)) > 0) {
325 	    buf[r] = '\0';
326 	    parseColors(buf, &palette);
327 	}
328 	fclose(f);
329     }
330 
331     newtSetColors(palette);
332 }
333 
newtFlushInput(void)334 void newtFlushInput(void) {
335     while (SLang_input_pending(0)) {
336 	getkey();
337     }
338 }
339 
340 /**
341  * @brief Refresh the screen
342  */
newtRefresh(void)343 void newtRefresh(void) {
344     SLsmg_refresh();
345 }
346 
newtSuspend(void)347 void newtSuspend(void) {
348     SLtt_set_cursor_visibility (1);
349     SLsmg_suspend_smg();
350     SLang_reset_tty();
351     SLtt_set_cursor_visibility (cursorOn);
352 }
353 
354 /**
355  *  @brief Return after suspension.
356  *  @return 0 on success.
357  */
newtResume(void)358 int newtResume(void) {
359     SLsmg_resume_smg ();
360     SLsmg_refresh();
361     return SLang_init_tty(0, noFlowCtrl, 0);
362 }
363 
newtCls(void)364 void newtCls(void) {
365     SLsmg_set_color(NEWT_COLORSET_ROOT);
366     SLsmg_gotorc(0, 0);
367     SLsmg_erase_eos();
368 
369     newtRefresh();
370 }
371 
372 /**
373  * @brief Resize the screen
374  * @param redraw - boolean - should we redraw the screen?
375  */
newtResizeScreen(int redraw)376 void newtResizeScreen(int redraw) {
377     /* we can't redraw from scratch, just redisplay SLang screen */
378     SLtt_get_screen_size();
379     /* SLsmg_reinit_smg(); */
380     if (redraw) {
381         SLsmg_touch_lines(0, SLtt_Screen_Rows);
382         newtRefresh();
383     }
384 }
385 
386 /**
387  * @brief Initialize the newt library
388  * @return int - 0 for success, else < 0
389  */
newtInit(void)390 int newtInit(void) {
391     char * MonoValue, * MonoEnv = "NEWT_MONO";
392     char * NoFlowCtrlValue, * NoFlowCtrlEnv = "NEWT_NOFLOWCTRL";
393     const char *lang;
394     int ret;
395 
396     if ((lang = getenv("LC_ALL")) == NULL)
397         if ((lang = getenv("LC_CTYPE")) == NULL)
398             if ((lang = getenv("LANG")) == NULL)
399                 lang = "";
400     /* slang doesn't support multibyte encodings except UTF-8,
401        avoid character corruption by redrawing the screen */
402     if (strstr (lang, ".euc") != NULL)
403 	trashScreen = 1;
404 
405     (void) strlen(ident);
406 
407     SLutf8_enable(-1);
408     SLtt_get_terminfo();
409     SLtt_get_screen_size();
410 
411     MonoValue = getenv(MonoEnv);
412     if ( MonoValue != NULL )
413 	SLtt_Use_Ansi_Colors = 0;
414 
415     NoFlowCtrlValue = getenv(NoFlowCtrlEnv);
416     if ( NoFlowCtrlValue != NULL )
417         noFlowCtrl = 1;
418 
419     if ((ret = SLsmg_init_smg()) < 0)
420 	return ret;
421     if ((ret = SLang_init_tty(0, noFlowCtrl, 0)) < 0)
422 	return ret;
423 
424     initColors();
425     newtCursorOff();
426     initKeymap();
427 
428     SLsignal_intr(SIGWINCH, handleSigwinch);
429     SLang_getkey_intr_hook = getkeyInterruptHook;
430 
431     return 0;
432 }
433 
434 /**
435  * @brief Closedown the newt library, tidying screen.
436  * @returns int , 0. (no errors reported)
437  */
newtFinished(void)438 int newtFinished(void) {
439     if (currentWindow) {
440 	for (; currentWindow >= windowStack; currentWindow--) {
441 	    free(currentWindow->buffer);
442 	    free(currentWindow->title);
443 	}
444 	currentWindow = NULL;
445     }
446 
447     if (currentHelpline) {
448 	for (; currentHelpline >= helplineStack; currentHelpline--)
449 	    free(*currentHelpline);
450 	currentHelpline = NULL;
451     }
452 
453     freeKeymap();
454 
455     SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
456     newtCursorOn();
457     SLsmg_refresh();
458     SLsmg_reset_smg();
459     SLang_reset_tty();
460 
461     return 0;
462 }
463 
464 /**
465  * @brief Set the colors used.
466  * @param colors - newtColor struct used.
467  */
newtSetColors(struct newtColors colors)468 void newtSetColors(struct newtColors colors) {
469     if (!SLtt_Use_Ansi_Colors) {
470         int i;
471 
472         for (i = 2; i < 25; i++)
473             SLtt_set_mono(i, NULL, 0);
474 
475         SLtt_set_mono(NEWT_COLORSET_SELLISTBOX, NULL, SLTT_BOLD_MASK);
476 
477         SLtt_set_mono(NEWT_COLORSET_ACTBUTTON, NULL, SLTT_REV_MASK);
478         SLtt_set_mono(NEWT_COLORSET_ACTCHECKBOX, NULL, SLTT_REV_MASK);
479         SLtt_set_mono(NEWT_COLORSET_ACTLISTBOX, NULL, SLTT_REV_MASK);
480         SLtt_set_mono(NEWT_COLORSET_ACTTEXTBOX, NULL, SLTT_REV_MASK);
481 
482         SLtt_set_mono(NEWT_COLORSET_ACTSELLISTBOX, NULL, SLTT_REV_MASK | SLTT_BOLD_MASK);
483 
484         SLtt_set_mono(NEWT_COLORSET_DISENTRY, NULL, 0); // FIXME
485         SLtt_set_mono(NEWT_COLORSET_FULLSCALE, NULL, SLTT_ULINE_MASK | SLTT_REV_MASK);
486         SLtt_set_mono(NEWT_COLORSET_EMPTYSCALE, NULL, SLTT_ULINE_MASK);
487         return;
488     }
489     SLtt_set_color(NEWT_COLORSET_ROOT, "", colors.rootFg, colors.rootBg);
490     SLtt_set_color(NEWT_COLORSET_BORDER, "", colors.borderFg, colors.borderBg);
491     SLtt_set_color(NEWT_COLORSET_WINDOW, "", colors.windowFg, colors.windowBg);
492     SLtt_set_color(NEWT_COLORSET_SHADOW, "", colors.shadowFg, colors.shadowBg);
493     SLtt_set_color(NEWT_COLORSET_TITLE, "", colors.titleFg, colors.titleBg);
494     SLtt_set_color(NEWT_COLORSET_BUTTON, "", colors.buttonFg, colors.buttonBg);
495     SLtt_set_color(NEWT_COLORSET_ACTBUTTON, "", colors.actButtonFg,
496 			colors.actButtonBg);
497     SLtt_set_color(NEWT_COLORSET_CHECKBOX, "", colors.checkboxFg,
498 			colors.checkboxBg);
499     SLtt_set_color(NEWT_COLORSET_ACTCHECKBOX, "", colors.actCheckboxFg,
500 			colors.actCheckboxBg);
501     SLtt_set_color(NEWT_COLORSET_ENTRY, "", colors.entryFg, colors.entryBg);
502     SLtt_set_color(NEWT_COLORSET_LABEL, "", colors.labelFg, colors.labelBg);
503     SLtt_set_color(NEWT_COLORSET_LISTBOX, "", colors.listboxFg,
504 			colors.listboxBg);
505     SLtt_set_color(NEWT_COLORSET_ACTLISTBOX, "", colors.actListboxFg,
506 			colors.actListboxBg);
507     SLtt_set_color(NEWT_COLORSET_TEXTBOX, "", colors.textboxFg,
508 			colors.textboxBg);
509     SLtt_set_color(NEWT_COLORSET_ACTTEXTBOX, "", colors.actTextboxFg,
510 			colors.actTextboxBg);
511     SLtt_set_color(NEWT_COLORSET_HELPLINE, "", colors.helpLineFg,
512 			colors.helpLineBg);
513     SLtt_set_color(NEWT_COLORSET_ROOTTEXT, "", colors.rootTextFg,
514 			colors.rootTextBg);
515 
516     SLtt_set_color(NEWT_COLORSET_EMPTYSCALE, "", "white",
517 			colors.emptyScale);
518     SLtt_set_color(NEWT_COLORSET_FULLSCALE, "", "white",
519 			colors.fullScale);
520     SLtt_set_color(NEWT_COLORSET_DISENTRY, "", colors.disabledEntryFg,
521 			colors.disabledEntryBg);
522 
523     SLtt_set_color(NEWT_COLORSET_COMPACTBUTTON, "", colors.compactButtonFg,
524 			colors.compactButtonBg);
525 
526     SLtt_set_color(NEWT_COLORSET_ACTSELLISTBOX, "", colors.actSelListboxFg,
527 		   colors.actSelListboxBg);
528     SLtt_set_color(NEWT_COLORSET_SELLISTBOX, "", colors.selListboxFg,
529 		   colors.selListboxBg);
530 }
531 
newtSetColor(int colorset,char * fg,char * bg)532 void newtSetColor(int colorset, char *fg, char *bg) {
533     if (colorset < NEWT_COLORSET_ROOT ||
534         (colorset > NEWT_COLORSET_SELLISTBOX && colorset < NEWT_COLORSET_CUSTOM(0)) ||
535 	    !SLtt_Use_Ansi_Colors)
536 	return;
537 
538     SLtt_set_color(colorset, "", fg, bg);
539 }
540 
541 /* Keymap handling - rewritten by Henning Makholm <henning@makholm.net>,
542  * November 2003.
543  */
544 
545 struct kmap_trie_entry {
546     char alloced; /* alloced/not first element in array */
547     char c ;   /* character got from terminal */
548     int code;  /* newt key, or 0 if c does not make a complete sequence */
549     struct kmap_trie_entry *contseq; /* sub-trie for character following c */
550     struct kmap_trie_entry *next;    /* try this if char received != c */
551 };
552 
553 static struct kmap_trie_entry *kmap_trie_root = NULL;
554 static int keyreader_buf_len = 10 ;
555 static unsigned char default_keyreader_buf[10];
556 static unsigned char *keyreader_buf = default_keyreader_buf;
557 
558 #if 0 /* for testing of the keymap manipulation code */
559 static void dumpkeys_recursive(struct kmap_trie_entry *curr, int i, FILE *f) {
560     int j, ps ;
561     char seen[256]={0};
562     if( curr && i >= keyreader_buf_len ) {
563         fprintf(f,"ARGH! Too long sequence!\n") ;
564         return ;
565     }
566     for(;curr;curr=curr->next) {
567         keyreader_buf[i] = curr->c ;
568         ps = seen[(unsigned char)curr->c]++ ;
569         if( ps || curr->code || (!curr->code && !curr->contseq) ) {
570             for(j=0;j<=i;j++) {
571                 if( keyreader_buf[j] > 32 && keyreader_buf[j]<127 &&
572                     keyreader_buf[j] != '^' && keyreader_buf[j] != '\\' )
573                     fprintf(f,"%c",keyreader_buf[j]);
574                 else if( keyreader_buf[j] > 0 && keyreader_buf[j]<=32 )
575                     fprintf(f,"^%c",keyreader_buf[j] + 0x40);
576                 else
577                     fprintf(f,"\\%03o",
578                             (unsigned)(unsigned char)keyreader_buf[j]);
579             }
580             if( curr->code )
581                 fprintf(f,": 0x%X\n",curr->code);
582             else
583                 fprintf(f,": (just keymap)\n");
584         }
585         dumpkeys_recursive(curr->contseq,i+1,f);
586     }
587 }
588 static void dump_keymap(void) {
589     FILE *f = fopen("newt.keydump","wt");
590     if (f) {
591         dumpkeys_recursive(kmap_trie_root, 0, f);
592         fclose(f);
593     }
594 }
595 #endif
596 
597 /* newtBindKey may overwrite a binding that is there already */
newtBindKey(char * keyseq,int meaning)598 static void newtBindKey(char *keyseq, int meaning) {
599     struct kmap_trie_entry *root = kmap_trie_root ;
600     struct kmap_trie_entry **curptr = &root ;
601 
602     /* Try to make sure the common matching buffer is long enough. */
603     if( strlen(keyseq) > keyreader_buf_len ) {
604         int i = strlen(keyseq)+10;
605         unsigned char *newbuf = malloc(i);
606         if (newbuf) {
607             if (keyreader_buf != default_keyreader_buf)
608                 free(keyreader_buf);
609             keyreader_buf = newbuf;
610             keyreader_buf_len = i;
611         }
612     }
613 
614     if (*keyseq == 0) return; /* binding the empty sequence is meaningless */
615 
616     while(1) {
617         while ((*curptr) && (*curptr)->c != *keyseq)
618             curptr = &(*curptr)->next;
619         if ((*curptr)==0) {
620             struct kmap_trie_entry* fresh
621                 =  calloc(strlen(keyseq),sizeof(struct kmap_trie_entry));
622             if (fresh == 0) return; /* despair! */
623 	    fresh->alloced = 1;
624             *curptr = fresh;
625             while (keyseq[1]) {
626                 fresh->contseq = fresh+1;
627                 (fresh++)->c = *(keyseq++);
628             }
629             fresh->c = *keyseq;
630             fresh->code = meaning;
631             return;
632         }
633         if (keyseq[1]==0) {
634             (*curptr)->code = meaning;
635             return;
636         } else {
637             curptr = &(*curptr)->contseq;
638             keyseq++;
639         }
640     }
641 }
642 
643 /* This function recursively inserts all entries in the "to" trie into
644    corresponding positions in the "from" trie, except positions that
645    are already defined in the "from" trie. */
kmap_trie_fallback(struct kmap_trie_entry * to,struct kmap_trie_entry ** from)646 static void kmap_trie_fallback(struct kmap_trie_entry *to,
647                                struct kmap_trie_entry **from) {
648     if (*from == NULL)
649         *from = to ;
650     if (*from == to)
651         return ;
652     for (;to!=NULL;to=to->next) {
653         struct kmap_trie_entry **fromcopy = from ;
654         while ((*fromcopy) && (*fromcopy)->c != to->c)
655             fromcopy = &(*fromcopy)->next ;
656         if (*fromcopy) {
657             if ((*fromcopy)->code == 0)
658                 (*fromcopy)->code = to->code;
659             kmap_trie_fallback(to->contseq, &(*fromcopy)->contseq);
660         } else {
661             *fromcopy = malloc(sizeof(struct kmap_trie_entry));
662             if (*fromcopy) {
663                 **fromcopy = *to ;
664 		(*fromcopy)->alloced = 1;
665                 (*fromcopy)->next = 0 ;
666             }
667         }
668     }
669 }
670 
newtGetKey(void)671 int newtGetKey(void) {
672     int key, lastcode, errors = 0;
673     unsigned char *chptr = keyreader_buf, *lastmatch;
674     struct kmap_trie_entry *curr = kmap_trie_root;
675 
676     do {
677 	key = getkey();
678 	if (key == SLANG_GETKEY_ERROR) {
679 	    if (needResize) {
680                 needResize = 0;
681 		return NEWT_KEY_RESIZE;
682             }
683 
684 	    /* Ignore other signals, but assume that stdin disappeared (the
685 	     * parent terminal was proably closed) if the error persists.
686 	     */
687 	    if (errors++ > 10)
688 		return NEWT_KEY_ERROR;
689 
690 	    continue;
691 	}
692 
693 	if (key == NEWT_KEY_SUSPEND && suspendCallback)
694 	    suspendCallback(suspendCallbackData);
695     } while (key == NEWT_KEY_SUSPEND || key == SLANG_GETKEY_ERROR);
696 
697     /* Read more characters, matching against the trie as we go */
698     lastcode = *chptr = key;
699     lastmatch = chptr ;
700     while(1) {
701          while (curr->c != key) {
702              curr = curr->next ;
703              if (curr==NULL) goto break2levels;
704          }
705          if (curr->code) {
706              lastcode = curr->code;
707              lastmatch = chptr;
708          }
709          curr = curr->contseq;
710          if (curr==NULL) break;
711 
712          if (SLang_input_pending(5) <= 0)
713              break;
714 
715          if (chptr==keyreader_buf+keyreader_buf_len-1) break;
716          *++chptr = key = getkey();
717     }
718    break2levels:
719 
720       /* The last time the trie matched was at position lastmatch. Back
721        * up if we have read too many characters. */
722       while (chptr > lastmatch)
723           SLang_ungetkey(*chptr--);
724 
725       return lastcode;
726 }
727 
728 /**
729  * @brief Wait for a keystroke
730  */
newtWaitForKey(void)731 void newtWaitForKey(void) {
732     newtRefresh();
733 
734     getkey();
735     newtClearKeyBuffer();
736 }
737 
738 /**
739  * @brief Clear the keybuffer
740  */
newtClearKeyBuffer(void)741 void newtClearKeyBuffer(void) {
742     while (SLang_input_pending(1)) {
743 	getkey();
744     }
745 }
746 
747 /**
748  * Open a new window.
749  * @param left. int Size; _not_ including border
750  * @param top: int size, _not_ including border
751  * @param width unsigned int
752  * @param height unsigned int
753  * @param title - title string
754  * @return zero on success
755  */
newtOpenWindow(int left,int top,unsigned int width,unsigned int height,const char * title)756 int newtOpenWindow(int left, int top,
757                    unsigned int width, unsigned int height,
758 			  const char * title) {
759     int j, row, col;
760     int n;
761     int i;
762 
763     newtFlushInput();
764 
765     if (currentWindow && currentWindow - windowStack + 1
766 	    >= sizeof (windowStack) / sizeof (struct Window))
767 	return 1;
768 
769     if (!currentWindow) {
770 	currentWindow = windowStack;
771     } else {
772 	currentWindow++;
773     }
774 
775     currentWindow->left = left;
776     currentWindow->top = top;
777     currentWindow->width = width;
778     currentWindow->height = height;
779     currentWindow->title = title ? strdup(title) : NULL;
780 
781     currentWindow->buffer = malloc(sizeof(SLsmg_Char_Type) * (width + 5) * (height + 3));
782 
783     row = top - 1;
784     col = left - 2;
785     /* clip to the current screen bounds - msw */
786     if (row < 0)
787 	row = 0;
788     if (col < 0)
789 	col = 0;
790     if (left + width > SLtt_Screen_Cols)
791 	width = SLtt_Screen_Cols - left;
792     if (top + height > SLtt_Screen_Rows)
793 	height = SLtt_Screen_Rows - top;
794     n = 0;
795     for (j = 0; j < height + 3; j++, row++) {
796 	SLsmg_gotorc(row, col);
797 	SLsmg_read_raw(currentWindow->buffer + n,
798 		       currentWindow->width + 5);
799 	n += currentWindow->width + 5;
800     }
801 
802     newtTrashScreen();
803 
804     SLsmg_set_color(NEWT_COLORSET_BORDER);
805     SLsmg_set_char_set(1);
806     SLsmg_draw_box(top - 1, left - 1, height + 2, width + 2);
807     SLsmg_set_char_set(0);
808 
809     if (currentWindow->title) {
810 	trim_string (currentWindow->title, width-4);
811 	i = wstrlen(currentWindow->title,-1) + 4;
812 	i = ((width - i) / 2) + left;
813 	SLsmg_gotorc(top - 1, i);
814 	SLsmg_set_char_set(1);
815 	SLsmg_write_char(SLSMG_RTEE_CHAR);
816 	SLsmg_set_char_set(0);
817 	SLsmg_write_char(' ');
818 	SLsmg_set_color(NEWT_COLORSET_TITLE);
819 	SLsmg_write_string((char *)currentWindow->title);
820 	SLsmg_set_color(NEWT_COLORSET_BORDER);
821 	SLsmg_write_char(' ');
822 	SLsmg_set_char_set(1);
823 	SLsmg_write_char(SLSMG_LTEE_CHAR);
824 	SLsmg_set_char_set(0);
825     }
826 
827     SLsmg_set_color(NEWT_COLORSET_WINDOW);
828     SLsmg_fill_region(top, left, height, width, ' ');
829 
830     SLsmg_set_color(NEWT_COLORSET_SHADOW);
831     SLsmg_fill_region(top + height + 1, left, 1, width + 2, ' ');
832     SLsmg_fill_region(top, left + width + 1, height + 1, 1, ' ');
833 
834     for (i = top; i < (top + height + 1); i++) {
835 	SLsmg_gotorc(i, left + width + 1);
836 	SLsmg_write_string(" ");
837     }
838 
839     return 0;
840 }
841 
842 /**
843  * @brief Draw a centered window.
844  * @param width - width in char cells
845  * @param height - no. of char cells.
846  * @param title - fixed title
847  * @returns zero on success
848  */
newtCenteredWindow(unsigned int width,unsigned int height,const char * title)849 int newtCenteredWindow(unsigned int width,unsigned int height,
850                        const char * title) {
851     int top, left;
852 
853     top = (int)(SLtt_Screen_Rows - height) / 2;
854 
855     /* I don't know why, but this seems to look better */
856     if ((SLtt_Screen_Rows % 2) && (top % 2)) top--;
857 
858     left = (int)(SLtt_Screen_Cols - width) / 2;
859 
860     return newtOpenWindow(left, top, width, height, title);
861 }
862 
863 /**
864  * @brief Remove the top window
865  */
newtPopWindow(void)866 void newtPopWindow(void) {
867     newtPopWindowNoRefresh();
868     newtRefresh();
869 }
870 
newtPopWindowNoRefresh(void)871 void newtPopWindowNoRefresh(void) {
872     int j, row, col;
873     int n = 0;
874 
875     if (currentWindow == NULL)
876 	return;
877 
878     row = col = 0;
879 
880     row = currentWindow->top - 1;
881     col = currentWindow->left - 2;
882     if (row < 0)
883 	row = 0;
884     if (col < 0)
885 	col = 0;
886     for (j = 0; j < currentWindow->height + 3; j++, row++) {
887 	SLsmg_gotorc(row, col);
888 	SLsmg_write_raw(currentWindow->buffer + n,
889 			currentWindow->width + 5);
890 	n += currentWindow->width + 5;
891     }
892 
893     free(currentWindow->buffer);
894     free(currentWindow->title);
895 
896     if (currentWindow == windowStack)
897 	currentWindow = NULL;
898     else
899 	currentWindow--;
900 
901     SLsmg_set_char_set(0);
902 
903     newtTrashScreen();
904 }
905 
newtGetWindowPos(int * x,int * y)906 void newtGetWindowPos(int * x, int * y) {
907     if (currentWindow) {
908 	*x = currentWindow->left;
909 	*y = currentWindow->top;
910     } else
911 	*x = *y = 0;
912 }
913 
newtGetrc(int * row,int * col)914 void newtGetrc(int * row, int * col) {
915     *row = cursorRow;
916     *col = cursorCol;
917 
918     if (currentWindow) {
919 	*row -= currentWindow->top;
920 	*col -= currentWindow->left;
921     }
922 }
923 
newtGotorc(int newRow,int newCol)924 void newtGotorc(int newRow, int newCol) {
925     if (currentWindow) {
926 	newRow += currentWindow->top;
927 	newCol += currentWindow->left;
928     }
929 
930     cursorRow = newRow;
931     cursorCol = newCol;
932     SLsmg_gotorc(cursorRow, cursorCol);
933 }
934 
newtDrawBox(int left,int top,int width,int height,int shadow)935 void newtDrawBox(int left, int top, int width, int height, int shadow) {
936     if (currentWindow) {
937 	top += currentWindow->top;
938 	left += currentWindow->left;
939     }
940 
941     SLsmg_draw_box(top, left, height, width);
942 
943     if (shadow) {
944 	SLsmg_set_color(NEWT_COLORSET_SHADOW);
945 	SLsmg_fill_region(top + height, left + 1, 1, width - 1, ' ');
946 	SLsmg_fill_region(top + 1, left + width, height, 1, ' ');
947     }
948 }
949 
newtClearBox(int left,int top,int width,int height)950 void newtClearBox(int left, int top, int width, int height) {
951     if (currentWindow) {
952 	top += currentWindow->top;
953 	left += currentWindow->left;
954     }
955 
956     SLsmg_fill_region(top, left, height, width, ' ');
957 }
958 
initKeymap(void)959 static void initKeymap(void) {
960     const struct keymap * curr;
961     struct kmap_trie_entry *kmap_trie_escBrack, *kmap_trie_escO;
962 
963     /* Here are some entries that will help in handling esc O foo and
964        esc [ foo as variants of each other. */
965     kmap_trie_root = calloc(3, sizeof (struct kmap_trie_entry));
966     kmap_trie_escBrack = kmap_trie_root + 1;
967     kmap_trie_escO = kmap_trie_root + 2;
968 
969     kmap_trie_root->alloced = 1;
970     kmap_trie_root->c = '\033';
971     kmap_trie_root->contseq = kmap_trie_escBrack;
972 
973     kmap_trie_escBrack->c = '[';
974     kmap_trie_escBrack->next = kmap_trie_escO;
975 
976     kmap_trie_escO->c = 'O';
977 
978     /* First bind built-in default bindings. They may be shadowed by
979        the termcap entries that get bound later. */
980     for (curr = keymap; curr->code; curr++) {
981         if (curr->str)
982             newtBindKey(curr->str,curr->code);
983     }
984 
985     /* Then bind strings from termcap entries */
986     for (curr = keymap; curr->code; curr++) {
987 	if (curr->tc) {
988             char *pc = SLtt_tgetstr(curr->tc);
989             if (pc) {
990                 newtBindKey(pc,curr->code);
991             }
992         }
993     }
994 
995     /* Finally, invent lowest-priority keybindings that correspond to
996        searching for esc-O-foo if esc-[-foo was not found and vice
997        versa.  That is needed because of strong confusion among
998        different emulators of VTxxx terminals; some terminfo/termcap
999        descriptions are apparently written by people who were not
1000        aware of the differences between "applicataion" and "terminal"
1001        keypad modes. Or perhaps they were, but tried to make their
1002        description work with a program that puts the keyboard in the
1003        wrong emulation mode. In short, one needs this: */
1004     kmap_trie_fallback(kmap_trie_escO->contseq, &kmap_trie_escBrack->contseq);
1005     kmap_trie_fallback(kmap_trie_escBrack->contseq, &kmap_trie_escO->contseq);
1006 }
1007 
free_keys(struct kmap_trie_entry * kmap,struct kmap_trie_entry * parent,int prepare)1008 static void free_keys(struct kmap_trie_entry *kmap, struct kmap_trie_entry *parent, int prepare) {
1009     if (kmap == NULL)
1010 	return;
1011 
1012     free_keys(kmap->contseq, kmap, prepare);
1013     free_keys(kmap->next, kmap, prepare);
1014 
1015     if (!kmap->alloced && kmap - parent == 1)
1016 	    return;
1017 
1018     /* find first element in array */
1019     while (!kmap->alloced)
1020 	kmap--;
1021 
1022     kmap->alloced += prepare ? 1 : -1;
1023     if (!prepare && kmap->alloced == 1)
1024 	free(kmap);
1025 }
1026 
freeKeymap()1027 static void freeKeymap() {
1028     free_keys(kmap_trie_root, NULL, 1);
1029     free_keys(kmap_trie_root, NULL, 0);
1030     kmap_trie_root = NULL;
1031 }
1032 
1033 /**
1034  * @brief Delay for a specified number of usecs
1035  * @param int - number of usecs to wait for.
1036  */
newtDelay(unsigned int usecs)1037 void newtDelay(unsigned int usecs) {
1038     usleep(usecs);
1039 }
1040 
newtDefaultEventHandler(newtComponent c,struct event ev)1041 struct eventResult newtDefaultEventHandler(newtComponent c,
1042 					   struct event ev) {
1043     struct eventResult er;
1044 
1045     er.result = ER_IGNORED;
1046     return er;
1047 }
1048 
newtRedrawHelpLine(void)1049 void newtRedrawHelpLine(void) {
1050     char * buf;
1051 
1052     SLsmg_set_color(NEWT_COLORSET_HELPLINE);
1053 
1054     if (currentHelpline) {
1055 	/* buffer size needs to be wide enough to hold all the multibyte
1056 	   currentHelpline + all the single byte ' ' to fill the line */
1057 	int wlen = wstrlen(*currentHelpline, -1);
1058 	int len;
1059 
1060 	if (wlen > SLtt_Screen_Cols)
1061 	    wlen = SLtt_Screen_Cols;
1062 	len = strlen(*currentHelpline) + (SLtt_Screen_Cols - wlen);
1063 	buf = alloca(len + 1);
1064 	memset(buf, ' ', len);
1065 	memcpy(buf, *currentHelpline, strlen(*currentHelpline));
1066 	buf[len] = '\0';
1067     } else {
1068 	buf = alloca(SLtt_Screen_Cols + 1);
1069 	memset(buf, ' ', SLtt_Screen_Cols);
1070 	buf[SLtt_Screen_Cols] = '\0';
1071     }
1072     SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
1073     SLsmg_write_string(buf);
1074     SLsmg_gotorc(cursorRow, cursorCol);
1075 }
1076 
newtPushHelpLine(const char * text)1077 void newtPushHelpLine(const char * text) {
1078     if (currentHelpline && currentHelpline - helplineStack + 1
1079 	    >= sizeof (helplineStack) / sizeof (char *))
1080 	return;
1081 
1082     if (!text)
1083 	text = defaultHelpLine;
1084 
1085     if (currentHelpline)
1086 	(*(++currentHelpline)) = strdup(text);
1087     else {
1088 	currentHelpline = helplineStack;
1089 	*currentHelpline = strdup(text);
1090     }
1091 
1092     newtRedrawHelpLine();
1093 }
1094 
newtPopHelpLine(void)1095 void newtPopHelpLine(void) {
1096     if (!currentHelpline) return;
1097 
1098     free(*currentHelpline);
1099     if (currentHelpline == helplineStack)
1100 	currentHelpline = NULL;
1101     else
1102 	currentHelpline--;
1103 
1104     newtRedrawHelpLine();
1105 }
1106 
newtDrawRootText(int col,int row,const char * text)1107 void newtDrawRootText(int col, int row, const char * text) {
1108     SLsmg_set_color(NEWT_COLORSET_ROOTTEXT);
1109 
1110     if (col < 0) {
1111 	col = SLtt_Screen_Cols + col;
1112     }
1113 
1114     if (row < 0) {
1115 	row = SLtt_Screen_Rows + row;
1116     }
1117 
1118     SLsmg_gotorc(row, col);
1119     SLsmg_write_string((char *)text);
1120 }
1121 
newtSetFlags(int oldFlags,int newFlags,enum newtFlagsSense sense)1122 int newtSetFlags(int oldFlags, int newFlags, enum newtFlagsSense sense) {
1123     switch (sense) {
1124       case NEWT_FLAGS_SET:
1125 	return oldFlags | newFlags;
1126 
1127       case NEWT_FLAGS_RESET:
1128 	return oldFlags & (~newFlags);
1129 
1130       case NEWT_FLAGS_TOGGLE:
1131 	return oldFlags ^ newFlags;
1132 
1133       default:
1134 	return oldFlags;
1135     }
1136 }
1137 
newtBell(void)1138 void newtBell(void)
1139 {
1140     SLtt_beep();
1141 }
1142 
newtGetScreenSize(int * cols,int * rows)1143 void newtGetScreenSize(int * cols, int * rows) {
1144     if (rows) *rows = SLtt_Screen_Rows;
1145     if (cols) *cols = SLtt_Screen_Cols;
1146 }
1147 
newtDefaultPlaceHandler(newtComponent c,int newLeft,int newTop)1148 void newtDefaultPlaceHandler(newtComponent c, int newLeft, int newTop) {
1149     c->left = newLeft;
1150     c->top = newTop;
1151 }
1152 
newtDefaultMappedHandler(newtComponent c,int isMapped)1153 void newtDefaultMappedHandler(newtComponent c, int isMapped) {
1154     c->isMapped = isMapped;
1155 }
1156 
newtCursorOff(void)1157 void newtCursorOff(void) {
1158     cursorOn = 0;
1159     SLtt_set_cursor_visibility (cursorOn);
1160 }
1161 
newtCursorOn(void)1162 void newtCursorOn(void) {
1163     cursorOn = 1;
1164     SLtt_set_cursor_visibility (cursorOn);
1165 }
1166 
newtTrashScreen(void)1167 void newtTrashScreen(void) {
1168     if (trashScreen)
1169 	SLsmg_touch_lines(0, SLtt_Screen_Rows);
1170 }
1171 
newtComponentGetPosition(newtComponent co,int * left,int * top)1172 void newtComponentGetPosition(newtComponent co, int * left, int * top) {
1173     if (left) *left = co->left;
1174     if (top) *top = co->top;
1175 }
1176 
newtComponentGetSize(newtComponent co,int * width,int * height)1177 void newtComponentGetSize(newtComponent co, int * width, int * height) {
1178     if (width) *width = co->width;
1179     if (height) *height = co->height;
1180 }
1181