1 /* $LynxId: LYStrings.c,v 1.214 2013/05/28 23:38:17 tom Exp $ */
2 #include <HTUtils.h>
3 #include <HTCJK.h>
4 #include <UCAux.h>
5 #include <LYGlobalDefs.h>
6 #include <LYUtils.h>
7 #include <LYStrings.h>
8 #include <GridText.h>
9 #include <LYKeymap.h>
10 #include <LYClean.h>
11 #include <LYMail.h>
12 #include <LYNews.h>
13 #include <LYOptions.h>
14 #include <LYCharSets.h>
15 #include <HTAlert.h>
16 #include <HTString.h>
17 #include <LYCharUtils.h>
18 #include <HTList.h>
19 #include <HTParse.h>
20 #ifdef USE_MOUSE
21 #include <LYMainLoop.h>
22 #endif
23 
24 #ifdef DJGPP_KEYHANDLER
25 #include <pc.h>
26 #include <keys.h>
27 #endif /* DJGPP_KEYHANDLER */
28 
29 #ifdef USE_COLOR_STYLE
30 #include <LYHash.h>
31 #include <AttrList.h>
32 #endif
33 
34 #ifdef USE_SCROLLBAR
35 #include <LYMainLoop.h>
36 #endif
37 
38 #ifdef USE_CMD_LOGGING
39 #include <LYReadCFG.h>
40 #include <LYrcFile.h>
41 #endif
42 
43 #include <LYShowInfo.h>
44 #include <LYLeaks.h>
45 
46 #if defined(WIN_EX)
47 #undef  BUTTON_CTRL
48 #define BUTTON_CTRL	0	/* Quick hack */
49 #endif
50 
51 #ifdef DEBUG_EDIT
52 #define CTRACE_EDIT(p) CTRACE(p)
53 #else
54 #define CTRACE_EDIT(p)		/*nothing */
55 #endif
56 
57 /*
58  * The edit_history lists allow the user to press tab when entering URL to get
59  * the closest match in the closet
60  */
61 #define LYClosetSize 100
62 
63 static HTList *URL_edit_history;
64 static HTList *MAIL_edit_history;
65 
66 /* If you want to add mouse support for some new platform, it's fairly
67  * simple to do.  Once you've determined the X and Y coordinates of
68  * the mouse event, loop through the elements in the links[] array and
69  * see if the coordinates fall within a highlighted link area.	If so,
70  * the code must set mouse_link to the index of the chosen link,
71  * and return a key value that corresponds to LYK_ACTIVATE.  The
72  * LYK_ACTIVATE code in LYMainLoop.c will then check mouse_link
73  * and activate that link.  If the mouse event didn't fall within a
74  * link, the code should just set mouse_link to -1 and return -1. --AMK
75  */
76 
77 /* The number of the link selected w/ the mouse (-1 if none) */
78 static int mouse_link = -1;
79 
80 static int have_levent;
81 
82 #if defined(USE_MOUSE) && defined(NCURSES)
83 static MEVENT levent;
84 #endif
85 
86 /* Return the value of mouse_link */
peek_mouse_levent(void)87 int peek_mouse_levent(void)
88 {
89 #if defined(USE_MOUSE) && defined(NCURSES)
90     if (have_levent > 0) {
91 	ungetmouse(&levent);
92 	have_levent--;
93 	return 1;
94     }
95 #endif
96     return 0;
97 }
98 
99 /* Return the value of mouse_link, erasing it */
get_mouse_link(void)100 int get_mouse_link(void)
101 {
102     int t;
103 
104     t = mouse_link;
105     mouse_link = -1;
106     if (t < 0)
107 	t = -1;			/* Backward compatibility. */
108     return t;
109 }
110 
111 /* Return the value of mouse_link */
peek_mouse_link(void)112 int peek_mouse_link(void)
113 {
114     return mouse_link;
115 }
116 
fancy_mouse(WINDOW * win,int row,int * position)117 int fancy_mouse(WINDOW * win, int row,
118 		int *position)
119 {
120     int cmd = LYK_DO_NOTHING;
121 
122 #ifdef USE_MOUSE
123 /*********************************************************************/
124 
125 #if defined(WIN_EX) && defined(PDCURSES)
126 
127     request_mouse_pos();
128 
129     if (BUTTON_STATUS(1)
130 	&& (MOUSE_X_POS >= getbegx(win) &&
131 	    MOUSE_X_POS < (getbegx(win) + getmaxx(win)))) {
132 	int mypos = MOUSE_Y_POS - getbegy(win);
133 	int delta = mypos - row;
134 
135 	if (mypos + 1 == getmaxy(win)) {
136 	    /* At the decorative border: scroll forward */
137 	    if (BUTTON_STATUS(1) & BUTTON1_TRIPLE_CLICKED)
138 		cmd = LYK_END;
139 	    else if (BUTTON_STATUS(1) & BUTTON1_DOUBLE_CLICKED)
140 		cmd = LYK_NEXT_PAGE;
141 	    else
142 		cmd = LYK_NEXT_LINK;
143 	} else if (mypos >= getmaxy(win)) {
144 	    if (BUTTON_STATUS(1) & (BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED))
145 		cmd = LYK_END;
146 	    else
147 		cmd = LYK_NEXT_PAGE;
148 	} else if (mypos == 0) {
149 	    /* At the decorative border: scroll back */
150 	    if (BUTTON_STATUS(1) & BUTTON1_TRIPLE_CLICKED)
151 		cmd = LYK_HOME;
152 	    else if (BUTTON_STATUS(1) & BUTTON1_DOUBLE_CLICKED)
153 		cmd = LYK_PREV_PAGE;
154 	    else
155 		cmd = LYK_PREV_LINK;
156 	} else if (mypos < 0) {
157 	    if (BUTTON_STATUS(1) & (BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED))
158 		cmd = LYK_HOME;
159 	    else
160 		cmd = LYK_PREV_PAGE;
161 #ifdef KNOW_HOW_TO_TOGGLE
162 	} else if (BUTTON_STATUS(1) & (BUTTON_CTRL)) {
163 	    cur_selection += delta;
164 	    cmd = LYX_TOGGLE;
165 #endif
166 	} else if (BUTTON_STATUS(1) & (BUTTON_ALT | BUTTON_SHIFT | BUTTON_CTRL)) {
167 	    /* Probably some unrelated activity, such as selecting some text.
168 	     * Select, but do nothing else.
169 	     */
170 	    *position += delta;
171 	    cmd = -1;
172 	} else {
173 	    /* No scrolling or overflow checks necessary. */
174 	    *position += delta;
175 	    cmd = LYK_ACTIVATE;
176 	}
177     } else if (BUTTON_STATUS(1) & (BUTTON3_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED)) {
178 	cmd = LYK_QUIT;
179     }
180 #else
181 #if defined(NCURSES)
182 #define ButtonModifiers (BUTTON_ALT | BUTTON_SHIFT | BUTTON_CTRL)
183     MEVENT event;
184 
185     getmouse(&event);
186     if ((event.bstate & (BUTTON1_CLICKED |
187 			 BUTTON1_DOUBLE_CLICKED |
188 			 BUTTON1_TRIPLE_CLICKED))) {
189 	int mypos = event.y - getbegy(win);
190 	int delta = mypos - row;
191 
192 	if ((event.x < getbegx(win) ||
193 	     event.x >= (getbegx(win) + getmaxx(win)))
194 	    && !(event.bstate & ButtonModifiers))
195 	    return LYK_QUIT;	/* User clicked outside, wants to quit? */
196 	if (mypos + 1 == getmaxy(win)) {
197 	    /* At the decorative border: scroll forward */
198 	    if (event.bstate & BUTTON1_TRIPLE_CLICKED)
199 		cmd = LYK_END;
200 	    else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
201 		cmd = LYK_NEXT_PAGE;
202 	    else
203 		cmd = LYK_NEXT_LINK;
204 	} else if (mypos >= getmaxy(win)) {
205 	    if (event.bstate & (BUTTON1_DOUBLE_CLICKED |
206 				BUTTON1_TRIPLE_CLICKED))
207 		cmd = LYK_END;
208 	    else
209 		cmd = LYK_NEXT_PAGE;
210 	} else if (mypos == 0) {
211 	    /* At the decorative border: scroll back */
212 	    if (event.bstate & BUTTON1_TRIPLE_CLICKED)
213 		cmd = LYK_HOME;
214 	    else if (event.bstate & BUTTON1_DOUBLE_CLICKED)
215 		cmd = LYK_PREV_PAGE;
216 	    else
217 		cmd = LYK_PREV_LINK;
218 	} else if (mypos < 0) {
219 	    if (event.bstate & (BUTTON1_DOUBLE_CLICKED |
220 				BUTTON1_TRIPLE_CLICKED))
221 		cmd = LYK_HOME;
222 	    else
223 		cmd = LYK_PREV_PAGE;
224 #ifdef KNOW_HOW_TO_TOGGLE
225 	} else if (event.bstate & (BUTTON_CTRL)) {
226 	    cur_selection += delta;
227 	    cmd = LYX_TOGGLE;
228 #endif
229 	} else if (event.x <= getbegx(win) + 1 ||
230 		   event.x >= getbegx(win) + getmaxx(win) - 2) {
231 	    /* Click on left or right border for positioning without
232 	     * immediate action: select, but do nothing else.
233 	     * Actually, allow an error of one position inwards. - kw
234 	     */
235 	    *position += delta;
236 	    cmd = -1;
237 	} else if (event.bstate & ButtonModifiers) {
238 	    /* Probably some unrelated activity, such as selecting some text.
239 	     * Select, but do nothing else.
240 	     */
241 	    /* Possibly this is never returned by ncurses, so this case
242 	     * may be useless depending on situation (kind of mouse support
243 	     * and library versions). - kw
244 	     */
245 	    *position += delta;
246 	    cmd = -1;
247 	} else {
248 	    /* No scrolling or overflow checks necessary. */
249 	    *position += delta;
250 	    cmd = LYK_ACTIVATE;
251 	}
252     } else if (event.bstate & (BUTTON3_CLICKED | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED)) {
253 	cmd = LYK_QUIT;
254     }
255 #endif /* NCURSES */
256 #endif /* PDCURSES */
257 
258 /************************************************************************/
259 #endif /* USE_MOUSE */
260     (void) win;
261     (void) row;
262     (void) position;
263 
264     return cmd;
265 }
266 
267 /*
268  * Manage the collection of edit-histories
269  */
whichRecall(RecallType recall)270 static HTList *whichRecall(RecallType recall)
271 {
272     HTList **list;
273 
274     switch (recall) {
275     case RECALL_CMD:
276 	return LYcommandList();
277     case RECALL_MAIL:
278 	list = &MAIL_edit_history;
279 	break;
280     default:
281 	list = &URL_edit_history;
282 	break;
283     }
284     if (*list == 0)
285 	*list = HTList_new();
286     return *list;
287 }
288 
289 /*
290  * Remove the oldest item in the closet
291  */
LYRemoveFromCloset(HTList * list)292 static void LYRemoveFromCloset(HTList *list)
293 {
294     void *data = HTList_removeFirstObject(list);
295 
296     if (data != 0)
297 	FREE(data);
298 }
299 
LYCloseCloset(RecallType recall)300 void LYCloseCloset(RecallType recall)
301 {
302     HTList *list = whichRecall(recall);
303 
304     while (!HTList_isEmpty(list)) {
305 	LYRemoveFromCloset(list);
306     }
307     HTList_delete(list);	/* should already be empty */
308 }
309 
310 /*
311  * Strategy:  We begin at the top and search downwards.  We return the first
312  * match, i.e., the newest since we search from the top.  This should be made
313  * more intelligent, but works for now.
314  */
LYFindInCloset(RecallType recall,char * base)315 static char *LYFindInCloset(RecallType recall, char *base)
316 {
317     HTList *list = whichRecall(recall);
318     char *data;
319     size_t len = strlen(base);
320 
321     while (!HTList_isEmpty(list)) {
322 	data = (char *) HTList_nextObject(list);
323 	if (data != NULL && !StrNCmp(base, data, len))
324 	    return (data);
325     }
326 
327     return (0);
328 }
329 
LYAddToCloset(RecallType recall,char * str)330 static void LYAddToCloset(RecallType recall, char *str)
331 {
332     HTList *list = whichRecall(recall);
333     char *data = NULL;
334 
335     StrAllocCopy(data, str);
336     HTList_addObject(list, data);
337     while (HTList_count(list) > LYClosetSize)
338 	LYRemoveFromCloset(list);
339 }
340 
341 #ifdef USE_MOUSE
XYdist(int x1,int y1,int x2,int y2,int dx2)342 static int XYdist(int x1,
343 		  int y1,
344 		  int x2,
345 		  int y2,
346 		  int dx2)
347 {
348     int xerr = 3 * (x2 - x1), yerr = 9 * (y2 - y1);
349 
350     if (xerr < 0)
351 	xerr = 3 * (x1 - x2 - dx2) + 1;		/* pos after string not really in it */
352     if (xerr < 0)
353 	xerr = 0;
354     if (yerr < 0)
355 	yerr = -yerr;
356     if (!yerr)			/* same line is good */
357 	return (xerr > 0) ? (xerr * 2 - 1) : 0;
358     if (xerr < 9 && yerr)	/* x-dist of 3 cell better than y-dist of 1 cell */
359 	yerr += (9 - xerr);
360     return 2 * xerr + yerr;	/* Subjective factor; ratio -> approx. 6 / 9 */
361 /*
362 old: (IZ 1999-07-30)
363  3  2  2  2  1  1  1 XX XX XX XX XX  0  1  1  1  2  2  2  3  3
364  4\ 3  3  3  2  2  2  2  2  2  2  2  2  2  2  2  3  3  3/ 4  4
365  5  4  4  4\ 3  3  3  3  3  3  3  3  3  3  3  3/ 4  4  4  5  5
366  6  5  5  5  4  4  4  4  4  4  4  4  4  4  4  4  5  5  5  6  5
367 now: (kw 1999-10-23)
368 41 35 29|23 17 11  5 XX XX XX XX XX  1  7 13 19 25|31 37 43 49
369    45 39 33\27 24 21 18 18 18 18 18 19 22 25 28/34 40 46 50
370       48 42 36 33 30\27 27 27 27 27 28/31 34 37 43 49
371          51 45 42 39 36 36 36 36 36 37 40 43 46 49
372                51 48 45 45 45 45 45 46 49 52
373 */
374 }
375 
376 /* Given X and Y coordinates of a mouse event, set mouse_link to the
377  * index of the corresponding hyperlink, or set mouse_link to -1 if no
378  * link matches the event.  Returns -1 if no link matched the click,
379  * or a keycode that must be returned from LYgetch() to activate the
380  * link.
381  */
382 
set_clicked_link(int x,int y,int code,int clicks)383 static int set_clicked_link(int x,
384 			    int y,
385 			    int code,
386 			    int clicks)
387 {
388     int left = 6;
389     int right = LYcolLimit - 5;
390 
391     /* yes, I am assuming that my screen will be a certain width. */
392     int i;
393     int c = -1;
394 
395     if (y == (LYlines - 1) || y == 0) {		/* First or last row */
396 	/* XXXX In fact # is not always at x==0?  KANJI_CODE_OVERRIDE? */
397 	int toolbar = (y == 0 && HText_hasToolbar(HTMainText));
398 
399 	mouse_link = -2;
400 	if (x == 0 && toolbar)	/* On '#' */
401 	    c = LAC_TO_LKC0(LYK_TOOLBAR);
402 #if defined(CAN_CUT_AND_PASTE) && defined(USE_COLOR_STYLE)
403 	else if (y == 0 && x == LYcolLimit && s_hot_paste != NOSTYLE)
404 	    c = LAC_TO_LKC0(LYK_PASTE_URL);
405 #endif
406 	else if (clicks > 1) {
407 	    if (x < left + toolbar)
408 		c = (code == FOR_PROMPT && y)
409 		    ? HOME : LAC_TO_LKC0(LYK_MAIN_MENU);
410 	    else if (x > right)
411 		c = (code == FOR_PROMPT && y)
412 		    ? END_KEY : LAC_TO_LKC0(LYK_VLINKS);
413 	    else if (y)		/* Last row */
414 		c = LAC_TO_LKC0(LYK_END);
415 	    else		/* First row */
416 		c = LAC_TO_LKC0(LYK_HOME);
417 	} else {
418 	    if (x < left + toolbar)
419 		c = (code == FOR_PROMPT && y)
420 		    ? LTARROW
421 		    : (
422 #ifdef USE_COLOR_STYLE
423 			  (s_forw_backw != NOSTYLE && x - toolbar >= 3)
424 			  ? LAC_TO_LKC0(LYK_NEXT_DOC)
425 			  : LAC_TO_LKC0(LYK_PREV_DOC)
426 #else
427 			  LAC_TO_LKC0(LYK_NEXT_DOC)
428 #endif
429 		    );
430 	    else if (x > right)
431 		c = (code == FOR_PROMPT && y)
432 		    ? RTARROW : LAC_TO_LKC0(LYK_HISTORY);
433 	    else if (y)		/* Last row */
434 		c = LAC_TO_LKC0(LYK_NEXT_PAGE);
435 	    else		/* First row */
436 		c = LAC_TO_LKC0(LYK_PREV_PAGE);
437 	}
438 #ifdef USE_SCROLLBAR
439     } else if (x == (LYcols - 1) && LYShowScrollbar && LYsb_begin >= 0) {
440 	int h = display_lines - 2 * (LYsb_arrow != 0);
441 
442 	mouse_link = -2;
443 	y -= 1 + (LYsb_arrow != 0);
444 	if (y < 0)
445 	    return LAC_TO_LKC0(LYK_UP_TWO);
446 	if (y >= h)
447 	    return LAC_TO_LKC0(LYK_DOWN_TWO);
448 
449 	if (clicks >= 2) {
450 	    double frac = (1. * y) / (h - 1);
451 	    int l = HText_getNumOfLines() + 1;	/* NOL() off by one? */
452 
453 	    l -= display_lines;
454 	    if (l > 0)
455 		LYSetNewline((int) (frac * l + 1 + 0.5));
456 	    return LYReverseKeymap(LYK_DO_NOTHING);
457 	}
458 
459 	if (y < LYsb_begin)
460 	    return LAC_TO_LKC0(LYK_PREV_PAGE);
461 	if (y >= LYsb_end)
462 	    return LAC_TO_LKC0(LYK_NEXT_PAGE);
463 	mouse_link = -1;	/* No action in edit fields */
464 #endif
465     } else {
466 	int mouse_err = 29, /* subjctv-dist better than this for approx stuff */ cur_err;
467 
468 	/* Loop over the links and see if we can get a match */
469 	for (i = 0; i < nlinks; i++) {
470 	    int len, lx = links[i].lx, is_text = 0;
471 	    int count = 0;
472 	    const char *text = LYGetHiliteStr(i, count);
473 
474 	    if (links[i].type == WWW_FORM_LINK_TYPE
475 		&& F_TEXTLIKE(links[i].l_form->type))
476 		is_text = 1;
477 
478 	    /* Check the first line of the link */
479 	    if (text != NULL) {
480 		if (is_text)
481 		    len = links[i].l_form->size;
482 		else
483 		    len = (int) LYstrCells(text);
484 		cur_err = XYdist(x, y, links[i].lx, links[i].ly, len);
485 		/* Check the second line */
486 		while (cur_err > 0
487 		       && (text = LYGetHiliteStr(i, ++count)) != NULL) {
488 		    /* Note that there is at most one hightext if is_text */
489 		    int cur_err_2 = XYdist(x, y,
490 					   LYGetHilitePos(i, count),
491 					   links[i].ly + count,
492 					   (int) LYstrCells(text));
493 
494 		    cur_err = HTMIN(cur_err, cur_err_2);
495 		}
496 		if (cur_err > 0 && is_text)
497 		    cur_err--;	/* a bit of preference for text fields,
498 				   enter field if hit exactly at end - kw */
499 		if (cur_err == 0) {
500 		    int cury, curx;
501 
502 		    LYGetYX(cury, curx);
503 		    /* double-click, if we care:
504 		       submit text submit fields. - kw */
505 		    if (clicks > 1 && is_text &&
506 			links[i].l_form->type == F_TEXT_SUBMIT_TYPE) {
507 			if (code != FOR_INPUT
508 			/* submit current input field directly */
509 			    || !(cury == y &&
510 				 (curx >= lx) &&
511 				 ((curx - lx) <= len))) {
512 			    c = LAC_TO_LKC0(LYK_MOUSE_SUBMIT);
513 			    mouse_link = i;
514 			} else {
515 			    c = LAC_TO_LKC0(LYK_MOUSE_SUBMIT);
516 			    mouse_link = -1;
517 			}
518 			mouse_err = 0;
519 			break;
520 		    }
521 		    if (code != FOR_INPUT
522 		    /* Do not pick up the current input field */
523 			|| !((cury == y && (curx >= lx) && ((curx - lx) <= len)))) {
524 			if (is_text) {
525 			    have_levent = 1;
526 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
527 			    if (x == links[i].lx && y == links[i].ly)
528 				textinput_redrawn = FALSE;
529 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
530 			}
531 			mouse_link = i;
532 		    } else
533 			mouse_link = -1;
534 		    mouse_err = 0;
535 		    break;
536 		} else if (cur_err < mouse_err) {
537 		    mouse_err = cur_err;
538 		    mouse_link = i;
539 		}
540 	    }
541 	}
542 	/*
543 	 * If a link was hit, we must look for a key which will activate
544 	 * LYK_ACTIVATE We expect to find LYK_ACTIVATE (it's usually mapped to
545 	 * the Enter key).
546 	 */
547 	if (mouse_link >= 0) {
548 	    if (mouse_err == 0) {
549 		if (c == -1)
550 		    c = LAC_TO_LKC0(LYK_ACTIVATE);
551 	    } else if (mouse_err >= 0)
552 		c = LAC_TO_LKC0(LYK_CHANGE_LINK);
553 	} else {
554 	    if (2 * y > LYlines) {	/* Bottom Half of the screen */
555 		if (4 * y < 3 * LYlines) {
556 		    c = LAC_TO_LKC0(LYK_DOWN_TWO);	/* Third quarter */
557 		} else
558 		    c = LAC_TO_LKC0(LYK_DOWN_HALF);	/* Fourth quarter */
559 	    } else {		/* Upper Half of the screen */
560 		if (4 * y < LYlines) {
561 		    c = LAC_TO_LKC0(LYK_UP_HALF);	/* First quarter */
562 		} else
563 		    c = LAC_TO_LKC0(LYK_UP_TWO);	/* Second quarter */
564 	    }
565 	}
566     }
567     return c;
568 }
569 #endif /* USE_MOUSE */
570 
571 /*
572  * LYstrncpy() terminates strings with a null byte.  Writes a null byte into
573  * the n+1 byte of dst.
574  */
LYstrncpy(char * dst,const char * src,int n)575 char *LYstrncpy(char *dst,
576 		const char *src,
577 		int n)
578 {
579     char *val = dst;
580     int len;
581 
582     if (src == 0)
583 	src = "";
584     len = (int) strlen(src);
585 
586     if (n > 0) {
587 	if (n > len)
588 	    n = len;
589 	(void) StrNCpy(dst, src, n);
590     } else {
591 	n = 0;
592     }
593     dst[n] = '\0';
594     return val;
595 }
596 
597 #define IS_NEW_GLYPH(ch) (utf_flag && (UCH(ch)&0xc0) != 0x80)
598 #define IS_UTF_EXTRA(ch) (utf_flag && (UCH(ch)&0xc0) == 0x80)
599 
600 /*
601  * LYmbcsstrncpy() terminates strings with a null byte.  It takes account of
602  * multibyte characters.  The src string is copied until either end of string
603  * or max number of either bytes or glyphs (mbcs sequences) (CJK or UTF8).  The
604  * utf_flag argument should be TRUE for UTF8.  - KW & FM
605  */
LYmbcsstrncpy(char * dst,const char * src,int n_bytes,int n_glyphs,int utf_flag)606 char *LYmbcsstrncpy(char *dst,
607 		    const char *src,
608 		    int n_bytes,
609 		    int n_glyphs,
610 		    int utf_flag)
611 {
612     char *val = dst;
613     int i_bytes = 0, i_glyphs = 0;
614 
615     if (n_bytes < 0)
616 	n_bytes = 0;
617     if (n_glyphs < 0)
618 	n_glyphs = 0;
619 
620     for (; *src != '\0' && i_bytes < n_bytes; i_bytes++) {
621 	if (IS_NEW_GLYPH(*src)) {
622 	    if (i_glyphs++ >= n_glyphs) {
623 		*dst = '\0';
624 		return val;
625 	    }
626 	}
627 	*(dst++) = *(src++);
628     }
629     *dst = '\0';
630 
631     return val;
632 }
633 
634 /*
635  * LYmbcs_skip_glyphs() skips a given number of character positions in a string
636  * and returns the resulting pointer.  It takes account of UTF-8 encoded
637  * characters.  - KW
638  */
LYmbcs_skip_glyphs(const char * data,int n_glyphs,int utf_flag)639 const char *LYmbcs_skip_glyphs(const char *data,
640 			       int n_glyphs,
641 			       int utf_flag)
642 {
643     int i_glyphs = 0;
644 
645     if (n_glyphs < 0)
646 	n_glyphs = 0;
647 
648     if (non_empty(data)) {
649 	if (!utf_flag) {
650 	    while (n_glyphs-- > 0) {
651 		if (!*++data)
652 		    break;
653 	    }
654 	} else {
655 	    while (*data) {
656 		if (IS_NEW_GLYPH(*data)) {
657 		    if (i_glyphs++ >= n_glyphs) {
658 			break;
659 		    }
660 		}
661 		data++;
662 	    }
663 	}
664     }
665     return data;
666 }
667 
668 /*
669  * LYmbcs_skip_cells() skips a given number of display positions in a string
670  * and returns the resulting pointer.  It takes account of UTF-8 encoded
671  * characters.  - TD
672  */
LYmbcs_skip_cells(const char * data,int n_cells,int utf_flag)673 const char *LYmbcs_skip_cells(const char *data,
674 			      int n_cells,
675 			      int utf_flag)
676 {
677     const char *result;
678     int actual;
679     int target = n_cells;
680 
681     do {
682 	result = LYmbcs_skip_glyphs(data, target--, utf_flag);
683 	actual = LYstrExtent2(data, (int) (result - data));
684     } while ((actual > 0) && (actual > n_cells));
685     return result;
686 }
687 
688 /*
689  * LYmbcsstrlen() returns the printable length of a string that might contain
690  * IsSpecial or multibyte (CJK or UTF8) characters.  - FM
691  *
692  * Counts glyph cells if count_gcells is set.  (Full-width characters in CJK
693  * mode count as two.) Counts character glyphs if count_gcells is unset.
694  * (Full- width characters in CJK mode count as one.) - kw
695  */
LYmbcsstrlen(const char * str,int utf_flag,int count_gcells)696 int LYmbcsstrlen(const char *str,
697 		 int utf_flag,
698 		 int count_gcells)
699 {
700     int i, j, len = 0;
701 
702     if (non_empty(str)) {
703 #ifdef WIDEC_CURSES
704 	if (count_gcells) {
705 	    len = LYstrCells(str);
706 	} else
707 #endif
708 	{
709 	    for (i = 0; str[i] != '\0'; i++) {
710 		if (!IsSpecialAttrChar(str[i])) {
711 		    len++;
712 		    if (IS_NEW_GLYPH(str[i])) {
713 			j = 0;
714 			while (IsNormalChar(str[(i + 1)]) &&
715 			       j < 5 &&
716 			       IS_UTF_EXTRA(str[(i + 1)])) {
717 			    i++;
718 			    j++;
719 			}
720 		    } else if (!utf_flag && IS_CJK_TTY && !count_gcells &&
721 			       is8bits(str[i]) &&
722 			       IsNormalChar(str[(i + 1)])) {
723 			i++;
724 		    }
725 		}
726 	    }
727 	}
728     }
729     return (len);
730 }
731 
732 #undef GetChar
733 
734 #ifdef USE_SLANG
735 #if defined(VMS)
736 #define GetChar() ttgetc()
737 #elif defined(__DJGPP__)
738 #define GetChar() getxkey()	/* HTDos.c */
739 #elif defined(__CYGWIN__)
740 #define GetChar SLkp_getkey
741 #else
742 #define GetChar (int)SLang_getkey
743 #endif
744 #else /* curses */
745 #if defined(DJGPP)
746 #define GetChar() (djgpp_idle_loop(), wgetch(LYtopwindow()))
747 #elif defined(NCURSES_VERSION) && defined(__BEOS__)
748 #define GetChar() myGetCharNodelay()
749 #elif defined(NCURSES)
750 #define GetChar() wgetch(LYtopwindow())
751 #endif
752 #endif
753 
754 #ifdef USE_CURSES_NODELAY
755 /* PDCurses - until version 2.7 in 2005 - defined ERR as 0, unlike other
756  * versions of curses.  Generally both EOF and ERR are defined as -1s.
757  * However, there is a special case (see HTCheckForInterrupt()) to handle a
758  * case where no select() function is used in the win32 environment.
759  *
760  * HTCheckForInterrupt() uses nodelay() in this special case to check for
761  * pending input.  That normally returns ERR.  But LYgetch_for() checks the
762  * return value of this function for EOF (to handle some antique runtime
763  * libraries which did not set the state for feof/ferror).  Returning a zero
764  * (0) is safer since normally that is not mapped to any commands, and will be
765  * ignored by lynx.
766  */
myGetCharNodelay(void)767 static int myGetCharNodelay(void)
768 {
769     int c = wgetch(LYwin);
770 
771     if (c == -1)
772 	c = 0;
773 
774     return c;
775 }
776 #else
777 #define myGetCharNodelay() wgetch(LYwin)
778 #endif
779 
780 #if !defined(GetChar) && defined(PDCURSES) && defined(PDC_BUILD) && PDC_BUILD >= 2401
781 /* PDCurses sends back key-modifiers that we don't use, but would waste time
782  * upon, e.g., repainting the status line
783  */
myGetChar(void)784 static int myGetChar(void)
785 {
786     int c;
787     BOOL done = FALSE;
788 
789     do {
790 	switch (c = myGetCharNodelay()) {
791 	case KEY_SHIFT_L:
792 	case KEY_SHIFT_R:
793 	case KEY_CONTROL_L:
794 	case KEY_CONTROL_R:
795 	case KEY_ALT_L:
796 	case KEY_ALT_R:
797 	case KEY_RESIZE:
798 	    break;
799 	default:
800 	    done = TRUE;
801 	    break;
802 	}
803     } while (!done);
804 
805     return c;
806 }
807 #define GetChar() myGetChar()
808 #endif
809 
810 #if !defined(GetChar) && defined(SNAKE)
811 #define GetChar() wgetch(LYwin)
812 #endif
813 
814 #if !defined(GetChar) && defined(VMS)
815 #define GetChar() ttgetc()
816 #endif
817 
818 #if !defined(GetChar)
819 #ifdef HAVE_KEYPAD
820 #define GetChar() getch()
821 #else
822 #ifndef USE_GETCHAR
823 #define USE_GETCHAR
824 #endif /* !USE_GETCHAR */
825 #define GetChar() getchar()	/* used to be "getc(stdin)" and "getch()" */
826 #endif /* HAVE_KEYPAD */
827 #endif /* !defined(GetChar) */
828 
829 #if defined(USE_SLANG) && defined(USE_MOUSE)
sl_parse_mouse_event(int * x,int * y,int * button)830 static int sl_parse_mouse_event(int *x, int *y, int *button)
831 {
832     /* "ESC [ M" has already been processed.  There more characters are
833      * expected:  BUTTON X Y
834      */
835     *button = (int) SLang_getkey();
836     switch (*button) {
837     case 040:			/* left button */
838     case 041:			/* middle button */
839     case 042:			/* right button */
840 	*button -= 040;
841 	break;
842 
843     default:			/* Hmmm.... */
844 	SLang_flush_input();
845 	return -1;
846     }
847 
848     *x = (int) SLang_getkey();
849     if (*x == CH_ESC)		/* Undo 7-bit replace for large x - kw */
850 	*x = (int) SLang_getkey() + 64 - 33;
851     else
852 	*x -= 33;
853     *y = (int) SLang_getkey();
854     if (*y == CH_ESC)		/* Undo 7-bit replace for large y - kw */
855 	*y = (int) SLang_getkey() + 64 - 33;
856     else
857 	*y -= 33;
858     return 0;
859 }
860 
sl_read_mouse_event(int code)861 static int sl_read_mouse_event(int code)
862 {
863     int mouse_x, mouse_y, button;
864 
865     mouse_link = -1;
866     if (-1 != sl_parse_mouse_event(&mouse_x, &mouse_y, &button)) {
867 	if (button == 0)	/* left */
868 	    return set_clicked_link(mouse_x, mouse_y, FOR_PANEL, 1);
869 
870 	if (button == 1)	/* middle */
871 	    return LYReverseKeymap(LYK_VIEW_BOOKMARK);
872 
873 	if (button == 2)	/* right */
874 	{
875 	    /* Right button: go back to prev document.
876 	     * The problem is that we need to determine
877 	     * what to return to achieve this.
878 	     */
879 	    return LYReverseKeymap(LYK_PREV_DOC);
880 	}
881     }
882     if (code == FOR_INPUT || code == FOR_PROMPT)
883 	return DO_NOTHING;
884     else
885 	return -1;
886 }
887 #endif /* USE_SLANG and USE_MOUSE */
888 
889 static BOOLEAN csi_is_csi = TRUE;
ena_csi(int flag)890 void ena_csi(int flag)
891 {
892     csi_is_csi = (BOOLEAN) flag;
893 }
894 
895 #if defined(USE_KEYMAPS)
896 
897 #ifdef USE_SLANG
898 #define define_key(string, code) \
899 	SLkm_define_keysym ((SLFUTURE_CONST char*)(string), \
900 			    (unsigned) code, \
901 			    Keymap_List)
902 #if SLANG_VERSION < 20000
903 #define expand_substring(dst, first, last, final) \
904  	(SLexpand_escaped_string(dst, \
905 				 (char *)first, \
906 				 (char *)last), 1)
SLang_get_error(void)907 static int SLang_get_error(void)
908 {
909     return SLang_Error;
910 }
911 #else
912 int LY_Slang_UTF8_Mode = 0;
913 
914 #define expand_substring(dst, first, last, final) \
915 	(SLexpand_escaped_string(dst, \
916 				 (char *)first, \
917 				 (char *)last, \
918 				 LY_Slang_UTF8_Mode), 1)
919 #endif
920 
921 static SLKeyMap_List_Type *Keymap_List;
922 
923 /* This value should be larger than anything in LYStrings.h */
924 #define MOUSE_KEYSYM 0x0400
925 #endif
926 
927 /*
928  * For ncurses, we use the predefined keysyms, since that lets us also reuse
929  * the CSI logic and other special cases for VMS, NCSA telnet, etc.
930  */
931 #ifdef USE_SLANG
932 # ifdef VMS
933 #  define EXTERN_KEY(string,string1,lynx,curses) {string,lynx}
934 # else
935 #  define EXTERN_KEY(string,string1,lynx,curses) {string,lynx},{string1,lynx}
936 # endif
937 # define INTERN_KEY(string,lynx,curses)          {string,lynx}
938 #else
939 #define INTERN_KEY(string,lynx,curses)           {string,curses}
940 #define EXTERN_KEY(string,string1,lynx,curses)   {string,curses}
941 #endif
942 
943 typedef struct {
944     const char *string;
945     int value;
946 } Keysym_String_List;
947 /* *INDENT-OFF* */
948 static Keysym_String_List Keysym_Strings [] =
949 {
950     INTERN_KEY( "UPARROW",	UPARROW,	KEY_UP ),
951     INTERN_KEY( "DNARROW",	DNARROW,	KEY_DOWN ),
952     INTERN_KEY( "RTARROW",	RTARROW,	KEY_RIGHT ),
953     INTERN_KEY( "LTARROW",	LTARROW,	KEY_LEFT ),
954     INTERN_KEY( "PGDOWN",	PGDOWN,		KEY_NPAGE ),
955     INTERN_KEY( "PGUP",		PGUP,		KEY_PPAGE ),
956     INTERN_KEY( "HOME",		HOME,		KEY_HOME ),
957     INTERN_KEY( "END",		END_KEY,	KEY_END ),
958     INTERN_KEY( "F1",		F1,		KEY_F(1) ),
959     INTERN_KEY( "DO_KEY",	DO_KEY,		KEY_F(16) ),
960     INTERN_KEY( "FIND_KEY",	FIND_KEY,	KEY_FIND ),
961     INTERN_KEY( "SELECT_KEY",	SELECT_KEY,	KEY_SELECT ),
962     INTERN_KEY( "INSERT_KEY",	INSERT_KEY,	KEY_IC ),
963     INTERN_KEY( "REMOVE_KEY",	REMOVE_KEY,	KEY_DC ),
964     INTERN_KEY( "DO_NOTHING",	DO_NOTHING,	DO_NOTHING|LKC_ISLKC ),
965     INTERN_KEY( NULL,		-1,		ERR )
966 };
967 /* *INDENT-ON* */
968 
969 #ifdef NCURSES_VERSION
970 /*
971  * Ncurses stores the termcap/terminfo names in arrays sorted to match the
972  * array of strings in the TERMTYPE struct.
973  */
lookup_tiname(char * name,NCURSES_CONST char * const * names)974 static int lookup_tiname(char *name, NCURSES_CONST char *const *names)
975 {
976     int code;
977 
978     for (code = 0; names[code] != 0; code++)
979 	if (!strcmp(names[code], name))
980 	    return code;
981     return -1;
982 }
983 
expand_tiname(const char * first,size_t len,char ** result,char * final)984 static const char *expand_tiname(const char *first, size_t len, char **result, char *final)
985 {
986     char name[BUFSIZ];
987     int code;
988 
989     LYStrNCpy(name, first, len);
990     if ((code = lookup_tiname(name, strnames)) >= 0
991 	|| (code = lookup_tiname(name, strfnames)) >= 0) {
992 	if (cur_term->type.Strings[code] != 0) {
993 	    LYStrNCpy(*result, cur_term->type.Strings[code], (final - *result));
994 	    (*result) += strlen(*result);
995 	}
996     }
997     return first + len;
998 }
999 
expand_tichar(const char * first,char ** result,char * final)1000 static const char *expand_tichar(const char *first, char **result, char *final)
1001 {
1002     int ch;
1003     int limit = 0;
1004     int radix = 0;
1005     int value = 0;
1006     const char *name = 0;
1007 
1008     switch (ch = *first++) {
1009     case 'E':
1010     case 'e':
1011 	value = 27;
1012 	break;
1013     case 'a':
1014 	name = "bel";
1015 	break;
1016     case 'b':
1017 	value = '\b';
1018 	break;
1019     case 'f':
1020 	value = '\f';
1021 	break;
1022     case 'n':
1023 	value = '\n';
1024 	break;
1025     case 'r':
1026 	value = '\r';
1027 	break;
1028     case 't':
1029 	value = '\t';
1030 	break;
1031     case 'v':
1032 	value = '\v';
1033 	break;
1034     case 'd':
1035 	radix = 10;
1036 	limit = 3;
1037 	break;
1038     case 'x':
1039 	radix = 16;
1040 	limit = 2;
1041 	break;
1042     default:
1043 	if (isdigit(ch)) {
1044 	    radix = 8;
1045 	    limit = 3;
1046 	    first--;
1047 	} else {
1048 	    value = *first;
1049 	}
1050 	break;
1051     }
1052 
1053     if (radix != 0) {
1054 	char *last = 0;
1055 	char tmp[80];
1056 
1057 	LYStrNCpy(tmp, first, limit);
1058 	value = (int) strtol(tmp, &last, radix);
1059 	if (last != 0 && last != tmp)
1060 	    first += (last - tmp);
1061     }
1062 
1063     if (name != 0) {
1064 	(void) expand_tiname(name, strlen(name), result, final);
1065     } else {
1066 	**result = (char) value;
1067 	(*result) += 1;
1068     }
1069 
1070     return first;
1071 }
1072 
expand_substring(char * dst,const char * first,const char * last,char * final)1073 static BOOLEAN expand_substring(char *dst,
1074 				const char *first,
1075 				const char *last,
1076 				char *final)
1077 {
1078     int ch;
1079 
1080     while (first < last) {
1081 	switch (ch = *first++) {
1082 	case ESCAPE:
1083 	    first = expand_tichar(first, &dst, final);
1084 	    break;
1085 	case '^':
1086 	    ch = *first++;
1087 	    if (ch == LPAREN) {
1088 		const char *s = strchr(first, RPAREN);
1089 		char *was = dst;
1090 
1091 		if (s == 0)
1092 		    s = first + strlen(first);
1093 		first = expand_tiname(first, (size_t) (s - first), &dst, final);
1094 		if (dst == was)
1095 		    return FALSE;
1096 		if (*first)
1097 		    first++;
1098 	    } else if (ch == '?') {	/* ASCII delete? */
1099 		*dst++ = 127;
1100 	    } else if ((ch & 0x3f) < 0x20) {	/* ASCII control char? */
1101 		*dst++ = (char) (ch & 0x1f);
1102 	    } else {
1103 		*dst++ = '^';
1104 		first--;	/* not legal... */
1105 	    }
1106 	    break;
1107 	case 0:		/* convert nulls for terminfo */
1108 	    ch = 0200;
1109 	    /* FALLTHRU */
1110 	default:
1111 	    *dst++ = (char) ch;
1112 	    break;
1113 	}
1114     }
1115     *dst = '\0';
1116     return TRUE;
1117 }
1118 #endif
1119 
unescaped_char(const char * parse,int * keysym)1120 static void unescaped_char(const char *parse, int *keysym)
1121 {
1122     size_t len = strlen(parse);
1123     char buf[BUFSIZ];
1124 
1125     if (len >= 3) {
1126 	(void) expand_substring(buf,
1127 				parse + 1,
1128 				parse + len - 1,
1129 				buf + sizeof(buf) - 1);
1130 	if (strlen(buf) == 1)
1131 	    *keysym = *buf;
1132     }
1133 }
1134 
unescape_string(char * src,char * dst,char * final)1135 static BOOLEAN unescape_string(char *src, char *dst, char *final)
1136 {
1137     BOOLEAN ok = FALSE;
1138 
1139     if (*src == SQUOTE) {
1140 	int keysym = -1;
1141 
1142 	unescaped_char(src, &keysym);
1143 	if (keysym >= 0) {
1144 	    dst[0] = (char) keysym;
1145 	    dst[1] = '\0';
1146 	    ok = TRUE;
1147 	}
1148     } else if (*src == DQUOTE) {
1149 	if (expand_substring(dst, src + 1, src + strlen(src) - 1, final))
1150 	    ok = TRUE;
1151 	(void) final;
1152     }
1153     return ok;
1154 }
1155 
map_string_to_keysym(const char * str,int * keysym)1156 int map_string_to_keysym(const char *str, int *keysym)
1157 {
1158     int modifier = 0;
1159 
1160     *keysym = -1;
1161 
1162     if (strncasecomp(str, "LAC:", 4) == 0) {
1163 	char *other = strchr(str + 4, ':');
1164 
1165 	if (other) {
1166 	    int othersym = lecname_to_lec(other + 1);
1167 	    char buf[BUFSIZ];
1168 
1169 	    if (othersym >= 0 && other - str - 4 < BUFSIZ) {
1170 		LYStrNCpy(buf, str + 4, (other - str - 4));
1171 		*keysym = lacname_to_lac(buf);
1172 		if (*keysym >= 0) {
1173 		    *keysym = LACLEC_TO_LKC0(*keysym, othersym);
1174 		    return (*keysym);
1175 		}
1176 	    }
1177 	}
1178 	*keysym = lacname_to_lac(str + 4);
1179 	if (*keysym >= 0) {
1180 	    *keysym = LAC_TO_LKC0(*keysym);
1181 	    return (*keysym);
1182 	}
1183     }
1184     if (strncasecomp(str, "Meta-", 5) == 0) {
1185 	str += 5;
1186 	modifier = LKC_MOD2;
1187 	if (*str) {
1188 	    size_t len = strlen(str);
1189 
1190 	    if (len == 1) {
1191 		return (*keysym = (UCH(str[0])) | modifier);
1192 	    } else if (len == 2 && str[0] == '^' &&
1193 		       (isalpha(UCH(str[1])) ||
1194 			(TOASCII(str[1]) >= '@' && TOASCII(str[1]) <= '_'))) {
1195 		return (*keysym = FROMASCII(UCH(str[1] & 0x1f)) | modifier);
1196 	    } else if (len == 2 && str[0] == '^' &&
1197 		       str[1] == '?') {
1198 		return (*keysym = CH_DEL | modifier);
1199 	    }
1200 	    if (*str == '^' || *str == '\\') {
1201 		char buf[BUFSIZ];
1202 
1203 		(void) expand_substring(buf,
1204 					str,
1205 					str + HTMIN(len, 28),
1206 					buf + sizeof(buf) - 1);
1207 		if (strlen(buf) <= 1)
1208 		    return (*keysym = (UCH(buf[0])) | modifier);
1209 	    }
1210 	}
1211     }
1212     if (*str == SQUOTE) {
1213 	unescaped_char(str, keysym);
1214     } else if (isdigit(UCH(*str))) {
1215 	char *tmp;
1216 	long value = strtol(str, &tmp, 0);
1217 
1218 	if (!isalnum(UCH(*tmp))) {
1219 	    *keysym = (int) value;
1220 #ifndef USE_SLANG
1221 	    if (*keysym > 255)
1222 		*keysym |= LKC_ISLKC;	/* caller should remove this flag - kw */
1223 #endif
1224 	}
1225     } else {
1226 	Keysym_String_List *k;
1227 
1228 	k = Keysym_Strings;
1229 	while (k->string != NULL) {
1230 	    if (0 == strcmp(k->string, str)) {
1231 		*keysym = k->value;
1232 		break;
1233 	    }
1234 	    k++;
1235 	}
1236     }
1237 
1238     if (*keysym >= 0)
1239 	*keysym |= modifier;
1240     return (*keysym);
1241 }
1242 
1243 /*
1244  * Starting at a nonblank character, skip over a token, counting quoted and
1245  * escaped characters.
1246  */
skip_keysym(char * parse)1247 static char *skip_keysym(char *parse)
1248 {
1249     int quoted = 0;
1250     int escaped = 0;
1251 
1252     while (*parse) {
1253 	if (escaped) {
1254 	    escaped = 0;
1255 	} else if (quoted) {
1256 	    if (*parse == ESCAPE) {
1257 		escaped = 1;
1258 	    } else if (*parse == quoted) {
1259 		quoted = 0;
1260 	    }
1261 	} else if (*parse == ESCAPE) {
1262 	    escaped = 1;
1263 	} else if (*parse == DQUOTE || *parse == SQUOTE) {
1264 	    quoted = *parse;
1265 	} else if (isspace(UCH(*parse))) {
1266 	    break;
1267 	}
1268 	parse++;
1269     }
1270     return (quoted || escaped) ? 0 : parse;
1271 }
1272 
1273 /*
1274  * The first token is the string to define, the second is the name (of the
1275  * keysym) to define it to.
1276  */
1277 #define MY_TRACE(p) CTRACE2(TRACE_CFG, p)
1278 
setkey_cmd(char * parse)1279 static int setkey_cmd(char *parse)
1280 {
1281     char *s, *t;
1282     int keysym;
1283     char buf[BUFSIZ];
1284 
1285     MY_TRACE((tfp, "KEYMAP(PA): in=%s", parse));	/* \n-terminated */
1286     if ((s = skip_keysym(parse)) != 0) {
1287 	if (isspace(UCH(*s))) {
1288 	    *s++ = '\0';
1289 	    s = LYSkipBlanks(s);
1290 	    if ((t = skip_keysym(s)) == 0) {
1291 		MY_TRACE((tfp, "KEYMAP(SKIP) no key expansion found\n"));
1292 		return -1;
1293 	    }
1294 	    if (t != s)
1295 		*t = '\0';
1296 	    if (map_string_to_keysym(s, &keysym) >= 0) {
1297 		if (!unescape_string(parse, buf, buf + sizeof(buf) - 1)) {
1298 		    MY_TRACE((tfp, "KEYMAP(SKIP) could unescape key\n"));
1299 		    return 0;	/* Trace the failure and continue. */
1300 		}
1301 		if (LYTraceLogFP == 0) {
1302 		    MY_TRACE((tfp, "KEYMAP(DEF) keysym=%#x\n", keysym));
1303 		} else {
1304 		    MY_TRACE((tfp, "KEYMAP(DEF) keysym=%#x, seq='%s'\n",
1305 			      keysym, buf));
1306 		}
1307 		return define_key(buf, keysym);
1308 	    } else {
1309 		MY_TRACE((tfp, "KEYMAP(SKIP) could not map to keysym\n"));
1310 	    }
1311 	} else {
1312 	    MY_TRACE((tfp, "KEYMAP(SKIP) junk after key description: '%s'\n", s));
1313 	}
1314     } else {
1315 	MY_TRACE((tfp, "KEYMAP(SKIP) no key description\n"));
1316     }
1317     return -1;
1318 }
1319 #undef MY_TRACE
1320 
unsetkey_cmd(char * parse)1321 static int unsetkey_cmd(char *parse)
1322 {
1323     char *s = skip_keysym(parse);
1324 
1325     if (s != parse) {
1326 	*s = '\0';
1327 #ifdef NCURSES_VERSION
1328 	/*
1329 	 * This won't work with Slang.  Remove the definition for the given
1330 	 * keysym.
1331 	 */
1332 	{
1333 	    int keysym;
1334 
1335 	    if (map_string_to_keysym(parse, &keysym) >= 0)
1336 		define_key((char *) 0, keysym);
1337 	}
1338 #endif
1339 #ifdef USE_SLANG
1340 	/* Slang implements this, for undefining the string which is associated
1341 	 * with a keysym (the reverse of what we normally want, but may
1342 	 * occasionally find useful).
1343 	 */
1344 	SLang_undefine_key(parse, Keymap_List);
1345 	if (SLang_get_error())
1346 	    return -1;
1347 #endif
1348     }
1349     return 0;
1350 }
1351 
1352 #ifdef FNAMES_8_3
1353 #define FNAME_LYNX_KEYMAPS "_lynxkey.map"
1354 #else
1355 #define FNAME_LYNX_KEYMAPS ".lynx-keymaps"
1356 #endif /* FNAMES_8_3 */
1357 
read_keymap_file(void)1358 static int read_keymap_file(void)
1359 {
1360     /* *INDENT-OFF* */
1361     static struct {
1362 	const char *name;
1363 	int (*func) (char *s);
1364     } table[] = {
1365 	{ "setkey",   setkey_cmd },
1366 	{ "unsetkey", unsetkey_cmd },
1367     };
1368     /* *INDENT-ON* */
1369 
1370     char *line = NULL;
1371     FILE *fp;
1372     char file[LY_MAXPATH];
1373     int linenum;
1374     size_t n;
1375 
1376     LYAddPathToHome(file, sizeof(file), FNAME_LYNX_KEYMAPS);
1377 
1378     if ((fp = fopen(file, "r")) == 0)
1379 	return 0;
1380 
1381     linenum = 0;
1382     while (LYSafeGets(&line, fp) != 0) {
1383 	char *s = LYSkipBlanks(line);
1384 
1385 	linenum++;
1386 
1387 	if ((*s == 0) || (*s == '#'))
1388 	    continue;
1389 
1390 	for (n = 0; n < TABLESIZE(table); n++) {
1391 	    size_t len = strlen(table[n].name);
1392 
1393 	    if (strlen(s) > len && !StrNCmp(s, table[n].name, len)
1394 		&& (*(table[n].func)) (LYSkipBlanks(s + len)) < 0)
1395 		fprintf(stderr, FAILED_READING_KEYMAP, linenum, file);
1396 	}
1397     }
1398     FREE(line);
1399     LYCloseInput(fp);
1400     return 0;
1401 }
1402 
setup_vtXXX_keymap(void)1403 static void setup_vtXXX_keymap(void)
1404 {
1405     /* *INDENT-OFF* */
1406     static Keysym_String_List table[] = {
1407 	INTERN_KEY( "\033[A",	UPARROW,	KEY_UP ),
1408 	INTERN_KEY( "\033OA",	UPARROW,	KEY_UP ),
1409 	INTERN_KEY( "\033[B",	DNARROW,	KEY_DOWN ),
1410 	INTERN_KEY( "\033OB",	DNARROW,	KEY_DOWN ),
1411 	INTERN_KEY( "\033[C",	RTARROW,	KEY_RIGHT ),
1412 	INTERN_KEY( "\033OC",	RTARROW,	KEY_RIGHT ),
1413 	INTERN_KEY( "\033[D",	LTARROW,	KEY_LEFT ),
1414 	INTERN_KEY( "\033OD",	LTARROW,	KEY_LEFT ),
1415 	INTERN_KEY( "\033[1~",	FIND_KEY,	KEY_FIND ),
1416 	INTERN_KEY( "\033[2~",	INSERT_KEY,	KEY_IC ),
1417 	INTERN_KEY( "\033[3~",	REMOVE_KEY,	KEY_DC ),
1418 	INTERN_KEY( "\033[4~",	SELECT_KEY,	KEY_SELECT ),
1419 	INTERN_KEY( "\033[5~",	PGUP,		KEY_PPAGE ),
1420 	INTERN_KEY( "\033[6~",	PGDOWN,		KEY_NPAGE ),
1421 	INTERN_KEY( "\033[7~",	HOME,		KEY_HOME),
1422 	INTERN_KEY( "\033[8~",	END_KEY,	KEY_END ),
1423 	INTERN_KEY( "\033[11~",	F1,		KEY_F(1) ),
1424 	INTERN_KEY( "\033[28~",	F1,		KEY_F(1) ),
1425 	INTERN_KEY( "\033OP",	F1,		KEY_F(1) ),
1426 	INTERN_KEY( "\033[OP",	F1,		KEY_F(1) ),
1427 	INTERN_KEY( "\033[29~",	DO_KEY,		KEY_F(16) ),
1428 #if defined(USE_SLANG) && (defined(__WIN32__) || defined(__MINGW32__))
1429 	INTERN_KEY( "\xE0H",	UPARROW,	KEY_UP ),
1430 	INTERN_KEY( "\xE0P",	DNARROW,	KEY_DOWN ),
1431 	INTERN_KEY( "\xE0M",	RTARROW,	KEY_RIGHT ),
1432 	INTERN_KEY( "\xE0K",	LTARROW,	KEY_LEFT ),
1433 	INTERN_KEY( "\xE0R",	INSERT_KEY,	KEY_IC ),
1434 	INTERN_KEY( "\xE0S",	REMOVE_KEY,	KEY_DC ),
1435 	INTERN_KEY( "\xE0I",	PGUP,		KEY_PPAGE ),
1436 	INTERN_KEY( "\xE0Q",	PGDOWN,		KEY_NPAGE ),
1437 	INTERN_KEY( "\xE0G",	HOME,		KEY_HOME),
1438 	INTERN_KEY( "\xE0O",	END_KEY,	KEY_END ),
1439 #endif
1440 #if defined(USE_SLANG) && !defined(VMS)
1441 	INTERN_KEY(	"^(ku)", UPARROW,	KEY_UP ),
1442 	INTERN_KEY(	"^(kd)", DNARROW,	KEY_DOWN ),
1443 	INTERN_KEY(	"^(kr)", RTARROW,	KEY_RIGHT ),
1444 	INTERN_KEY(	"^(kl)", LTARROW,	KEY_LEFT ),
1445 	INTERN_KEY(	"^(@0)", FIND_KEY,	KEY_FIND ),
1446 	INTERN_KEY(	"^(kI)", INSERT_KEY,	KEY_IC ),
1447 	INTERN_KEY(	"^(kD)", REMOVE_KEY,	KEY_DC ),
1448 	INTERN_KEY(	"^(*6)", SELECT_KEY,	KEY_SELECT ),
1449 	INTERN_KEY(	"^(kP)", PGUP,		KEY_PPAGE ),
1450 	INTERN_KEY(	"^(kN)", PGDOWN,	KEY_NPAGE ),
1451 	INTERN_KEY(	"^(@7)", END_KEY,	KEY_END ),
1452 	INTERN_KEY(	"^(kh)", HOME,		KEY_HOME),
1453 	INTERN_KEY(	"^(k1)", F1,		KEY_F(1) ),
1454 	INTERN_KEY(	"^(F6)", DO_KEY,	KEY_F(16) ),
1455 #endif /* SLANG && !VMS */
1456     };
1457     /* *INDENT-ON* */
1458 
1459     size_t n;
1460 
1461     for (n = 0; n < TABLESIZE(table); n++)
1462 	define_key(table[n].string, table[n].value);
1463 }
1464 
lynx_initialize_keymaps(void)1465 int lynx_initialize_keymaps(void)
1466 {
1467 #ifdef USE_SLANG
1468     int i;
1469     char keybuf[2];
1470 
1471     /* The escape sequences may contain embedded termcap strings.  Make
1472      * sure the library is initialized for that.
1473      */
1474     SLtt_get_terminfo();
1475 
1476     if (NULL == (Keymap_List = SLang_create_keymap("Lynx", NULL)))
1477 	return -1;
1478 
1479     keybuf[1] = 0;
1480     for (i = 1; i < 256; i++) {
1481 	keybuf[0] = (char) i;
1482 	define_key(keybuf, i);
1483     }
1484 
1485     setup_vtXXX_keymap();
1486     define_key("\033[M", MOUSE_KEYSYM);
1487 
1488     if (SLang_get_error())
1489 	SLang_exit_error("Unable to initialize keymaps");
1490 #else
1491     setup_vtXXX_keymap();
1492 #endif
1493     return read_keymap_file();
1494 }
1495 
1496 #endif /* USE_KEYMAPS */
1497 
1498 #if defined(USE_MOUSE) && (defined(NCURSES))
LYmouse_menu(int x,int y,int atlink,int code)1499 static int LYmouse_menu(int x, int y, int atlink, int code)
1500 {
1501 #define ENT_ONLY_DOC	1
1502 #define ENT_ONLY_LINK	2
1503     /* *INDENT-OFF* */
1504     static const struct {
1505 	const char *txt;
1506 	int  action;
1507 	unsigned int  flag;
1508     } possible_entries[] = {
1509 	{"Quit",			LYK_ABORT,		ENT_ONLY_DOC},
1510 	{"Home page",			LYK_MAIN_MENU,		ENT_ONLY_DOC},
1511 	{"Previous document",		LYK_PREV_DOC,		ENT_ONLY_DOC},
1512 	{"Beginning of document",	LYK_HOME,		ENT_ONLY_DOC},
1513 	{"Page up",			LYK_PREV_PAGE,		ENT_ONLY_DOC},
1514 	{"Half page up",		LYK_UP_HALF,		ENT_ONLY_DOC},
1515 	{"Two lines up",		LYK_UP_TWO,		ENT_ONLY_DOC},
1516 	{"History",			LYK_HISTORY,		ENT_ONLY_DOC},
1517 	{"Help",			LYK_HELP,		0},
1518 	{"Do nothing (refresh)",	LYK_REFRESH,		0},
1519 	{"Load again",			LYK_RELOAD,		ENT_ONLY_DOC},
1520 	{"Edit Doc URL and load",	LYK_ECGOTO,		ENT_ONLY_DOC},
1521 	{"Edit Link URL and load",	LYK_ELGOTO,		ENT_ONLY_LINK},
1522 	{"Show info",			LYK_INFO,		0},
1523 	{"Search",			LYK_WHEREIS,		ENT_ONLY_DOC},
1524 	{"Print",			LYK_PRINT,		ENT_ONLY_DOC},
1525 	{"Two lines down",		LYK_DOWN_TWO,		ENT_ONLY_DOC},
1526 	{"Half page down",		LYK_DOWN_HALF,		ENT_ONLY_DOC},
1527 	{"Page down",			LYK_NEXT_PAGE,		ENT_ONLY_DOC},
1528 	{"End of document",		LYK_END,		ENT_ONLY_DOC},
1529 	{"Bookmarks",			LYK_VIEW_BOOKMARK,	ENT_ONLY_DOC},
1530 	{"Cookie jar",			LYK_COOKIE_JAR,		ENT_ONLY_DOC},
1531 #ifdef USE_CACHEJAR
1532 	{"Cache jar",			LYK_CACHE_JAR,		ENT_ONLY_DOC},
1533 #endif
1534 	{"Search index",		LYK_INDEX_SEARCH,	ENT_ONLY_DOC},
1535 	{"Set Options",			LYK_OPTIONS,		ENT_ONLY_DOC},
1536 	{"Activate this link",		LYK_MOUSE_SUBMIT,	ENT_ONLY_LINK},
1537 	{"Download",			LYK_DOWNLOAD,		ENT_ONLY_LINK}
1538     };
1539     /* *INDENT-ON* */
1540 
1541 #define TOTAL_MENUENTRIES	TABLESIZE(possible_entries)
1542     const char *choices[TOTAL_MENUENTRIES + 1];
1543     int actions[TOTAL_MENUENTRIES];
1544 
1545     int c, c1, retlac;
1546     unsigned filter_out = (unsigned) (atlink ? ENT_ONLY_DOC : ENT_ONLY_LINK);
1547 
1548     c = c1 = 0;
1549     while (c < (int) TOTAL_MENUENTRIES) {
1550 	if (!(possible_entries[c].flag & filter_out)) {
1551 	    choices[c1] = possible_entries[c].txt;
1552 	    actions[c1++] = possible_entries[c].action;
1553 	}
1554 	c++;
1555     }
1556     choices[c1] = NULL;
1557 
1558     /* Somehow the mouse is over the number instead of being over the
1559        name, so we decrease x. */
1560     c = LYChoosePopup((atlink ? 2 : 10) - 1, y, (x > 5 ? x - 5 : 1),
1561 		      choices, c1, FALSE, TRUE);
1562 
1563     /*
1564      * LYhandlePopupList() wasn't really meant to be used outside of old-style
1565      * Options menu processing.  One result of mis-using it here is that we
1566      * have to deal with side-effects regarding SIGINT signal handler and the
1567      * term_options global variable.  - kw
1568      */
1569     if (term_options) {
1570 	retlac = LYK_DO_NOTHING;
1571 	term_options = FALSE;
1572     } else {
1573 	retlac = actions[c];
1574     }
1575 
1576     if (code == FOR_INPUT && mouse_link == -1) {
1577 	switch (retlac) {
1578 	case LYK_ABORT:
1579 	    retlac = LYK_QUIT;	/* a bit softer... */
1580 	    /* fall through */
1581 	case LYK_MAIN_MENU:
1582 	case LYK_PREV_DOC:
1583 	case LYK_HOME:
1584 	case LYK_PREV_PAGE:
1585 	case LYK_UP_HALF:
1586 	case LYK_UP_TWO:
1587 	case LYK_HISTORY:
1588 	case LYK_HELP:
1589 /*	    case LYK_REFRESH:*/
1590 	case LYK_RELOAD:
1591 	case LYK_ECGOTO:
1592 	case LYK_INFO:
1593 	case LYK_WHEREIS:
1594 	case LYK_PRINT:
1595 	case LYK_DOWN_TWO:
1596 	case LYK_DOWN_HALF:
1597 	case LYK_NEXT_PAGE:
1598 	case LYK_END:
1599 	case LYK_VIEW_BOOKMARK:
1600 	case LYK_COOKIE_JAR:
1601 #ifdef USE_CACHEJAR
1602 	case LYK_CACHE_JAR:
1603 #endif
1604 	case LYK_INDEX_SEARCH:
1605 	case LYK_OPTIONS:
1606 	    mouse_link = -3;	/* so LYgetch_for() passes it on - kw */
1607 	}
1608     }
1609     if (retlac == LYK_DO_NOTHING ||
1610 	retlac == LYK_REFRESH) {
1611 	mouse_link = -1;	/* mainloop should not change cur link - kw */
1612     }
1613     if (code == FOR_INPUT && retlac == LYK_DO_NOTHING) {
1614 	repaint_main_statusline(FOR_INPUT);
1615     }
1616     return retlac;
1617 }
1618 #endif /* USE_MOUSE && (NCURSES || PDCURSES) */
1619 
1620 #if defined(USE_KEYMAPS) && defined(USE_SLANG)
1621 /************************************************************************/
1622 
1623 static int current_sl_modifier = 0;
1624 
1625 /* We cannot guarantee the type for 'GetChar', and should not use a cast. */
myGetChar(void)1626 static int myGetChar(void)
1627 {
1628     int i = GetChar();
1629 
1630     if (i == 0)			/* trick to get NUL char through - kw */
1631 	current_sl_modifier = LKC_ISLKC;
1632     return i;
1633 }
1634 
LYgetch_for(int code)1635 static int LYgetch_for(int code)
1636 {
1637     SLang_Key_Type *key;
1638     int keysym;
1639 
1640     current_sl_modifier = 0;
1641 
1642     key = SLang_do_key(Keymap_List, myGetChar);
1643     if ((key == NULL) || (key->type != SLKEY_F_KEYSYM)) {
1644 #if defined(__WIN32__) || defined(__MINGW32__)
1645 	if ((key == NULL) && (current_sl_modifier == LKC_ISLKC)) {
1646 	    key = SLang_do_key(Keymap_List, myGetChar);
1647 	    keysym = key->f.keysym;
1648 	    switch (keysym) {
1649 	    case 'H':
1650 		keysym = UPARROW;
1651 		break;
1652 	    case 'P':
1653 		keysym = DNARROW;
1654 		break;
1655 	    case 'M':
1656 		keysym = RTARROW;
1657 		break;
1658 	    case 'K':
1659 		keysym = LTARROW;
1660 		break;
1661 	    case 'R':
1662 		keysym = INSERT_KEY;
1663 		break;
1664 	    case 'S':
1665 		keysym = REMOVE_KEY;
1666 		break;
1667 	    case 'I':
1668 		keysym = PGUP;
1669 		break;
1670 	    case 'Q':
1671 		keysym = PGDOWN;
1672 		break;
1673 	    case 'G':
1674 		keysym = HOME;
1675 		break;
1676 	    case 'O':
1677 		keysym = END_KEY;
1678 		break;
1679 	    case ';':
1680 		keysym = F1;
1681 		break;
1682 	    }
1683 	    return (keysym);
1684 	}
1685 #endif
1686 
1687 	return (current_sl_modifier ? 0 : DO_NOTHING);
1688     }
1689 
1690     keysym = (int) key->f.keysym;
1691 
1692 #if defined (USE_MOUSE)
1693     if (keysym == MOUSE_KEYSYM)
1694 	return sl_read_mouse_event(code);
1695 #endif
1696 
1697     if (keysym < 0)
1698 	return 0;
1699 
1700     if (keysym & (LKC_ISLECLAC | LKC_ISLAC))
1701 	return (keysym);
1702 
1703     current_sl_modifier = 0;
1704     if (LKC_HAS_ESC_MOD(keysym)) {
1705 	current_sl_modifier = LKC_MOD2;
1706 	keysym &= LKC_MASK;
1707     }
1708 
1709     if (keysym + 1 >= KEYMAP_SIZE)
1710 	return 0;
1711 
1712     return (keysym | current_sl_modifier);
1713 }
1714 
1715 /************************************************************************/
1716 #else /* NOT  defined(USE_KEYMAPS) && defined(USE_SLANG) */
1717 
1718 /*
1719  * LYgetch() translates some escape sequences and may fake noecho.
1720  */
1721 #define found_CSI(first,second) ((second) == '[' || (first) == 155)
1722 #define found_TLD(value)	((value) == '~')
1723 
LYgetch_for(int code)1724 static int LYgetch_for(int code)
1725 {
1726     int a, b, c, d = -1;
1727     int current_modifier = 0;
1728     BOOLEAN done_esc = FALSE;
1729 
1730     (void) code;
1731 
1732     have_levent = 0;
1733 
1734   re_read:
1735 #if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
1736     if (errno == EINTR)
1737 	set_errno(0);		/* reset - kw */
1738 #endif /* UCX && VAXC */
1739 #ifndef USE_SLANG
1740     clearerr(stdin);		/* needed here for ultrix and SOCKETSHR, but why? - FM */
1741 #endif /* !USE_SLANG */
1742 #if !defined(USE_SLANG) || defined(VMS) || defined(DJGPP_KEYHANDLER)
1743     c = GetChar();
1744     lynx_nl2crlf(FALSE);
1745 #else
1746     if (LYCursesON) {
1747 	c = GetChar();
1748 	lynx_nl2crlf(FALSE);
1749     } else {
1750 	c = getchar();
1751 	if (c == EOF && errno == EINTR)		/* Ctrl-Z causes EINTR in getchar() */
1752 	    clearerr(stdin);
1753 	if (feof(stdin) || ferror(stdin) || c == EOF) {
1754 #ifdef IGNORE_CTRL_C
1755 	    if (sigint)
1756 		sigint = FALSE;
1757 #endif /* IGNORE_CTRL_C */
1758 	    CTRACE((tfp, "GETCH: Translate ^C to ^G.\n"));
1759 	    return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
1760 	}
1761     }
1762 #endif /* !USE_SLANG || VMS */
1763 
1764     CTRACE((tfp, "GETCH%d: Got %#x.\n", code, c));
1765 #ifdef MISC_EXP
1766     if (LYNoZapKey > 1 && errno != EINTR &&
1767 	(c == EOF
1768 #ifdef USE_SLANG
1769 	 || (c == 0xFFFF)
1770 #endif
1771 	)) {
1772 
1773 	CTRACE((tfp,
1774 		"nozap: Got EOF, curses %s, stdin is %p, LYNoZapKey reduced from %d to 0.\n",
1775 		LYCursesON ? "on" : "off", (void *) stdin, LYNoZapKey));
1776 	LYNoZapKey = 0;		/* 2 -> 0 */
1777 	if (LYReopenInput() > 0) {
1778 	    if (LYCursesON) {
1779 		stop_curses();
1780 		start_curses();
1781 		LYrefresh();
1782 	    }
1783 	    goto re_read;
1784 	}
1785     }
1786 #endif /* MISC_EXP */
1787 
1788 #ifdef USE_GETCHAR
1789     if (c == EOF && errno == EINTR)	/* Ctrl-Z causes EINTR in getchar() */
1790 	goto re_read;
1791 #else
1792     if (c == EOF && errno == EINTR) {
1793 
1794 #if defined(HAVE_SIZECHANGE) || defined(USE_SLANG)
1795 	CTRACE((tfp, "Got EOF with EINTR, recent_sizechange so far is %d\n",
1796 		recent_sizechange));
1797 	if (!recent_sizechange) {	/* not yet detected by ourselves */
1798 	    size_change(0);
1799 	    CTRACE((tfp, "Now recent_sizechange is %d\n", recent_sizechange));
1800 	}
1801 #else /* HAVE_SIZECHANGE || USE_SLANG */
1802 	CTRACE((tfp, "Got EOF with EINTR, recent_sizechange is %d\n",
1803 		recent_sizechange));
1804 #endif /* HAVE_SIZECHANGE || USE_SLANG */
1805 #if !defined(UCX) || !defined(VAXC)	/* errno not modifiable ? */
1806 	set_errno(0);		/* reset - kw */
1807 #endif /* UCX && VAXC */
1808 	return (DO_NOTHING);
1809     }
1810 #endif /* USE_GETCHAR */
1811 
1812 #ifdef USE_SLANG
1813     if (c == 0xFFFF && LYCursesON) {
1814 #ifdef IGNORE_CTRL_C
1815 	if (sigint) {
1816 	    sigint = FALSE;
1817 	    goto re_read;
1818 	}
1819 #endif /* IGNORE_CTRL_C */
1820 	return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
1821     }
1822 #else /* not USE_SLANG: */
1823     if (feof(stdin) || ferror(stdin) || c == EOF) {
1824 	if (recent_sizechange)
1825 	    return (LYCharINTERRUPT2);	/* use ^G to cancel whatever called us. */
1826 #ifdef IGNORE_CTRL_C
1827 	if (sigint) {
1828 	    sigint = FALSE;
1829 	    /* clearerr(stdin);  don't need here if stays above - FM */
1830 	    goto re_read;
1831 	}
1832 #endif /* IGNORE_CTRL_C */
1833 #if !defined(USE_GETCHAR) && !defined(VMS) && !defined(NCURSES)
1834 	if (c == ERR && errno == EINTR)		/* may have been handled signal - kw */
1835 	    goto re_read;
1836 #endif /* USE_GETCHAR */
1837 
1838 	cleanup();
1839 	exit_immediately(EXIT_SUCCESS);
1840     }
1841 #endif /* USE_SLANG */
1842 
1843     if (!escape_bound
1844 	&& (c == CH_ESC || (csi_is_csi && c == UCH(CH_ESC_PAR)))) {
1845 	/* handle escape sequence  S/390 -- gil -- 2024 */
1846 	done_esc = TRUE;	/* Flag: we did it, not keypad() */
1847 	b = GetChar();
1848 
1849 	if (b == '[' || b == 'O') {
1850 	    a = GetChar();
1851 	} else {
1852 	    a = b;
1853 	}
1854 
1855 	switch (a) {
1856 	case 'A':
1857 	    c = UPARROW;
1858 	    break;
1859 	case 'B':
1860 	    c = DNARROW;
1861 	    break;
1862 	case 'C':
1863 	    c = RTARROW;
1864 	    break;
1865 	case 'D':
1866 	    c = LTARROW;
1867 	    break;
1868 	case 'q':		/* vt100 application keypad 1 */
1869 	    c = END_KEY;
1870 	    break;
1871 	case 'r':		/* vt100 application keypad 2 */
1872 	    c = DNARROW;
1873 	    break;
1874 	case 's':		/* vt100 application keypad 3 */
1875 	    c = PGDOWN;
1876 	    break;
1877 	case 't':		/* vt100 application keypad 4 */
1878 	    c = LTARROW;
1879 	    break;
1880 	case 'v':		/* vt100 application keypad 6 */
1881 	    c = RTARROW;
1882 	    break;
1883 	case 'w':		/* vt100 application keypad 7 */
1884 	    c = HOME;
1885 	    break;
1886 	case 'x':		/* vt100 application keypad 8 */
1887 	    c = UPARROW;
1888 	    break;
1889 	case 'y':		/* vt100 application keypad 9 */
1890 	    c = PGUP;
1891 	    break;
1892 	case 'M':
1893 #if defined(USE_SLANG) && defined(USE_MOUSE)
1894 	    if (found_CSI(c, b)) {
1895 		c = sl_read_mouse_event(code);
1896 	    } else
1897 #endif
1898 		c = '\n';	/* keypad enter on pc ncsa telnet */
1899 	    break;
1900 
1901 	case 'm':
1902 #ifdef VMS
1903 	    if (b != 'O')
1904 #endif /* VMS */
1905 		c = '-';	/* keypad on pc ncsa telnet */
1906 	    break;
1907 	case 'k':
1908 	    if (b == 'O')
1909 		c = '+';	/* keypad + on my xterminal :) */
1910 	    else
1911 		done_esc = FALSE;	/* we have another look below - kw */
1912 	    break;
1913 	case 'l':
1914 #ifdef VMS
1915 	    if (b != 'O')
1916 #endif /* VMS */
1917 		c = '+';	/* keypad on pc ncsa telnet */
1918 	    break;
1919 	case 'P':
1920 #ifdef VMS
1921 	    if (b != 'O')
1922 #endif /* VMS */
1923 		c = F1;
1924 	    break;
1925 	case 'u':
1926 #ifdef VMS
1927 	    if (b != 'O')
1928 #endif /* VMS */
1929 		c = F1;		/* macintosh help button */
1930 	    break;
1931 	case 'p':
1932 #ifdef VMS
1933 	    if (b == 'O')
1934 #endif /* VMS */
1935 		c = '0';	/* keypad 0 */
1936 	    break;
1937 	case '1':		/* VTxxx  Find  */
1938 	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
1939 		c = FIND_KEY;
1940 	    else
1941 		done_esc = FALSE;	/* we have another look below - kw */
1942 	    break;
1943 	case '2':
1944 	    if (found_CSI(c, b)) {
1945 		if (found_TLD(d = GetChar()))	/* VTxxx Insert */
1946 		    c = INSERT_KEY;
1947 		else if ((d == '8' ||
1948 			  d == '9') &&
1949 			 found_TLD(GetChar())) {
1950 		    if (d == '8')	/* VTxxx   Help */
1951 			c = F1;
1952 		    else if (d == '9')	/* VTxxx    Do  */
1953 			c = DO_KEY;
1954 		    d = -1;
1955 		}
1956 	    } else
1957 		done_esc = FALSE;	/* we have another look below - kw */
1958 	    break;
1959 	case '3':			     /** VTxxx Delete **/
1960 	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
1961 		c = REMOVE_KEY;
1962 	    else
1963 		done_esc = FALSE;	/* we have another look below - kw */
1964 	    break;
1965 	case '4':			     /** VTxxx Select **/
1966 	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
1967 		c = SELECT_KEY;
1968 	    else
1969 		done_esc = FALSE;	/* we have another look below - kw */
1970 	    break;
1971 	case '5':			     /** VTxxx PrevScreen **/
1972 	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
1973 		c = PGUP;
1974 	    else
1975 		done_esc = FALSE;	/* we have another look below - kw */
1976 	    break;
1977 	case '6':			     /** VTxxx NextScreen **/
1978 	    if (found_CSI(c, b) && found_TLD(d = GetChar()))
1979 		c = PGDOWN;
1980 	    else
1981 		done_esc = FALSE;	/* we have another look below - kw */
1982 	    break;
1983 	case '[':			     /** Linux F1-F5: ^[[[A etc. **/
1984 	    if (found_CSI(c, b)) {
1985 		if ((d = GetChar()) == 'A')
1986 		    c = F1;
1987 		break;
1988 	    }
1989 	    /* FALLTHRU */
1990 	default:
1991 	    if (c == CH_ESC && a == b && !found_CSI(c, b)) {
1992 		current_modifier = LKC_MOD2;
1993 		c = a;
1994 		/* We're not yet done if ESC + curses-keysym: */
1995 		done_esc = (BOOL) ((a & ~0xFF) == 0);
1996 		break;
1997 	    }
1998 	    CTRACE((tfp, "Unknown key sequence: %d:%d:%d\n", c, b, a));
1999 	    CTRACE_SLEEP(MessageSecs);
2000 	    break;
2001 	}
2002 	if (isdigit(a) && found_CSI(c, b) && d != -1 && !found_TLD(d))
2003 	    d = GetChar();
2004 	if (!done_esc && (a & ~0xFF) == 0) {
2005 	    if (a == b && !found_CSI(c, b) && c == CH_ESC) {
2006 		current_modifier = LKC_MOD2;
2007 		c = a;
2008 		done_esc = TRUE;
2009 	    } else {
2010 		done_esc = TRUE;
2011 	    }
2012 	}
2013     }
2014 #ifdef USE_KEYMAPS
2015     /* Extract a single code if two are merged: */
2016     if (c >= 0 && (c & LKC_ISLECLAC)) {
2017 	if (!(code == FOR_INPUT || code == FOR_PROMPT))
2018 	    c = LKC2_TO_LKC(c);
2019     } else if (c >= 0 && (c & LKC_ISLKC)) {
2020 	c &= ~LKC_ISLKC;
2021 	done_esc = TRUE;	/* already a lynxkeycode, skip keypad switches - kw */
2022     }
2023     if (c >= 0 && LKC_HAS_ESC_MOD(c)) {
2024 	current_modifier = LKC_MOD2;
2025 	c &= LKC_MASK;
2026     }
2027     if (c >= 0 && (c & (LKC_ISLECLAC | LKC_ISLAC))) {
2028 	done_esc = TRUE;	/* already a lynxactioncode, skip keypad switches - iz */
2029     }
2030 #endif
2031     if (done_esc) {
2032 	/* don't do keypad() switches below, we already got it - kw */
2033     } else {
2034 #ifdef HAVE_KEYPAD
2035 	/*
2036 	 * Convert keypad() mode keys into Lynx defined keys.
2037 	 */
2038 	switch (c) {
2039 	case KEY_DOWN:		/* The four arrow keys ... */
2040 	    c = DNARROW;
2041 	    break;
2042 	case KEY_UP:
2043 	    c = UPARROW;
2044 	    break;
2045 	case KEY_LEFT:
2046 	    c = LTARROW;
2047 	    break;
2048 	case KEY_RIGHT:	/* ... */
2049 	    c = RTARROW;
2050 	    break;
2051 #if defined(PDCURSES)		/* for NEC PC-9800 1998/08/30 (Sun) 21:50:35 */
2052 	case KEY_C2:
2053 	    c = DNARROW;
2054 	    break;
2055 	case KEY_A2:
2056 	    c = UPARROW;
2057 	    break;
2058 	case KEY_B1:
2059 	    c = LTARROW;
2060 	    break;
2061 	case KEY_B3:
2062 	    c = RTARROW;
2063 	    break;
2064 	case PAD0:		/* PC-9800 Ins */
2065 	    c = INSERT_KEY;
2066 	    break;
2067 	case PADSTOP:		/* PC-9800 DEL */
2068 	    c = REMOVE_KEY;
2069 	    break;
2070 #endif /* PDCURSES */
2071 	case KEY_HOME:		/* Home key (upward+left arrow) */
2072 	    c = HOME;
2073 	    break;
2074 	case KEY_CLEAR:	/* Clear screen */
2075 	    c = 18;		/* CTRL-R */
2076 	    break;
2077 	case KEY_NPAGE:	/* Next page */
2078 	    c = PGDOWN;
2079 	    break;
2080 	case KEY_PPAGE:	/* Previous page */
2081 	    c = PGUP;
2082 	    break;
2083 	case KEY_LL:		/* home down or bottom (lower left) */
2084 	    c = END_KEY;
2085 	    break;
2086 #if defined(KEY_A1) && defined(KEY_C3)
2087 	    /* The keypad is arranged like this: */
2088 	    /*    a1    up    a3   */
2089 	    /*   left   b2  right  */
2090 	    /*    c1   down   c3   */
2091 	case KEY_A1:		/* upper left of keypad */
2092 	    c = HOME;
2093 	    break;
2094 	case KEY_A3:		/* upper right of keypad */
2095 	    c = PGUP;
2096 	    break;
2097 	case KEY_B2:		/* center of keypad */
2098 	    c = DO_NOTHING;
2099 	    break;
2100 	case KEY_C1:		/* lower left of keypad */
2101 	    c = END_KEY;
2102 	    break;
2103 	case KEY_C3:		/* lower right of keypad */
2104 	    c = PGDOWN;
2105 	    break;
2106 #endif /* defined(KEY_A1) && defined(KEY_C3) */
2107 #ifdef KEY_ENTER
2108 	case KEY_ENTER:	/* enter/return      */
2109 	    c = '\n';
2110 	    break;
2111 #endif /* KEY_ENTER */
2112 #ifdef PADENTER			/* PDCURSES */
2113 	case PADENTER:
2114 	    c = '\n';
2115 	    break;
2116 #endif /* PADENTER */
2117 #ifdef KEY_END
2118 	case KEY_END:		/* end key           001 */
2119 	    c = END_KEY;
2120 	    break;
2121 #endif /* KEY_END */
2122 #ifdef KEY_HELP
2123 	case KEY_HELP:		/* help key          001 */
2124 	    c = F1;
2125 	    break;
2126 #endif /* KEY_HELP */
2127 #ifdef KEY_BACKSPACE
2128 	case KEY_BACKSPACE:
2129 	    c = CH_DEL;		/* backspace key (delete, not Ctrl-H)  S/390 -- gil -- 2041 */
2130 	    break;
2131 #endif /* KEY_BACKSPACE */
2132 	case KEY_F(1):
2133 	    c = F1;		/* VTxxx Help */
2134 	    break;
2135 #if defined(KEY_F) && !defined(__DJGPP__) && !defined(_WINDOWS)
2136 	case KEY_F(16):
2137 	    c = DO_KEY;		/* VTxxx Do */
2138 	    break;
2139 #endif /* KEY_F */
2140 #ifdef KEY_REDO
2141 	case KEY_REDO:		/* VTxxx Do */
2142 	    c = DO_KEY;
2143 	    break;
2144 #endif /* KEY_REDO */
2145 #ifdef KEY_FIND
2146 	case KEY_FIND:
2147 	    c = FIND_KEY;	/* VTxxx Find */
2148 	    break;
2149 #endif /* KEY_FIND */
2150 #ifdef KEY_SELECT
2151 	case KEY_SELECT:
2152 	    c = SELECT_KEY;	/* VTxxx Select */
2153 	    break;
2154 #endif /* KEY_SELECT */
2155 #ifdef KEY_IC
2156 	case KEY_IC:
2157 	    c = INSERT_KEY;	/* VTxxx Insert */
2158 	    break;
2159 #endif /* KEY_IC */
2160 #ifdef KEY_DC
2161 	case KEY_DC:
2162 	    c = REMOVE_KEY;	/* VTxxx Remove */
2163 	    break;
2164 #endif /* KEY_DC */
2165 #ifdef KEY_BTAB
2166 	case KEY_BTAB:
2167 	    c = BACKTAB_KEY;	/* Back tab, often Shift-Tab */
2168 	    break;
2169 #endif /* KEY_BTAB */
2170 #ifdef KEY_RESIZE
2171 	case KEY_RESIZE:	/* size change detected by ncurses */
2172 #if defined(HAVE_SIZECHANGE) || defined(USE_SLANG)
2173 	    /* Make call to detect new size, if that may be implemented.
2174 	     * The call may set recent_sizechange (except for USE_SLANG),
2175 	     * which will tell mainloop() to refresh. - kw
2176 	     */
2177 	    CTRACE((tfp, "Got KEY_RESIZE, recent_sizechange so far is %d\n",
2178 		    recent_sizechange));
2179 	    size_change(0);
2180 	    CTRACE((tfp, "Now recent_sizechange is %d\n", recent_sizechange));
2181 #else /* HAVE_SIZECHANGE || USE_SLANG */
2182 	    CTRACE((tfp, "Got KEY_RESIZE, recent_sizechange is %d\n",
2183 		    recent_sizechange));
2184 #endif /* HAVE_SIZECHANGE || USE_SLANG */
2185 	    if (!recent_sizechange) {
2186 #if 0				/* assumption seems flawed? */
2187 		/* Not detected by us or already processed by us.  It can
2188 		 * happens that ncurses lags behind us in detecting the change,
2189 		 * since its own SIGTSTP handler is not installed so detecting
2190 		 * happened *at the end* of the last refresh.  Tell it to
2191 		 * refresh again...  - kw
2192 		 */
2193 		LYrefresh();
2194 #endif
2195 #if defined(NCURSES)
2196 		/*
2197 		 * Work-around for scenario (Linux libc5) where we got a
2198 		 * recent sizechange before reading KEY_RESIZE.  If we do
2199 		 * not reset the flag, we'll next get an EOF read, which
2200 		 * causes Lynx to exit.
2201 		 */
2202 		recent_sizechange = TRUE;
2203 #endif
2204 		/*
2205 		 * May be just the delayed effect of mainloop()'s call to
2206 		 * resizeterm().  Pretend we haven't read anything yet, don't
2207 		 * return.  - kw
2208 		 */
2209 		goto re_read;
2210 	    }
2211 	    /*
2212 	     * Yep, we agree there was a change.  Return now so that the caller
2213 	     * can react to it.  - kw
2214 	     */
2215 	    c = DO_NOTHING;
2216 	    break;
2217 #endif /* KEY_RESIZE */
2218 
2219 /* The following maps PDCurses keys away from lynx reserved values */
2220 #if (defined(_WINDOWS) || defined(__DJGPP__)) && !defined(USE_SLANG)
2221 	case KEY_F(2):
2222 	    c = 0x213;
2223 	    break;
2224 	case KEY_F(3):
2225 	    c = 0x214;
2226 	    break;
2227 	case KEY_F(4):
2228 	    c = 0x215;
2229 	    break;
2230 	case KEY_F(5):
2231 	    c = 0x216;
2232 	    break;
2233 	case KEY_F(6):
2234 	    c = 0x217;
2235 	    break;
2236 	case KEY_F(7):
2237 	    c = 0x218;
2238 	    break;
2239 #endif /* PDCurses */
2240 
2241 #if defined(USE_MOUSE)
2242 /********************************************************************/
2243 
2244 #if defined(NCURSES) || defined(PDCURSES)
2245 	case KEY_MOUSE:
2246 	    CTRACE((tfp, "KEY_MOUSE\n"));
2247 	    if (code == FOR_CHOICE) {
2248 		c = MOUSE_KEY;	/* Will be processed by the caller */
2249 	    }
2250 #if defined(NCURSES)
2251 	    else if (code == FOR_SINGLEKEY) {
2252 		MEVENT event;
2253 
2254 		getmouse(&event);	/* Completely ignore event - kw */
2255 		c = DO_NOTHING;
2256 	    }
2257 #endif
2258 	    else {
2259 #if defined(NCURSES)
2260 		MEVENT event;
2261 		int err;
2262 		int lac = LYK_UNKNOWN;
2263 
2264 		c = -1;
2265 		mouse_link = -1;
2266 		err = getmouse(&event);
2267 		if (err != OK) {
2268 		    CTRACE((tfp, "Mouse error: no event available!\n"));
2269 		    return (code == FOR_PANEL ? 0 : DO_NOTHING);
2270 		}
2271 		levent = event;	/* Allow setting pos in entry fields */
2272 		if (event.bstate & BUTTON1_CLICKED) {
2273 		    c = set_clicked_link(event.x, event.y, code, 1);
2274 		} else if (event.bstate & BUTTON1_DOUBLE_CLICKED) {
2275 		    c = set_clicked_link(event.x, event.y, code, 2);
2276 		    if (c == LAC_TO_LKC0(LYK_MOUSE_SUBMIT) &&
2277 			code == FOR_INPUT)
2278 			lac = LYK_MOUSE_SUBMIT;
2279 		} else if (event.bstate & BUTTON3_CLICKED) {
2280 		    c = LAC_TO_LKC0(LYK_PREV_DOC);
2281 		} else if (code == FOR_PROMPT
2282 		    /* Cannot ignore: see LYCurses.c */
2283 			   || (event.bstate &
2284 			       (BUTTON1_PRESSED | BUTTON1_RELEASED
2285 				| BUTTON2_PRESSED | BUTTON2_RELEASED
2286 				| BUTTON3_PRESSED | BUTTON3_RELEASED))) {
2287 		    /* Completely ignore - don't return anything, to
2288 		       avoid canceling the prompt - kw */
2289 		    goto re_read;
2290 		} else if (event.bstate & BUTTON2_CLICKED) {
2291 		    int atlink;
2292 
2293 		    c = set_clicked_link(event.x, event.y, code, 1);
2294 		    atlink = (c == LAC_TO_LKC0(LYK_ACTIVATE));
2295 		    if (!atlink)
2296 			mouse_link = -1;	/* Forget about approx stuff. */
2297 
2298 		    lac = LYmouse_menu(event.x, event.y, atlink, code);
2299 		    if (lac == LYK_MOUSE_SUBMIT) {
2300 			if (mouse_link == -1)
2301 			    lac = LYK_ACTIVATE;
2302 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
2303 			else if (mouse_link >= 0 &&
2304 				 textfields_need_activation &&
2305 				 links[mouse_link].type == WWW_FORM_LINK_TYPE &&
2306 				 F_TEXTLIKE(links[mouse_link].l_form->type))
2307 			    lac = LYK_ACTIVATE;
2308 #endif
2309 		    }
2310 		    if (lac == LYK_ACTIVATE && mouse_link == -1) {
2311 			HTAlert(gettext("No link chosen"));
2312 			lac = LYK_REFRESH;
2313 		    }
2314 		    c = LAC_TO_LKC(lac);
2315 #if 0				/* Probably not necessary any more - kw */
2316 		    lynx_force_repaint();
2317 		    LYrefresh();
2318 #endif
2319 		}
2320 #if NCURSES_MOUSE_VERSION > 1
2321 		else if (event.bstate & BUTTON4_PRESSED) {
2322 		    c = LAC_TO_LKC(LYK_UP_HALF);
2323 		} else if (event.bstate & BUTTON5_PRESSED) {
2324 		    c = LAC_TO_LKC(LYK_DOWN_HALF);
2325 		}
2326 #endif
2327 		if (code == FOR_INPUT && mouse_link == -1 &&
2328 		    lac != LYK_REFRESH &&
2329 		    lac != LYK_MOUSE_SUBMIT) {
2330 		    ungetmouse(&event);		/* Caller will process this. */
2331 		    wgetch(LYwin);	/* ungetmouse puts KEY_MOUSE back */
2332 		    c = MOUSE_KEY;
2333 		}
2334 #else /* pdcurses version */
2335 
2336 #define H_CMD_AREA	6
2337 #define HIST_CMD_2	12
2338 #define V_CMD_AREA	1
2339 
2340 		int left = H_CMD_AREA;
2341 		int right = (LYcolLimit - H_CMD_AREA - 1);
2342 
2343 		/* yes, I am assuming that my screen will be a certain width. */
2344 
2345 		int tick_count;
2346 		char *p = NULL;
2347 		char mouse_info[128];
2348 		static int old_click = 0;	/* [m Sec] */
2349 
2350 		c = -1;
2351 		mouse_link = -1;
2352 
2353 		if (!system_is_NT) {
2354 		    tick_count = GetTickCount();
2355 
2356 		    /* Guard Mouse button miss click */
2357 		    if ((tick_count - old_click) < 700) {
2358 			c = DO_NOTHING;
2359 			break;
2360 		    } else {
2361 			old_click = tick_count;
2362 		    }
2363 		}
2364 		request_mouse_pos();
2365 
2366 		if (BUTTON_STATUS(1) & BUTTON_PRESSED) {
2367 		    if (MOUSE_Y_POS > (LYlines - V_CMD_AREA - 1)) {
2368 			/* Screen BOTTOM */
2369 			if (MOUSE_X_POS < left) {
2370 			    c = LTARROW;
2371 			    p = "<-";
2372 			} else if (MOUSE_X_POS < HIST_CMD_2) {
2373 			    c = RTARROW;
2374 			    p = "->";
2375 			} else if (MOUSE_X_POS > right) {
2376 			    c = 'z';
2377 			    p = "Cancel";
2378 			} else {
2379 			    c = PGDOWN;
2380 			    p = "PGDOWN";
2381 			}
2382 		    } else if (MOUSE_Y_POS < V_CMD_AREA) {
2383 			/* Screen TOP */
2384 			if (MOUSE_X_POS < left) {
2385 			    c = LTARROW;
2386 			    p = "<-";
2387 			} else if (MOUSE_X_POS < HIST_CMD_2) {
2388 			    c = RTARROW;
2389 			    p = "->";
2390 			} else if (MOUSE_X_POS > right) {
2391 			    c = 'z';
2392 			    p = "Cancel";
2393 			} else {
2394 			    c = PGUP;
2395 			    p = "PGUP";
2396 			}
2397 		    } else {
2398 			c = set_clicked_link(MOUSE_X_POS,
2399 					     MOUSE_Y_POS,
2400 					     FOR_PANEL, 1);
2401 		    }
2402 		}
2403 		if (p && c != -1) {
2404 		    sprintf(mouse_info, "Mouse = 0x%x, [%s]", c, p);
2405 		    SetConsoleTitle(mouse_info);
2406 		}
2407 #endif /* !(WIN_EX) */
2408 		if ((c + 1) >= KEYMAP_SIZE && (c & LKC_ISLAC))
2409 		    return (c);
2410 	    }
2411 	    break;
2412 #endif /* NCURSES || PDCURSES */
2413 
2414 /********************************************************************/
2415 #endif /* USE_MOUSE */
2416 
2417 	}
2418 #endif /* HAVE_KEYPAD */
2419 #ifdef DJGPP_KEYHANDLER
2420 	switch (c) {
2421 	case K_Down:		/* The four arrow keys ... */
2422 	case K_EDown:
2423 	    c = DNARROW;
2424 	    break;
2425 	case K_Up:
2426 	case K_EUp:
2427 	    c = UPARROW;
2428 	    break;
2429 	case K_Left:
2430 	case K_ELeft:
2431 	    c = LTARROW;
2432 	    break;
2433 	case K_Right:		/* ... */
2434 	case K_ERight:
2435 	    c = RTARROW;
2436 	    break;
2437 	case K_Home:		/* Home key (upward+left arrow) */
2438 	case K_EHome:
2439 	    c = HOME;
2440 	    break;
2441 	case K_PageDown:	/* Next page */
2442 	case K_EPageDown:
2443 	    c = PGDOWN;
2444 	    break;
2445 	case K_PageUp:		/* Previous page */
2446 	case K_EPageUp:
2447 	    c = PGUP;
2448 	    break;
2449 	case K_End:		/* home down or bottom (lower left) */
2450 	case K_EEnd:
2451 	    c = END_KEY;
2452 	    break;
2453 	case K_F1:		/* F1 key */
2454 	    c = F1;
2455 	    break;
2456 	case K_Insert:		/* Insert key */
2457 	case K_EInsert:
2458 	    c = INSERT_KEY;
2459 	    break;
2460 	case K_Delete:		/* Delete key */
2461 	case K_EDelete:
2462 	    c = REMOVE_KEY;
2463 	    break;
2464 	case K_Alt_Escape:	/* Alt-Escape */
2465 	    c = 0x1a7;
2466 	    break;
2467 	case K_Control_At:	/* CTRL-@ */
2468 	    c = 0x1a8;
2469 	    break;
2470 	case K_Alt_Backspace:	/* Alt-Backspace */
2471 	    c = 0x1a9;
2472 	    break;
2473 	case K_BackTab:	/* BackTab */
2474 	    c = BACKTAB_KEY;
2475 	    break;
2476 	}
2477 #endif /* DGJPP_KEYHANDLER */
2478 #if defined(USE_SLANG) && (defined(__DJGPP__) || defined(__CYGWIN__)) && !defined(DJGPP_KEYHANDLER)  && !defined(USE_KEYMAPS)
2479 	switch (c) {
2480 	case SL_KEY_DOWN:	/* The four arrow keys ... */
2481 	    c = DNARROW;
2482 	    break;
2483 	case SL_KEY_UP:
2484 	    c = UPARROW;
2485 	    break;
2486 	case SL_KEY_LEFT:
2487 	    c = LTARROW;
2488 	    break;
2489 	case SL_KEY_RIGHT:	/* ... */
2490 	    c = RTARROW;
2491 	    break;
2492 	case SL_KEY_HOME:	/* Home key (upward+left arrow) */
2493 	case SL_KEY_A1:	/* upper left of keypad */
2494 	    c = HOME;
2495 	    break;
2496 	case SL_KEY_NPAGE:	/* Next page */
2497 	case SL_KEY_C3:	/* lower right of keypad */
2498 	    c = PGDOWN;
2499 	    break;
2500 	case SL_KEY_PPAGE:	/* Previous page */
2501 	case SL_KEY_A3:	/* upper right of keypad */
2502 	    c = PGUP;
2503 	    break;
2504 	case SL_KEY_END:	/* home down or bottom (lower left) */
2505 	case SL_KEY_C1:	/* lower left of keypad */
2506 	    c = END_KEY;
2507 	    break;
2508 	case SL_KEY_F(1):	/* F1 key */
2509 	    c = F1;
2510 	    break;
2511 	case SL_KEY_IC:	/* Insert key */
2512 	    c = INSERT_KEY;
2513 	    break;
2514 	case SL_KEY_DELETE:	/* Delete key */
2515 	    c = REMOVE_KEY;
2516 	    break;
2517 	}
2518 #endif /* USE_SLANG && __DJGPP__ && !DJGPP_KEYHANDLER && !USE_KEYMAPS */
2519     }
2520 
2521     if (c & (LKC_ISLAC | LKC_ISLECLAC))
2522 	return (c);
2523     if ((c + 1) >= KEYMAP_SIZE) {
2524 	/*
2525 	 * Don't return raw values for KEYPAD symbols which we may have missed
2526 	 * in the switch above if they are obviously invalid when used as an
2527 	 * index into (e.g.) keypad[].  - KW
2528 	 */
2529 	return (0);
2530     } else {
2531 	return (c | current_modifier);
2532     }
2533 }
2534 
2535 /************************************************************************/
2536 #endif /* NOT  defined(USE_KEYMAPS) && defined(USE_SLANG) */
2537 
LYgetch(void)2538 int LYgetch(void)
2539 {
2540     return LYReadCmdKey(FOR_PANEL);
2541 }
2542 
2543 /*
2544  * Read a single keystroke, allows mouse-selection.
2545  */
LYgetch_choice(void)2546 int LYgetch_choice(void)
2547 {
2548     int ch = LYReadCmdKey(FOR_CHOICE);
2549 
2550     if (ch == LYCharINTERRUPT1)
2551 	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
2552     return ch;
2553 }
2554 
2555 /*
2556  * Read a single keystroke, allows mouse events.
2557  */
LYgetch_input(void)2558 int LYgetch_input(void)
2559 {
2560     int ch = LYReadCmdKey(FOR_INPUT);
2561 
2562     if (ch == LYCharINTERRUPT1)
2563 	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
2564     return ch;
2565 }
2566 
2567 /*
2568  * Read a single keystroke, ignoring case by translating it to uppercase.
2569  * Ignore mouse events, if any.
2570  */
LYgetch_single(void)2571 int LYgetch_single(void)
2572 {
2573     int ch = LYReadCmdKey(FOR_SINGLEKEY);
2574 
2575     if (ch == LYCharINTERRUPT1)
2576 	ch = LYCharINTERRUPT2;	/* treat ^C the same as ^G */
2577     else if (ch > 0 && ch < 256)
2578 	ch = TOUPPER(ch);	/* will ignore case of result */
2579     return ch;
2580 }
2581 
2582 /*
2583  * Convert a null-terminated string to lowercase
2584  */
LYLowerCase(char * arg_buffer)2585 void LYLowerCase(char *arg_buffer)
2586 {
2587     register unsigned char *buffer = (unsigned char *) arg_buffer;
2588     size_t i;
2589 
2590     for (i = 0; buffer[i]; i++)
2591 #ifdef SUPPORT_MULTIBYTE_EDIT	/* 1998/11/23 (Mon) 17:04:55 */
2592     {
2593 	if ((buffer[i] & 0x80) != 0
2594 	    && buffer[i + 1] != 0) {
2595 	    if ((kanji_code == SJIS) && IS_SJIS_X0201KANA(UCH((buffer[i])))) {
2596 		continue;
2597 	    }
2598 	    i++;
2599 	} else {
2600 	    buffer[i] = UCH(TOLOWER(buffer[i]));
2601 	}
2602     }
2603 #else
2604 	buffer[i] = TOLOWER(buffer[i]);
2605 #endif
2606 }
2607 
2608 /*
2609  * Convert a null-terminated string to uppercase
2610  */
LYUpperCase(char * arg_buffer)2611 void LYUpperCase(char *arg_buffer)
2612 {
2613     register unsigned char *buffer = (unsigned char *) arg_buffer;
2614     size_t i;
2615 
2616     for (i = 0; buffer[i]; i++)
2617 #ifdef SUPPORT_MULTIBYTE_EDIT	/* 1998/11/23 (Mon) 17:05:10 */
2618     {
2619 	if ((buffer[i] & 0x80) != 0
2620 	    && buffer[i + 1] != 0) {
2621 	    if ((kanji_code == SJIS) && IS_SJIS_X0201KANA(UCH((buffer[i])))) {
2622 		continue;
2623 	    }
2624 	    i++;
2625 	} else {
2626 	    buffer[i] = UCH(TOUPPER(buffer[i]));
2627 	}
2628     }
2629 #else
2630 	buffer[i] = UCH(TOUPPER(buffer[i]));
2631 #endif
2632 }
2633 
2634 /*
2635  * Remove newlines from a string, returning true if we removed any.
2636  */
LYRemoveNewlines(char * buffer)2637 BOOLEAN LYRemoveNewlines(char *buffer)
2638 {
2639     if (buffer != 0) {
2640 	register char *buf = buffer;
2641 
2642 	for (; *buf && *buf != '\n' && *buf != '\r'; buf++) ;
2643 	if (*buf) {
2644 	    /* runs very seldom */
2645 	    char *old = buf;
2646 
2647 	    for (; *old; old++) {
2648 		if (*old != '\n' && *old != '\r')
2649 		    *buf++ = *old;
2650 	    }
2651 	    *buf = '\0';
2652 	    return TRUE;
2653 	}
2654     }
2655     return FALSE;
2656 }
2657 
2658 /*
2659  * Remove leading/trailing whitespace from a string, reduce runs of embedded
2660  * whitespace to single blanks.
2661  */
LYReduceBlanks(char * buffer)2662 char *LYReduceBlanks(char *buffer)
2663 {
2664     if (non_empty(buffer)) {
2665 	LYTrimLeading(buffer);
2666 	LYTrimTrailing(buffer);
2667 	convert_to_spaces(buffer, TRUE);
2668     }
2669     return buffer;
2670 }
2671 
2672 /*
2673  * Remove ALL whitespace from a string (including embedded blanks), and returns
2674  * a pointer to the end of the trimmed string.
2675  */
LYRemoveBlanks(char * buffer)2676 char *LYRemoveBlanks(char *buffer)
2677 {
2678     if (buffer != 0) {
2679 	register char *buf = buffer;
2680 
2681 	for (; *buf && !isspace(UCH(*buf)); buf++) ;
2682 	if (*buf) {
2683 	    /* runs very seldom */
2684 	    char *old = buf;
2685 
2686 	    for (; *old; old++) {
2687 		if (!isspace(UCH(*old)))
2688 		    *buf++ = *old;
2689 	    }
2690 	    *buf = '\0';
2691 	}
2692 	return buf;
2693     }
2694     return NULL;
2695 }
2696 
2697 /*
2698  * Skip whitespace
2699  */
LYSkipBlanks(char * buffer)2700 char *LYSkipBlanks(char *buffer)
2701 {
2702     while (isspace(UCH((*buffer))))
2703 	buffer++;
2704     return buffer;
2705 }
2706 
2707 /*
2708  * Skip non-whitespace
2709  */
LYSkipNonBlanks(char * buffer)2710 char *LYSkipNonBlanks(char *buffer)
2711 {
2712     while (*buffer != 0 && !isspace(UCH((*buffer))))
2713 	buffer++;
2714     return buffer;
2715 }
2716 
2717 /*
2718  * Skip const whitespace
2719  */
LYSkipCBlanks(const char * buffer)2720 const char *LYSkipCBlanks(const char *buffer)
2721 {
2722     while (isspace(UCH((*buffer))))
2723 	buffer++;
2724     return buffer;
2725 }
2726 
2727 /*
2728  * Skip const non-whitespace
2729  */
LYSkipCNonBlanks(const char * buffer)2730 const char *LYSkipCNonBlanks(const char *buffer)
2731 {
2732     while (*buffer != 0 && !isspace(UCH((*buffer))))
2733 	buffer++;
2734     return buffer;
2735 }
2736 
2737 /*
2738  * Trim leading blanks from a string
2739  */
LYTrimLeading(char * buffer)2740 void LYTrimLeading(char *buffer)
2741 {
2742     char *skipped = LYSkipBlanks(buffer);
2743 
2744     while ((*buffer++ = *skipped++) != 0) ;
2745 }
2746 
2747 /*
2748  * Trim trailing newline(s) from a string
2749  */
LYTrimNewline(char * buffer)2750 char *LYTrimNewline(char *buffer)
2751 {
2752     size_t i = strlen(buffer);
2753 
2754     while (i != 0 && (buffer[i - 1] == '\n' || buffer[i - 1] == '\r'))
2755 	buffer[--i] = 0;
2756     return buffer;
2757 }
2758 
2759 /*
2760  * Trim trailing blanks from a string
2761  */
LYTrimTrailing(char * buffer)2762 void LYTrimTrailing(char *buffer)
2763 {
2764     size_t i = strlen(buffer);
2765 
2766     while (i != 0 && isspace(UCH(buffer[i - 1])))
2767 	buffer[--i] = 0;
2768 }
2769 
2770 /* 1997/11/10 (Mon) 14:26:10, originally string_short() in LYExterns.c, but
2771  * moved here because LYExterns is not always configured.
2772  */
LYElideString(char * str,int cut_pos)2773 char *LYElideString(char *str,
2774 		    int cut_pos)
2775 {
2776     char buff[MAX_LINE], *s, *d;
2777     static char s_str[MAX_LINE];
2778     int len;
2779 
2780     LYStrNCpy(buff, str, sizeof(buff) - 1);
2781     len = (int) strlen(buff);
2782     if (len > (LYcolLimit - 9)) {
2783 	buff[cut_pos] = '.';
2784 	buff[cut_pos + 1] = '.';
2785 	for (s = (buff + len) - (LYcolLimit - 9) + cut_pos + 1,
2786 	     d = (buff + cut_pos) + 2;
2787 	     s >= buff &&
2788 	     d >= buff &&
2789 	     d < buff + LYcols &&
2790 	     (*d++ = *s++) != 0;) ;
2791 	buff[LYcols] = 0;
2792     }
2793     strcpy(s_str, buff);
2794     return (s_str);
2795 }
2796 
2797 /*
2798  * Trim a startfile, returning true if it looks like one of the Lynx tags.
2799  */
LYTrimStartfile(char * buffer)2800 BOOLEAN LYTrimStartfile(char *buffer)
2801 {
2802     LYTrimHead(buffer);
2803     if (isLYNXEXEC(buffer) ||
2804 	isLYNXPROG(buffer)) {
2805 	/*
2806 	 * The original implementations of these schemes expected white space
2807 	 * without hex escaping, and did not check for hex escaping, so we'll
2808 	 * continue to support that, until that code is redone in conformance
2809 	 * with SGML principles.  - FM
2810 	 */
2811 	HTUnEscapeSome(buffer, " \r\n\t");
2812 	convert_to_spaces(buffer, TRUE);
2813 	return TRUE;
2814     }
2815     return FALSE;
2816 }
2817 
2818 /*
2819  * Escape unsafe characters in startfile, except for lynx internal URLs.
2820  */
LYEscapeStartfile(char ** buffer)2821 void LYEscapeStartfile(char **buffer)
2822 {
2823     if (!LYTrimStartfile(*buffer)) {
2824 	char *escaped = HTEscapeUnsafe(*buffer);
2825 
2826 	StrAllocCopy(*buffer, escaped);
2827 	FREE(escaped);
2828     }
2829 }
2830 
2831 /*
2832  * Trim all blanks from startfile, except for lynx internal URLs.
2833  */
LYTrimAllStartfile(char * buffer)2834 void LYTrimAllStartfile(char *buffer)
2835 {
2836     if (!LYTrimStartfile(buffer)) {
2837 	LYRemoveBlanks(buffer);
2838     }
2839 }
2840 
2841 /*
2842  *  Display the current value of the string and allow the user
2843  *  to edit it.
2844  */
2845 
2846 #define EDREC	 EditFieldData
2847 
2848 /*
2849  * Shorthand to get rid of the "edit->suchandsos".
2850  */
2851 #define IsDirty  edit->dirty
2852 #define IsHidden edit->hidden
2853 #define StartX	 edit->sx
2854 #define StartY	 edit->sy
2855 #define Buf	 edit->buffer
2856 #define Pos	 edit->pos	/* current editing position (bytes) */
2857 #define StrLen	 edit->buffer_used	/* length (bytes) */
2858 #define MaxLen	 edit->buffer_size
2859 #define BufLimit edit->buffer_limit
2860 #define DspWdth  edit->dspwdth
2861 #define DspStart edit->xpan	/* display-start (columns) */
2862 #define Margin	 edit->margin
2863 #define PanOn	 edit->panon
2864 #define PadChar  edit->pad
2865 #ifdef ENHANCED_LINEEDIT
2866 #define Mark	 edit->mark
2867 #endif
2868 #define CurModif edit->current_modifiers
2869 #define Offs2Col edit->offset2col
2870 
2871 #ifdef ENHANCED_LINEEDIT
2872 static bstring *killbuffer;
2873 #endif
2874 
updateMargin(EDREC * edit)2875 static void updateMargin(EDREC * edit)
2876 {
2877     if ((int) MaxLen > DspWdth) {	/* Need panning? */
2878 	if (DspWdth > 4)	/* Else "{}" take up precious screen space */
2879 	    PanOn = TRUE;
2880 
2881 	/*
2882 	 * Figure out margins.  If too big, we do a lot of unnecessary
2883 	 * scrolling.  If too small, user doesn't have sufficient look-ahead.
2884 	 * Let's say 25% for each margin, upper bound is 10 columns.
2885 	 */
2886 	Margin = DspWdth / 4;
2887 	if (Margin > 10)
2888 	    Margin = 10;
2889     }
2890 }
2891 
2892 /*
2893  * Before using an array position, make sure that the array is long enough.
2894  * Reallocate if needed.
2895  */
ExtendEditor(EDREC * edit,int position)2896 static void ExtendEditor(EDREC * edit, int position)
2897 {
2898     size_t need = (size_t) (++position);
2899 
2900     if (need >= MaxLen && (BufLimit == 0 || need < BufLimit)) {
2901 	CTRACE((tfp, "ExtendEditor from %u to %u\n",
2902 		(unsigned) MaxLen,
2903 		(unsigned) need));
2904 	Buf = typeRealloc(char, Buf, need);
2905 	Offs2Col = typeRealloc(int, Offs2Col, need + 1);
2906 
2907 	MaxLen = need;
2908 	updateMargin(edit);
2909     }
2910 }
2911 
LYFinishEdit(EDREC * edit)2912 void LYFinishEdit(EDREC * edit)
2913 {
2914     CTRACE((tfp, "LYFinishEdit:%s\n", NonNull(Buf)));
2915 
2916     FREE(Buf);
2917     FREE(Offs2Col);
2918 }
2919 
LYSetupEdit(EDREC * edit,char * old_value,size_t buffer_limit,int display_limit)2920 void LYSetupEdit(EDREC * edit, char *old_value, size_t buffer_limit, int display_limit)
2921 {
2922     CTRACE((tfp, "LYSetupEdit buffer %u, display %d:%s\n",
2923 	    (unsigned) buffer_limit,
2924 	    display_limit,
2925 	    old_value));
2926 
2927     BufLimit = buffer_limit;
2928     if (buffer_limit == 0)
2929 	buffer_limit = strlen(old_value) + 1;
2930 
2931     /*
2932      * Initialize edit record
2933      */
2934     LYGetYX(StartY, StartX);
2935     PadChar = ' ';
2936     IsDirty = TRUE;
2937     PanOn = FALSE;
2938     CurModif = 0;
2939 
2940     MaxLen = buffer_limit;
2941     DspWdth = display_limit;
2942     Margin = 0;
2943     Pos = (int) strlen(old_value);
2944 #ifdef ENHANCED_LINEEDIT
2945     Mark = -1;			/* pos=0, but do not show it yet */
2946 #endif
2947     DspStart = 0;
2948 
2949     updateMargin(edit);
2950 
2951     StrLen = strlen(old_value);
2952     Buf = typecallocn(char, MaxLen + 1);
2953 
2954     if (Buf == 0)
2955 	outofmem(__FILE__, "LYSetupEdit");
2956 
2957     LYStrNCpy(Buf, old_value, buffer_limit);
2958     Offs2Col = typecallocn(int, MaxLen + 1);
2959 
2960     if (Offs2Col == 0)
2961 	outofmem(__FILE__, "LYSetupEdit");
2962 }
2963 
2964 #ifdef SUPPORT_MULTIBYTE_EDIT
2965 
2966 /*
2967  * MBCS positioning routines below are specific to SUPPORT_MULTIBYTE_EDIT code.
2968  * Currently they handle UTF-8 and (hopefully) CJK.
2969  * Current encoding is recognized using defines below.
2970  *
2971  * LYmbcs* functions don't look very convenient to use here...
2972  * Do we really need utf_flag as an argument?
2973  *
2974  * It is set (see IS_UTF8_TTY) for every invocation out there, and they use
2975  * HTCJK flag internally anyway.  Something like LYmbcsstrnlen == mbcs_glyphs
2976  * would be useful to work with string slices -Sergej Kvachonok
2977  */
2978 
2979 #define IS_UTF8_EXTRA(x) (((unsigned char)(x) & 0300) == 0200)
2980 
2981 /*
2982  * Counts glyphs in a multibyte (sub)string s of length len.
2983  */
mbcs_glyphs(char * s,int len)2984 static int mbcs_glyphs(char *s, int len)
2985 {
2986     int glyphs = 0;
2987     int i;
2988 
2989     if (IS_UTF8_TTY) {
2990 	for (i = 0; s[i] && i < len; i++)
2991 	    if (!IS_UTF8_EXTRA(s[i]))
2992 		glyphs++;
2993     } else if (IS_CJK_TTY) {
2994 	for (i = 0; s[i] && i < len; i++, glyphs++)
2995 	    if (is8bits(s[i]))
2996 		i++;
2997     } else {
2998 	glyphs = len;
2999     }
3000     return glyphs;
3001 }
3002 
3003 /*
3004  * Calculates offset in bytes of a glyph at cell position pos.
3005  */
mbcs_skip(char * s,int pos)3006 static int mbcs_skip(char *s, int pos)
3007 {
3008     int p, i;
3009 
3010     if (IS_UTF8_TTY) {
3011 	for (i = 0, p = 0; s[i]; i++) {
3012 	    if (!IS_UTF8_EXTRA(s[i]))
3013 		p++;
3014 	    if (p > pos)
3015 		break;
3016 	}
3017     } else if (IS_CJK_TTY) {
3018 	for (p = i = 0; s[i] && p < pos; p++, i++)
3019 	    if (is8bits(s[i]))
3020 		i++;
3021     } else {
3022 	i = pos;
3023     }
3024 
3025     return i;
3026 }
3027 
3028 /*
3029  * Given a string that would display (at least) the given number of cells,
3030  * determine the number of multibyte characters that comprised those cells.
3031  */
cell2char(char * s,int cells)3032 static int cell2char(char *s, int cells)
3033 {
3034     int result = 0;
3035     int len = (int) strlen(s);
3036     int pos;
3037     int have;
3038 
3039     CTRACE_EDIT((tfp, "cell2char(%d) %d:%s\n", cells, len, s));
3040     /* FIXME - make this a binary search */
3041     if (len != 0) {
3042 	for (pos = 0; pos <= len; ++pos) {
3043 	    have = LYstrExtent2(s, pos);
3044 	    CTRACE_EDIT((tfp, "  %2d:%2d:%.*s\n", pos, have, pos, s));
3045 	    if (have >= cells) {
3046 		break;
3047 	    }
3048 	}
3049 	if (pos > len)
3050 	    pos = len;
3051     } else {
3052 	pos = 0;
3053     }
3054     result = mbcs_glyphs(s, pos);
3055     CTRACE_EDIT((tfp, "->%d\n", result));
3056     return result;
3057 }
3058 
3059 #endif /* SUPPORT_MULTIBYTE_EDIT */
3060 
3061 #ifdef EXP_KEYBOARD_LAYOUT
3062 static int map_active = 0;
3063 
3064 #else
3065 #define map_active 0
3066 #endif
3067 
LYEditInsert(EDREC * edit,unsigned const char * s,int len,int map GCC_UNUSED,int maxMessage)3068 int LYEditInsert(EDREC * edit, unsigned const char *s,
3069 		 int len,
3070 		 int map GCC_UNUSED,
3071 		 int maxMessage)
3072 {
3073     int length = (int) strlen(Buf);
3074     int remains = (int) MaxLen - (length + len);
3075     int edited = 0, overflow = 0;
3076 
3077     /*
3078      * ch is (presumably) printable character.
3079      */
3080     if (remains < 0) {
3081 	overflow = 1;
3082 	len = 0;
3083 	if ((int) MaxLen > length)	/* Insert as much as we can */
3084 	    len = (int) MaxLen - length;
3085 	else
3086 	    goto finish;
3087     }
3088     ExtendEditor(edit, length + len);
3089     Buf[length + len] = '\0';
3090     for (; length >= Pos; length--)	/* Make room */
3091 	Buf[length + len] = Buf[length];
3092 #ifdef EXP_KEYBOARD_LAYOUT
3093     if (map < 0)
3094 	map = map_active;
3095     if (map && IS_UTF8_TTY) {
3096 	int off = Pos;
3097 	unsigned const char *e = s + len;
3098 	char *tail = 0;
3099 
3100 	while (s < e) {
3101 	    char utfbuf[8];
3102 	    int l = 1;
3103 
3104 	    utfbuf[0] = (char) *s;
3105 	    if (*s < 128 && LYKbLayouts[current_layout][*s]) {
3106 		UCode_t ucode = LYKbLayouts[current_layout][*s];
3107 
3108 		if (ucode > 127) {
3109 		    if (UCConvertUniToUtf8(ucode, utfbuf)) {
3110 			l = (int) strlen(utfbuf);
3111 			remains -= l - 1;
3112 			if (remains < 0) {
3113 			    if (tail)
3114 				strcpy(Buf + off, tail);
3115 			    FREE(tail);
3116 			    len = off;
3117 			    overflow = 1;
3118 			    goto finish;
3119 			}
3120 			if (l > 1 && !tail)
3121 			    StrAllocCopy(tail, Buf + Pos + len);
3122 		    } else
3123 			utfbuf[0] = '?';
3124 		} else
3125 		    utfbuf[0] = (char) ucode;
3126 	    }
3127 	    StrNCpy(Buf + off, utfbuf, l);
3128 	    edited = 1;
3129 	    off += l;
3130 	    s++;
3131 	}
3132 	if (tail)
3133 	    strcpy(Buf + off, tail);
3134 	len = off - Pos;
3135 	FREE(tail);
3136     } else if (map) {
3137 	unsigned const char *e = s + len;
3138 	unsigned char *t = (unsigned char *) Buf + Pos;
3139 
3140 	while (s < e) {
3141 	    int ch;
3142 
3143 	    if (*s < 128 && LYKbLayouts[current_layout][*s]) {
3144 		ch = UCTransUniChar((UCode_t) LYKbLayouts[current_layout][*s],
3145 				    current_char_set);
3146 		if (ch < 0)
3147 		    ch = '?';
3148 	    } else
3149 		ch = *s;
3150 	    *t = UCH(ch);
3151 	    t++, s++;
3152 	}
3153 	edited = 1;
3154     } else
3155 #endif /* defined EXP_KEYBOARD_LAYOUT */
3156     {
3157 	StrNCpy(Buf + Pos, (const char *) s, len);
3158 	edited = 1;
3159     }
3160 
3161   finish:
3162     Pos += len;
3163     StrLen += (size_t) len;
3164     if (edited)
3165 	IsDirty = TRUE;
3166     if (overflow && maxMessage)
3167 	_statusline(MAXLEN_REACHED_DEL_OR_MOV);
3168 #ifdef ENHANCED_LINEEDIT
3169     if (Mark > Pos)
3170 	Mark += len;
3171     else if (Mark < -(1 + Pos))
3172 	Mark -= len;
3173     if (Mark >= 0)
3174 	Mark = -(1 + Mark);	/* Disable it */
3175 #endif
3176     return edited;
3177 }
3178 
3179 /* returns 0    character processed
3180  *         -ch  if action should be performed outside of line-editing mode
3181  *         ch   otherwise
3182  */
LYEdit1(EDREC * edit,int ch,int action,int maxMessage)3183 int LYEdit1(EDREC * edit, int ch,
3184 	    int action,
3185 	    int maxMessage)
3186 {
3187     int i;
3188     int length;
3189     unsigned char uch;
3190     int offset;
3191 
3192     if ((int) MaxLen <= 0)
3193 	return (0);		/* Be defensive */
3194 
3195     StrLen = strlen(&Buf[0]);
3196     length = (int) StrLen;
3197 
3198     switch (action) {
3199 #ifdef EXP_KEYBOARD_LAYOUT
3200     case LYE_SWMAP:
3201 	/*
3202 	 * Turn input character mapping on or off.
3203 	 */
3204 	map_active = ~map_active;
3205 	break;
3206 #endif
3207 #ifndef CJK_EX
3208     case LYE_AIX:
3209 	/*
3210 	 * Hex 97.
3211 	 * Fall through as a character for CJK, or if this is a valid character
3212 	 * in the current display character set.  Otherwise, we treat this as
3213 	 * LYE_ENTER.
3214 	 */
3215 	if (!IS_CJK_TTY && LYlowest_eightbit[current_char_set] > 0x97)
3216 	    return (ch);
3217 	/* FALLTHRU */
3218 #endif
3219     case LYE_CHAR:
3220 	uch = UCH(ch);
3221 	LYEditInsert(edit, &uch, 1, map_active, maxMessage);
3222 	return 0;		/* All changes already registered */
3223 
3224     case LYE_C1CHAR:
3225 	/*
3226 	 * ch is the second part (in most cases, a capital letter) of a 7-bit
3227 	 * replacement for a character in the 8-bit C1 control range.
3228 	 *
3229 	 * This is meant to undo transformations like 0x81 -> 0x1b 0x41 (ESC A)
3230 	 * etc.  done by slang on Unix and possibly some comm programs.  It's
3231 	 * an imperfect workaround that doesn't work for all such characters.
3232 	 */
3233 	ch &= 0xFF;
3234 	if (ch + 64 >= LYlowest_eightbit[current_char_set])
3235 	    ch += 64;
3236 
3237 	if (Pos <= ((int) MaxLen) && StrLen < MaxLen) {
3238 #ifdef ENHANCED_LINEEDIT
3239 	    if (Mark > Pos)
3240 		Mark++;
3241 	    else if (Mark < -(1 + Pos))
3242 		Mark--;
3243 	    if (Mark >= 0)
3244 		Mark = -(1 + Mark);	/* Disable it */
3245 #endif
3246 	    ExtendEditor(edit, length + 1);
3247 	    for (i = length; i >= Pos; i--)	/* Make room */
3248 		Buf[i + 1] = Buf[i];
3249 	    Buf[length + 1] = '\0';
3250 	    Buf[Pos] = (char) ch;
3251 	    Pos++;
3252 	} else {
3253 	    if (maxMessage) {
3254 		_statusline(MAXLEN_REACHED_DEL_OR_MOV);
3255 	    }
3256 	    return (ch);
3257 	}
3258 	break;
3259 
3260     case LYE_BACKW:
3261 	/*
3262 	 * Backword.
3263 	 * Definition of word is very naive:  1 or more a/n characters.
3264 	 */
3265 #ifndef SUPPORT_MULTIBYTE_EDIT
3266 	while (Pos && !isalnum(UCH(Buf[Pos - 1])))
3267 	    Pos--;
3268 	while (Pos && isalnum(UCH(Buf[Pos - 1])))
3269 	    Pos--;
3270 #else
3271 	while (Pos && !(isalnum(UCH(Buf[Pos - 1])) || is8bits(Buf[Pos - 1])))
3272 	    Pos--;
3273 	while (Pos && (isalnum(UCH(Buf[Pos - 1])) || is8bits(Buf[Pos - 1])))
3274 	    Pos--;
3275 #endif
3276 	break;
3277 
3278     case LYE_FORWW:
3279 	/*
3280 	 * Word forward.
3281 	 */
3282 #ifndef SUPPORT_MULTIBYTE_EDIT
3283 	while (isalnum(UCH(Buf[Pos])))
3284 	    Pos++;		/* '\0' is not a/n */
3285 	while (!isalnum(UCH(Buf[Pos])) && Buf[Pos])
3286 	    Pos++;
3287 #else
3288 	while (isalnum(UCH(Buf[Pos])) || is8bits(Buf[Pos]))
3289 	    Pos++;		/* '\0' is not a/n */
3290 	while (!(isalnum(UCH(Buf[Pos])) || is8bits(Buf[Pos])) && Buf[Pos])
3291 	    Pos++;
3292 #endif
3293 	break;
3294 
3295     case LYE_ERASE:
3296 	/*
3297 	 * Erase the line to start fresh.
3298 	 */
3299 	Buf[0] = '\0';
3300 #ifdef ENHANCED_LINEEDIT
3301 	Mark = -1;		/* Do not show the mark */
3302 #endif
3303 	/* fall through */
3304 
3305     case LYE_BOL:
3306 	/*
3307 	 * Go to first column.
3308 	 */
3309 	Pos = 0;
3310 	break;
3311 
3312     case LYE_EOL:
3313 	/*
3314 	 * Go to last column.
3315 	 */
3316 	Pos = length;
3317 	break;
3318 
3319     case LYE_DELNW:
3320 	/*
3321 	 * Delete next word.
3322 	 */
3323 	offset = Pos;
3324 	LYEdit1(edit, 0, LYE_FORWW, FALSE);
3325 	offset = Pos - offset;
3326 	Pos -= offset;
3327 
3328 	goto shrink;		/* right below */
3329 
3330     case LYE_DELPW:
3331 	/*
3332 	 * Delete previous word.
3333 	 */
3334 	offset = Pos;
3335 	LYEdit1(edit, 0, LYE_BACKW, FALSE);
3336 	offset -= Pos;
3337 
3338       shrink:
3339 	for (i = Pos; i < length - offset + 1; i++)
3340 	    Buf[i] = Buf[i + offset];
3341 #ifdef ENHANCED_LINEEDIT
3342 	if (Mark >= 0)
3343 	    Mark = -(1 + Mark);	/* Disable it */
3344 	if (Mark <= -(1 + Pos + offset))
3345 	    Mark += offset;	/* Shift it */
3346 	if (-(1 + Pos + offset) < Mark && Mark < -(1 + Pos))
3347 	    Mark = -(1 + Pos);	/* Set to the current position */
3348 #endif
3349 
3350 	break;
3351 
3352     case LYE_DELBL:
3353 	/*
3354 	 * Delete from current cursor position back to BOL.
3355 	 */
3356 	for (i = Pos; i < length + 1; i++)
3357 	    Buf[i - Pos] = Buf[i];
3358 
3359 #ifdef ENHANCED_LINEEDIT
3360 	if (Mark >= 0)
3361 	    Mark = -(1 + Mark);	/* Disable it */
3362 	if (Mark <= -(1 + Pos))
3363 	    Mark += Pos;	/* Shift it */
3364 	else
3365 	    Mark = -1;		/* Reset it */
3366 #endif
3367 	Pos = 0;
3368 	break;
3369 
3370     case LYE_DELEL:		/* @@@ */
3371 	/*
3372 	 * Delete from current cursor position thru EOL.
3373 	 */
3374 	Buf[Pos] = '\0';
3375 #ifdef ENHANCED_LINEEDIT
3376 	if (Mark >= 0)
3377 	    Mark = -(1 + Mark);	/* Disable it */
3378 	if (Mark <= -(1 + Pos))
3379 	    Mark = -1;		/* Reset it */
3380 #endif
3381 	break;
3382 
3383     case LYE_DELN:
3384 	/*
3385 	 * Delete next character (I-beam style cursor), or current character
3386 	 * (box/underline style cursor).
3387 	 */
3388 	if (Pos >= length)
3389 	    break;
3390 #ifndef SUPPORT_MULTIBYTE_EDIT
3391 	Pos++;
3392 #else
3393 	Pos += mbcs_skip(Buf + Pos, 1);
3394 #endif
3395 	/* fall through - DO NOT RELOCATE the LYE_DELN case wrt LYE_DELP */
3396 
3397     case LYE_DELP:
3398 	/*
3399 	 * Delete preceding character.
3400 	 */
3401 	if (length == 0 || Pos == 0)
3402 	    break;
3403 
3404 #ifndef SUPPORT_MULTIBYTE_EDIT
3405 #ifdef ENHANCED_LINEEDIT
3406 	if (Mark >= 0)
3407 	    Mark = -(1 + Mark);	/* Disable it */
3408 	if (Mark <= -(1 + Pos))
3409 	    Mark++;
3410 #endif
3411 	Pos--;
3412 	for (i = Pos; i < length; i++)
3413 	    Buf[i] = Buf[i + 1];
3414 #else /* SUPPORT_MULTIBYTE_EDIT */
3415 	offset = Pos - mbcs_skip(Buf, mbcs_glyphs(Buf, Pos) - 1);
3416 	Pos -= offset;
3417 	for (i = Pos; i < length - offset + 1; i++)
3418 	    Buf[i] = Buf[i + offset];
3419 
3420 #ifdef ENHANCED_LINEEDIT
3421 	if (Mark >= 0)
3422 	    Mark = -(1 + Mark);	/* Disable it */
3423 	if (Mark <= -(1 + Pos))
3424 	    Mark += offset;	/* Shift it */
3425 #endif
3426 
3427 #endif /* SUPPORT_MULTIBYTE_EDIT */
3428 	break;
3429 
3430     case LYE_FORW_RL:
3431     case LYE_FORW:
3432 	/*
3433 	 * Move cursor to the right.
3434 	 */
3435 #ifndef SUPPORT_MULTIBYTE_EDIT
3436 	if (Pos < length)
3437 	    Pos++;
3438 #else
3439 	if (Pos < length)
3440 	    Pos += mbcs_skip(Buf + Pos, 1);
3441 #endif
3442 	else if (action == LYE_FORW_RL)
3443 	    return -ch;
3444 	break;
3445 
3446     case LYE_BACK_LL:
3447     case LYE_BACK:
3448 	/*
3449 	 * Left-arrow move cursor to the left.
3450 	 */
3451 #ifndef SUPPORT_MULTIBYTE_EDIT
3452 	if (Pos > 0)
3453 	    Pos--;
3454 #else
3455 	if (Pos > 0)
3456 	    Pos = mbcs_skip(Buf, mbcs_glyphs(Buf, Pos) - 1);
3457 #endif
3458 	else if (action == LYE_BACK_LL)
3459 	    return -ch;
3460 	break;
3461 
3462 #ifdef ENHANCED_LINEEDIT
3463     case LYE_TPOS:
3464 	/*
3465 	 * Transpose characters - bash or ksh(emacs not gmacs) style
3466 	 */
3467 
3468 #ifdef SUPPORT_MULTIBYTE_EDIT
3469 	if (IS_UTF8_TTY || IS_CJK_TTY)
3470 	    break;		/* Can't help it now */
3471 #endif
3472 
3473 	if (length <= 1 || Pos == 0)
3474 	    return (ch);
3475 	if (Pos == length)
3476 	    Pos--;
3477 	if (Mark < 0)
3478 	    Mark = -(1 + Mark);	/* Temporary enable it */
3479 	if (Mark == Pos || Mark == Pos + 1)
3480 	    Mark = Pos - 1;
3481 	if (Mark >= 0)
3482 	    Mark = -(1 + Mark);	/* Disable it */
3483 	if (Buf[Pos - 1] == Buf[Pos]) {
3484 	    Pos++;
3485 	    break;
3486 	}
3487 	i = Buf[Pos - 1];
3488 	Buf[Pos - 1] = Buf[Pos];
3489 	Buf[Pos++] = (char) i;
3490 	break;
3491 
3492     case LYE_SETMARK:
3493 	/*
3494 	 * primitive emacs-like set-mark-command
3495 	 */
3496 	Mark = Pos;
3497 	return (0);
3498 
3499     case LYE_XPMARK:
3500 	/*
3501 	 * emacs-like exchange-point-and-mark
3502 	 */
3503 	if (Mark < 0)
3504 	    Mark = -(1 + Mark);	/* Enable it */
3505 	if (Mark == Pos)
3506 	    return (0);
3507 	i = Pos;
3508 	Pos = Mark;
3509 	Mark = i;
3510 	break;
3511 
3512     case LYE_KILLREG:
3513 	/*
3514 	 * primitive emacs-like kill-region
3515 	 */
3516 	if (Mark < 0)
3517 	    Mark = -(1 + Mark);	/* Enable it */
3518 	if (Mark == Pos) {
3519 	    BStrFree(killbuffer);
3520 	    return (0);
3521 	}
3522 	if (Mark > Pos)
3523 	    LYEdit1(edit, 0, LYE_XPMARK, FALSE);
3524 	{
3525 	    int reglen = Pos - Mark;
3526 
3527 	    BStrCopy1(killbuffer, Buf + Mark, reglen);
3528 	    for (i = Mark; Buf[i + reglen]; i++)
3529 		Buf[i] = Buf[i + reglen];
3530 	    Buf[i] = Buf[i + reglen];	/* terminate */
3531 	    Pos = Mark;
3532 	}
3533 	if (Mark >= 0)
3534 	    Mark = -(1 + Mark);	/* Disable it */
3535 	break;
3536 
3537     case LYE_YANK:
3538 	/*
3539 	 * primitive emacs-like yank
3540 	 */
3541 	if (!killbuffer) {
3542 	    Mark = -(1 + Pos);
3543 	    return (0);
3544 	} else {
3545 	    int yanklen = killbuffer->len;
3546 
3547 	    if ((Pos + yanklen) <= (int) MaxLen &&
3548 		StrLen + (size_t) yanklen <= MaxLen) {
3549 
3550 		ExtendEditor(edit, Pos + yanklen);
3551 
3552 		Mark = -(1 + Pos);
3553 
3554 		for (i = length; i >= Pos; i--)		/* Make room */
3555 		    Buf[i + yanklen] = Buf[i];
3556 		for (i = 0; i < yanklen; i++)
3557 		    Buf[Pos++] = killbuffer->str[i];
3558 
3559 	    } else if (maxMessage) {
3560 		_statusline(MAXLEN_REACHED_DEL_OR_MOV);
3561 	    }
3562 	}
3563 	break;
3564 
3565 #endif /* ENHANCED_LINEEDIT */
3566 
3567     case LYE_UPPER:
3568 	LYUpperCase(Buf);
3569 	break;
3570 
3571     case LYE_LOWER:
3572 	LYLowerCase(Buf);
3573 	break;
3574 
3575     default:
3576 	return (ch);
3577     }
3578     IsDirty = TRUE;
3579     StrLen = strlen(&Buf[0]);
3580     return (0);
3581 }
3582 
3583 /*
3584  *  This function prompts for a choice or page number.
3585  *  If a 'g' or 'p' suffix is included, that will be
3586  *  loaded into c.  Otherwise, c is zeroed. - FM & LE
3587  */
get_popup_number(const char * msg,int * c,int * rel)3588 int get_popup_number(const char *msg,
3589 		     int *c,
3590 		     int *rel)
3591 {
3592     bstring *temp = NULL;
3593     int result = 0;
3594 
3595     /*
3596      * Load the c argument into the prompt buffer.
3597      */
3598     BStrCopy0(temp, "?");
3599     temp->str[0] = (char) *c;
3600 
3601     _statusline(msg);
3602 
3603     /*
3604      * Get the number, possibly with a suffix, from the user.
3605      */
3606     if (LYgetBString(&temp, VISIBLE, 0, NORECALL) < 0 || isBEmpty(temp)) {
3607 	HTInfoMsg(CANCELLED);
3608 	*c = '\0';
3609 	*rel = '\0';
3610     } else {
3611 	char *p = temp->str;
3612 
3613 	*rel = '\0';
3614 	result = atoi(p);
3615 	while (isdigit(UCH(*p)))
3616 	    ++p;
3617 	switch (*p) {
3618 	case '+':
3619 	case '-':
3620 	    /* 123+ or 123- */
3621 	    *rel = *p++;
3622 	    *c = *p;
3623 	    break;
3624 	default:
3625 	    *c = *p++;
3626 	    *rel = *p;
3627 	    break;
3628 	case 0:
3629 	    break;
3630 	}
3631 
3632 	/*
3633 	 * If we had a 'g' or 'p' suffix, load it into c.  Otherwise, zero c.  Then
3634 	 * return the number.
3635 	 */
3636 	if (*p == 'g' || *p == 'G') {
3637 	    *c = 'g';
3638 	} else if (*p == 'p' || *p == 'P') {
3639 	    *c = 'p';
3640 	} else {
3641 	    *c = '\0';
3642 	}
3643 	if (*rel != '+' && *rel != '-')
3644 	    *rel = 0;
3645     }
3646     BStrFree(temp);
3647     return result;
3648 }
3649 
3650 #ifdef USE_COLOR_STYLE
3651 #  define TmpStyleOn(s)		curses_style((s), STACK_ON)
3652 #  define TmpStyleOff(s)	curses_style((s), STACK_OFF)
3653 #else
3654 #  define TmpStyleOn(s)
3655 #  define TmpStyleOff(s)
3656 #endif /* defined USE_COLOR_STYLE */
3657 
remember_column(EDREC * edit,int offset)3658 static void remember_column(EDREC * edit, int offset)
3659 {
3660     int y0, x0;
3661 
3662 #if defined(USE_SLANG)
3663     y0 = 0;
3664     x0 = SLsmg_get_column();
3665 #elif defined(USE_CURSES_PADS)
3666     getyx(LYwin, y0, x0);
3667 #else
3668     getyx(stdscr, y0, x0);
3669 #endif
3670     Offs2Col[offset] = x0;
3671 
3672     (void) y0;
3673     (void) x0;
3674 }
3675 
fill_edited_line(int prompting GCC_UNUSED,int length,int ch)3676 static void fill_edited_line(int prompting GCC_UNUSED, int length, int ch)
3677 {
3678     int i;
3679 
3680     TmpStyleOn(prompting ? s_prompt_edit_pad : s_aedit_pad);
3681 
3682     for (i = 0; i < length; i++) {
3683 	LYaddch(UCH(ch));
3684     }
3685 
3686     TmpStyleOff(prompting ? s_prompt_edit_pad : s_aedit_pad);
3687 }
3688 
3689 /*
3690  * Multibyte string display subroutine.
3691  * EDREC fields retain their values as byte offsets.
3692  * All external logic still works fine with byte values.
3693  */
LYRefreshEdit(EDREC * edit)3694 void LYRefreshEdit(EDREC * edit)
3695 {
3696     /* bytes and characters are not the same thing */
3697 #if defined(DEBUG_EDIT)
3698     int all_bytes;
3699 #endif
3700     int pos_bytes = Pos;
3701     int dpy_bytes;
3702     int lft_bytes;		/* base of string which is displayed */
3703 
3704     /* cells refer to display-columns on the screen */
3705     int all_cells;		/* total of display-cells in Buf */
3706     int dpy_cells;		/* number of cells which are displayed */
3707     int lft_cells;		/* number of cells before display (on left) */
3708     int pos_cells;		/* number of display-cells up to Pos */
3709 
3710 #if defined(SUPPORT_MULTIBYTE_EDIT)
3711     int dpy_chars;
3712     int lft_chars;
3713 
3714 #if defined(DEBUG_EDIT)
3715     int all_chars;
3716     int pos_chars;
3717 #endif
3718 #endif
3719 
3720     /* other data */
3721     int i;
3722     int padsize;
3723     char *str;
3724     int lft_shift = 0;
3725     int rgt_shift = 0;
3726 
3727 #ifdef USE_COLOR_STYLE
3728     int estyle;
3729 #endif
3730     int prompting = 0;
3731 
3732     (void) pos_bytes;
3733 
3734     /*
3735      * If we've made no changes, or if there is nothing to display, just leave.
3736      */
3737     if (!IsDirty || (DspWdth == 0))
3738 	return;
3739 
3740     CTRACE((tfp, "LYRefreshEdit:%s\n", Buf));
3741 
3742     IsDirty = FALSE;
3743 
3744     StrLen = strlen(&Buf[0]);
3745 
3746     all_cells = LYstrCells(Buf);
3747     pos_cells = LYstrExtent2(Buf, Pos);
3748 
3749 #if defined(SUPPORT_MULTIBYTE_EDIT) && defined(DEBUG_EDIT)
3750     all_bytes = (int) StrLen;
3751     lft_chars = mbcs_glyphs(Buf, DspStart);
3752     pos_chars = mbcs_glyphs(Buf, Pos);
3753     all_chars = mbcs_glyphs(Buf, all_bytes);
3754 #endif
3755 
3756     /*
3757      * Now we have:
3758      *                .--DspWdth---.
3759      *      +---------+=============+-----------+
3760      *      |         |M           M|           |   (M=margin)
3761      *      +---------+=============+-----------+
3762      *      0         DspStart                   StrLen
3763      *
3764      * Insertion point can be anywhere between 0 and stringlength.  Figure out
3765      * new display starting point.
3766      *
3767      * The first "if" below makes Lynx scroll several columns at a time when
3768      * extending the string.  Looks awful, but that way we can keep up with
3769      * data entry at low baudrates.
3770      */
3771 
3772     lft_bytes = DspStart;
3773     lft_cells = LYstrExtent2(Buf, DspStart);
3774 
3775     if ((lft_cells + DspWdth) <= all_cells) {
3776 	if (pos_cells >= (lft_cells + DspWdth) - Margin) {
3777 	    lft_cells = (pos_cells - DspWdth) + Margin;
3778 #ifdef SUPPORT_MULTIBYTE_EDIT
3779 	    lft_chars = cell2char(Buf, lft_cells);
3780 	    lft_bytes = mbcs_skip(Buf, lft_chars);
3781 #else
3782 	    lft_bytes = lft_cells;
3783 #endif /* SUPPORT_MULTIBYTE_EDIT */
3784 	}
3785     }
3786 
3787     if (pos_cells < lft_cells + Margin) {
3788 	lft_cells = pos_cells - Margin;
3789 	if (lft_cells < 0)
3790 	    lft_cells = 0;
3791 #ifdef SUPPORT_MULTIBYTE_EDIT
3792 	lft_chars = cell2char(Buf, lft_cells);
3793 	lft_bytes = mbcs_skip(Buf, lft_chars);
3794 #else
3795 	lft_bytes = lft_cells;
3796 #endif /* SUPPORT_MULTIBYTE_EDIT */
3797     }
3798 
3799     LYmove(StartY, StartX);
3800 
3801     /*
3802      * Draw the left scrolling-indicator now, to avoid the complication of
3803      * overwriting part of a multicolumn character which may lie in the first
3804      * position.
3805      */
3806     if (PanOn && lft_cells) {
3807 	CTRACE_EDIT((tfp, "Draw left scroll-indicator\n"));
3808 	TmpStyleOn(prompting ? s_prompt_edit_arr : s_aedit_arr);
3809 	LYmove(StartY, StartX);
3810 	LYaddch(ACS_LARROW);
3811 	TmpStyleOff(prompting ? s_prompt_edit_arr : s_aedit_arr);
3812 	lft_shift = 1;
3813     }
3814 
3815     str = &Buf[lft_bytes];
3816     DspStart = lft_bytes;
3817 
3818     dpy_cells = all_cells - lft_cells;
3819     CTRACE_EDIT((tfp, "Comparing dpy_cells %d > (%d - %d)\n",
3820 		 dpy_cells, DspWdth, lft_shift));
3821     if (dpy_cells > (DspWdth - lft_shift)) {
3822 	rgt_shift = 1;
3823 	dpy_cells = (DspWdth - lft_shift - rgt_shift);
3824     }
3825     for (;;) {
3826 #ifdef SUPPORT_MULTIBYTE_EDIT
3827 	dpy_chars = cell2char(str, dpy_cells);
3828 	dpy_bytes = mbcs_skip(str, dpy_chars);
3829 #else
3830 	dpy_bytes = dpy_cells;
3831 #endif /* SUPPORT_MULTIBYTE_EDIT */
3832 	/*
3833 	 * The last character on the display may be multicolumn, and if we take
3834 	 * away a single cell for the right scroll-indicator, that would force
3835 	 * us to display fewer characters.  Check for that, and recompute.
3836 	 */
3837 	if (rgt_shift && *str) {
3838 	    int old_cells = dpy_cells;
3839 
3840 	    dpy_cells = LYstrExtent2(str, dpy_bytes);
3841 	    if (dpy_cells > old_cells)
3842 		dpy_cells = old_cells - 1;
3843 
3844 	    CTRACE_EDIT((tfp, "Comparing cells %d vs %d\n", dpy_cells, old_cells));
3845 	    if (dpy_cells < old_cells) {
3846 		CTRACE_EDIT((tfp, "Recomputing...\n"));
3847 		continue;
3848 	    }
3849 	}
3850 	break;
3851     }
3852 
3853     CTRACE_EDIT((tfp, "BYTES left %2d pos %2d dpy %2d all %2d\n",
3854 		 lft_bytes, pos_bytes, dpy_bytes, all_bytes));
3855     CTRACE_EDIT((tfp, "CELLS left %2d pos %2d dpy %2d all %2d\n",
3856 		 lft_cells, pos_cells, dpy_cells, all_cells));
3857     CTRACE_EDIT((tfp, "CHARS left %2d pos %2d dpy %2d all %2d\n",
3858 		 lft_chars, pos_chars, dpy_chars, all_chars));
3859 
3860 #ifdef USE_COLOR_STYLE
3861     /*
3862      * If this is the last screen line, set attributes to normal, should only
3863      * be needed for color styles.  The curses function may be used directly to
3864      * avoid complications.  - kw
3865      */
3866     if (StartY == (LYlines - 1))
3867 	prompting = 1;
3868     if (prompting) {
3869 	estyle = s_prompt_edit;
3870     } else {
3871 	estyle = s_aedit;
3872     }
3873     CTRACE2(TRACE_STYLE,
3874 	    (tfp, "STYLE.getstr: switching to <edit.%s>.\n",
3875 	     prompting ? "prompt" : "active"));
3876     if (estyle != NOSTYLE) {
3877 	curses_style(estyle, STACK_ON);
3878     } else {
3879 	(void) wattrset(LYwin, A_NORMAL);	/* need to do something about colors? */
3880     }
3881 #endif
3882     if (IsHidden) {
3883 	BOOL utf_flag = IS_UTF8_TTY;
3884 	int cell = 0;
3885 
3886 	fill_edited_line(0, dpy_cells, '*');
3887 
3888 	i = 0;
3889 	do {
3890 	    const char *last = str + i;
3891 	    const char *next = LYmbcs_skip_glyphs(last, 1, utf_flag);
3892 	    int j = (int) (next - str);
3893 
3894 	    while (i < j) {
3895 		Offs2Col[i++] = cell + StartX;
3896 	    }
3897 	    cell += LYstrExtent2(last, (int) (next - last));
3898 	} while (i < dpy_bytes);
3899 	Offs2Col[i] = cell + StartX;
3900     } else {
3901 #if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
3902 	if (Mark >= 0 && DspStart > Mark)
3903 	    TmpStyleOn(prompting ? s_prompt_sel : s_aedit_sel);
3904 #endif
3905 	remember_column(edit, 0);
3906 	for (i = 0; i < dpy_bytes; i++) {
3907 #if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
3908 	    if (Mark >= 0 && ((DspStart + i == Mark && Pos > Mark)
3909 			      || (DspStart + i == Pos && Pos < Mark)))
3910 		TmpStyleOn(prompting ? s_prompt_sel : s_aedit_sel);
3911 	    if (Mark >= 0 && ((DspStart + i == Mark && Pos < Mark)
3912 			      || (DspStart + i == Pos && Pos > Mark)))
3913 		TmpStyleOff(prompting ? s_prompt_sel : s_aedit_sel);
3914 #endif
3915 	    if (str[i] == 1 || str[i] == 2 ||
3916 		(UCH(str[i]) == 160 &&
3917 		 !(HTPassHighCtrlRaw || IS_CJK_TTY ||
3918 		   (LYCharSet_UC[current_char_set].enc != UCT_ENC_8859 &&
3919 		    !(LYCharSet_UC[current_char_set].like8859
3920 		      & UCT_R_8859SPECL))))) {
3921 		LYaddch(' ');
3922 	    } else if (str[i] == '\t') {
3923 		int col = Offs2Col[i] - StartX;
3924 
3925 		/*
3926 		 * Like LYwaddnstr(), expand tabs from the beginning of the
3927 		 * field.
3928 		 */
3929 		while (++col % 8)
3930 		    LYaddch(' ');
3931 		LYaddch(' ');
3932 	    } else {
3933 		LYaddch(UCH(str[i]));
3934 	    }
3935 	    remember_column(edit, i + 1);
3936 	}
3937 #if defined(ENHANCED_LINEEDIT) && defined(USE_COLOR_STYLE)
3938 	if (Mark >= 0 &&
3939 	    ((DspStart + dpy_bytes <= Mark && DspStart + dpy_bytes > Pos)
3940 	     || (DspStart + dpy_bytes > Mark
3941 		 && DspStart + dpy_bytes <= Pos))) {
3942 	    TmpStyleOff(prompting ? s_prompt_sel : s_aedit_sel);
3943 	}
3944 #endif
3945     }
3946 
3947     /*
3948      * Erase rest of input area.
3949      */
3950     padsize = DspWdth - (Offs2Col[dpy_bytes] - StartX);
3951     fill_edited_line(prompting, padsize, PadChar);
3952 
3953     /*
3954      * Scrolling indicators.
3955      */
3956     if (PanOn && dpy_bytes && rgt_shift) {
3957 	CTRACE((tfp, "Draw right-scroller offset (%d + %d)\n",
3958 		dpy_cells, lft_shift));
3959 	TmpStyleOn(prompting ? s_prompt_edit_arr : s_aedit_arr);
3960 	LYmove(StartY, StartX + dpy_cells + lft_shift);
3961 	LYaddch(ACS_RARROW);
3962 	TmpStyleOff(prompting ? s_prompt_edit_arr : s_aedit_arr);
3963     }
3964 
3965     /*
3966      * Finally, move the cursor to the point where the next edit will occur.
3967      */
3968     LYmove(StartY, Offs2Col[Pos - DspStart]);
3969 
3970 #ifdef USE_COLOR_STYLE
3971     if (estyle != NOSTYLE)
3972 	curses_style(estyle, STACK_OFF);
3973 #endif
3974     LYrefresh();
3975 }
3976 
reinsertEdit(EditFieldData * edit,char * result)3977 static void reinsertEdit(EditFieldData *edit, char *result)
3978 {
3979     if (result != 0) {
3980 	LYEdit1(edit, '\0', LYE_ERASE, FALSE);
3981 	while (*result != '\0') {
3982 	    LYLineEdit(edit, (int) (*result), FALSE);
3983 	    result++;
3984 	}
3985     }
3986 }
3987 
caselessCmpList(const void * a,const void * b)3988 static int caselessCmpList(const void *a,
3989 			   const void *b)
3990 {
3991     return strcasecomp(*(STRING2PTR) a, *(STRING2PTR) b);
3992 }
3993 
normalCmpList(const void * a,const void * b)3994 static int normalCmpList(const void *a,
3995 			 const void *b)
3996 {
3997     return strcmp(*(STRING2PTR) a, *(STRING2PTR) b);
3998 }
3999 
sortedList(HTList * list,int ignorecase)4000 static char **sortedList(HTList *list, int ignorecase)
4001 {
4002     size_t count = (unsigned) HTList_count(list);
4003     size_t j = 0;
4004     size_t k, jk;
4005     char **result = typecallocn(char *, count + 1);
4006 
4007     if (result == 0)
4008 	outofmem(__FILE__, "sortedList");
4009 
4010     assert(result != 0);
4011 
4012     while (!HTList_isEmpty(list))
4013 	result[j++] = (char *) HTList_nextObject(list);
4014 
4015     if (count > 1) {
4016 	qsort((char *) result, count, sizeof(*result),
4017 	      ignorecase ? caselessCmpList : normalCmpList);
4018 
4019 	/* remove duplicate entries from the sorted index */
4020 	for (j = 0; result[j] != 0; j++) {
4021 	    k = j;
4022 	    while (result[k] != 0
4023 		   && !strcmp(result[j], result[k])) {
4024 		k++;
4025 	    }
4026 	    k--;
4027 	    if (j != k) {
4028 		for (jk = j;; jk++) {
4029 		    result[jk] = result[jk + k - j];
4030 		    if (result[jk] == 0)
4031 			break;
4032 		}
4033 	    }
4034 	}
4035     }
4036 
4037     return result;
4038 }
4039 
LYarrayLength(STRING2PTR list)4040 int LYarrayLength(STRING2PTR list)
4041 {
4042     int result = 0;
4043 
4044     while (*list++ != 0)
4045 	result++;
4046     return result;
4047 }
4048 
LYarrayWidth(STRING2PTR list)4049 int LYarrayWidth(STRING2PTR list)
4050 {
4051     int result = 0;
4052     int check;
4053 
4054     while (*list != 0) {
4055 	check = (int) strlen(*list++);
4056 	if (check > result)
4057 	    result = check;
4058     }
4059     return result;
4060 }
4061 
FormatChoiceNum(char * dst,int num_choices,int choice,const char * value)4062 static void FormatChoiceNum(char *dst,
4063 			    int num_choices,
4064 			    int choice,
4065 			    const char *value)
4066 {
4067     if (num_choices >= 0) {
4068 	int digits = (num_choices > 9) ? 2 : 1;
4069 
4070 	sprintf(dst, "%*d: %.*s",
4071 		digits, (choice + 1),
4072 		MAX_LINE - 9 - digits, value);
4073     } else {
4074 	LYStrNCpy(dst, value, MAX_LINE - 1);
4075     }
4076 }
4077 
options_width(STRING2PTR list)4078 static unsigned options_width(STRING2PTR list)
4079 {
4080     unsigned width = 0;
4081     int count = 0;
4082 
4083     while (list[count] != 0) {
4084 	unsigned ncells = (unsigned) LYstrCells(list[count]);
4085 
4086 	if (ncells > width) {
4087 	    width = ncells;
4088 	}
4089 	count++;
4090     }
4091     return width;
4092 }
4093 
draw_option(WINDOW * win,int entry,int width,int reversed,int num_choices,int number,const char * value)4094 static void draw_option(WINDOW * win, int entry,
4095 			int width,
4096 			int reversed,
4097 			int num_choices,
4098 			int number,
4099 			const char *value)
4100 {
4101     char Cnum[MAX_LINE];
4102 
4103     (void) width;
4104 
4105     FormatChoiceNum(Cnum, num_choices, number, "");
4106 #ifdef USE_SLANG
4107     SLsmg_gotorc(win->top_y + entry, (win->left_x + 2));
4108     LYaddstr(Cnum);
4109     if (reversed)
4110 	SLsmg_set_color(2);
4111     SLsmg_write_nstring((SLFUTURE_CONST char *) value, (unsigned) win->width);
4112     if (reversed)
4113 	SLsmg_set_color(0);
4114 #else
4115     wmove(win, entry, 1);
4116     LynxWChangeStyle(win, s_menu_entry, STACK_ON);
4117     waddch(win, ' ');
4118     LynxWChangeStyle(win, s_menu_entry, STACK_OFF);
4119     LynxWChangeStyle(win, s_menu_number, STACK_ON);
4120     waddstr(win, Cnum);
4121     LynxWChangeStyle(win, s_menu_number, STACK_OFF);
4122 #ifdef USE_COLOR_STYLE
4123     LynxWChangeStyle(win, reversed ? s_menu_active : s_menu_entry, STACK_ON);
4124 #else
4125     if (reversed)
4126 	wstart_reverse(win);
4127 #endif
4128     LYpaddstr(win, width, value);
4129 #ifdef USE_COLOR_STYLE
4130     LynxWChangeStyle(win, reversed ? s_menu_active : s_menu_entry, STACK_OFF);
4131 #else
4132     if (reversed)
4133 	wstop_reverse(win);
4134 #endif
4135     LynxWChangeStyle(win, s_menu_entry, STACK_ON);
4136     waddch(win, ' ');
4137     LynxWChangeStyle(win, s_menu_entry, STACK_OFF);
4138 #endif /* USE_SLANG */
4139 }
4140 
4141 /*
4142  * This function offers the choices for values of an option via a popup window
4143  * which functions like that for selection of options in a form.  - FM
4144  *
4145  * Also used for mouse popups with ncurses; this is indicated by for_mouse.
4146  */
LYhandlePopupList(int cur_choice,int ly,int lx,STRING2PTR choices,int width,int i_length,int disabled,int for_mouse)4147 int LYhandlePopupList(int cur_choice,
4148 		      int ly,
4149 		      int lx,
4150 		      STRING2PTR choices,
4151 		      int width,
4152 		      int i_length,
4153 		      int disabled,
4154 		      int for_mouse)
4155 {
4156     BOOLEAN numbered = (BOOLEAN) (keypad_mode != NUMBERS_AS_ARROWS);
4157     int c = 0, cmd = 0, i = 0, j = 0, rel = 0;
4158     int orig_choice;
4159     WINDOW *form_window;
4160     int num_choices = 0;
4161     int max_choices = 0;
4162     int top, bottom, length = -1;
4163     int window_offset = 0;
4164     int lines_to_show;
4165     char Cnum[64];
4166     int Lnum;
4167     int npages;
4168     static bstring *prev_target = NULL;		/* Search string buffer */
4169     static bstring *next_target = NULL;		/* Next search buffer */
4170     static BOOL first = TRUE;
4171     char *cp;
4172     int ch = 0;
4173     RecallType recall;
4174     int QueryTotal;
4175     int QueryNum;
4176     BOOLEAN FirstRecall = TRUE;
4177     BOOLEAN ReDraw = FALSE;
4178     int number;
4179     char buffer[MAX_LINE];
4180     const char *popup_status_msg = NULL;
4181     STRING2PTR Cptr = NULL;
4182 
4183 #define CAN_SCROLL_DOWN	1
4184 #define CAN_SCROLL_UP	2
4185 #define CAN_SCROLL	4
4186     int can_scroll = 0, can_scroll_was = 0;
4187 
4188     orig_choice = cur_choice;
4189     if (cur_choice < 0)
4190 	cur_choice = 0;
4191 
4192     /*
4193      * Initialize the search string buffer. - FM
4194      */
4195     if (first) {
4196 	BStrCopy0(next_target, "");
4197 	first = FALSE;
4198     }
4199     BStrCopy0(prev_target, "");
4200     QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
4201     recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
4202     QueryNum = QueryTotal;
4203 
4204     /*
4205      * Count the number of choices to be displayed, where num_choices ranges
4206      * from 0 to n, and set width to the longest choice string length.  Also
4207      * set Lnum to the length for the highest choice number, then decrement
4208      * num_choices so as to be zero-based.  The window width will be based on
4209      * the sum of width and Lnum.  - FM
4210      */
4211     num_choices = LYarrayLength(choices) - 1;
4212     if (width <= 0)
4213 	width = (int) options_width(choices);
4214     if (numbered) {
4215 	sprintf(Cnum, "%d: ", num_choices);
4216 	Lnum = (int) strlen(Cnum);
4217 	max_choices = num_choices;
4218     } else {
4219 	Lnum = 0;
4220 	max_choices = -1;
4221     }
4222 
4223     /*
4224      * Let's assume for the sake of sanity that ly is the number corresponding
4225      * to the line the choice is on.
4226      *
4227      * Let's also assume that cur_choice is the number of the item that should
4228      * be initially selected, as 0 being the first item.
4229      *
4230      * So what we have, is the top equal to the current screen line subtracting
4231      * the cur_choice + 1 (the one must be for the top line we will draw in a
4232      * box).  If the top goes under 0, consider it 0.
4233      */
4234     top = ly - (cur_choice + 1);
4235     if (top < 0)
4236 	top = 0;
4237 
4238     /*
4239      * Check and see if we need to put the i_length parameter up to the number
4240      * of real choices.
4241      */
4242     if (i_length < 1) {
4243 	i_length = num_choices;
4244     } else {
4245 	/*
4246 	 * Otherwise, it is really one number too high.
4247 	 */
4248 	i_length--;
4249     }
4250 
4251     /*
4252      * The bottom is the value of the top plus the number of options to view
4253      * plus 3 (one for the top line, one for the bottom line, and one to offset
4254      * the 0 counted in the num_choices).
4255      */
4256     bottom = top + i_length + 3;
4257 
4258     /*
4259      * Set lines_to_show based on the user_mode global.
4260      */
4261     if (user_mode == NOVICE_MODE)
4262 	lines_to_show = LYlines - 4;
4263     else
4264 	lines_to_show = LYlines - 2;
4265 
4266     if (for_mouse && user_mode == NOVICE_MODE && lines_to_show > 2)
4267 	lines_to_show--;
4268 
4269     /*
4270      * Hmm...  If the bottom goes beyond the number of lines available,
4271      */
4272     if (bottom > lines_to_show) {
4273 	/*
4274 	 * Position the window at the top if we have more choices than will fit
4275 	 * in the window.
4276 	 */
4277 	if ((i_length + 3) > lines_to_show) {
4278 	    top = 0;
4279 	    bottom = (top + (i_length + 3));
4280 	    if (bottom > lines_to_show)
4281 		bottom = (lines_to_show + 1);
4282 	} else {
4283 	    /*
4284 	     * Try to position the window so that the selected choice will
4285 	     * appear where the selection box currently is positioned.  It
4286 	     * could end up too high, at this point, but we'll move it down
4287 	     * latter, if that has happened.
4288 	     */
4289 	    top = (lines_to_show + 1) - (i_length + 3);
4290 	    bottom = (lines_to_show + 1);
4291 	}
4292     }
4293 
4294     /*
4295      * This is really fun, when the length is 4, it means 0 to 4, or 5.
4296      */
4297     length = (bottom - top) - 2;
4298     if (length <= num_choices)
4299 	can_scroll = CAN_SCROLL;
4300 
4301     /*
4302      * Move the window down if it's too high.
4303      */
4304     if (bottom < ly + 2) {
4305 	bottom = ly + 2;
4306 	if (bottom > lines_to_show + 1)
4307 	    bottom = lines_to_show + 1;
4308 	top = bottom - length - 2;
4309     }
4310 
4311     if (for_mouse) {
4312 	int check = (Lnum + (int) width + 4);
4313 	int limit = LYcols;
4314 
4315 	/* shift horizontally to lie within screen width, if possible */
4316 	if (check < limit) {
4317 	    if (lx - 1 + check > limit)
4318 		lx = limit + 1 - check;
4319 	    else if (lx <= 0)
4320 		lx = 1;
4321 	}
4322     }
4323 
4324     /*
4325      * Set up the overall window, including the boxing characters ('*'), if it
4326      * all fits.  Otherwise, set up the widest window possible.  - FM
4327      */
4328     width += Lnum;
4329     bottom -= top;
4330 
4331     if (num_choices <= 0
4332 	|| cur_choice > num_choices
4333 	|| (form_window = LYstartPopup(&top,
4334 				       &lx,
4335 				       &bottom,
4336 				       &width)) == 0)
4337 	return (orig_choice);
4338 
4339     width -= Lnum;
4340     bottom += top;
4341 
4342     /*
4343      * Clear the command line and write the popup statusline.  - FM
4344      */
4345     if (disabled) {
4346 	popup_status_msg = CHOICE_LIST_UNM_MSG;
4347     } else if (!for_mouse) {
4348 	popup_status_msg = CHOICE_LIST_MESSAGE;
4349 #if defined(USE_MOUSE) && (defined(NCURSES) || defined(PDCURSES))
4350     } else {
4351 	popup_status_msg =
4352 	    gettext("Left mouse button or return to select, arrow keys to scroll.");
4353 #endif
4354     }
4355     _statusline(popup_status_msg);
4356 
4357     /*
4358      * Set up the window_offset for choices.
4359      * cur_choice ranges from 0...n
4360      * length ranges from 0...m
4361      */
4362     if (cur_choice >= length) {
4363 	window_offset = cur_choice - length + 1;
4364     }
4365 
4366     /*
4367      * Compute the number of popup window pages.  - FM
4368      */
4369     npages = ((num_choices + 1) > length) ?
4370 	(((num_choices + 1) + (length - 1)) / (length))
4371 	: 1;
4372     /*
4373      * OH!  I LOVE GOTOs!  hack hack hack
4374      */
4375   redraw:
4376 
4377     /*
4378      * Display the boxed choices.
4379      */
4380     for (i = 0; i <= num_choices; i++) {
4381 	if (i >= window_offset && i - window_offset < length) {
4382 	    draw_option(form_window, ((i + 1) - window_offset), width, FALSE,
4383 			max_choices, i, choices[i]);
4384 	}
4385     }
4386     LYbox(form_window, !numbered);
4387     Cptr = NULL;
4388 
4389     /*
4390      * Loop on user input.
4391      */
4392     while (cmd != LYK_ACTIVATE) {
4393 	int row = ((i + 1) - window_offset);
4394 
4395 	/* Show scroll indicators. */
4396 	if (can_scroll) {
4397 	    can_scroll = ((window_offset ? CAN_SCROLL_UP : 0)
4398 			  | (num_choices - window_offset >= length
4399 			     ? CAN_SCROLL_DOWN : 0));
4400 	    if (~can_scroll & can_scroll_was) {		/* Need to redraw */
4401 		LYbox(form_window, !numbered);
4402 		can_scroll_was = 0;
4403 	    }
4404 	    if (can_scroll & ~can_scroll_was & CAN_SCROLL_UP) {
4405 		wmove(form_window, 1, Lnum + width + 3);
4406 		LynxWChangeStyle(form_window, s_menu_sb, STACK_ON);
4407 		waddch(form_window, ACS_UARROW);
4408 		LynxWChangeStyle(form_window, s_menu_sb, STACK_OFF);
4409 	    }
4410 	    if (can_scroll & ~can_scroll_was & CAN_SCROLL_DOWN) {
4411 		wmove(form_window, length, Lnum + width + 3);
4412 		LynxWChangeStyle(form_window, s_menu_sb, STACK_ON);
4413 		waddch(form_window, ACS_DARROW);
4414 		LynxWChangeStyle(form_window, s_menu_sb, STACK_OFF);
4415 	    }
4416 	}
4417 
4418 	/*
4419 	 * Unreverse cur choice.
4420 	 */
4421 	if (Cptr != NULL) {
4422 	    draw_option(form_window, row, width, FALSE,
4423 			max_choices, i, Cptr[i]);
4424 	}
4425 	Cptr = choices;
4426 	i = cur_choice;
4427 	row = ((cur_choice + 1) - window_offset);
4428 	draw_option(form_window, row, width, TRUE,
4429 		    max_choices, cur_choice, Cptr[cur_choice]);
4430 	LYstowCursor(form_window, row, 1);
4431 
4432 	c = LYgetch_choice();
4433 	if (term_options || LYCharIsINTERRUPT(c)) {	/* Control-C or Control-G */
4434 	    cmd = LYK_QUIT;
4435 #ifndef USE_SLANG
4436 	} else if (c == MOUSE_KEY) {
4437 	    if ((cmd = fancy_mouse(form_window, row, &cur_choice)) < 0)
4438 		goto redraw;
4439 	    if (cmd == LYK_ACTIVATE)
4440 		break;
4441 #endif
4442 	} else {
4443 	    cmd = LKC_TO_LAC(keymap, c);
4444 	}
4445 #ifdef VMS
4446 	if (HadVMSInterrupt) {
4447 	    HadVMSInterrupt = FALSE;
4448 	    cmd = LYK_QUIT;
4449 	}
4450 #endif /* VMS */
4451 
4452 	switch (cmd) {
4453 	case LYK_F_LINK_NUM:
4454 	    c = '\0';
4455 	    /* FALLTHRU */
4456 	case LYK_1:		/* FALLTHRU */
4457 	case LYK_2:		/* FALLTHRU */
4458 	case LYK_3:		/* FALLTHRU */
4459 	case LYK_4:		/* FALLTHRU */
4460 	case LYK_5:		/* FALLTHRU */
4461 	case LYK_6:		/* FALLTHRU */
4462 	case LYK_7:		/* FALLTHRU */
4463 	case LYK_8:		/* FALLTHRU */
4464 	case LYK_9:
4465 	    /*
4466 	     * Get a number from the user, possibly with a 'g' or 'p' suffix
4467 	     * (which will be loaded into c).  - FM & LE
4468 	     */
4469 	    number = get_popup_number(SELECT_OPTION_NUMBER, &c, &rel);
4470 
4471 	    /* handle + or - suffix */
4472 	    CTRACE((tfp, "got popup option number %d, ", number));
4473 	    CTRACE((tfp, "rel='%c', c='%c', cur_choice=%d\n",
4474 		    rel, c, cur_choice));
4475 	    if (c == 'p') {
4476 		int curpage = ((cur_choice + 1) > length) ?
4477 		(((cur_choice + 1) + (length - 1)) / (length))
4478 		: 1;
4479 
4480 		CTRACE((tfp, "  curpage=%d\n", curpage));
4481 		if (rel == '+')
4482 		    number = curpage + number;
4483 		else if (rel == '-')
4484 		    number = curpage - number;
4485 	    } else if (rel == '+') {
4486 		number = cur_choice + number + 1;
4487 	    } else if (rel == '-') {
4488 		number = cur_choice - number + 1;
4489 	    }
4490 	    if (rel)
4491 		CTRACE((tfp, "new number=%d\n", number));
4492 	    /*
4493 	     * Check for a 'p' suffix.  - FM
4494 	     */
4495 	    if (c == 'p') {
4496 		/*
4497 		 * Treat 1 or less as the first page.  - FM
4498 		 */
4499 		if (number <= 1) {
4500 		    if (window_offset == 0) {
4501 			HTUserMsg(ALREADY_AT_OPTION_BEGIN);
4502 			_statusline(popup_status_msg);
4503 			break;
4504 		    }
4505 		    window_offset = 0;
4506 		    cur_choice = 0;
4507 		    _statusline(popup_status_msg);
4508 		    goto redraw;
4509 		}
4510 
4511 		/*
4512 		 * Treat a number equal to or greater than the number of pages
4513 		 * as the last page.  - FM
4514 		 */
4515 		if (number >= npages) {
4516 		    if (window_offset >= ((num_choices - length) + 1)) {
4517 			HTUserMsg(ALREADY_AT_OPTION_END);
4518 			_statusline(popup_status_msg);
4519 			break;
4520 		    }
4521 		    window_offset = ((npages - 1) * length);
4522 		    if (window_offset > (num_choices - length)) {
4523 			window_offset = (num_choices - length + 1);
4524 		    }
4525 		    if (cur_choice < window_offset)
4526 			cur_choice = window_offset;
4527 		    _statusline(popup_status_msg);
4528 		    goto redraw;
4529 		}
4530 
4531 		/*
4532 		 * We want an intermediate page.  - FM
4533 		 */
4534 		if (((number - 1) * length) == window_offset) {
4535 		    char *msg = 0;
4536 
4537 		    HTSprintf0(&msg, ALREADY_AT_OPTION_PAGE, number);
4538 		    HTUserMsg(msg);
4539 		    FREE(msg);
4540 		    _statusline(popup_status_msg);
4541 		    break;
4542 		}
4543 		cur_choice = window_offset = ((number - 1) * length);
4544 		_statusline(popup_status_msg);
4545 		goto redraw;
4546 
4547 	    }
4548 
4549 	    /*
4550 	     * Check for a positive number, which signifies that a choice
4551 	     * should be sought.  - FM
4552 	     */
4553 	    if (number > 0) {
4554 		/*
4555 		 * Decrement the number so as to correspond with our cur_choice
4556 		 * values.  - FM
4557 		 */
4558 		number--;
4559 
4560 		/*
4561 		 * If the number is in range and had no legal suffix, select
4562 		 * the indicated choice.  - FM
4563 		 */
4564 		if (number <= num_choices && c == '\0') {
4565 		    cur_choice = number;
4566 		    cmd = LYK_ACTIVATE;
4567 		    break;
4568 		}
4569 
4570 		/*
4571 		 * Verify that we had a 'g' suffix, and act on the number.  -
4572 		 * FM
4573 		 */
4574 		if (c == 'g') {
4575 		    if (cur_choice == number) {
4576 			/*
4577 			 * The choice already is current.  - FM
4578 			 */
4579 			char *msg = 0;
4580 
4581 			HTSprintf0(&msg, OPTION_ALREADY_CURRENT, (number + 1));
4582 			HTUserMsg(msg);
4583 			FREE(msg);
4584 			_statusline(popup_status_msg);
4585 			break;
4586 		    }
4587 
4588 		    if (number <= num_choices) {
4589 			/*
4590 			 * The number is in range and had a 'g' suffix, so make
4591 			 * it the current option, scrolling if needed.  - FM
4592 			 */
4593 			j = (number - cur_choice);
4594 			cur_choice = number;
4595 			if ((j > 0) &&
4596 			    (cur_choice - window_offset) >= length) {
4597 			    window_offset += j;
4598 			    if (window_offset > (num_choices - length + 1))
4599 				window_offset = (num_choices - length + 1);
4600 			} else if ((cur_choice - window_offset) < 0) {
4601 			    window_offset -= abs(j);
4602 			    if (window_offset < 0)
4603 				window_offset = 0;
4604 			}
4605 			_statusline(popup_status_msg);
4606 			goto redraw;
4607 		    }
4608 
4609 		    /*
4610 		     * Not in range.  - FM
4611 		     */
4612 		    HTUserMsg(BAD_OPTION_NUM_ENTERED);
4613 		}
4614 	    }
4615 
4616 	    /*
4617 	     * Restore the popup statusline.  - FM
4618 	     */
4619 	    _statusline(popup_status_msg);
4620 	    break;
4621 
4622 	case LYK_PREV_LINK:
4623 	case LYK_LPOS_PREV_LINK:
4624 	case LYK_FASTBACKW_LINK:
4625 	case LYK_UP_LINK:
4626 
4627 	    if (cur_choice > 0)
4628 		cur_choice--;
4629 
4630 	    /*
4631 	     * Scroll the window up if necessary.
4632 	     */
4633 	    if ((cur_choice - window_offset) < 0) {
4634 		window_offset--;
4635 		goto redraw;
4636 	    }
4637 	    break;
4638 
4639 	case LYK_NEXT_LINK:
4640 	case LYK_LPOS_NEXT_LINK:
4641 	case LYK_FASTFORW_LINK:
4642 	case LYK_DOWN_LINK:
4643 	    if (cur_choice < num_choices)
4644 		cur_choice++;
4645 
4646 	    /*
4647 	     * Scroll the window down if necessary
4648 	     */
4649 	    if ((cur_choice - window_offset) >= length) {
4650 		window_offset++;
4651 		goto redraw;
4652 	    }
4653 	    break;
4654 
4655 	case LYK_NEXT_PAGE:
4656 	    /*
4657 	     * Okay, are we on the last page of the list?  If not then,
4658 	     */
4659 	    if (window_offset != (num_choices - length + 1)) {
4660 		/*
4661 		 * Modify the current choice to not be a coordinate in the
4662 		 * list, but a coordinate on the item selected in the window.
4663 		 */
4664 		cur_choice -= window_offset;
4665 
4666 		/*
4667 		 * Page down the proper length for the list.  If simply to far,
4668 		 * back up.
4669 		 */
4670 		window_offset += length;
4671 		if (window_offset > (num_choices - length)) {
4672 		    window_offset = (num_choices - length + 1);
4673 		}
4674 
4675 		/*
4676 		 * Readjust the current selection to be a list coordinate
4677 		 * rather than window.  Redraw this thing.
4678 		 */
4679 		cur_choice += window_offset;
4680 		goto redraw;
4681 	    } else if (cur_choice < num_choices) {
4682 		/*
4683 		 * Already on last page of the list so just redraw it with the
4684 		 * last item selected.
4685 		 */
4686 		cur_choice = num_choices;
4687 	    }
4688 	    break;
4689 
4690 	case LYK_PREV_PAGE:
4691 	    /*
4692 	     * Are we on the first page of the list?  If not then,
4693 	     */
4694 	    if (window_offset != 0) {
4695 		/*
4696 		 * Modify the current selection to not be a list coordinate,
4697 		 * but a window coordinate.
4698 		 */
4699 		cur_choice -= window_offset;
4700 
4701 		/*
4702 		 * Page up the proper length.  If too far, back up.
4703 		 */
4704 		window_offset -= length;
4705 		if (window_offset < 0) {
4706 		    window_offset = 0;
4707 		}
4708 
4709 		/*
4710 		 * Readjust the current choice.
4711 		 */
4712 		cur_choice += window_offset;
4713 		goto redraw;
4714 	    } else if (cur_choice > 0) {
4715 		/*
4716 		 * Already on the first page so just back up to the first item.
4717 		 */
4718 		cur_choice = 0;
4719 	    }
4720 	    break;
4721 
4722 	case LYK_HOME:
4723 	    cur_choice = 0;
4724 	    if (window_offset > 0) {
4725 		window_offset = 0;
4726 		goto redraw;
4727 	    }
4728 	    break;
4729 
4730 	case LYK_END:
4731 	    cur_choice = num_choices;
4732 	    if (window_offset != (num_choices - length + 1)) {
4733 		window_offset = (num_choices - length + 1);
4734 		goto redraw;
4735 	    }
4736 	    break;
4737 
4738 	case LYK_DOWN_TWO:
4739 	    cur_choice += 2;
4740 	    if (cur_choice > num_choices)
4741 		cur_choice = num_choices;
4742 
4743 	    /*
4744 	     * Scroll the window down if necessary.
4745 	     */
4746 	    if ((cur_choice - window_offset) >= length) {
4747 		window_offset += 2;
4748 		if (window_offset > (num_choices - length + 1))
4749 		    window_offset = (num_choices - length + 1);
4750 		goto redraw;
4751 	    }
4752 	    break;
4753 
4754 	case LYK_UP_TWO:
4755 	    cur_choice -= 2;
4756 	    if (cur_choice < 0)
4757 		cur_choice = 0;
4758 
4759 	    /*
4760 	     * Scroll the window up if necessary.
4761 	     */
4762 	    if ((cur_choice - window_offset) < 0) {
4763 		window_offset -= 2;
4764 		if (window_offset < 0)
4765 		    window_offset = 0;
4766 		goto redraw;
4767 	    }
4768 	    break;
4769 
4770 	case LYK_DOWN_HALF:
4771 	    cur_choice += (length / 2);
4772 	    if (cur_choice > num_choices)
4773 		cur_choice = num_choices;
4774 
4775 	    /*
4776 	     * Scroll the window down if necessary.
4777 	     */
4778 	    if ((cur_choice - window_offset) >= length) {
4779 		window_offset += (length / 2);
4780 		if (window_offset > (num_choices - length + 1))
4781 		    window_offset = (num_choices - length + 1);
4782 		goto redraw;
4783 	    }
4784 	    break;
4785 
4786 	case LYK_UP_HALF:
4787 	    cur_choice -= (length / 2);
4788 	    if (cur_choice < 0)
4789 		cur_choice = 0;
4790 
4791 	    /*
4792 	     * Scroll the window up if necessary.
4793 	     */
4794 	    if ((cur_choice - window_offset) < 0) {
4795 		window_offset -= (length / 2);
4796 		if (window_offset < 0)
4797 		    window_offset = 0;
4798 		goto redraw;
4799 	    }
4800 	    break;
4801 
4802 	case LYK_REFRESH:
4803 	    lynx_force_repaint();
4804 	    LYrefresh();
4805 	    break;
4806 
4807 	case LYK_NEXT:
4808 	    if (recall && isBEmpty(next_target)) {
4809 		/*
4810 		 * We got a 'n'ext command with no prior query specified within
4811 		 * the popup window.  See if one was entered when the popup was
4812 		 * retracted, and if so, assume that's what's wanted.  Note
4813 		 * that it will become the default within popups, unless
4814 		 * another is entered within a popup.  If the within popup
4815 		 * default is to be changed at that point, use WHEREIS ('/')
4816 		 * and enter it, or the up- or down-arrow keys to seek any of
4817 		 * the previously entered queries, regardless of whether they
4818 		 * were entered within or outside of a popup window.  - FM
4819 		 */
4820 		if ((cp = (char *) HTList_objectAt(search_queries,
4821 						   0)) != NULL) {
4822 		    BStrCopy0(next_target, cp);
4823 		    QueryNum = 0;
4824 		    FirstRecall = FALSE;
4825 		}
4826 	    }
4827 	    BStrCopy(prev_target, next_target);
4828 	    /* FALLTHRU */
4829 	case LYK_WHEREIS:
4830 	    if (isBEmpty(prev_target)) {
4831 		_statusline(ENTER_WHEREIS_QUERY);
4832 		if ((ch = LYgetBString(&prev_target, VISIBLE, 0, recall)) < 0) {
4833 		    /*
4834 		     * User cancelled the search via ^G.  - FM
4835 		     */
4836 		    HTInfoMsg(CANCELLED);
4837 		    goto restore_popup_statusline;
4838 		}
4839 	    }
4840 
4841 	  check_recall:
4842 	    if (isBEmpty(prev_target) &&
4843 		!(recall && (ch == UPARROW || ch == DNARROW))) {
4844 		/*
4845 		 * No entry.  Simply break.  - FM
4846 		 */
4847 		HTInfoMsg(CANCELLED);
4848 		goto restore_popup_statusline;
4849 	    }
4850 
4851 	    if (recall && ch == UPARROW) {
4852 		if (FirstRecall) {
4853 		    /*
4854 		     * Use the current string or last query in the list.  - FM
4855 		     */
4856 		    FirstRecall = FALSE;
4857 		    if (!isBEmpty(next_target)) {
4858 			for (QueryNum = (QueryTotal - 1);
4859 			     QueryNum > 0; QueryNum--) {
4860 			    if ((cp = (char *) HTList_objectAt(search_queries,
4861 							       QueryNum))
4862 				!= NULL &&
4863 				!strcmp(next_target->str, cp)) {
4864 				break;
4865 			    }
4866 			}
4867 		    } else {
4868 			QueryNum = 0;
4869 		    }
4870 		} else {
4871 		    /*
4872 		     * Go back to the previous query in the list.  - FM
4873 		     */
4874 		    QueryNum++;
4875 		}
4876 		if (QueryNum >= QueryTotal) {
4877 		    /*
4878 		     * Roll around to the last query in the list.  - FM
4879 		     */
4880 		    QueryNum = 0;
4881 		}
4882 		if ((cp = (char *) HTList_objectAt(search_queries,
4883 						   QueryNum)) != NULL) {
4884 		    BStrCopy0(prev_target, cp);
4885 		    if (!isBEmpty(next_target) &&
4886 			!strcmp(next_target->str, prev_target->str)) {
4887 			_statusline(EDIT_CURRENT_QUERY);
4888 		    } else if ((!isBEmpty(next_target) && QueryTotal == 2) ||
4889 			       (isBEmpty(next_target) && QueryTotal == 1)) {
4890 			_statusline(EDIT_THE_PREV_QUERY);
4891 		    } else {
4892 			_statusline(EDIT_A_PREV_QUERY);
4893 		    }
4894 		    if ((ch = LYgetBString(&prev_target,
4895 					   VISIBLE, 0, recall)) < 0) {
4896 			/*
4897 			 * User cancelled the search via ^G.  - FM
4898 			 */
4899 			HTInfoMsg(CANCELLED);
4900 			goto restore_popup_statusline;
4901 		    }
4902 		    goto check_recall;
4903 		}
4904 	    } else if (recall && ch == DNARROW) {
4905 		if (FirstRecall) {
4906 		    /*
4907 		     * Use the current string or first query in the list.  - FM
4908 		     */
4909 		    FirstRecall = FALSE;
4910 		    if (!isBEmpty(next_target)) {
4911 			for (QueryNum = 0;
4912 			     QueryNum < (QueryTotal - 1); QueryNum++) {
4913 			    if ((cp = (char *) HTList_objectAt(search_queries,
4914 							       QueryNum))
4915 				!= NULL &&
4916 				!strcmp(next_target->str, cp)) {
4917 				break;
4918 			    }
4919 			}
4920 		    } else {
4921 			QueryNum = (QueryTotal - 1);
4922 		    }
4923 		} else {
4924 		    /*
4925 		     * Advance to the next query in the list.  - FM
4926 		     */
4927 		    QueryNum--;
4928 		}
4929 		if (QueryNum < 0) {
4930 		    /*
4931 		     * Roll around to the first query in the list.  - FM
4932 		     */
4933 		    QueryNum = (QueryTotal - 1);
4934 		}
4935 		if ((cp = (char *) HTList_objectAt(search_queries,
4936 						   QueryNum)) != NULL) {
4937 		    BStrCopy0(prev_target, cp);
4938 		    if (isBEmpty(next_target) &&
4939 			!strcmp(next_target->str, prev_target->str)) {
4940 			_statusline(EDIT_CURRENT_QUERY);
4941 		    } else if ((!isBEmpty(next_target) && QueryTotal == 2) ||
4942 			       (isBEmpty(next_target) && QueryTotal == 1)) {
4943 			_statusline(EDIT_THE_PREV_QUERY);
4944 		    } else {
4945 			_statusline(EDIT_A_PREV_QUERY);
4946 		    }
4947 		    if ((ch = LYgetBString(&prev_target,
4948 					   VISIBLE, 0, recall)) < 0) {
4949 			/*
4950 			 * User cancelled the search via ^G. - FM
4951 			 */
4952 			HTInfoMsg(CANCELLED);
4953 			goto restore_popup_statusline;
4954 		    }
4955 		    goto check_recall;
4956 		}
4957 	    }
4958 	    /*
4959 	     * Replace the search string buffer with the new target.  - FM
4960 	     */
4961 	    BStrCopy(next_target, prev_target);
4962 	    HTAddSearchQuery(next_target->str);
4963 
4964 	    /*
4965 	     * Start search at the next choice.  - FM
4966 	     */
4967 	    for (j = 1; Cptr[i + j] != NULL; j++) {
4968 		FormatChoiceNum(buffer, max_choices, (i + j), Cptr[i + j]);
4969 		if (LYcase_sensitive) {
4970 		    if (strstr(buffer, next_target->str) != NULL)
4971 			break;
4972 		} else {
4973 		    if (LYstrstr(buffer, next_target->str) != NULL)
4974 			break;
4975 		}
4976 	    }
4977 	    if (Cptr[i + j] != NULL) {
4978 		/*
4979 		 * We have a hit, so make that choice the current.  - FM
4980 		 */
4981 		cur_choice += j;
4982 		/*
4983 		 * Scroll the window down if necessary.
4984 		 */
4985 		if ((cur_choice - window_offset) >= length) {
4986 		    window_offset += j;
4987 		    if (window_offset > (num_choices - length + 1))
4988 			window_offset = (num_choices - length + 1);
4989 		    ReDraw = TRUE;
4990 		}
4991 		goto restore_popup_statusline;
4992 	    }
4993 
4994 	    /*
4995 	     * If we started at the beginning, it can't be present.  - FM
4996 	     */
4997 	    if (cur_choice == 0) {
4998 		HTUserMsg2(STRING_NOT_FOUND, next_target->str);
4999 		goto restore_popup_statusline;
5000 	    }
5001 
5002 	    /*
5003 	     * Search from the beginning to the current choice.  - FM
5004 	     */
5005 	    for (j = 0; j < cur_choice; j++) {
5006 		FormatChoiceNum(buffer, max_choices, (j + 1), Cptr[j]);
5007 		if (LYcase_sensitive) {
5008 		    if (strstr(buffer, next_target->str) != NULL)
5009 			break;
5010 		} else {
5011 		    if (LYstrstr(buffer, next_target->str) != NULL)
5012 			break;
5013 		}
5014 	    }
5015 	    if (j < cur_choice) {
5016 		/*
5017 		 * We have a hit, so make that choice the current.  - FM
5018 		 */
5019 		j = (cur_choice - j);
5020 		cur_choice -= j;
5021 		/*
5022 		 * Scroll the window up if necessary.
5023 		 */
5024 		if ((cur_choice - window_offset) < 0) {
5025 		    window_offset -= j;
5026 		    if (window_offset < 0)
5027 			window_offset = 0;
5028 		    ReDraw = TRUE;
5029 		}
5030 		goto restore_popup_statusline;
5031 	    }
5032 
5033 	    /*
5034 	     * Didn't find it in the preceding choices either.  - FM
5035 	     */
5036 	    HTUserMsg2(STRING_NOT_FOUND, next_target->str);
5037 
5038 	  restore_popup_statusline:
5039 	    /*
5040 	     * Restore the popup statusline and reset the search variables.  -
5041 	     * FM
5042 	     */
5043 	    _statusline(popup_status_msg);
5044 	    BStrCopy0(prev_target, "");
5045 	    QueryTotal = (search_queries ? HTList_count(search_queries)
5046 			  : 0);
5047 	    recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
5048 	    QueryNum = QueryTotal;
5049 	    if (ReDraw == TRUE) {
5050 		ReDraw = FALSE;
5051 		goto redraw;
5052 	    }
5053 	    break;
5054 
5055 	case LYK_QUIT:
5056 	case LYK_ABORT:
5057 	case LYK_PREV_DOC:
5058 	case LYK_INTERRUPT:
5059 	    cur_choice = orig_choice;
5060 	    cmd = LYK_ACTIVATE;	/* to exit */
5061 	    break;
5062 	}
5063     }
5064     LYstopPopup();
5065 
5066     return (disabled ? orig_choice : cur_choice);
5067 }
5068 
5069 /*
5070  * Allow the user to edit a string.
5071  */
LYgetBString(bstring ** inputline,int hidden,size_t max_cols,RecallType recall)5072 int LYgetBString(bstring **inputline,
5073 		 int hidden,
5074 		 size_t max_cols,
5075 		 RecallType recall)
5076 {
5077     int x, y;
5078     int ch;
5079     int xlec = -2;
5080     int last_xlec = -1;
5081     int last_xlkc = -1;
5082     EditFieldData MyEdit, *edit = &MyEdit;
5083 
5084 #ifdef SUPPORT_MULTIBYTE_EDIT
5085     BOOL refresh_mb = TRUE;
5086 #endif /* SUPPORT_MULTIBYTE_EDIT */
5087 
5088     CTRACE((tfp, "called LYgetBString hidden %d, recall %d\n", hidden, (int) recall));
5089 
5090     LYGetYX(y, x);		/* Use screen from cursor position to eol */
5091 
5092     (void) y;
5093     (void) x;
5094 
5095     if (*inputline == NULL)	/* caller may not have initialized this */
5096 	BStrCopy0(*inputline, "");
5097 
5098     LYSetupEdit(edit, (*inputline)->str, max_cols, LYcolLimit - x);
5099     IsHidden = (BOOL) hidden;
5100 #ifdef FEPCTRL
5101     fep_on();
5102 #endif
5103 
5104     for (;;) {
5105       again:
5106 #ifndef SUPPORT_MULTIBYTE_EDIT
5107 	LYRefreshEdit(edit);
5108 #else /* SUPPORT_MULTIBYTE_EDIT */
5109 	if (refresh_mb)
5110 	    LYRefreshEdit(edit);
5111 #endif /* SUPPORT_MULTIBYTE_EDIT */
5112 	ch = LYReadCmdKey(FOR_PROMPT);
5113 #ifdef SUPPORT_MULTIBYTE_EDIT
5114 #ifdef CJK_EX			/* for SJIS code */
5115 	if (!refresh_mb
5116 	    && (EditBinding(ch) != LYE_CHAR))
5117 	    goto again;
5118 #else
5119 	if (!refresh_mb
5120 	    && (EditBinding(ch) != LYE_CHAR)
5121 	    && (EditBinding(ch) != LYE_AIX))
5122 	    goto again;
5123 #endif
5124 #endif /* SUPPORT_MULTIBYTE_EDIT */
5125 
5126 	if (term_letter || term_options
5127 #ifdef VMS
5128 	    || HadVMSInterrupt
5129 #endif /* VMS */
5130 #ifndef DISABLE_NEWS
5131 	    || term_message
5132 #endif
5133 	    ) {
5134 #ifdef VMS
5135 	    HadVMSInterrupt = FALSE;
5136 #endif /* VMS */
5137 	    ch = LYCharINTERRUPT2;
5138 	}
5139 
5140 	if (recall != NORECALL && (ch == UPARROW || ch == DNARROW)) {
5141 	    BStrCopy0(*inputline, Buf);
5142 	    LYAddToCloset(recall, Buf);
5143 	    CTRACE((tfp, "LYgetstr(%s) recall\n", (*inputline)->str));
5144 #ifdef FEPCTRL
5145 	    fep_off();
5146 #endif
5147 	    LYFinishEdit(edit);
5148 	    return (ch);
5149 	}
5150 	ch |= CurModif;
5151 	CurModif = 0;
5152 	if (last_xlkc != -1) {
5153 	    if (ch == last_xlkc)
5154 		ch |= LKC_MOD3;
5155 	    last_xlkc = -1;	/* consumed */
5156 	}
5157 #ifndef WIN_EX
5158 	if (LKC_TO_LAC(keymap, ch) == LYK_REFRESH)
5159 	    goto again;
5160 #endif
5161 	last_xlec = xlec;
5162 	xlec = EditBinding(ch);
5163 	if ((xlec & LYE_DF) && !(xlec & LYE_FORM_LAC)) {
5164 	    last_xlkc = ch;
5165 	    xlec &= ~LYE_DF;
5166 	} else {
5167 	    last_xlkc = -1;
5168 	}
5169 	switch (xlec) {
5170 	case LYE_SETM1:
5171 	    /*
5172 	     * Set flag for modifier 1.
5173 	     */
5174 	    CurModif |= LKC_MOD1;
5175 	    break;
5176 	case LYE_SETM2:
5177 	    /*
5178 	     * Set flag for modifier 2.
5179 	     */
5180 	    CurModif |= LKC_MOD2;
5181 	    break;
5182 	case LYE_TAB:
5183 	    if (xlec == last_xlec && recall != NORECALL) {
5184 		HTList *list = whichRecall(recall);
5185 
5186 		if (!HTList_isEmpty(list)) {
5187 		    char **data = sortedList(list, (BOOL) (recall == RECALL_CMD));
5188 		    int old_y, old_x;
5189 		    int cur_choice = 0;
5190 		    int num_options = LYarrayLength((STRING2PTR) data);
5191 
5192 		    while (cur_choice < num_options
5193 			   && strcasecomp(data[cur_choice], Buf) < 0)
5194 			cur_choice++;
5195 
5196 		    LYGetYX(old_y, old_x);
5197 		    cur_choice = LYhandlePopupList(cur_choice,
5198 						   0,
5199 						   old_x,
5200 						   (STRING2PTR) data,
5201 						   -1,
5202 						   -1,
5203 						   FALSE,
5204 						   FALSE);
5205 		    if (cur_choice >= 0) {
5206 			if (recall == RECALL_CMD)
5207 			    _statusline(": ");
5208 			reinsertEdit(edit, data[cur_choice]);
5209 		    }
5210 		    LYmove(old_y, old_x);
5211 		    FREE(data);
5212 		}
5213 	    } else {
5214 		reinsertEdit(edit, LYFindInCloset(recall, Buf));
5215 	    }
5216 	    break;
5217 
5218 #ifndef CJK_EX			/* 1997/11/03 (Mon) 20:13:45 */
5219 	case LYE_AIX:
5220 	    /*
5221 	     * Hex 97.
5222 	     * Treat as a character for CJK, or if this is a valid character in
5223 	     * the current display character set.  Otherwise, we treat this as
5224 	     * LYE_ENTER.
5225 	     */
5226 	    if (ch != '\t' &&
5227 		(IS_CJK_TTY ||
5228 		 LYlowest_eightbit[current_char_set] <= 0x97)) {
5229 		LYLineEdit(edit, ch, FALSE);
5230 		break;
5231 	    }
5232 	    /* FALLTHRU */
5233 #endif
5234 	case LYE_ENTER:
5235 	    /*
5236 	     * Terminate the string and return.
5237 	     */
5238 	    BStrCopy0(*inputline, Buf);
5239 	    if (!hidden)
5240 		LYAddToCloset(recall, Buf);
5241 	    CTRACE((tfp, "LYgetstr(%s) LYE_ENTER\n", (*inputline)->str));
5242 #ifdef FEPCTRL
5243 	    fep_off();
5244 #endif
5245 	    LYFinishEdit(edit);
5246 	    return (ch);
5247 
5248 #ifdef CAN_CUT_AND_PASTE
5249 	    /* 1998/10/01 (Thu) 15:05:49 */
5250 
5251 	case LYE_PASTE:
5252 	    {
5253 		unsigned char *s = (unsigned char *) get_clip_grab(), *e;
5254 		int len;
5255 
5256 		if (!s)
5257 		    break;
5258 		len = (int) strlen((const char *) s);
5259 		e = s + len;
5260 
5261 		if (len > 0) {
5262 		    unsigned char *e1 = s;
5263 
5264 		    while (e1 < e) {
5265 			if (*e1 < ' ') {	/* Stop here? */
5266 			    if (e1 > s)
5267 				LYEditInsert(edit, s, (int) (e1 - s),
5268 					     map_active, TRUE);
5269 			    s = e1;
5270 			    if (*e1 == '\t') {	/* Replace by space */
5271 				LYEditInsert(edit,
5272 					     (unsigned const char *) " ",
5273 					     1,
5274 					     map_active,
5275 					     TRUE);
5276 				s = ++e1;
5277 			    } else
5278 				break;
5279 			} else
5280 			    ++e1;
5281 		    }
5282 		    if (e1 > s)
5283 			LYEditInsert(edit, s, (int) (e1 - s), map_active, TRUE);
5284 		}
5285 		get_clip_release();
5286 		break;
5287 	    }
5288 #endif
5289 
5290 	case LYE_ABORT:
5291 	    /*
5292 	     * Control-C or Control-G aborts.
5293 	     */
5294 	    CTRACE((tfp, "LYgetstr LYE_ABORT\n"));
5295 #ifdef FEPCTRL
5296 	    fep_off();
5297 #endif
5298 	    LYFinishEdit(edit);
5299 	    BStrCopy0(*inputline, "");
5300 	    return (-1);
5301 
5302 	case LYE_STOP:
5303 	    /*
5304 	     * Deactivate.
5305 	     */
5306 	    CTRACE((tfp, "LYgetstr LYE_STOP\n"));
5307 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
5308 	    textfields_need_activation = TRUE;
5309 	    LYFinishEdit(edit);
5310 	    BStrCopy0(*inputline, "");
5311 	    return (-1);
5312 #else
5313 #ifdef ENHANCED_LINEEDIT
5314 	    if (Mark >= 0)
5315 		Mark = -(1 + Mark);	/* Disable it */
5316 #endif
5317 	    break;
5318 #endif
5319 
5320 	case LYE_LKCMD:
5321 	    /*
5322 	     * Used only in form_getstr() for invoking the LYK_F_LINK_NUM
5323 	     * prompt when in form text fields.  - FM
5324 	     */
5325 	    break;
5326 
5327 	case LYE_FORM_PASS:
5328 	    /*
5329 	     * Used in form_getstr() to end line editing and pass on the input
5330 	     * char/lynxkeycode.  Here it is just ignored.  - kw
5331 	     */
5332 	    break;
5333 
5334 	default:
5335 	    if (xlec & LYE_FORM_LAC) {
5336 		/*
5337 		 * Used in form_getstr() to end line editing and pass on the
5338 		 * lynxkeycode already containing a lynxactioncode.  Here it is
5339 		 * just ignored.  - kw
5340 		 */
5341 		break;
5342 	    }
5343 #ifndef SUPPORT_MULTIBYTE_EDIT
5344 	    LYLineEdit(edit, ch, FALSE);
5345 #else /* SUPPORT_MULTIBYTE_EDIT */
5346 	    if (LYLineEdit(edit, ch, FALSE) == 0) {
5347 		if (refresh_mb && IS_CJK_TTY && (0x81 <= ch) && (ch <= 0xfe))
5348 		    refresh_mb = FALSE;
5349 		else
5350 		    refresh_mb = TRUE;
5351 	    } else {
5352 		if (!refresh_mb) {
5353 		    LYEdit1(edit, 0, LYE_DELP, FALSE);
5354 		}
5355 	    }
5356 #endif /* SUPPORT_MULTIBYTE_EDIT */
5357 	}
5358     }
5359 }
5360 
5361 /*
5362  * Use this for fixed-buffer edits which have not been converted to use
5363  * LYgetBString().
5364  */
LYgetstr(char * inputline,int hidden,size_t bufsize,RecallType recall)5365 int LYgetstr(char *inputline,	/* fixed-size buffer for input/output */
5366 	     int hidden,	/* true to suppress from command-history */
5367 	     size_t bufsize,	/* sizeof(inputline) */
5368 	     RecallType recall)	/* type of command-history */
5369 {
5370     int ch;
5371     bstring *my_bstring = NULL;
5372 
5373     BStrCopy0(my_bstring, inputline);
5374     if (my_bstring != 0) {
5375 	ch = LYgetBString(&my_bstring, hidden, bufsize, recall);
5376 	if (ch >= 0 && my_bstring != 0)
5377 	    LYStrNCpy(inputline, my_bstring->str, bufsize);
5378 	BStrFree(my_bstring);
5379     } else {
5380 	ch = -1;
5381     }
5382     return ch;
5383 }
5384 
LYLineeditHelpURL(void)5385 const char *LYLineeditHelpURL(void)
5386 {
5387     static int lasthelp_lineedit = -1;
5388     static char helpbuf[LY_MAXPATH] = "\0";
5389     static char *phelp = &helpbuf[0];
5390 
5391     if (lasthelp_lineedit == current_lineedit)
5392 	return &helpbuf[0];
5393     if (lasthelp_lineedit == -1) {
5394 	LYStrNCpy(helpbuf, helpfilepath, sizeof(helpbuf) - 1);
5395 	phelp += strlen(helpbuf);
5396     }
5397     if (LYLineeditHelpURLs[current_lineedit] &&
5398 	strlen(LYLineeditHelpURLs[current_lineedit]) &&
5399 	(strlen(LYLineeditHelpURLs[current_lineedit]) <=
5400 	 sizeof(helpbuf) - (unsigned) (phelp - helpbuf))) {
5401 	LYStrNCpy(phelp, LYLineeditHelpURLs[current_lineedit],
5402 		  (int) (sizeof(helpbuf) - (unsigned) (phelp - helpbuf) - 1));
5403 	lasthelp_lineedit = current_lineedit;
5404 	return (&helpbuf[0]);
5405     }
5406     return NULL;
5407 }
5408 
5409 /*
5410  * Wrapper for sscanf to ensure that lynx can "always" read a POSIX float.
5411  * In some locales, the decimal point changes.
5412  */
LYscanFloat2(const char ** source,float * result)5413 int LYscanFloat2(const char **source, float *result)
5414 {
5415     int count = 0;
5416     char *temp;
5417     const char *src = *source;
5418 
5419     src = LYSkipCBlanks(src);
5420     *result = 0.0;
5421     if (strchr(src, '.') != 0) {
5422 	long frc_part = 0;
5423 	float scale = 1.0;
5424 
5425 	if (*src != '.') {
5426 	    temp = NULL;
5427 	    frc_part = strtol(src, &temp, 10);
5428 	    *result = (float) frc_part;
5429 	    src = temp;
5430 	}
5431 	if (src != 0 && *src == '.') {
5432 	    ++src;
5433 	    if (isdigit(UCH(*src))) {
5434 		temp = NULL;
5435 		frc_part = strtol(src, &temp, 10);
5436 		if (temp != 0) {
5437 		    int digits = (int) (temp - src);
5438 
5439 		    while (digits-- > 0)
5440 			scale *= (float) 10.0;
5441 		    *result += ((float) frc_part / scale);
5442 		}
5443 		src = temp;
5444 	    }
5445 	}
5446 	if (src != 0 && *src != '\0' && strchr(" \t+", *src) == 0) {
5447 	    char *extra = (char *) malloc(2 + strlen(src));
5448 
5449 	    if (extra != 0) {
5450 		extra[0] = '1';
5451 		strcpy(extra + 1, src);
5452 		if (sscanf(extra, "%f", &scale) == 1) {
5453 		    *result *= scale;
5454 		}
5455 		FREE(extra);
5456 		src = LYSkipCNonBlanks(src);
5457 	    } else {
5458 		src = 0;
5459 	    }
5460 	}
5461 	if (src != 0)
5462 	    count = 1;
5463     } else {
5464 	count = sscanf(src, "%f", result);
5465 	src = LYSkipCNonBlanks(src);
5466     }
5467     CTRACE2(TRACE_CFG,
5468 	    (tfp, "LYscanFloat \"%s\" -> %f (%s)\n",
5469 	     *source, *result,
5470 	     count ? "ok" : "error"));
5471     *source = src;
5472     return count;
5473 }
5474 
LYscanFloat(const char * source,float * result)5475 int LYscanFloat(const char *source, float *result)
5476 {
5477     const char *temp = source;
5478 
5479     return LYscanFloat2(&temp, result);
5480 }
5481 
5482 /*
5483  * A replacement for 'strsep()'
5484  */
LYstrsep(char ** stringp,const char * delim)5485 char *LYstrsep(char **stringp,
5486 	       const char *delim)
5487 {
5488     char *tmp, *out;
5489 
5490     if (isEmpty(stringp))	/* nothing to do? */
5491 	return 0;		/* then don't fall on our faces */
5492 
5493     out = *stringp;		/* save the start of the string */
5494     tmp = strpbrk(*stringp, delim);
5495     if (tmp) {
5496 	*tmp = '\0';		/* terminate the substring with \0 */
5497 	*stringp = ++tmp;	/* point at the next substring */
5498     } else
5499 	*stringp = 0;		/* this was the last substring: */
5500     /* let caller see he's done */
5501     return out;
5502 }
5503 
5504 /*
5505  * LYstrstr will find the first occurrence of the string pointed to by tarptr
5506  * in the string pointed to by chptr.  It returns NULL if string not found.  It
5507  * is a case insensitive search.
5508  */
LYstrstr(char * chptr,const char * tarptr)5509 char *LYstrstr(char *chptr,
5510 	       const char *tarptr)
5511 {
5512     int len = (int) strlen(tarptr);
5513 
5514     for (; *chptr != '\0'; chptr++) {
5515 	if (0 == UPPER8(*chptr, *tarptr)) {
5516 	    if (0 == strncasecomp8(chptr + 1, tarptr + 1, len - 1))
5517 		return (chptr);
5518 	}
5519     }				/* end for */
5520 
5521     return (NULL);		/* string not found or initial chptr was empty */
5522 }
5523 
5524 /*
5525  * LYno_attr_char_case_strstr will find the first occurrence of the
5526  * string pointed to by tarptr in the string pointed to by chptr.
5527  * It ignores the characters:  LY_UNDERLINE_START_CHAR and
5528  *			       LY_UNDERLINE_END_CHAR
5529  *			       LY_BOLD_START_CHAR
5530  *			       LY_BOLD_END_CHAR
5531  *			       LY_SOFT_HYPHEN
5532  *			       if present in chptr.
5533  * It is a case insensitive search.
5534  */
LYno_attr_char_case_strstr(const char * chptr,const char * tarptr)5535 const char *LYno_attr_char_case_strstr(const char *chptr,
5536 				       const char *tarptr)
5537 {
5538     register const char *tmpchptr, *tmptarptr;
5539 
5540     if (!chptr)
5541 	return (NULL);
5542 
5543     while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
5544 	chptr++;
5545 
5546     for (; *chptr != '\0'; chptr++) {
5547 	if (0 == UPPER8(*chptr, *tarptr)) {
5548 	    /*
5549 	     * See if they line up.
5550 	     */
5551 	    tmpchptr = chptr + 1;
5552 	    tmptarptr = tarptr + 1;
5553 
5554 	    if (*tmptarptr == '\0')	/* one char target */
5555 		return (chptr);
5556 
5557 	    while (1) {
5558 		if (!IsSpecialAttrChar(*tmpchptr)) {
5559 		    if (0 != UPPER8(*tmpchptr, *tmptarptr))
5560 			break;
5561 		    tmpchptr++;
5562 		    tmptarptr++;
5563 		} else {
5564 		    tmpchptr++;
5565 		}
5566 		if (*tmptarptr == '\0')
5567 		    return (chptr);
5568 		if (*tmpchptr == '\0')
5569 		    break;
5570 	    }
5571 	}
5572     }				/* end for */
5573 
5574     return (NULL);
5575 }
5576 
5577 /*
5578  * LYno_attr_char_strstr will find the first occurrence of the
5579  * string pointed to by tarptr in the string pointed to by chptr.
5580  * It ignores the characters:  LY_UNDERLINE_START_CHAR and
5581  *			       LY_UNDERLINE_END_CHAR
5582  *			       LY_BOLD_START_CHAR
5583  *			       LY_BOLD_END_CHAR
5584  *			       LY_SOFT_HYPHEN
5585  *			       if present in chptr.
5586  * It is a case sensitive search.
5587  */
LYno_attr_char_strstr(const char * chptr,const char * tarptr)5588 const char *LYno_attr_char_strstr(const char *chptr,
5589 				  const char *tarptr)
5590 {
5591     register const char *tmpchptr, *tmptarptr;
5592 
5593     if (!chptr)
5594 	return (NULL);
5595 
5596     while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
5597 	chptr++;
5598 
5599     for (; *chptr != '\0'; chptr++) {
5600 	if ((*chptr) == (*tarptr)) {
5601 	    /*
5602 	     * See if they line up.
5603 	     */
5604 	    tmpchptr = chptr + 1;
5605 	    tmptarptr = tarptr + 1;
5606 
5607 	    if (*tmptarptr == '\0')	/* one char target */
5608 		return (chptr);
5609 
5610 	    while (1) {
5611 		if (!IsSpecialAttrChar(*tmpchptr)) {
5612 		    if ((*tmpchptr) != (*tmptarptr))
5613 			break;
5614 		    tmpchptr++;
5615 		    tmptarptr++;
5616 		} else {
5617 		    tmpchptr++;
5618 		}
5619 		if (*tmptarptr == '\0')
5620 		    return (chptr);
5621 		if (*tmpchptr == '\0')
5622 		    break;
5623 	    }
5624 	}
5625     }				/* end for */
5626 
5627     return (NULL);
5628 }
5629 
5630 /*
5631  * LYno_attr_mbcs_case_strstr will find the first occurrence of the string
5632  * pointed to by tarptr in the string pointed to by chptr.  It takes account of
5633  * MultiByte Character Sequences (UTF8).  The physical lengths of the displayed
5634  * string up to the start and end (= next position after) of the target string
5635  * are returned in *nstartp and *nendp if the search is successful.
5636  *
5637  * These lengths count glyph cells if count_gcells is set.  (Full-width
5638  * characters in CJK mode count as two.) Normally that's what we want.  They
5639  * count actual glyphs if count_gcells is unset.  (Full-width characters in CJK
5640  * mode count as one.)
5641  *
5642  * It ignores the characters: LY_UNDERLINE_START_CHAR and
5643  *			      LY_UNDERLINE_END_CHAR
5644  *			      LY_BOLD_START_CHAR
5645  *			      LY_BOLD_END_CHAR
5646  *			      LY_SOFT_HYPHEN
5647  *			      if present in chptr.
5648  * It assumes UTF8 if utf_flag is set.
5649  * It is a case insensitive search.  - KW & FM
5650  */
LYno_attr_mbcs_case_strstr(const char * chptr,const char * tarptr,int utf_flag,int count_gcells,int * nstartp,int * nendp)5651 const char *LYno_attr_mbcs_case_strstr(const char *chptr,
5652 				       const char *tarptr,
5653 				       int utf_flag,
5654 				       int count_gcells,
5655 				       int *nstartp,
5656 				       int *nendp)
5657 {
5658     const char *tmpchptr;
5659     const char *tmptarptr;
5660     int len = 0;
5661     int offset;
5662 
5663     if (!(chptr && tarptr))
5664 	return (NULL);
5665 
5666     /*
5667      * Skip initial IsSpecial chars.  - FM
5668      */
5669     while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
5670 	chptr++;
5671 
5672     /*
5673      * Seek a first target match.  - FM
5674      */
5675     for (; *chptr != '\0'; chptr++) {
5676 	if ((!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
5677 	     *chptr == *tarptr &&
5678 	     IsNormalChar(*(chptr + 1))) ||
5679 	    (0 == UPPER8(*chptr, *tarptr))) {
5680 	    int tarlen = 0;
5681 
5682 	    offset = len;
5683 	    len++;
5684 
5685 	    /*
5686 	     * See if they line up.
5687 	     */
5688 	    tmpchptr = (chptr + 1);
5689 	    tmptarptr = (tarptr + 1);
5690 
5691 	    if (*tmptarptr == '\0') {
5692 		/*
5693 		 * One char target.
5694 		 */
5695 		if (nstartp)
5696 		    *nstartp = offset;
5697 		if (nendp)
5698 		    *nendp = len;
5699 		return (chptr);
5700 	    }
5701 	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
5702 		*chptr == *tarptr &&
5703 		IsNormalChar(*tmpchptr)) {
5704 		/*
5705 		 * Check the CJK multibyte.  - FM
5706 		 */
5707 		if (*tmpchptr == *tmptarptr) {
5708 		    /*
5709 		     * It's a match.  Advance to next char.  - FM
5710 		     */
5711 		    tmpchptr++;
5712 		    tmptarptr++;
5713 		    if (count_gcells)
5714 			tarlen++;
5715 		    if (*tmptarptr == '\0') {
5716 			/*
5717 			 * One character match.  - FM
5718 			 */
5719 			if (nstartp)
5720 			    *nstartp = offset;
5721 			if (nendp)
5722 			    *nendp = len + tarlen;
5723 			return (chptr);
5724 		    }
5725 		} else {
5726 		    /*
5727 		     * It's not a match, so go back to seeking a first target
5728 		     * match.  - FM
5729 		     */
5730 		    chptr++;
5731 		    if (count_gcells)
5732 			len++;
5733 		    continue;
5734 		}
5735 	    }
5736 	    /*
5737 	     * See if the rest of the target matches.  - FM
5738 	     */
5739 	    while (1) {
5740 		if (!IsSpecialAttrChar(*tmpchptr)) {
5741 		    if (!utf_flag && IS_CJK_TTY && is8bits(*tmpchptr)) {
5742 			if (*tmpchptr == *tmptarptr &&
5743 			    *(tmpchptr + 1) == *(tmptarptr + 1) &&
5744 			    !IsSpecialAttrChar(*(tmpchptr + 1))) {
5745 			    tmpchptr++;
5746 			    tmptarptr++;
5747 			    if (count_gcells)
5748 				tarlen++;
5749 			} else {
5750 			    break;
5751 			}
5752 		    } else if (0 != UPPER8(*tmpchptr, *tmptarptr)) {
5753 			break;
5754 		    }
5755 
5756 		    if (!IS_UTF_EXTRA(*tmptarptr)) {
5757 			tarlen++;
5758 		    }
5759 		    tmpchptr++;
5760 		    tmptarptr++;
5761 
5762 		} else {
5763 		    tmpchptr++;
5764 		}
5765 
5766 		if (*tmptarptr == '\0') {
5767 		    if (nstartp)
5768 			*nstartp = offset;
5769 		    if (nendp)
5770 			*nendp = len + tarlen;
5771 		    return (chptr);
5772 		}
5773 		if (*tmpchptr == '\0')
5774 		    break;
5775 	    }
5776 	} else if (!(IS_UTF_EXTRA(*chptr) ||
5777 		     IsSpecialAttrChar(*chptr))) {
5778 	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
5779 		IsNormalChar(*(chptr + 1))) {
5780 		chptr++;
5781 		if (count_gcells)
5782 		    len++;
5783 	    }
5784 	    len++;
5785 	}
5786     }				/* end for */
5787 
5788     return (NULL);
5789 }
5790 
5791 /*
5792  * LYno_attr_mbcs_strstr will find the first occurrence of the string pointed
5793  * to by tarptr in the string pointed to by chptr.
5794  *
5795  * It takes account of CJK and MultiByte Character Sequences (UTF8).  The
5796  * physical lengths of the displayed string up to the start and end (= next
5797  * position after) the target string are returned in *nstartp and *nendp if the
5798  * search is successful.
5799  *
5800  * These lengths count glyph cells if count_gcells is set.  (Full-width
5801  * characters in CJK mode count as two.) Normally that's what we want.  They
5802  * count actual glyphs if count_gcells is unset.  (Full-width characters in CJK
5803  * mode count as one.)
5804  *
5805  * It ignores the characters: LY_UNDERLINE_START_CHAR and
5806  *			      LY_UNDERLINE_END_CHAR
5807  *			      LY_BOLD_START_CHAR
5808  *			      LY_BOLD_END_CHAR
5809  *			      LY_SOFT_HYPHEN
5810  *			      if present in chptr.
5811  * It assumes UTF8 if utf_flag is set.
5812  * It is a case sensitive search.  - KW & FM
5813  */
LYno_attr_mbcs_strstr(const char * chptr,const char * tarptr,int utf_flag,int count_gcells,int * nstartp,int * nendp)5814 const char *LYno_attr_mbcs_strstr(const char *chptr,
5815 				  const char *tarptr,
5816 				  int utf_flag,
5817 				  int count_gcells,
5818 				  int *nstartp,
5819 				  int *nendp)
5820 {
5821     const char *tmpchptr;
5822     const char *tmptarptr;
5823     int len = 0;
5824     int offset;
5825 
5826     if (!(chptr && tarptr))
5827 	return (NULL);
5828 
5829     /*
5830      * Skip initial IsSpecial chars.  - FM
5831      */
5832     while (IsSpecialAttrChar(*chptr) && *chptr != '\0')
5833 	chptr++;
5834 
5835     /*
5836      * Seek a first target match.  - FM
5837      */
5838     for (; *chptr != '\0'; chptr++) {
5839 	if ((*chptr) == (*tarptr)) {
5840 	    int tarlen = 0;
5841 
5842 	    offset = len;
5843 	    len++;
5844 
5845 	    /*
5846 	     * See if they line up.
5847 	     */
5848 	    tmpchptr = (chptr + 1);
5849 	    tmptarptr = (tarptr + 1);
5850 
5851 	    if (*tmptarptr == '\0') {
5852 		/*
5853 		 * One char target.
5854 		 */
5855 		if (nstartp)
5856 		    *nstartp = offset;
5857 		if (nendp)
5858 		    *nendp = len;
5859 		return (chptr);
5860 	    }
5861 	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
5862 		IsNormalChar(*tmpchptr)) {
5863 		/*
5864 		 * Check the CJK multibyte.  - FM
5865 		 */
5866 		if (*tmpchptr == *tmptarptr) {
5867 		    /*
5868 		     * It's a match.  Advance to next char.  - FM
5869 		     */
5870 		    tmpchptr++;
5871 		    tmptarptr++;
5872 		    if (count_gcells)
5873 			tarlen++;
5874 		    if (*tmptarptr == '\0') {
5875 			/*
5876 			 * One character match.  - FM
5877 			 */
5878 			if (nstartp)
5879 			    *nstartp = offset;
5880 			if (nendp)
5881 			    *nendp = len + tarlen;
5882 			return (chptr);
5883 		    }
5884 		} else {
5885 		    /*
5886 		     * It's not a match, so go back to seeking a first target
5887 		     * match.  - FM
5888 		     */
5889 		    chptr++;
5890 		    if (count_gcells)
5891 			len++;
5892 		    continue;
5893 		}
5894 	    }
5895 	    /*
5896 	     * See if the rest of the target matches.  - FM
5897 	     */
5898 	    while (1) {
5899 		if (!IsSpecialAttrChar(*tmpchptr)) {
5900 		    if (!utf_flag && IS_CJK_TTY && is8bits(*tmpchptr)) {
5901 			if (*tmpchptr == *tmptarptr &&
5902 			    *(tmpchptr + 1) == *(tmptarptr + 1) &&
5903 			    !IsSpecialAttrChar(*(tmpchptr + 1))) {
5904 			    tmpchptr++;
5905 			    tmptarptr++;
5906 			    if (count_gcells)
5907 				tarlen++;
5908 			} else {
5909 			    break;
5910 			}
5911 		    } else if ((*tmpchptr) != (*tmptarptr)) {
5912 			break;
5913 		    }
5914 
5915 		    if (!IS_UTF_EXTRA(*tmptarptr)) {
5916 			tarlen++;
5917 		    }
5918 		    tmpchptr++;
5919 		    tmptarptr++;
5920 		} else {
5921 		    tmpchptr++;
5922 		}
5923 
5924 		if (*tmptarptr == '\0') {
5925 		    if (nstartp)
5926 			*nstartp = offset;
5927 		    if (nendp)
5928 			*nendp = len + tarlen;
5929 		    return (chptr);
5930 		}
5931 		if (*tmpchptr == '\0')
5932 		    break;
5933 	    }
5934 	} else if (!(IS_UTF_EXTRA(*chptr) ||
5935 		     IsSpecialAttrChar(*chptr))) {
5936 	    if (!utf_flag && IS_CJK_TTY && is8bits(*chptr) &&
5937 		IsNormalChar(*(chptr + 1))) {
5938 		chptr++;
5939 		if (count_gcells)
5940 		    len++;
5941 	    }
5942 	    len++;
5943 	}
5944     }				/* end for */
5945 
5946     return (NULL);
5947 }
5948 
5949 /*
5950  * Allocate a new copy of a string, and returns it.
5951  */
SNACopy(char ** dest,const char * src,int n)5952 char *SNACopy(char **dest,
5953 	      const char *src,
5954 	      int n)
5955 {
5956     FREE(*dest);
5957     if (src) {
5958 	*dest = typeMallocn(char, (unsigned) n + 1);
5959 
5960 	if (*dest == NULL) {
5961 	    CTRACE((tfp, "Tried to malloc %d bytes\n", n));
5962 	    outofmem(__FILE__, "SNACopy");
5963 	    assert(*dest != NULL);
5964 	}
5965 	LYStrNCpy(*dest, src, n);
5966     }
5967     return *dest;
5968 }
5969 
5970 /*
5971  * String Allocate and Concatenate.
5972  */
SNACat(char ** dest,const char * src,int n)5973 char *SNACat(char **dest,
5974 	     const char *src,
5975 	     int n)
5976 {
5977     if (non_empty(src)) {
5978 	if (*dest) {
5979 	    int length = (int) strlen(*dest);
5980 
5981 	    *dest = typeRealloc(char, *dest, length + n + 1);
5982 
5983 	    if (*dest == NULL)
5984 		outofmem(__FILE__, "SNACat");
5985 	    assert(*dest != NULL);
5986 	    LYStrNCpy(*dest + length, src, n);
5987 	} else {
5988 	    *dest = typeMallocn(char, (unsigned) n + 1);
5989 
5990 	    if (*dest == NULL)
5991 		outofmem(__FILE__, "SNACat");
5992 	    assert(*dest != NULL);
5993 	    MemCpy(*dest, src, n);
5994 	    (*dest)[n] = '\0';	/* terminate */
5995 	}
5996     }
5997     return *dest;
5998 }
5999 
6000 #include <caselower.h>
6001 
6002 /*
6003  * Returns lowercase equivalent for unicode,
6004  * transparent output if no equivalent found.
6005  */
UniToLowerCase(long upper)6006 static long UniToLowerCase(long upper)
6007 {
6008     size_t i, high, low;
6009     long diff = 0;
6010 
6011     /*
6012      * Make check for sure.
6013      */
6014     if (upper <= 0)
6015 	return (upper);
6016 
6017     /*
6018      * Try unicode_to_lower_case[].
6019      */
6020     low = 0;
6021     high = TABLESIZE(unicode_to_lower_case);
6022     while (low < high) {
6023 	/*
6024 	 * Binary search.
6025 	 */
6026 	i = (low + (high - low) / 2);
6027 	diff = (unicode_to_lower_case[i].upper - upper);
6028 	if (diff < 0)
6029 	    low = i + 1;
6030 	if (diff > 0)
6031 	    high = i;
6032 	if (diff == 0)
6033 	    return (unicode_to_lower_case[i].lower);
6034     }
6035 
6036     return (upper);		/* if we came here */
6037 }
6038 
6039 /*
6040  *   UPPER8 ?
6041  *   it was "TOUPPER(a) - TOUPPER(b)" in its previous life...
6042  *
6043  *   It was realized that case-insensitive user search
6044  *   got information about upper/lower mapping from TOUPPER
6045  *   (precisely from "(TOUPPER(a) - TOUPPER(b))==0")
6046  *   and depends on locale in its 8bit mapping. -
6047  *   Usually fails with DOS/WINDOWS display charsets
6048  *   as well as on non-UNIX systems.
6049  *
6050  *   So use unicode case mapping.
6051  */
UPPER8(int ch1,int ch2)6052 int UPPER8(int ch1, int ch2)
6053 {
6054     /* if they are the same or one is a null characters return immediately. */
6055     if (ch1 == ch2)
6056 	return 0;
6057     if (!ch2)
6058 	return UCH(ch1);
6059     else if (!ch1)
6060 	return -UCH(ch2);
6061 
6062     /* case-insensitive match for us-ascii */
6063     if (UCH(TOASCII(ch1)) < 128 && UCH(TOASCII(ch2)) < 128)
6064 	return (TOUPPER(ch1) - TOUPPER(ch2));
6065 
6066     /* case-insensitive match for upper half */
6067     if (UCH(TOASCII(ch1)) > 127 &&	/* S/390 -- gil -- 2066 */
6068 	UCH(TOASCII(ch2)) > 127) {
6069 	if (DisplayCharsetMatchLocale)
6070 	    return (TOUPPER(ch1) - TOUPPER(ch2));	/* old-style */
6071 	else {
6072 	    long uni_ch2 = UCTransToUni((char) ch2, current_char_set);
6073 	    long uni_ch1;
6074 
6075 	    if (uni_ch2 < 0)
6076 		return UCH(ch1);
6077 	    uni_ch1 = UCTransToUni((char) ch1, current_char_set);
6078 	    return (int) (UniToLowerCase(uni_ch1) - UniToLowerCase(uni_ch2));
6079 	}
6080     }
6081 
6082     return (-10);		/* mismatch, if we come to here */
6083 }
6084 
6085 /*
6086  * Replaces 'fgets()' calls into a fixed-size buffer with reads into a buffer
6087  * that is allocated.  When an EOF or error is found, the buffer is freed
6088  * automatically.
6089  */
LYSafeGets(char ** src,FILE * fp)6090 char *LYSafeGets(char **src,
6091 		 FILE *fp)
6092 {
6093     char buffer[BUFSIZ];
6094     char *result = 0;
6095 
6096     if (src != 0)
6097 	result = *src;
6098     if (result != 0)
6099 	*result = 0;
6100 
6101     while (fgets(buffer, (int) sizeof(buffer), fp) != NULL) {
6102 	if (*buffer)
6103 	    result = StrAllocCat(result, buffer);
6104 	if (strchr(buffer, '\n') != 0)
6105 	    break;
6106     }
6107     if (ferror(fp)) {
6108 	FREE(result);
6109     } else if (feof(fp) && result && *result == '\0') {
6110 	/*
6111 	 * If the file ends in the middle of a line, return the partial line;
6112 	 * if another call is made after this, it will return NULL.  - kw
6113 	 */
6114 	FREE(result);
6115     }
6116     if (src != 0)
6117 	*src = result;
6118     return result;
6119 }
6120 
6121 #ifdef USE_CMD_LOGGING
6122 static FILE *cmd_logfile;
6123 static FILE *cmd_script;
6124 
LYOpenCmdLogfile(int argc,char ** argv)6125 void LYOpenCmdLogfile(int argc,
6126 		      char **argv)
6127 {
6128     int n;
6129 
6130     if (lynx_cmd_logfile != 0) {
6131 	cmd_logfile = LYNewTxtFile(lynx_cmd_logfile);
6132 	if (cmd_logfile != 0) {
6133 	    fprintf(cmd_logfile, "# Command logfile created by %s %s (%s)\n",
6134 		    LYNX_NAME, LYNX_VERSION, LYVersionDate());
6135 	    for (n = 0; n < argc; n++) {
6136 		fprintf(cmd_logfile, "# Arg%d = %s\n", n, argv[n]);
6137 	    }
6138 	}
6139     }
6140 }
6141 
LYHaveCmdScript(void)6142 BOOL LYHaveCmdScript(void)
6143 {
6144     return (BOOL) (cmd_script != 0);
6145 }
6146 
LYOpenCmdScript(void)6147 void LYOpenCmdScript(void)
6148 {
6149     if (lynx_cmd_script != 0) {
6150 	cmd_script = fopen(lynx_cmd_script, TXT_R);
6151 	CTRACE((tfp, "LYOpenCmdScript(%s) %s\n",
6152 		lynx_cmd_script,
6153 		cmd_script != 0 ? "SUCCESS" : "FAIL"));
6154     }
6155 }
6156 
LYReadCmdKey(int mode)6157 int LYReadCmdKey(int mode)
6158 {
6159     int ch = -1;
6160 
6161     if (cmd_script != 0) {
6162 	char *buffer = 0;
6163 	char *src;
6164 	char *tmp;
6165 
6166 	while ((ch < 0) && LYSafeGets(&buffer, cmd_script) != 0) {
6167 	    LYTrimTrailing(buffer);
6168 	    src = LYSkipBlanks(buffer);
6169 	    tmp = LYSkipNonBlanks(src);
6170 	    switch ((unsigned) (tmp - src)) {
6171 	    case 4:
6172 		if (!strncasecomp(src, "exit", 4))
6173 		    exit_immediately(0);
6174 		break;
6175 	    case 3:
6176 		if (!strncasecomp(src, "key", 3)) {
6177 		    ch = LYStringToKeycode(LYSkipBlanks(tmp));
6178 		} else if (!strncasecomp(src, "set", 3)) {
6179 		    src = LYSkipBlanks(tmp);
6180 		    tmp = src;
6181 		    while (*tmp != '\0') {
6182 			if (isspace(UCH(*tmp)) || *tmp == '=')
6183 			    break;
6184 			++tmp;
6185 		    }
6186 		    if (*tmp != '\0') {
6187 			*tmp++ = '\0';
6188 			tmp = LYSkipBlanks(tmp);
6189 		    }
6190 		    if (LYSetConfigValue(src, tmp)) {
6191 			CTRACE((tfp, "LYSetConfigValue(%s, %s)\n", src, tmp));
6192 		    } else if (LYsetRcValue(src, tmp)) {
6193 			CTRACE((tfp, "LYsetRcValue(%s, %s)\n", src, tmp));
6194 		    } else {
6195 			CTRACE((tfp, "?? set ignored %s\n", src));
6196 		    }
6197 		}
6198 		break;
6199 	    }
6200 	}
6201 	if (feof(cmd_script)) {
6202 	    fclose(cmd_script);
6203 	    cmd_script = 0;
6204 	}
6205 	if (ch >= 0) {
6206 	    LYSleepReplay();
6207 	    LYrefresh();
6208 	}
6209 	FREE(buffer);
6210     } else {
6211 	ch = LYgetch_for(mode);
6212     }
6213     CTRACE((tfp, "LYReadCmdKey(%d) ->%s (%#x)\n",
6214 	    mode, LYKeycodeToString(ch, TRUE), ch));
6215     LYWriteCmdKey(ch);
6216     return ch;
6217 }
6218 
6219 /*
6220  * Write a LYKeymapCode 'ch' to the logfile.
6221  */
LYWriteCmdKey(int ch)6222 void LYWriteCmdKey(int ch)
6223 {
6224     if (cmd_logfile != 0) {
6225 	fprintf(cmd_logfile, "key %s\n", LYKeycodeToString(ch, FALSE));
6226     }
6227 }
6228 
LYCloseCmdLogfile(void)6229 void LYCloseCmdLogfile(void)
6230 {
6231     if (cmd_logfile != 0) {
6232 	LYCloseOutput(cmd_logfile);
6233 	cmd_logfile = 0;
6234     }
6235     if (cmd_script != 0) {
6236 	LYCloseInput(cmd_script);
6237 	cmd_script = 0;
6238     }
6239     FREE(lynx_cmd_logfile);
6240     FREE(lynx_cmd_script);
6241 }
6242 #endif /* USE_CMD_LOGGING */
6243