1 /** Forms viewing/manipulation handling
9 #define _GNU_SOURCE /* XXX: we want memrchr() ! */
16 #include <sys/types.h>
21 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
26 #include "bfu/listmenu.h"
27 #include "bfu/dialog.h"
28 #include "config/kbdbind.h"
29 #include "dialogs/menu.h"
30 #include "document/document.h"
31 #include "document/forms.h"
32 #include "document/view.h"
33 #include "intl/gettext/libintl.h"
34 #include "formhist/formhist.h"
35 #include "mime/mime.h"
36 #include "osdep/ascii.h"
37 #include "osdep/osdep.h"
38 #include "protocol/uri.h"
39 #include "session/session.h"
40 #include "session/task.h"
41 #include "terminal/kbd.h"
42 #include "terminal/terminal.h"
43 #include "terminal/window.h"
44 #include "util/conv.h"
45 #include "util/error.h"
46 #include "util/file.h"
47 #include "util/memory.h"
48 #include "util/string.h"
49 #include "viewer/action.h"
50 #include "viewer/text/draw.h"
51 #include "viewer/text/form.h"
52 #include "viewer/text/link.h"
53 #include "viewer/text/textarea.h"
54 #include "viewer/text/view.h"
55 #include "viewer/text/vs.h"
58 /* TODO: Some of these (particulary those encoding routines) would feel better
59 * in viewer/common/. --pasky */
62 LIST_HEAD(struct files_offset
);
63 /* offset of the filename in the data generated by encode_multipart.
64 * data[begin] is the FILE_CHAR, data + begin + 1 is the filename. */
66 /* end of filename. data[end] is the FILE_CHAR. In normal strings
67 * it would be a nul char. */
72 /** @relates submitted_value */
74 struct submitted_value
*
75 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
76 struct form_control
*fc
, int position
)
78 struct submitted_value
*sv
;
80 sv
= mem_alloc(sizeof(*sv
));
83 sv
->value
= stracpy(value
);
84 if (!sv
->value
) { mem_free(sv
); return NULL
; }
86 sv
->name
= stracpy(name
);
87 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
90 sv
->form_control
= fc
;
91 sv
->position
= position
;
96 /** @relates submitted_value */
98 done_submitted_value(struct submitted_value
*sv
)
101 mem_free_if(sv
->value
);
102 mem_free_if(sv
->name
);
107 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
112 if_assert_failed
return;
115 && fs
->state
< fc
->nvalues
116 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
119 for (i
= 0; i
< fc
->nvalues
; i
++)
120 if (!strcmp(fc
->values
[i
], fs
->value
)) {
127 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
129 : (unsigned char *) ""));
134 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
136 struct session
*ses
= ses_
;
137 int item
= (long) item_
;
138 struct document_view
*doc_view
;
140 struct form_state
*fs
;
141 struct form_control
*fc
;
144 if_assert_failed
return;
145 doc_view
= current_frame(ses
);
147 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
148 if_assert_failed
return;
150 link
= get_current_link(doc_view
);
151 if (!link
|| link
->type
!= LINK_SELECT
) return;
153 fc
= get_link_form_control(link
);
154 fs
= find_form_state(doc_view
, fc
);
156 if (item
>= 0 && item
< fc
->nvalues
) {
158 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
160 fixup_select_state(fc
, fs
);
163 refresh_view(ses
, doc_view
, 0);
167 init_form_state(struct document_view
*doc_view
,
168 struct form_control
*fc
, struct form_state
*fs
)
170 struct terminal
*term
;
171 int doc_cp
, viewer_cp
;
174 if_assert_failed
return;
176 doc_cp
= doc_view
->document
->cp
;
177 term
= doc_view
->session
->tab
->term
;
178 viewer_cp
= get_opt_codepage_tree(term
->spec
, "charset", NULL
);
180 mem_free_set(&fs
->value
, NULL
);
185 #ifdef CONFIG_FORMHIST
186 fs
->value
= null_or_stracpy(
187 get_form_history_value(
188 fc
->form
->action
, fc
->name
));
189 #endif /* CONFIG_FORMHIST */
192 if (fs
->value
== NULL
) {
193 fs
->value
= convert_string(
194 get_translation_table(doc_cp
, viewer_cp
),
196 strlen(fc
->default_value
),
198 &fs
->state
, NULL
, NULL
);
200 fs
->state
= fs
->value
? strlen(fs
->value
) : 0;
202 if (fc
->type
== FC_TEXTAREA
)
204 #endif /* CONFIG_UTF8 */
208 fs
->value
= stracpy("");
213 fs
->value
= convert_string(
214 get_translation_table(doc_cp
, viewer_cp
),
216 strlen(fc
->default_value
),
218 &fs
->state
, NULL
, NULL
);
219 fs
->state
= fc
->default_state
;
220 fixup_select_state(fc
, fs
);
224 fs
->state
= fc
->default_state
;
231 /* We don't want to recode hidden fields. */
232 fs
->value
= stracpy(fc
->default_value
);
239 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
241 struct view_state
*vs
;
242 struct form_state
*fs
;
245 assert(doc_view
&& doc_view
->vs
&& fc
);
246 if_assert_failed
return NULL
;
251 if (n
>= vs
->form_info_len
) {
254 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
255 if (!fs
) return NULL
;
257 vs
->form_info_len
= nn
;
259 fs
= &vs
->form_info
[n
];
261 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
262 && fs
->g_ctrl_num
== fc
->g_ctrl_num
263 && fs
->position
== fc
->position
264 && fs
->type
== fc
->type
)
267 mem_free_if(fs
->value
);
268 memset(fs
, 0, sizeof(*fs
));
269 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
270 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
271 fs
->position
= fc
->position
;
273 init_form_state(doc_view
, fc
, fs
);
278 struct form_control
*
279 find_form_control(struct document
*document
, struct form_state
*fs
)
281 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
282 struct form_control
*fc
;
284 foreach (fc
, form
->items
) {
285 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
286 && fs
->position
== fc
->position
287 && fs
->type
== fc
->type
)
295 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
297 struct form_view
*fv
;
301 foreach (fv
, vs
->forms
)
302 if (fv
->form_num
== form_num
)
305 fv
= mem_calloc(1, sizeof(*fv
));
306 fv
->form_num
= form_num
;
307 add_to_list(vs
->forms
, fv
);
312 find_form_view(struct document_view
*doc_view
, struct form
*form
)
314 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
318 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
322 foreach (form
, document
->forms
) {
323 if (form
->form_num
== fv
->form_num
)
331 get_current_state(struct session
*ses
)
333 struct document_view
*doc_view
;
335 struct form_state
*fs
;
338 if_assert_failed
return -1;
339 doc_view
= current_frame(ses
);
341 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
342 if_assert_failed
return -1;
344 link
= get_current_link(doc_view
);
345 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
347 fs
= find_form_state(doc_view
, get_link_form_control(link
));
348 if (fs
) return fs
->state
;
353 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
356 struct form_state
*fs
;
357 struct form_control
*fc
;
358 struct view_state
*vs
;
362 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
363 if_assert_failed
return;
365 fc
= get_link_form_control(link
);
366 assertm(fc
!= NULL
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
367 if_assert_failed
return;
369 fs
= find_form_state(doc_view
, fc
);
372 box
= &doc_view
->box
;
379 unsigned char *text
, *end
, *last_in_view
;
381 #endif /* CONFIG_UTF8 */
388 if (!link
->npoints
) break;
390 y
= link
->points
[0].y
+ dy
;
391 if (!row_is_in_box(box
, y
))
394 x
= link
->points
[0].x
+ dx
;
396 if (term
->utf8_cp
) goto utf8
;
397 #endif /* CONFIG_UTF8 */
398 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
399 len
= strlen(fs
->value
) - fs
->vpos
;
401 for (i
= 0; i
< fc
->size
; i
++, x
++) {
404 if (!col_is_in_box(box
, x
)) continue;
406 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
407 data
= fc
->type
!= FC_PASSWORD
408 ? fs
->value
[i
+ fs
->vpos
] : '*';
412 draw_char_data(term
, x
, y
, data
);
421 if (!text
) text
= "";
423 int_bounds(&fs
->state
, 0, len
);
424 int_bounds(&fs
->vpos
, 0, fs
->state
);
429 for (i
= 0; i
< fc
->size
; ) {
432 unsigned char *maybe_in_view
= text
;
434 data
= utf8_to_unicode(&text
, end
);
435 if (data
== UCS_NO_CHAR
) /* end of string */
437 else if (fc
->type
== FC_PASSWORD
)
440 cells
= unicode_to_cell(data
);
441 if (i
+ cells
<= fc
->size
) {
442 last_in_view
= maybe_in_view
;
443 if (colspan_is_in_box(box
, x
+ i
, cells
)) {
444 /* The character fits completely.
445 * Draw the character, and mark any
446 * further cells with UCS_NO_CHAR. */
447 draw_char_data(term
, x
+ i
, y
, data
);
448 for (cell
= 1; cell
< cells
; cell
++)
449 draw_char_data(term
, x
+ i
+ cell
,
455 /* The character does not fit completely.
456 * Write UCS_ORPHAN_CELL to the cells that
458 for (cell
= 0; cell
< cells
; cell
++) {
459 if (col_is_in_box(box
, x
+ i
+ cell
)
460 && i
+ cell
< fc
->size
)
470 /* The int_bounds calls above ensured that the
471 * insertion point cannot be at the left side
472 * of the scrolled-visible part of the text.
473 * However it can still be at the right side.
474 * Check whether we need to change fs->vpos.
476 * This implementation attempts to follow
478 * - If the insertion point is at the end of
479 * the string, leave at least one empty cell
480 * so that there is a place for the cursor.
481 * - If a character follows the insertion
482 * point, make that character fully visible;
483 * note the character may be double-width.
484 * - If fc->size < 2, it is not possible to
485 * make a double-width character fully
486 * visible. In this case, it is OK if the
487 * output is ugly, but ELinks must not fall
488 * into an infinite loop or crash.
489 * - The length of the string should not affect
490 * how long this function takes. The width
491 * of the widget naturally will.
492 * - Optimize the case where fields are drawn
493 * several times without being modified.
496 * - If the "for i" loop above hit UCS_NO_CHAR,
497 * then there is no need to scroll.
498 * - When the string ends with a double-width
499 * character that fits in only partially,
500 * then text==end, but the field may have
502 if (fs
->value
&& last_in_view
503 && last_in_view
< fs
->value
+ fs
->state
) {
504 unsigned char *ptr
= fs
->value
+ fs
->state
;
505 int cells
= fc
->size
;
506 enum utf8_step how
= (fc
->type
== FC_PASSWORD
)
507 ? UTF8_STEP_CHARACTERS
508 : UTF8_STEP_CELLS_FEWER
;
510 /* The insertion point is at the right
511 * side of the scrolled-visible part
512 * of the text. Decide a new fs->vpos
513 * by counting cells backwards from
514 * @ptr. But first advance @ptr past
515 * the character that follows the
516 * insertion point, so that it will be
517 * fully displayed. If there is no
518 * such character, reserve one cell
519 * for the cursor anyway. */
520 if (utf8_to_unicode(&ptr
, end
) == UCS_NO_CHAR
)
522 ptr
= utf8_step_backward(ptr
, fs
->value
,
525 if (fs
->vpos
!= ptr
- fs
->value
) {
526 fs
->vpos
= ptr
- fs
->value
;
528 goto retry_after_scroll
;
532 #endif /* CONFIG_UTF8 */
534 draw_textarea(term
, fs
, doc_view
, link
);
538 if (link
->npoints
< 2) break;
539 x
= link
->points
[1].x
+ dx
;
540 y
= link
->points
[1].y
+ dy
;
541 if (is_in_box(box
, x
, y
))
542 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
545 fixup_select_state(fc
, fs
);
546 if (fs
->state
< fc
->nvalues
)
547 s
= fc
->labels
[fs
->state
];
549 /* XXX: when can this happen? --pasky */
552 if (term
->utf8_cp
) goto utf8_select
;
553 #endif /* CONFIG_UTF8 */
554 len
= s
? strlen(s
) : 0;
555 for (i
= 0; i
< link
->npoints
; i
++) {
556 x
= link
->points
[i
].x
+ dx
;
557 y
= link
->points
[i
].y
+ dy
;
558 if (is_in_box(box
, x
, y
))
559 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
565 end
= strchr(s
, '\0');
566 len
= utf8_ptr2cells(text
, end
);
567 for (i
= 0; i
< link
->npoints
; i
++) {
568 x
= link
->points
[i
].x
+ dx
;
569 y
= link
->points
[i
].y
+ dy
;
570 if (is_in_box(box
, x
, y
)) {
575 data
= utf8_to_unicode(&s
, end
);
576 cell
= unicode_to_cell(data
);
577 if (i
+ 1 < len
&& cell
== 2) {
578 draw_char_data(term
, x
++, y
, data
);
582 } else if (cell
== 2) {
583 data
= UCS_ORPHAN_CELL
;
587 draw_char_data(term
, x
, y
, data
);
591 #endif /* CONFIG_UTF8 */
602 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
604 struct link
*l1
, *l2
;
606 assert(term
&& doc_view
);
607 if_assert_failed
return;
609 l1
= get_first_link(doc_view
);
610 l2
= get_last_link(doc_view
);
613 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
614 /* Return path :-). */
618 struct form_control
*fc
= get_link_form_control(l1
);
621 draw_form_entry(term
, doc_view
, l1
);
627 /** @relates submitted_value */
629 done_submitted_value_list(LIST_OF(struct submitted_value
) *list
)
631 struct submitted_value
*sv
, *svtmp
;
634 if_assert_failed
return;
636 foreach (sv
, *list
) {
639 del_from_list(svtmp
);
640 done_submitted_value(svtmp
);
645 add_submitted_value_to_list(struct form_control
*fc
,
646 struct form_state
*fs
,
647 LIST_OF(struct submitted_value
) *list
)
649 struct submitted_value
*sub
;
654 assert(fc
&& fs
&& list
);
657 position
= fc
->position
;
665 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
666 if (sub
) add_to_list(*list
, sub
);
671 if (!fs
->state
) break;
678 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
680 if (sub
) add_to_list(*list
, sub
);
684 if (!fc
->nvalues
) break;
686 fixup_select_state(fc
, fs
);
687 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
688 if (sub
) add_to_list(*list
, sub
);
692 name
= straconcat(fc
->name
, ".x", (unsigned char *) NULL
);
694 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
696 if (sub
) add_to_list(*list
, sub
);
698 name
= straconcat(fc
->name
, ".y", (unsigned char *) NULL
);
700 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
702 if (sub
) add_to_list(*list
, sub
);
709 sort_submitted_values(LIST_OF(struct submitted_value
) *list
)
712 struct submitted_value
*sub
;
715 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
716 if (sub
->next
->position
< sub
->position
) {
717 struct submitted_value
*next
= sub
->next
;
720 add_at_pos(next
, sub
);
725 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
726 if (sub
->next
->position
< sub
->position
) {
727 struct submitted_value
*next
= sub
->next
;
730 add_at_pos(next
, sub
);
740 get_successful_controls(struct document_view
*doc_view
,
741 struct form_control
*fc
,
742 LIST_OF(struct submitted_value
) *list
)
744 struct form_control
*fc2
;
746 assert(doc_view
&& fc
&& fc
->form
&& list
);
747 if_assert_failed
return;
749 foreach (fc2
, fc
->form
->items
) {
750 if (((fc2
->type
!= FC_SUBMIT
&&
751 fc2
->type
!= FC_IMAGE
&&
752 fc2
->type
!= FC_RESET
&&
753 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
754 && fc2
->name
&& fc2
->name
[0]) {
755 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
759 add_submitted_value_to_list(fc2
, fs
, list
);
763 sort_submitted_values(list
);
767 encode_controls(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
768 int cp_from
, int cp_to
)
770 struct submitted_value
*sv
;
771 struct conv_table
*convert_table
= NULL
;
775 if_assert_failed
return;
778 unsigned char *p2
= NULL
;
781 add_char_to_string(data
, '&');
785 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
786 add_char_to_string(data
, '=');
788 /* Convert back to original encoding (see html_form_control()
789 * for the original recoding). */
790 if (sv
->type
== FC_TEXTAREA
) {
793 p
= encode_textarea(sv
);
796 convert_table
= get_translation_table(cp_from
, cp_to
);
798 p2
= convert_string(convert_table
, p
,
799 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
802 } else if (sv
->type
== FC_TEXT
||
803 sv
->type
== FC_PASSWORD
) {
805 convert_table
= get_translation_table(cp_from
, cp_to
);
807 p2
= convert_string(convert_table
, sv
->value
,
808 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
810 p2
= stracpy(sv
->value
);
814 encode_uri_string(data
, p2
, strlen(p2
), 1);
822 #define BOUNDARY_LENGTH 32
823 #define realloc_bound_ptrs(bptrs, bptrs_size) \
824 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
826 struct boundary_info
{
829 unsigned char string
[BOUNDARY_LENGTH
];
832 /** @relates boundary_info */
834 randomize_boundary(unsigned char *data
, int length
)
837 FILE *f
= fopen("/dev/urandom", "rb");
839 if (!f
) f
= fopen("/dev/prandom", "rb"); /* OpenBSD */
841 fread(data
, 1, length
, f
);
844 /* FIXME. What if both fails */
845 for (i
= 0; i
< length
; i
++) {
846 /* Only [0-9A-Za-z]. */
847 data
[i
] = data
[i
] & 63;
848 if (data
[i
] < 10) data
[i
] += '0';
849 else if (data
[i
] < 36) data
[i
] = data
[i
] - 10 + 'A';
850 else if (data
[i
] < 62) data
[i
] = data
[i
] - 36 + 'a';
855 /** @relates boundary_info */
857 init_boundary(struct boundary_info
*boundary
)
859 memset(boundary
, 0, sizeof(*boundary
));
860 randomize_boundary(boundary
->string
, BOUNDARY_LENGTH
);
863 /** Add boundary to string and save the offset
864 * @relates boundary_info */
866 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
868 add_to_string(data
, "--");
870 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
871 boundary
->offsets
[boundary
->count
++] = data
->length
;
873 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
877 /** @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
879 encode_multipart(struct session
*ses
, LIST_OF(struct submitted_value
) *l
,
880 struct string
*data
, struct boundary_info
*boundary
,
881 LIST_OF(struct files_offset
) *bfs
, int cp_from
, int cp_to
)
883 struct conv_table
*convert_table
= NULL
;
884 struct submitted_value
*sv
;
886 assert(ses
&& l
&& data
&& boundary
);
887 if_assert_failed
return;
889 init_boundary(boundary
);
892 add_boundary(data
, boundary
);
893 add_crlf_to_string(data
);
895 /** @bug FIXME: name is not encoded.
897 * multipart/form-data contains a series of parts.
898 * Each part is expected to contain a content-disposition
899 * header where the value is "form-data" and a name attribute
900 * specifies the field name within the form,
901 * e.g., 'content-disposition: form-data; name="xxxxx"',
902 * where xxxxx is the field name corresponding to that field.
903 * Field names originally in non-ASCII character sets may be
904 * encoded using the method outlined in RFC 1522. */
905 add_to_string(data
, "Content-Disposition: form-data; name=\"");
906 add_to_string(data
, sv
->name
);
907 add_char_to_string(data
, '"');
909 if (sv
->type
== FC_FILE
) {
910 unsigned char *extension
;
912 add_to_string(data
, "; filename=\"");
913 add_to_string(data
, get_filename_position(sv
->value
));
914 /* It sends bad data if the file name contains ", but
915 Netscape does the same */
916 /* FIXME: We should follow RFCs 1522, 1867,
917 * 2047 (updated by rfc 2231), to provide correct support
918 * for non-ASCII and special characters in values. --Zas */
919 add_char_to_string(data
, '"');
921 /* Add a Content-Type header if the type is configured */
922 extension
= strrchr(sv
->value
, '.');
924 unsigned char *type
= get_extension_content_type(extension
);
927 add_crlf_to_string(data
);
928 add_to_string(data
, "Content-Type: ");
929 add_to_string(data
, type
);
934 add_crlf_to_string(data
);
935 add_crlf_to_string(data
);
938 unsigned char *filename
;
939 struct files_offset
*bfs_new
;
941 if (get_cmd_opt_bool("anonymous")) {
946 filename
= expand_tilde(sv
->value
);
947 if (!filename
) goto encode_error
;
949 if (access(filename
, R_OK
)) {
953 bfs_new
= mem_calloc(1, sizeof(*bfs_new
));
958 bfs_new
->begin
= data
->length
;
959 add_char_to_string(data
, FILE_CHAR
);
960 add_to_string(data
, filename
);
961 add_char_to_string(data
, FILE_CHAR
);
962 bfs_new
->end
= data
->length
;
963 add_to_list_end(*bfs
, bfs_new
);
967 add_crlf_to_string(data
);
968 add_crlf_to_string(data
);
970 /* Convert back to original encoding (see
971 * html_special_form_control() for the original
973 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
974 sv
->type
== FC_TEXTAREA
) {
978 convert_table
= get_translation_table(cp_from
,
981 p
= convert_string(convert_table
, sv
->value
,
982 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
985 add_to_string(data
, p
);
989 add_to_string(data
, sv
->value
);
993 add_crlf_to_string(data
);
997 add_boundary(data
, boundary
);
998 add_to_string(data
, "--\r\n");
1000 mem_free_if(boundary
->offsets
);
1005 mem_free_if(boundary
->offsets
);
1008 /* XXX: This error message should move elsewhere. --Zas */
1009 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
1010 N_("Error while posting form"), ALIGN_CENTER
,
1011 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
1012 sv
->value
, strerror(errno
)));
1016 encode_newlines(struct string
*string
, unsigned char *data
)
1018 for (; *data
; data
++) {
1019 if (*data
== '\n' || *data
== '\r') {
1020 unsigned char buffer
[3];
1024 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
1025 buffer
[2] = hx(((int) *data
) & 0xF);
1026 add_bytes_to_string(string
, buffer
, 3);
1028 add_char_to_string(string
, *data
);
1034 encode_text_plain(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
1035 int cp_from
, int cp_to
)
1037 struct submitted_value
*sv
;
1038 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
1041 if_assert_failed
return;
1044 unsigned char *area51
= NULL
;
1045 unsigned char *value
= sv
->value
;
1047 add_to_string(data
, sv
->name
);
1048 add_char_to_string(data
, '=');
1052 value
= area51
= encode_textarea(sv
);
1057 /* Convert back to original encoding (see
1058 * html_form_control() for the original recoding). */
1059 value
= convert_string(convert_table
, value
,
1060 strlen(value
), -1, CSM_FORM
,
1063 /* Falling right through to free that textarea stuff */
1064 mem_free_if(area51
);
1066 /* Did the conversion fail? */
1069 encode_newlines(data
, value
);
1071 /* Free if we did convert something */
1072 if (value
!= sv
->value
) mem_free(value
);
1075 add_crlf_to_string(data
);
1080 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1082 struct form_control
*fc
;
1084 assert(doc_view
&& doc_view
->document
);
1085 if_assert_failed
return;
1087 foreach (fc
, form
->items
) {
1088 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1090 if (fs
) init_form_state(doc_view
, fc
, fs
);
1094 enum frame_event_status
1095 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1097 struct link
*link
= get_current_link(doc_view
);
1099 if (!link
) return FRAME_EVENT_OK
;
1101 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1102 draw_forms(ses
->tab
->term
, doc_view
);
1104 /* Could be the refresh return value and then ditch the draw_forms()
1106 return FRAME_EVENT_OK
;
1110 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1111 struct form_control
*fc
)
1113 struct boundary_info boundary
;
1114 INIT_LIST_OF(struct submitted_value
, submit
);
1115 INIT_LIST_OF(struct files_offset
, bfs
);
1122 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1123 if_assert_failed
return NULL
;
1124 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1125 if_assert_failed
return NULL
;
1129 if (fc
->type
== FC_RESET
) {
1130 do_reset_form(doc_view
, form
);
1135 || !init_string(&data
))
1138 get_successful_controls(doc_view
, fc
, &submit
);
1140 cp_from
= get_opt_codepage_tree(ses
->tab
->term
->spec
, "charset", NULL
);
1141 cp_to
= doc_view
->document
->cp
;
1142 switch (form
->method
) {
1143 case FORM_METHOD_GET
:
1144 case FORM_METHOD_POST
:
1145 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1148 case FORM_METHOD_POST_MP
:
1149 encode_multipart(ses
, &submit
, &data
, &boundary
,
1150 &bfs
, cp_from
, cp_to
);
1153 case FORM_METHOD_POST_TEXT_PLAIN
:
1154 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1157 #ifdef CONFIG_FORMHIST
1158 /* XXX: We check data.source here because a NULL value can indicate
1159 * not only a memory allocation failure, but also an error reading
1160 * a file that is to be uploaded. TODO: Distinguish between
1161 * these two classes of errors (is it worth it?). -- Miciah */
1163 && get_opt_bool("document.browse.forms.show_formhist", ses
))
1164 memorize_form(ses
, &submit
, form
);
1167 done_submitted_value_list(&submit
);
1170 || !init_string(&go
)) {
1175 switch (form
->method
) {
1176 case FORM_METHOD_GET
:
1178 unsigned char *pos
= strchr(form
->action
, '#');
1181 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1183 add_to_string(&go
, form
->action
);
1186 if (strchr(go
.source
, '?'))
1187 add_char_to_string(&go
, '&');
1189 add_char_to_string(&go
, '?');
1191 add_string_to_string(&go
, &data
);
1193 if (pos
) add_to_string(&go
, pos
);
1196 case FORM_METHOD_POST
:
1197 case FORM_METHOD_POST_MP
:
1198 case FORM_METHOD_POST_TEXT_PLAIN
:
1200 /* Note that we end content type here by a simple '\n',
1201 * replaced later by correct '\r\n' in http_send_header(). */
1203 add_to_string(&go
, form
->action
);
1204 add_char_to_string(&go
, POST_CHAR
);
1205 if (form
->method
== FORM_METHOD_POST
) {
1206 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1208 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1209 /* Dunno about this one but we don't want the full
1210 * hextcat thingy. --jonas */
1211 add_to_string(&go
, "text/plain\n");
1212 add_to_string(&go
, data
.source
);
1216 add_to_string(&go
, "multipart/form-data; boundary=");
1217 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1218 add_char_to_string(&go
, '\n');
1221 if (list_empty(bfs
)) {
1224 for (i
= 0; i
< data
.length
; i
++) {
1227 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1228 add_to_string(&go
, p
);
1231 struct files_offset
*b
;
1235 for (; i
< b
->begin
; i
++) {
1238 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1239 add_to_string(&go
, p
);
1241 add_bytes_to_string(&go
, data
.source
+ i
, b
->end
- b
->begin
);
1244 for (; i
< data
.length
; i
++) {
1247 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1248 add_to_string(&go
, p
);
1256 uri
= get_uri(go
.source
, 0);
1266 #undef BOUNDARY_LENGTH
1269 enum frame_event_status
1270 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1272 goto_current_link(ses
, doc_view
, do_reload
);
1273 return FRAME_EVENT_OK
;
1277 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1278 struct form
*form
, int do_reload
)
1280 /* Added support for submitting forms in hidden
1281 * links in 1.285, commented code can safely be removed once we have made sure the new
1282 * code does the right thing. */
1285 struct document
*document
= doc_view
->document
;
1288 for (link
= 0; link
< document
->nlinks
; link
++) {
1289 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1291 if (fc
&& fc
->form
== form
) {
1292 doc_view
->vs
->current_link
= link
;
1293 submit_form(ses
, doc_view
, 0);
1298 if (!list_empty(form
->items
)) {
1299 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1301 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1304 uri
= get_form_uri(ses
, doc_view
, fc
);
1306 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1312 auto_submit_form(struct session
*ses
)
1314 struct document
*document
= ses
->doc_view
->document
;
1316 if (!list_empty(document
->forms
))
1317 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1323 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1325 unsigned char *filename
= filename_
;
1326 struct form_state
*fs
= fs_
;
1328 /* The menu code doesn't free the filename data */
1329 mem_free_set(&fs
->value
, filename
);
1330 fs
->state
= strlen(filename
);
1331 redraw_terminal(term
);
1336 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1338 unsigned char *path
= path_
;
1339 struct form_state
*fs
= fs_
;
1341 /* FIXME: It doesn't work for ../../ */
1343 int valuelen
= strlen(fs
->value
);
1344 int pathlen
= strlen(path
);
1345 int no_elevator
= 0;
1347 /* Don't add elevators for subdirs menus */
1348 /* It is not perfect at all because fs->value is not updated for each
1349 * newly opened file menu. Maybe it should be dropped. */
1350 for (; valuelen
< pathlen
; valuelen
++) {
1351 if (dir_sep(path
[valuelen
- 1])) {
1358 auto_complete_file(term
, 0 /* no_elevator */, path
,
1359 set_file_form_state
,
1360 file_form_menu
, fs
);
1364 enum frame_event_status
1365 field_op(struct session
*ses
, struct document_view
*doc_view
,
1366 struct link
*link
, struct term_event
*ev
)
1368 struct form_control
*fc
;
1369 struct form_state
*fs
;
1370 enum edit_action action_id
;
1371 unsigned char *text
;
1373 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1375 const unsigned char *ctext
;
1376 int utf8
= ses
->tab
->term
->utf8_cp
;
1377 #endif /* CONFIG_UTF8 */
1379 assert(ses
&& doc_view
&& link
&& ev
);
1380 if_assert_failed
return FRAME_EVENT_OK
;
1382 fc
= get_link_form_control(link
);
1383 assertm(fc
!= NULL
, "link has no form control");
1384 if_assert_failed
return FRAME_EVENT_OK
;
1386 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1387 || (ses
->insert_mode
== INSERT_MODE_OFF
1388 && !(get_kbd_modifier(ev
) & KBD_MOD_PASTE
)))
1389 return FRAME_EVENT_IGNORED
;
1391 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1393 fs
= find_form_state(doc_view
, fc
);
1394 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1396 switch (action_id
) {
1399 if (fc
->type
== FC_TEXTAREA
) {
1400 status
= textarea_op_left(fs
, fc
, utf8
);
1404 unsigned char *new_value
;
1406 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1407 fs
->state
= new_value
- fs
->value
;
1409 #endif /* CONFIG_UTF8 */
1410 fs
->state
= int_max(fs
->state
- 1, 0);
1412 case ACT_EDIT_RIGHT
:
1414 if (fc
->type
== FC_TEXTAREA
) {
1415 status
= textarea_op_right(fs
, fc
, utf8
);
1419 unsigned char *text
= fs
->value
+ fs
->state
;
1420 unsigned char *end
= strchr(text
, '\0');
1422 utf8_to_unicode(&text
, end
);
1423 fs
->state
= (int)(text
- fs
->value
);
1425 #endif /* CONFIG_UTF8 */
1426 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1430 if (fc
->type
== FC_TEXTAREA
) {
1431 status
= textarea_op_home(fs
, fc
, utf8
);
1436 if (fc
->type
== FC_TEXTAREA
) {
1437 status
= textarea_op_home(fs
, fc
);
1442 #endif /* CONFIG_UTF8 */
1445 if (fc
->type
!= FC_TEXTAREA
)
1446 status
= FRAME_EVENT_IGNORED
;
1449 status
= textarea_op_up(fs
, fc
, utf8
);
1451 status
= textarea_op_up(fs
, fc
);
1452 #endif /* CONFIG_UTF8 */
1455 if (fc
->type
!= FC_TEXTAREA
)
1456 status
= FRAME_EVENT_IGNORED
;
1459 status
= textarea_op_down(fs
, fc
, utf8
);
1461 status
= textarea_op_down(fs
, fc
);
1462 #endif /* CONFIG_UTF8 */
1465 if (fc
->type
== FC_TEXTAREA
) {
1467 status
= textarea_op_end(fs
, fc
, utf8
);
1469 status
= textarea_op_end(fs
, fc
);
1470 #endif /* CONFIG_UTF8 */
1472 fs
->state
= strlen(fs
->value
);
1475 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1476 if (fc
->type
== FC_TEXTAREA
) {
1478 status
= textarea_op_bob(fs
, fc
, utf8
);
1481 status
= textarea_op_bob(fs
, fc
);
1482 #endif /* CONFIG_UTF8 */
1487 case ACT_EDIT_END_OF_BUFFER
:
1488 if (fc
->type
== FC_TEXTAREA
) {
1490 status
= textarea_op_eob(fs
, fc
, utf8
);
1492 status
= textarea_op_eob(fs
, fc
);
1493 #endif /* CONFIG_UTF8 */
1495 fs
->state
= strlen(fs
->value
);
1498 case ACT_EDIT_OPEN_EXTERNAL
:
1499 if (form_field_is_readonly(fc
))
1500 status
= FRAME_EVENT_IGNORED
;
1501 else if (fc
->type
== FC_TEXTAREA
)
1502 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1504 case ACT_EDIT_COPY_CLIPBOARD
:
1505 set_clipboard_text(fs
->value
);
1506 status
= FRAME_EVENT_OK
;
1508 case ACT_EDIT_CUT_CLIPBOARD
:
1509 set_clipboard_text(fs
->value
);
1510 if (!form_field_is_readonly(fc
))
1514 if (fc
->type
== FC_TEXTAREA
)
1516 #endif /* CONFIG_UTF8 */
1518 case ACT_EDIT_PASTE_CLIPBOARD
:
1519 if (form_field_is_readonly(fc
)) break;
1521 text
= get_clipboard_text();
1524 length
= strlen(text
);
1525 if (length
<= fc
->maxlength
) {
1526 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1530 memmove(v
, text
, length
+ 1);
1531 fs
->state
= strlen(fs
->value
);
1533 if (utf8
&& fc
->type
== FC_TEXTAREA
)
1535 #endif /* CONFIG_UTF8 */
1540 case ACT_EDIT_ENTER
:
1541 if (fc
->type
== FC_TEXTAREA
) {
1543 status
= textarea_op_enter(fs
, fc
, utf8
);
1545 status
= textarea_op_enter(fs
, fc
);
1546 #endif /* CONFIG_UTF8 */
1550 /* Set status to ok if either it is not possible to
1551 * submit the form or the posting fails. */
1552 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1553 if ((has_form_submit(fc
->form
)
1554 && !get_opt_bool("document.browse.forms.auto_submit", ses
))
1555 || goto_current_link(ses
, doc_view
, 0)) {
1556 if (ses
->insert_mode
== INSERT_MODE_ON
)
1557 ses
->insert_mode
= INSERT_MODE_OFF
;
1558 status
= FRAME_EVENT_OK
;
1561 case ACT_EDIT_BACKSPACE
:
1562 if (form_field_is_readonly(fc
)) {
1563 status
= FRAME_EVENT_IGNORED
;
1568 status
= FRAME_EVENT_OK
;
1573 int old_state
= fs
->state
;
1574 unsigned char *new_value
;
1576 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1577 fs
->state
= new_value
- fs
->value
;
1579 if (old_state
!= fs
->state
) {
1580 if (fc
->type
== FC_TEXTAREA
)
1582 length
= strlen(fs
->value
+ old_state
) + 1;
1583 memmove(new_value
, fs
->value
+ old_state
, length
);
1586 #endif /* CONFIG_UTF8 */
1588 length
= strlen(fs
->value
+ fs
->state
) + 1;
1589 text
= fs
->value
+ fs
->state
;
1591 memmove(text
- 1, text
, length
);
1595 case ACT_EDIT_DELETE
:
1596 if (form_field_is_readonly(fc
)) {
1597 status
= FRAME_EVENT_IGNORED
;
1601 length
= strlen(fs
->value
);
1602 if (fs
->state
>= length
) {
1603 status
= FRAME_EVENT_OK
;
1608 unsigned char *end
= fs
->value
+ length
;
1609 unsigned char *text
= fs
->value
+ fs
->state
;
1610 unsigned char *old
= text
;
1612 utf8_to_unicode(&text
, end
);
1615 (int)(end
- text
) + 1);
1619 #endif /* CONFIG_UTF8 */
1620 text
= fs
->value
+ fs
->state
;
1622 memmove(text
, text
+ 1, length
- fs
->state
);
1624 case ACT_EDIT_KILL_TO_BOL
:
1625 if (form_field_is_readonly(fc
)) {
1626 status
= FRAME_EVENT_IGNORED
;
1630 if (fs
->state
<= 0) {
1631 status
= FRAME_EVENT_OK
;
1635 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1637 /* Leave the new-line character if it does not
1638 * immediately precede the cursor. */
1639 if (text
!= &fs
->value
[fs
->state
- 1])
1645 length
= strlen(fs
->value
+ fs
->state
) + 1;
1646 memmove(text
, fs
->value
+ fs
->state
, length
);
1648 fs
->state
= (int) (text
- fs
->value
);
1651 if (fc
->type
== FC_TEXTAREA
)
1654 #endif /* CONFIG_UTF8 */
1656 case ACT_EDIT_KILL_TO_EOL
:
1657 if (form_field_is_readonly(fc
)) {
1658 status
= FRAME_EVENT_IGNORED
;
1662 if (!fs
->value
[fs
->state
]) {
1663 status
= FRAME_EVENT_OK
;
1667 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1669 fs
->value
[fs
->state
] = '\0';
1673 if (fs
->value
[fs
->state
] == ASCII_LF
)
1676 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1679 case ACT_EDIT_KILL_WORD_BACK
:
1680 if (form_field_is_readonly(fc
)) {
1681 status
= FRAME_EVENT_IGNORED
;
1685 if (fs
->state
<= 0) {
1686 status
= FRAME_EVENT_OK
;
1690 text
= &fs
->value
[fs
->state
];
1691 while (text
> fs
->value
&& isspace(*(text
- 1)))
1693 while (text
> fs
->value
&& !isspace(*(text
- 1)))
1695 if (*text
== ASCII_LF
1696 && text
!= &fs
->value
[fs
->state
- 1])
1699 length
= strlen(fs
->value
+ fs
->state
) + 1;
1700 memmove(text
, fs
->value
+ fs
->state
, length
);
1702 fs
->state
= (int) (text
- fs
->value
);
1705 case ACT_EDIT_MOVE_BACKWARD_WORD
:
1706 while (fs
->state
> 0
1707 && isspace(fs
->value
[fs
->state
- 1]))
1709 while (fs
->state
> 0
1710 && !isspace(fs
->value
[fs
->state
- 1]))
1714 case ACT_EDIT_MOVE_FORWARD_WORD
:
1715 while (isspace(fs
->value
[fs
->state
]))
1717 while (fs
->value
[fs
->state
]
1718 && !isspace(fs
->value
[fs
->state
]))
1720 while (isspace(fs
->value
[fs
->state
]))
1724 case ACT_EDIT_AUTO_COMPLETE
:
1725 if (fc
->type
!= FC_FILE
1726 || form_field_is_readonly(fc
)) {
1727 status
= FRAME_EVENT_IGNORED
;
1731 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1734 case ACT_EDIT_CANCEL
:
1735 if (ses
->insert_mode
== INSERT_MODE_ON
)
1736 ses
->insert_mode
= INSERT_MODE_OFF
;
1738 status
= FRAME_EVENT_IGNORED
;
1741 case ACT_EDIT_REDRAW
:
1742 redraw_terminal_cls(ses
->tab
->term
);
1743 status
= FRAME_EVENT_OK
;
1747 if (!check_kbd_textinput_key(ev
)) {
1748 status
= FRAME_EVENT_IGNORED
;
1752 if (form_field_is_readonly(fc
)
1754 || strlen(fs
->value
) >= fc
->maxlength
1755 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1756 #endif /* CONFIG_UTF8 */
1759 status
= FRAME_EVENT_OK
;
1764 /* fs->value is in the charset of the terminal. */
1765 ctext
= u2cp_no_nbsp(get_kbd_key(ev
),
1766 get_opt_codepage_tree(ses
->tab
->term
->spec
,
1769 length
= strlen(ctext
);
1771 if (strlen(fs
->value
) + length
> fc
->maxlength
1772 || !insert_in_string(&fs
->value
, fs
->state
, ctext
, length
)) {
1773 status
= FRAME_EVENT_OK
;
1777 fs
->state
+= length
;
1778 if (fc
->type
== FC_TEXTAREA
)
1781 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1782 #endif /* CONFIG_UTF8 */
1789 static unsigned char *
1790 get_form_label(struct form_control
*fc
)
1795 return N_("Reset form");
1797 return N_("Harmless button");
1802 if (!fc
->form
->action
) return NULL
;
1804 if (fc
->form
->method
== FORM_METHOD_GET
)
1805 return N_("Submit form to");
1806 return N_("Post form to");
1808 return N_("Radio button");
1810 return N_("Checkbox");
1812 return N_("Select field");
1814 return N_("Text field");
1816 return N_("Text area");
1818 return N_("File upload");
1820 return N_("Password field");
1827 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1828 unsigned char *name
, unsigned char *value
)
1830 add_to_string(string
, ", ");
1831 add_to_string(string
, _(name
, term
));
1833 add_char_to_string(string
, ' ');
1834 add_to_string(string
, value
);
1839 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1841 struct terminal
*term
= ses
->tab
->term
;
1842 struct link
*link
= get_current_link(doc_view
);
1843 struct form_control
*fc
;
1844 unsigned char *label
, *key
;
1849 fc
= get_link_form_control(link
);
1850 label
= get_form_label(fc
);
1851 if (!label
) return NULL
;
1853 if (!init_string(&str
)) return NULL
;
1855 add_to_string(&str
, _(label
, term
));
1857 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1858 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1865 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1867 if (!fs
->value
|| !fs
->value
[0])
1870 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1880 unsigned char *uristring
;
1882 if (form_field_is_readonly(fc
)) {
1883 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1886 /* Should we add info about entering insert mode or add info
1887 * about submitting the form? */
1888 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1889 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1893 if (form_field_is_readonly(fc
))
1894 label
= N_("press %s to navigate");
1896 label
= N_("press %s to edit");
1898 add_to_string(&str
, " (");
1899 add_format_to_string(&str
, _(label
, term
), key
);
1900 add_char_to_string(&str
, ')');
1906 if (fc
->type
== FC_TEXTAREA
)
1911 if (!fc
->form
->action
1912 || (has_form_submit(fc
->form
)
1913 && !get_opt_bool("document.browse.forms.auto_submit",
1917 uri
= get_uri(fc
->form
->action
, 0);
1920 /* Add the uri with password and post info stripped */
1921 uristring
= get_uri_string(uri
, URI_PUBLIC
);
1924 if (!uristring
) break;
1926 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1928 mem_free(uristring
);
1932 if (fc
->form
->method
== FORM_METHOD_GET
)
1933 label
= N_("press %s to submit to %s");
1935 label
= N_("press %s to post to %s");
1937 add_to_string(&str
, " (");
1938 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
1939 mem_free(uristring
);
1942 add_char_to_string(&str
, ')');
1947 add_char_to_string(&str
, ' ');
1950 /* Add the uri with password and post info stripped */
1951 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
1962 && get_opt_bool("document.browse.accesskey.display", ses
)) {
1963 add_to_string(&str
, " (");
1964 add_accesskey_to_string(&str
, link
->accesskey
);
1965 add_char_to_string(&str
, ')');
1972 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
1974 struct session
*ses
= ses_
;
1975 struct document_view
*doc_view
;
1976 int link_number
= *(int *) link_number_
;
1978 mem_free(link_number_
);
1980 assert(term
&& ses
);
1981 if_assert_failed
return;
1983 doc_view
= current_frame(ses
);
1984 if (!doc_view
) return;
1986 assert(doc_view
->vs
&& doc_view
->document
);
1987 if_assert_failed
return;
1989 jump_to_link_number(ses
, doc_view
, link_number
);
1990 refresh_view(ses
, doc_view
, 0);
1994 link_form_menu(struct session
*ses
)
1996 struct document_view
*doc_view
;
1998 struct menu_item
*mi
;
1999 struct form_control
*fc
;
2003 if_assert_failed
return;
2005 doc_view
= current_frame(ses
);
2006 if (!doc_view
) return;
2008 assert(doc_view
->vs
&& doc_view
->document
);
2009 if_assert_failed
return;
2011 link
= get_current_link(doc_view
);
2014 assert(link_is_form(link
));
2016 fc
= get_link_form_control(link
);
2021 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
2024 foreach (fc
, form
->items
) {
2025 unsigned char *text
;
2026 unsigned char *rtext
;
2037 text
= N_("Useless button");
2039 text
= N_("Submit button");
2043 text
= get_form_label(fc
);
2046 link_number
= get_form_control_link(doc_view
->document
, fc
);
2048 || !init_string(&str
))
2052 add_to_string(&str
, _(text
, ses
->tab
->term
));
2055 if (!rtext
) rtext
= fc
->alt
;
2057 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
2058 link_form_menu_func
, intdup(link_number
),
2062 do_menu(ses
->tab
->term
, mi
, ses
, 1);