1 /* $LynxId: LYForms.c,v 1.118 2018/05/08 20:51:57 tom Exp $ */
2 #include <HTUtils.h>
3 #include <HTCJK.h>
4 #include <HTTP.h>
5 #include <HTAlert.h>
6 #include <LYCurses.h>
7 #include <GridText.h>
8 #include <LYCharSets.h>
9 #include <UCAux.h>
10 #include <LYGlobalDefs.h>
11 #include <LYUtils.h>
12 #include <LYStrings.h>
13 #include <LYKeymap.h>
14 #include <LYClean.h>
15 
16 #include <LYLeaks.h>
17 
18 #ifdef USE_COLOR_STYLE
19 #include <AttrList.h>
20 #include <LYHash.h>
21 #endif
22 
23 #if defined(VMS) && !defined(USE_SLANG)
24 #define RepaintKey() 12		/* CTRL-L for repaint */
25 #else
26 #define RepaintKey() ((!enable_scrollback) ? 23 : 12)	/* CTRL-W or CTRL-L */
27 #endif /* VMS && !USE_SLANG */
28 
29 static int form_getstr(int cur,
30 		       int use_last_tfpos,
31 		       int redraw_only);
32 
33 /*
34  * Returns an array of pointers to the given list
35  */
options_list(OptionType * opt_ptr)36 static char **options_list(OptionType * opt_ptr)
37 {
38     char **result = 0;
39     size_t len;
40     int pass;
41     OptionType *tmp_ptr;
42 
43     for (pass = 0; pass < 2; pass++) {
44 	for (tmp_ptr = opt_ptr, len = 0; tmp_ptr != 0; tmp_ptr = tmp_ptr->next) {
45 	    if (pass != 0)
46 		result[len] = tmp_ptr->name;
47 	    len++;
48 	}
49 	if (pass == 0) {
50 	    len++;
51 	    result = typecallocn(char *, len);
52 
53 	    if (result == 0)
54 		outofmem(__FILE__, "options_list");
55 	} else {
56 	    result[len] = 0;
57 	}
58     }
59 
60     return result;
61 }
62 
change_form_link_ex(int cur,DocInfo * newdoc,BOOLEAN * refresh_screen,int use_last_tfpos,int immediate_submit,int redraw_only)63 int change_form_link_ex(int cur,
64 			DocInfo *newdoc,
65 			BOOLEAN *refresh_screen,
66 			int use_last_tfpos,
67 			int immediate_submit,
68 			int redraw_only)
69 {
70     FormInfo *form = links[cur].l_form;
71     char *link_name;
72     char *link_value;
73     int newdoc_changed = 0;
74     int c = DO_NOTHING;
75     int title_adjust = (no_title ? -TITLE_LINES : 0);
76     char **my_data = 0;
77 
78     /*
79      * If there is no form to perform action on, don't do anything.
80      */
81     if (form == NULL) {
82 	return (c);
83     }
84     link_name = form->name;
85     link_value = form->value;
86     my_data = options_list(form->select_list);
87 
88     /*
89      * Move to the link position.
90      */
91     LYmove(links[cur].ly + title_adjust, links[cur].lx);
92 
93     switch (form->type) {
94     case F_CHECKBOX_TYPE:
95 	if (FormIsReadonly(form))
96 	    break;
97 	LYSetHilite(cur, form->num_value ? unchecked_box : checked_box);
98 	form->num_value = !form->num_value;
99 	break;
100 
101     case F_OPTION_LIST_TYPE:
102 	if (form->select_list == 0) {
103 	    HTAlert(BAD_HTML_NO_POPUP);
104 	    c = DO_NOTHING;
105 	    break;
106 	}
107 
108 	if (FormIsReadonly(form)) {
109 	    (void) LYhandlePopupList(form->num_value,
110 				     links[cur].ly,
111 				     links[cur].lx,
112 				     (STRING2PTR) my_data,
113 				     form->size,
114 				     form->size_l,
115 				     FormIsReadonly(form),
116 				     FALSE);
117 	    c = RepaintKey();
118 	    break;
119 	}
120 	form->num_value = LYhandlePopupList(form->num_value,
121 					    links[cur].ly,
122 					    links[cur].lx,
123 					    (STRING2PTR) my_data,
124 					    form->size,
125 					    form->size_l,
126 					    FormIsReadonly(form),
127 					    FALSE);
128 	{
129 	    OptionType *opt_ptr = form->select_list;
130 	    int i;
131 
132 	    for (i = 0; i < form->num_value; i++, opt_ptr = opt_ptr->next) ;	/* null body */
133 	    /*
134 	     * Set the name.
135 	     */
136 	    form->value = opt_ptr->name;
137 	    /*
138 	     * Set the value.
139 	     */
140 	    form->cp_submit_value = opt_ptr->cp_submit_value;
141 	    /*
142 	     * Set charset in which we have the submit value.  - kw
143 	     */
144 	    form->value_cs = opt_ptr->value_cs;
145 	}
146 	c = RepaintKey();
147 	break;
148 
149     case F_RADIO_TYPE:
150 	if (FormIsReadonly(form))
151 	    break;
152 	/*
153 	 * Radio buttons must have one and only one down at a time!
154 	 */
155 	if (form->num_value) {
156 	    if (user_mode == NOVICE_MODE) {
157 		HTUserMsg(NEED_CHECKED_RADIO_BUTTON);
158 	    }
159 	} else {
160 	    int i;
161 
162 	    /*
163 	     * Run though list of the links on the screen and unselect any that
164 	     * are selected.  :)
165 	     */
166 	    lynx_start_radio_color();
167 	    for (i = 0; i < nlinks; i++) {
168 		if (links[i].type == WWW_FORM_LINK_TYPE
169 		    && links[i].l_form->type == F_RADIO_TYPE
170 		    && links[i].l_form->number == form->number
171 		/*
172 		 * If it has the same name and its on...
173 		 */
174 		    && !strcmp(links[i].l_form->name, form->name)
175 		    && links[i].l_form->num_value) {
176 		    LYmove(links[i].ly, links[i].lx);
177 		    LYaddstr(unchecked_radio);
178 		    LYSetHilite(i, unchecked_radio);
179 		}
180 	    }
181 	    lynx_stop_radio_color();
182 	    /*
183 	     * Will unselect other button and select this one.
184 	     */
185 	    HText_activateRadioButton(form);
186 	    /*
187 	     * Now highlight this one.
188 	     */
189 	    LYSetHilite(cur, checked_radio);
190 	}
191 	break;
192 
193     case F_FILE_TYPE:
194     case F_TEXT_TYPE:
195     case F_TEXTAREA_TYPE:
196     case F_PASSWORD_TYPE:
197 	c = form_getstr(cur, use_last_tfpos, redraw_only);
198 	LYSetHilite(cur, ((form->type == F_PASSWORD_TYPE)
199 			  ? STARS(LYstrCells(form->value))
200 			  : form->value));
201 	break;
202 
203     case F_RESET_TYPE:
204 	if (FormIsReadonly(form))
205 	    break;
206 	HText_ResetForm(form);
207 	*refresh_screen = TRUE;
208 	break;
209 
210     case F_TEXT_SUBMIT_TYPE:
211 	if (redraw_only) {
212 	    c = form_getstr(cur, use_last_tfpos, TRUE);
213 	    break;
214 	}
215 	if (!immediate_submit)
216 	    c = form_getstr(cur, use_last_tfpos, FALSE);
217 	if (FormIsReadonly(form) &&
218 	    (c == '\r' || c == '\n' || immediate_submit)) {
219 	    if (peek_mouse_link() >= 0)
220 		c = LAC_TO_LKC0(LYK_ACTIVATE);
221 	    else
222 		c = '\t';
223 	    break;
224 	}
225 	/*
226 	 * If immediate_submit is set, we didn't enter the line editor above,
227 	 * and will now try to call HText_SubmitForm() directly.  If
228 	 * immediate_submit is not set, c is the lynxkeycode returned from line
229 	 * editing.  Then if c indicates that a key was pressed that means we
230 	 * should submit, but with some extra considerations (i.e.  NOCACHE,
231 	 * DOWNLOAD, different from simple Enter), or if we should act on some
232 	 * *other* link selected with the mouse, we'll just return c and leave
233 	 * it to mainloop() to do the right thing; if everything checks out, it
234 	 * should call this function again, with immediate_submit set.
235 	 *
236 	 * If c indicates that line editing ended with Enter, we still defer to
237 	 * mainloop() for further checking if the submit action URL could
238 	 * require more checks than we do here.  Only in the remaining cases do
239 	 * we proceed to call HText_SubmitForm() directly before returning.  -
240 	 * kw
241 	 */
242 	if (immediate_submit ||
243 	    ((c == '\r' ||
244 	      c == '\n' ||
245 	      c == LAC_TO_LKC0(LYK_MOUSE_SUBMIT)) &&
246 	     peek_mouse_link() == -1)) {
247 	    LYSetHilite(cur, form->value);
248 #ifdef TEXT_SUBMIT_CONFIRM_WANTED
249 	    if (!immediate_submit && (c == '\r' || c == '\n') &&
250 		!HTConfirmDefault(NO_SUBMIT_BUTTON_QUERY, YES)) {
251 		/* User was prompted and declined; if canceled with ^G
252 		 * let mainloop stay on this field, otherwise move on to
253 		 * the next field or link. - kw
254 		 */
255 		if (HTLastConfirmCancelled())
256 		    c = DO_NOTHING;
257 		else
258 		    c = LAC_TO_LKC(LYK_NEXT_LINK);
259 		break;
260 	    }
261 #endif
262 	    if (isEmpty(form->submit_action)) {
263 		HTUserMsg(NO_FORM_ACTION);
264 		c = DO_NOTHING;
265 		break;
266 	    } else if (form->submit_method == URL_MAIL_METHOD && no_mail) {
267 		HTAlert(FORM_MAILTO_DISALLOWED);
268 		c = DO_NOTHING;
269 		break;
270 	    } else if (!immediate_submit &&
271 		       ((no_file_url &&
272 			 isFILE_URL(form->submit_action)) ||
273 			!strncasecomp(form->submit_action, "lynx", 4))) {
274 		c = LAC_TO_LKC0(LYK_MOUSE_SUBMIT);
275 		break;
276 	    } else {
277 		if (form->no_cache &&
278 		    form->submit_method != URL_MAIL_METHOD) {
279 		    LYforce_no_cache = TRUE;
280 		    reloading = TRUE;
281 		}
282 		newdoc_changed =
283 		    HText_SubmitForm(form, newdoc, link_name, form->value);
284 	    }
285 	    if (form->submit_method == URL_MAIL_METHOD) {
286 		*refresh_screen = TRUE;
287 	    } else {
288 		/*
289 		 * Returns new document URL.
290 		 */
291 		newdoc->link = 0;
292 		newdoc->internal_link = FALSE;
293 	    }
294 	    c = DO_NOTHING;
295 	    break;
296 	} else {
297 	    LYSetHilite(cur, form->value);
298 	}
299 	break;
300 
301     case F_SUBMIT_TYPE:
302     case F_IMAGE_SUBMIT_TYPE:
303 	if (FormIsReadonly(form))
304 	    break;
305 	if (form->no_cache &&
306 	    form->submit_method != URL_MAIL_METHOD) {
307 	    LYforce_no_cache = TRUE;
308 	    reloading = TRUE;
309 	}
310 	newdoc_changed =
311 	    HText_SubmitForm(form, newdoc, link_name, link_value);
312 	if (form->submit_method == URL_MAIL_METHOD)
313 	    *refresh_screen = TRUE;
314 	else {
315 	    /* returns new document URL */
316 	    newdoc->link = 0;
317 	    newdoc->internal_link = FALSE;
318 	}
319 	break;
320 
321     }
322 
323     if (newdoc_changed) {
324 	c = LKC_DONE;
325     } else {
326 	/*
327 	 * These flags may have been set in mainloop, anticipating that a
328 	 * request will be submitted.  But if we haven't filled in newdoc, that
329 	 * won't actually be the case, so unset them.  - kw
330 	 */
331 	LYforce_no_cache = FALSE;
332 	reloading = FALSE;
333     }
334     FREE(my_data);
335     return (c);
336 }
337 
change_form_link(int cur,DocInfo * newdoc,BOOLEAN * refresh_screen,int use_last_tfpos,int immediate_submit)338 int change_form_link(int cur,
339 		     DocInfo *newdoc,
340 		     BOOLEAN *refresh_screen,
341 		     int use_last_tfpos,
342 		     int immediate_submit)
343 {
344     /*pass all our args and FALSE as last arg */
345     return change_form_link_ex(cur,
346 			       newdoc,
347 			       refresh_screen,
348 			       use_last_tfpos,
349 			       immediate_submit,
350 			       FALSE /*redraw_only */ );
351 }
352 
353 static int LastTFPos = -1;	/* remember last text field position */
354 
LYSetLastTFPos(int pos)355 static void LYSetLastTFPos(int pos)
356 {
357     LastTFPos = pos;
358 }
359 
form_getstr(int cur,int use_last_tfpos,int redraw_only)360 static int form_getstr(int cur,
361 		       int use_last_tfpos,
362 		       int redraw_only)
363 {
364     FormInfo *form = links[cur].l_form;
365     char *link_value = form->value;
366     int ch;
367     int far_col;
368     int startcol, startline;
369     int action, repeat;
370     int last_xlkc = -1;
371 
372 #ifdef SUPPORT_MULTIBYTE_EDIT
373     BOOL refresh_mb = TRUE;
374 #endif
375 
376     FieldEditor MyEdit, *edit = &MyEdit;
377     BOOLEAN Edited = FALSE;	/* Value might be updated? */
378 
379     /*
380      * Get the initial position of the cursor.
381      */
382     LYGetYX(startline, startcol);
383     if (startline < 0)
384 	startline = 0;
385     if (startcol < 0)
386 	startcol = 0;
387     if ((startcol + form->size) > LYcolLimit)
388 	far_col = LYcolLimit;
389     else
390 	far_col = (startcol + form->size);
391 
392     /*
393      * Make sure the form field value does not exceed our buffer.  - FM
394      */
395     if (form->maxlength != 0 &&
396 	strlen(form->value) > form->maxlength) {
397 	/*
398 	 * We can't fit the entire value into the editing buffer, so enter as
399 	 * much of the tail as fits.  - FM
400 	 */
401 	link_value += (strlen(form->value) - form->maxlength);
402 	if (!FormIsReadonly(form) &&
403 	    !(form->submit_method == URL_MAIL_METHOD && no_mail)) {
404 	    /*
405 	     * If we can edit it, report that we are using the tail.  - FM
406 	     */
407 	    HTUserMsg(FORM_VALUE_TOO_LONG);
408 	    show_formlink_statusline(form, redraw_only ? FOR_PANEL : FOR_INPUT);
409 	    LYmove(startline, startcol);
410 	}
411     }
412 
413     /*
414      * Print panned line
415      */
416     LYSetupEdit(edit, link_value, form->maxlength, (far_col - startcol));
417     edit->efPadChar = '_';
418     edit->efIsMasked = (BOOL) (form->type == F_PASSWORD_TYPE);
419     if (use_last_tfpos &&
420 	LastTFPos >= 0 &&
421 	LastTFPos < (int) edit->efBufInUse) {
422 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
423 	if (redraw_only) {
424 	    if (!((int) edit->efBufInUse >= edit->efWidth &&
425 		  LastTFPos >= edit->efWidth - edit->efPanMargin)) {
426 		edit->efEditAt = LastTFPos;
427 		if ((int) edit->efBufInUse >= edit->efWidth)
428 		    textinput_redrawn = FALSE;
429 	    }
430 	} else
431 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
432 	    edit->efEditAt = LastTFPos;
433 #ifdef ENHANCED_LINEEDIT
434 	if (edit->efEditAt == 0)
435 	    /* Do not show the region. */
436 	    edit->efEditMark = -(int) (1 + edit->efBufInUse);
437 #endif
438     }
439     /* Try to prepare for setting position based on the last mouse event */
440 #if defined(TEXTFIELDS_MAY_NEED_ACTIVATION) && defined(INACTIVE_INPUT_STYLE_VH)
441     if (!redraw_only) {
442 	if (peek_mouse_levent()) {
443 	    if (!use_last_tfpos && !textinput_redrawn) {
444 		edit->efEditAt = 0;
445 	    }
446 	}
447 	textinput_redrawn = FALSE;
448     }
449 #else
450     if (peek_mouse_levent()) {
451 	if (!use_last_tfpos)
452 	    edit->efEditAt = 0;
453     }
454 #endif /* TEXTFIELDS_MAY_NEED_ACTIVATION && INACTIVE_INPUT_STYLE_VH */
455     LYRefreshEdit(edit);
456     if (redraw_only) {
457 	LYFinishEdit(edit);
458 	return 0;		/*return value won't be analysed */
459     }
460 #ifdef FEPCTRL
461     fep_on();
462 #endif
463     /*
464      * And go for it!
465      */
466     for (;;) {
467       again:
468 	repeat = -1;
469 	get_mouse_link();	/* Reset mouse_link. */
470 
471 	ch = LYgetch_input();
472 #ifdef SUPPORT_MULTIBYTE_EDIT
473 	if (!refresh_mb
474 	    && (EditBinding(ch) != LYE_CHAR)
475 #ifndef WIN_EX
476 	    && (EditBinding(ch) != LYE_AIX)
477 #endif
478 	    )
479 	    goto again;
480 #endif /* SUPPORT_MULTIBYTE_EDIT */
481 #ifdef VMS
482 	if (HadVMSInterrupt) {
483 	    HadVMSInterrupt = FALSE;
484 	    ch = LYCharINTERRUPT2;
485 	}
486 #endif /* VMS */
487 
488 	action = 0;
489 #ifdef USE_MOUSE
490 #  if defined(NCURSES) || defined(PDCURSES)
491 	if (ch != -1 && (ch & LKC_ISLAC) && !(ch & LKC_ISLECLAC))	/* already lynxactioncode? */
492 	    break;		/* @@@ maybe move these 2 lines outside ifdef -kw */
493 	if (ch == MOUSE_KEY) {	/* Need to process ourselves */
494 #if defined(PDCURSES)
495 	    int curx, cury;
496 
497 	    request_mouse_pos();
498 	    LYGetYX(cury, curx);
499 	    if (MOUSE_Y_POS == cury) {
500 		repeat = MOUSE_X_POS - curx;
501 		if (repeat < 0) {
502 		    action = LYE_BACK;
503 		    repeat = -repeat;
504 		} else
505 		    action = LYE_FORW;
506 	    }
507 #else
508 	    MEVENT event;
509 	    int curx, cury;
510 
511 	    getmouse(&event);
512 	    LYGetYX(cury, curx);
513 	    if (event.y == cury) {
514 		repeat = event.x - curx;
515 		if (repeat < 0) {
516 		    action = LYE_BACK;
517 		    repeat = -repeat;
518 		} else
519 		    action = LYE_FORW;
520 	    }
521 #endif /* PDCURSES */
522 	    else {
523 		/* Mouse event passed to us as MOUSE_KEY, and apparently not on
524 		 * this field's line?  Something is not as it should be...
525 		 *
526 		 * A call to statusline() may have happened, possibly from
527 		 * within a mouse menu.  Let's at least make sure here that the
528 		 * cursor position gets restored.  - kw
529 		 */
530 		edit->efIsDirty = TRUE;
531 	    }
532 	} else
533 #  endif /* NCURSES || PDCURSES */
534 #endif /* USE_MOUSE */
535 
536 	{
537 	    if (!(ch & LKC_ISLECLAC))
538 		ch |= edit->efInputMods;
539 	    edit->efInputMods = 0;
540 	    if (last_xlkc != -1) {
541 		if (ch == last_xlkc)
542 		    ch |= LKC_MOD3;
543 	    }
544 	}
545 	if (peek_mouse_link() != -1)
546 	    break;
547 
548 	if (!action)
549 	    action = EditBinding(ch);
550 	if ((action & LYE_DF) && !(action & LYE_FORM_LAC)) {
551 	    last_xlkc = ch;
552 	    action &= ~LYE_DF;
553 	} else {
554 	    last_xlkc = -1;
555 	}
556 
557 	if (action == LYE_SETM1) {
558 	    /*
559 	     * Set flag for modifier 1.
560 	     */
561 	    edit->efInputMods |= LKC_MOD1;
562 	    continue;
563 	}
564 	if (action == LYE_SETM2) {
565 	    /*
566 	     * Set flag for modifier 2.
567 	     */
568 	    edit->efInputMods |= LKC_MOD2;
569 	    continue;
570 	}
571 	/*
572 	 * Filter out global navigation keys that should not be passed to line
573 	 * editor, and LYK_REFRESH.
574 	 */
575 	if (action == LYE_ENTER)
576 	    break;
577 	if (action == LYE_FORM_PASS)
578 	    break;
579 	if (action & LYE_FORM_LAC) {
580 	    ch = (action & LAC_MASK) | LKC_ISLAC;
581 	    break;
582 	}
583 	if (action == LYE_LKCMD) {
584 	    _statusline(ENTER_LYNX_COMMAND);
585 	    ch = LYgetch();
586 #ifdef VMS
587 	    if (HadVMSInterrupt) {
588 		HadVMSInterrupt = FALSE;
589 		ch = LYCharINTERRUPT2;
590 	    }
591 #endif /* VMS */
592 	    break;
593 	}
594 #ifdef CAN_CUT_AND_PASTE	/* 1998/10/01 (Thu) 19:19:22 */
595 	if (action == LYE_PASTE) {
596 	    unsigned char *s = (unsigned char *) get_clip_grab(), *e;
597 	    char *buf = NULL;
598 	    int len;
599 
600 	    if (!s)
601 		break;
602 	    len = (int) strlen((const char *) s);
603 	    e = s + len;
604 
605 	    if (len > 0) {
606 		unsigned char *e1 = s;
607 
608 		while (e1 < e) {
609 		    if (*e1 < ' ') {	/* Stop here? */
610 			if (e1 > s)
611 			    LYEditInsert(edit, s, (int) (e1 - s), -1, TRUE);
612 			s = e1;
613 			if (*e1 == '\t') {	/* Replace by space */
614 			    LYEditInsert(edit, (unsigned const char *) " ", 1,
615 					 -1, TRUE);
616 			    s = ++e1;
617 			} else
618 			    break;
619 		    } else
620 			++e1;
621 		}
622 		if (e1 > s)
623 		    LYEditInsert(edit, s, (int) (e1 - s), -1, TRUE);
624 		while (e1 < e && *e1 == '\r')
625 		    e1++;
626 		if (e1 + 1 < e && *e1 == '\n')
627 		    StrAllocCopy(buf, (char *) e1 + 1);		/* Survive _release() */
628 		get_clip_release();
629 		_statusline(ENTER_TEXT_ARROWS_OR_TAB);
630 		if (strcmp(link_value, edit->efBuffer) != 0) {
631 		    Edited = TRUE;
632 		}
633 		if (buf) {
634 		    put_clip(buf);
635 		    FREE(buf);
636 		    ch = '\n';	/* Sometimes moves to the next line */
637 		    break;
638 		}
639 		LYRefreshEdit(edit);
640 	    } else {
641 		HTInfoMsg(gettext("Clipboard empty or Not text data."));
642 #ifdef FEPCTRL
643 		fep_off();
644 #endif
645 		continue;
646 	    }
647 	}
648 #endif
649 #ifndef WIN_EX
650 	if (action == LYE_AIX &&
651 	    (!IS_CJK_TTY && LYlowest_eightbit[current_char_set] > 0x97))
652 	    break;
653 #endif
654 	if (action == LYE_TAB) {
655 	    ch = (int) ('\t');
656 	    break;
657 	}
658 	if (action == LYE_ABORT) {
659 #ifdef FEPCTRL
660 	    fep_off();
661 #endif
662 	    LYFinishEdit(edit);
663 	    return (DO_NOTHING);
664 	}
665 	if (action == LYE_STOP) {
666 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
667 	    textfields_need_activation = TRUE;
668 	    break;
669 #else
670 #ifdef ENHANCED_LINEEDIT
671 	    if (edit->efEditMark >= 0)
672 		/* Disable. */
673 		edit->efEditMark = -(int) (1 + edit->efBufInUse);
674 #endif
675 #endif
676 	}
677 	if (action == LYE_NOP && LKC_TO_LAC(keymap, ch) == LYK_REFRESH)
678 	    break;
679 #ifdef SH_EX
680 /* ASATAKU emacskey hack 1997/08/26 (Tue) 09:19:23 */
681 	if (emacs_keys &&
682 	    (EditBinding(ch) == LYE_FORWW || EditBinding(ch) == LYE_BACKW))
683 	    goto breakfor;
684 /* ASATAKU emacskey hack */
685 #endif
686 	switch (ch) {
687 	default:
688 	    /* [ 1999/04/14 (Wed) 15:01:33 ]
689 	     * Left arrow in column 0 deserves special treatment here, else
690 	     * you can get trapped in a form without submit button!
691 	     */
692 	    if (action == LYE_BACK && edit->efEditAt == 0 && repeat == -1) {
693 		int c = YES;	/* Go back immediately if no changes */
694 
695 		if (textfield_prompt_at_left_edge) {
696 		    c = HTConfirmDefault(PREV_DOC_QUERY, NO);
697 		} else if (strcmp(edit->efBuffer, link_value)) {
698 		    c = HTConfirmDefault(PREV_DOC_QUERY, NO);
699 		}
700 		if (c == YES) {
701 #ifdef FEPCTRL
702 		    fep_off();
703 #endif
704 		    LYFinishEdit(edit);
705 		    return (ch);
706 		} else {
707 		    if (FormIsReadonly(form))
708 			_statusline(ARROWS_OR_TAB_TO_MOVE);
709 		    else
710 			_statusline(ENTER_TEXT_ARROWS_OR_TAB);
711 		}
712 	    }
713 	    if (FormIsReadonly(form)) {
714 		/*
715 		 * Allow actions that don't modify the contents even in
716 		 * disabled form fields, so the user can scroll through the
717 		 * line for reading if necessary.  - kw
718 		 */
719 		switch (action) {
720 		case LYE_BOL:
721 		case LYE_EOL:
722 		case LYE_FORW:
723 		case LYE_FORW_RL:
724 		case LYE_BACK:
725 		case LYE_BACK_LL:
726 		case LYE_FORWW:
727 		case LYE_BACKW:
728 #ifdef EXP_KEYBOARD_LAYOUT
729 		case LYE_SWMAP:
730 #endif
731 #ifdef ENHANCED_LINEEDIT
732 		case LYE_SETMARK:
733 		case LYE_XPMARK:
734 #endif
735 		    break;
736 		default:
737 		    goto again;
738 		}
739 	    }
740 	    /*
741 	     * Make sure the statusline uses editmode help.
742 	     */
743 	    if (repeat < 0)
744 		repeat = 1;
745 	    while (repeat--) {
746 		int rc = LYDoEdit(edit, ch, action & ~LYE_DF, TRUE);
747 
748 		if (rc < 0) {
749 		    ch = -rc;
750 		    /* FORW_RL and BACK_LL may require special attention.
751 		       BACK_LL wanted to switch to the previous link on
752 		       the same line.  However, if there is no such link,
753 		       then we would either disactivate the form
754 		       (with -tna), or will reenter the form, thus we jump
755 		       to the end of the line; both are counterintuitive.
756 		       Unfortunately, we do not have access to curdoc.link,
757 		       so we deduce it ourselves.  We don't have the info
758 		       to do it inside LYLineEdit().
759 		       This should work for prompts too.  */
760 		    switch (action) {
761 		    case LYE_BACK_LL:
762 			if (cur > 0
763 			    && links[cur - 1].ly == links[cur].ly) {
764 			    goto breakfor;
765 			}
766 			break;
767 		    case LYE_FORW_RL:
768 			if (cur >= 0
769 			    && cur < nlinks - 1
770 			    && links[cur + 1].ly == links[cur].ly) {
771 			    goto breakfor;
772 			}
773 			break;
774 		    default:
775 			goto breakfor;
776 		    }
777 		}
778 #ifdef SUPPORT_MULTIBYTE_EDIT
779 		if (rc == 0) {
780 		    if (IS_CJK_TTY && (0x80 <= ch)
781 			&& (ch <= 0xfe) && refresh_mb)
782 			refresh_mb = FALSE;
783 		    else
784 			refresh_mb = TRUE;
785 		} else {
786 		    if (!refresh_mb) {
787 			LYDoEdit(edit, 0, LYE_DELP, TRUE);
788 		    }
789 		}
790 #endif /* SUPPORT_MULTIBYTE_EDIT */
791 	    }
792 	    _statusline(ENTER_TEXT_ARROWS_OR_TAB);
793 	    if (strcmp(link_value, edit->efBuffer)) {
794 		Edited = TRUE;
795 	    }
796 #ifdef SUPPORT_MULTIBYTE_EDIT
797 	    if (refresh_mb)
798 #endif
799 		LYRefreshEdit(edit);
800 	    LYSetLastTFPos(edit->efEditAt);
801 	}
802     }
803   breakfor:
804     if (Edited) {
805 
806 	/*
807 	 * Load the new value.
808 	 */
809 	if (link_value == form->value) {
810 	    /*
811 	     * The previous value did fit in the line buffer, so replace it
812 	     * with the new value.  - FM
813 	     */
814 	    StrAllocCopy(form->value, edit->efBuffer);
815 	} else {
816 	    int old_len = (int) strlen(form->value);
817 	    int new_len = (int) strlen(link_value);
818 
819 	    /*
820 	     * Combine the modified tail with the unmodified head.  - FM
821 	     */
822 	    form->value[(old_len > new_len) ? (old_len - new_len) : 0] = '\0';
823 	    StrAllocCat(form->value, edit->efBuffer);
824 	    HTUserMsg(FORM_TAIL_COMBINED_WITH_HEAD);
825 	}
826 
827 	/* 2.8.4pre.3 - most browsers appear to preserve trailing spaces -VH */
828 	/*
829 	 * Remove trailing spaces
830 	 *
831 	 * Do we really need to do that here?  Trailing spaces will only be
832 	 * there if user keyed them in.  Rather rude to throw away their hard
833 	 * earned spaces.  Better deal with trailing spaces when submitting the
834 	 * form????
835 	 */
836 	if (LYtrimInputFields) {
837 	    LYTrimTrailing(form->value);
838 	}
839 
840 	/*
841 	 * If the field has been changed, assume that it is now in current
842 	 * display character set, even if for some reason it wasn't!  Hopefully
843 	 * a user will only submit the form if the non-ASCII characters are
844 	 * displayed correctly, which means (assuming that the display
845 	 * character set has been set truthfully) the user confirms by changing
846 	 * the field that the character encoding is right.  - kw
847 	 */
848 	if (non_empty(form->value))
849 	    form->value_cs = current_char_set;
850     }
851 #ifdef FEPCTRL
852     fep_off();
853 #endif
854     LYFinishEdit(edit);
855     return (ch);
856 }
857 
858 #ifdef TEXTFIELDS_MAY_NEED_ACTIVATION
859 #define TMA_PANEL(fp,np) ((for_what == FOR_PANEL) ? fp : np)
860 #else
861 #define TMA_PANEL(fp,np) np
862 #endif
863 
864 /*
865  * Display statusline info tailored for the current form field.
866  */
show_formlink_statusline(const FormInfo * form,int for_what)867 void show_formlink_statusline(const FormInfo * form,
868 			      int for_what)
869 {
870     switch (form->type) {
871     case F_PASSWORD_TYPE:
872 	if (FormIsReadonly(form))
873 	    statusline(FORM_LINK_PASSWORD_UNM_MSG);
874 	else
875 	    statusline(TMA_PANEL(FORM_LINK_PASSWORD_MESSAGE_INA,
876 				 FORM_LINK_PASSWORD_MESSAGE));
877 	break;
878     case F_OPTION_LIST_TYPE:
879 	if (FormIsReadonly(form)) {
880 	    statusline(FORM_LINK_OPTION_LIST_UNM_MSG);
881 	} else if (fields_are_named()) {
882 	    char *submit_str = NULL;
883 
884 	    HTSprintf0(&submit_str, FORM_LINK_OPTION_LIST_ADV_MSG, form->name);
885 	    statusline(submit_str);
886 	    FREE(submit_str);
887 	} else {
888 	    statusline(FORM_LINK_OPTION_LIST_MESSAGE);
889 	}
890 	break;
891     case F_CHECKBOX_TYPE:
892 	if (FormIsReadonly(form)) {
893 	    statusline(FORM_LINK_CHECKBOX_UNM_MSG);
894 	} else if (fields_are_named()) {
895 	    char *submit_str = NULL;
896 
897 	    HTSprintf0(&submit_str, FORM_LINK_CHECKBOX_ADV_MSG, form->name);
898 	    statusline(submit_str);
899 	    FREE(submit_str);
900 	} else {
901 	    statusline(FORM_LINK_CHECKBOX_MESSAGE);
902 	}
903 	break;
904     case F_RADIO_TYPE:
905 	if (FormIsReadonly(form)) {
906 	    statusline(FORM_LINK_RADIO_UNM_MSG);
907 	} else if (fields_are_named()) {
908 	    char *submit_str = NULL;
909 
910 	    HTSprintf0(&submit_str, FORM_LINK_RADIO_ADV_MSG, form->name);
911 	    statusline(submit_str);
912 	    FREE(submit_str);
913 	} else {
914 	    statusline(FORM_LINK_RADIO_MESSAGE);
915 	}
916 	break;
917     case F_TEXT_SUBMIT_TYPE:
918 	if (FormIsReadonly(form)) {
919 	    statusline(FORM_LINK_TEXT_SUBMIT_UNM_MSG);
920 	} else if (form->submit_method ==
921 		   URL_MAIL_METHOD) {
922 	    if (no_mail)
923 		statusline(FORM_LINK_TEXT_SUBMIT_MAILTO_DIS_MSG);
924 	    else
925 		statusline(TMA_PANEL(FORM_TEXT_SUBMIT_MAILTO_MSG_INA,
926 				     FORM_LINK_TEXT_SUBMIT_MAILTO_MSG));
927 	} else if (form->no_cache) {
928 	    statusline(TMA_PANEL(FORM_TEXT_RESUBMIT_MESSAGE_INA,
929 				 FORM_LINK_TEXT_RESUBMIT_MESSAGE));
930 	} else {
931 	    char *submit_str = NULL;
932 	    char *xkey_info = key_for_func_ext(LYK_NOCACHE, for_what);
933 
934 	    if (non_empty(xkey_info)) {
935 		HTSprintf0(&submit_str,
936 			   TMA_PANEL(FORM_TEXT_SUBMIT_MESSAGE_INA_X,
937 				     FORM_LINK_TEXT_SUBMIT_MESSAGE_X),
938 			   xkey_info);
939 		statusline(submit_str);
940 		FREE(submit_str);
941 	    } else {
942 		statusline(TMA_PANEL(FORM_LINK_TEXT_SUBMIT_MESSAGE_INA,
943 				     FORM_LINK_TEXT_SUBMIT_MESSAGE));
944 	    }
945 	    FREE(xkey_info);
946 	}
947 	break;
948     case F_SUBMIT_TYPE:
949     case F_IMAGE_SUBMIT_TYPE:
950 	if (FormIsReadonly(form)) {
951 	    statusline(FORM_LINK_SUBMIT_DIS_MSG);
952 	} else if (form->submit_method ==
953 		   URL_MAIL_METHOD) {
954 	    if (no_mail) {
955 		statusline(FORM_LINK_SUBMIT_MAILTO_DIS_MSG);
956 	    } else {
957 		if (user_mode == ADVANCED_MODE) {
958 		    char *submit_str = NULL;
959 
960 		    StrAllocCopy(submit_str, FORM_LINK_SUBMIT_MAILTO_PREFIX);
961 		    StrAllocCat(submit_str, form->submit_action);
962 		    statusline(submit_str);
963 		    FREE(submit_str);
964 		} else {
965 		    statusline(FORM_LINK_SUBMIT_MAILTO_MSG);
966 		}
967 	    }
968 	} else if (form->no_cache) {
969 	    if (user_mode == ADVANCED_MODE) {
970 		char *submit_str = NULL;
971 
972 		StrAllocCopy(submit_str, FORM_LINK_RESUBMIT_PREFIX);
973 		StrAllocCat(submit_str, form->submit_action);
974 		statusline(submit_str);
975 		FREE(submit_str);
976 	    } else {
977 		statusline(FORM_LINK_RESUBMIT_MESSAGE);
978 	    }
979 	} else {
980 	    if (user_mode == ADVANCED_MODE) {
981 		char *submit_str = NULL;
982 
983 		StrAllocCopy(submit_str, FORM_LINK_SUBMIT_PREFIX);
984 		StrAllocCat(submit_str, form->submit_action);
985 		statusline(submit_str);
986 		FREE(submit_str);
987 	    } else {
988 		statusline(FORM_LINK_SUBMIT_MESSAGE);
989 	    }
990 	}
991 	break;
992     case F_RESET_TYPE:
993 	if (FormIsReadonly(form))
994 	    statusline(FORM_LINK_RESET_DIS_MSG);
995 	else
996 	    statusline(FORM_LINK_RESET_MESSAGE);
997 	break;
998     case F_BUTTON_TYPE:
999 	if (FormIsReadonly(form)) {
1000 	    statusline(FORM_LINK_BUTTON_DIS_MSG);
1001 	} else if (fields_are_named()) {
1002 	    char *submit_str = NULL;
1003 
1004 	    HTSprintf0(&submit_str, FORM_LINK_BUTTON_ADV_MSG, form->name);
1005 	    statusline(submit_str);
1006 	    FREE(submit_str);
1007 	} else {
1008 	    statusline(FORM_LINK_BUTTON_MESSAGE);
1009 	}
1010 	break;
1011     case F_FILE_TYPE:
1012 	if (FormIsReadonly(form))
1013 	    statusline(FORM_LINK_FILE_UNM_MSG);
1014 	else
1015 	    statusline(FORM_LINK_FILE_MESSAGE);
1016 	break;
1017     case F_TEXT_TYPE:
1018 	if (FormIsReadonly(form)) {
1019 	    statusline(FORM_LINK_TEXT_UNM_MSG);
1020 	} else if (fields_are_named()) {
1021 	    char *submit_str = NULL;
1022 
1023 	    HTSprintf0(&submit_str,
1024 		       TMA_PANEL(FORM_LINK_TEXT_ADV_MSG_INA,
1025 				 FORM_LINK_TEXT_ADV_MSG),
1026 		       form->name);
1027 	    statusline(submit_str);
1028 	    FREE(submit_str);
1029 	} else {
1030 	    statusline(TMA_PANEL(FORM_LINK_TEXT_MESSAGE_INA,
1031 				 FORM_LINK_TEXT_MESSAGE));
1032 	}
1033 	break;
1034     case F_TEXTAREA_TYPE:
1035 	if (FormIsReadonly(form)) {
1036 	    statusline(FORM_LINK_TEXT_UNM_MSG);
1037 	} else {
1038 	    char *submit_str = NULL;
1039 	    char *xkey_info = NULL;
1040 
1041 	    if (!no_editor && non_empty(editor)) {
1042 		xkey_info = key_for_func_ext(LYK_EDITTEXTAREA, for_what);
1043 #ifdef TEXTAREA_AUTOEXTEDIT
1044 		if (!xkey_info)
1045 		    xkey_info = key_for_func_ext(LYK_DWIMEDIT, for_what);
1046 #endif
1047 	    }
1048 	    if (non_empty(xkey_info)) {
1049 		if (fields_are_named()) {
1050 		    HTSprintf0(&submit_str,
1051 			       TMA_PANEL(FORM_LINK_TEXTAREA_ADV_MSG_INA_E,
1052 					 FORM_LINK_TEXTAREA_ADV_MSG_E),
1053 			       form->name,
1054 			       xkey_info);
1055 		} else {
1056 		    HTSprintf0(&submit_str,
1057 			       TMA_PANEL(FORM_LINK_TEXTAREA_MESSAGE_INA_E,
1058 					 FORM_LINK_TEXTAREA_MESSAGE_E),
1059 			       xkey_info);
1060 		}
1061 		statusline(submit_str);
1062 		FREE(submit_str);
1063 	    } else {
1064 		if (fields_are_named()) {
1065 		    HTSprintf0(&submit_str,
1066 			       TMA_PANEL(FORM_LINK_TEXTAREA_ADV_MSG_INA,
1067 					 FORM_LINK_TEXTAREA_ADV_MSG),
1068 			       form->name);
1069 		    statusline(submit_str);
1070 		    FREE(submit_str);
1071 		} else {
1072 		    statusline(TMA_PANEL(FORM_LINK_TEXTAREA_MESSAGE_INA,
1073 					 FORM_LINK_TEXTAREA_MESSAGE));
1074 		}
1075 	    }
1076 	    FREE(xkey_info);
1077 	}
1078 	break;
1079     }
1080 }
1081