1 /* Forms viewing/manipulation handling */
8 #define _GNU_SOURCE /* XXX: we want memrchr() ! */
15 #include <sys/types.h>
20 #include <fcntl.h> /* OS/2 needs this after sys/types.h */
25 #include "bfu/listmenu.h"
26 #include "bfu/dialog.h"
27 #include "config/kbdbind.h"
28 #include "dialogs/menu.h"
29 #include "document/document.h"
30 #include "document/forms.h"
31 #include "document/html/parser.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 */
61 struct submitted_value
*
62 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
63 struct form_control
*fc
, int position
)
65 struct submitted_value
*sv
;
67 sv
= mem_alloc(sizeof(*sv
));
70 sv
->value
= stracpy(value
);
71 if (!sv
->value
) { mem_free(sv
); return NULL
; }
73 sv
->name
= stracpy(name
);
74 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
77 sv
->form_control
= fc
;
78 sv
->position
= position
;
84 done_submitted_value(struct submitted_value
*sv
)
87 mem_free_if(sv
->value
);
88 mem_free_if(sv
->name
);
93 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
98 if_assert_failed
return;
101 && fs
->state
< fc
->nvalues
102 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
105 for (i
= 0; i
< fc
->nvalues
; i
++)
106 if (!strcmp(fc
->values
[i
], fs
->value
)) {
113 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
115 : (unsigned char *) ""));
120 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
122 struct session
*ses
= ses_
;
123 int item
= (long) item_
;
124 struct document_view
*doc_view
;
126 struct form_state
*fs
;
127 struct form_control
*fc
;
130 if_assert_failed
return;
131 doc_view
= current_frame(ses
);
133 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
134 if_assert_failed
return;
136 link
= get_current_link(doc_view
);
137 if (!link
|| link
->type
!= LINK_SELECT
) return;
139 fc
= get_link_form_control(link
);
140 fs
= find_form_state(doc_view
, fc
);
142 if (item
>= 0 && item
< fc
->nvalues
) {
144 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
146 fixup_select_state(fc
, fs
);
149 refresh_view(ses
, doc_view
, 0);
153 init_form_state(struct form_control
*fc
, struct form_state
*fs
)
156 if_assert_failed
return;
158 mem_free_set(&fs
->value
, NULL
);
164 fs
->value
= stracpy(fc
->default_value
);
165 fs
->state
= strlen(fc
->default_value
);
167 if (fc
->type
== FC_TEXT
)
168 fs
->state_cell
= utf8_ptr2cells(fs
->value
, NULL
);
169 if (fc
->type
== FC_PASSWORD
)
170 fs
->state_cell
= utf8_ptr2chars(fs
->value
, NULL
);
171 if (fc
->type
== FC_TEXTAREA
)
173 #endif /* CONFIG_UTF_8 */
177 fs
->value
= stracpy("");
181 #endif /* CONFIG_UTF_8 */
185 fs
->value
= stracpy(fc
->default_value
);
186 fs
->state
= fc
->default_state
;
187 fixup_select_state(fc
, fs
);
191 fs
->state
= fc
->default_state
;
198 fs
->value
= stracpy(fc
->default_value
);
205 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
207 struct view_state
*vs
;
208 struct form_state
*fs
;
211 assert(doc_view
&& doc_view
->vs
&& fc
);
212 if_assert_failed
return NULL
;
217 if (n
>= vs
->form_info_len
) {
220 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
221 if (!fs
) return NULL
;
223 vs
->form_info_len
= nn
;
225 fs
= &vs
->form_info
[n
];
227 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
228 && fs
->g_ctrl_num
== fc
->g_ctrl_num
229 && fs
->position
== fc
->position
230 && fs
->type
== fc
->type
)
233 mem_free_if(fs
->value
);
234 memset(fs
, 0, sizeof(*fs
));
235 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
236 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
237 fs
->position
= fc
->position
;
239 init_form_state(fc
, fs
);
244 struct form_control
*
245 find_form_control(struct document
*document
, struct form_state
*fs
)
247 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
248 struct form_control
*fc
;
250 foreach (fc
, form
->items
) {
251 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
252 && fs
->position
== fc
->position
253 && fs
->type
== fc
->type
)
261 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
263 struct form_view
*fv
;
267 foreach (fv
, vs
->forms
)
268 if (fv
->form_num
== form_num
)
271 fv
= mem_calloc(1, sizeof(*fv
));
272 fv
->form_num
= form_num
;
273 add_to_list(vs
->forms
, fv
);
278 find_form_view(struct document_view
*doc_view
, struct form
*form
)
280 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
284 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
288 foreach (form
, document
->forms
) {
289 if (form
->form_num
== fv
->form_num
)
297 get_current_state(struct session
*ses
)
299 struct document_view
*doc_view
;
301 struct form_state
*fs
;
304 if_assert_failed
return -1;
305 doc_view
= current_frame(ses
);
307 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
308 if_assert_failed
return -1;
310 link
= get_current_link(doc_view
);
311 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
313 fs
= find_form_state(doc_view
, get_link_form_control(link
));
314 if (fs
) return fs
->state
;
319 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
322 struct form_state
*fs
;
323 struct form_control
*fc
;
324 struct view_state
*vs
;
328 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
329 if_assert_failed
return;
331 fc
= get_link_form_control(link
);
332 assertm(fc
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
333 if_assert_failed
return;
335 fs
= find_form_state(doc_view
, fc
);
338 box
= &doc_view
->box
;
345 unsigned char *text
, *end
, *last_in_view
;
347 #endif /* CONFIG_UTF_8 */
354 if (!link
->npoints
) break;
356 y
= link
->points
[0].y
+ dy
;
357 if (!row_is_in_box(box
, y
))
360 x
= link
->points
[0].x
+ dx
;
362 if (term
->utf8
) goto utf_8
;
363 #endif /* CONFIG_UTF_8 */
364 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
365 len
= strlen(fs
->value
) - fs
->vpos
;
367 for (i
= 0; i
< fc
->size
; i
++, x
++) {
370 if (!col_is_in_box(box
, x
)) continue;
372 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
373 data
= fc
->type
!= FC_PASSWORD
374 ? fs
->value
[i
+ fs
->vpos
] : '*';
378 draw_char_data(term
, x
, y
, data
);
387 if (!text
) text
= "";
389 int_bounds(&fs
->state
, 0, len
);
390 int_bounds(&fs
->vpos
, 0, fs
->state
);
395 for (i
= 0; i
< fc
->size
; ) {
398 unsigned char *maybe_in_view
= text
;
400 data
= utf_8_to_unicode(&text
, end
);
401 if (data
== UCS_NO_CHAR
) /* end of string */
403 else if (fc
->type
== FC_PASSWORD
)
406 cells
= unicode_to_cell(data
);
407 if (i
+ cells
<= fc
->size
) {
408 last_in_view
= maybe_in_view
;
409 if (colspan_is_in_box(box
, x
+ i
, cells
)) {
410 /* The character fits completely.
411 * Draw the character, and mark any
412 * further cells with UCS_NO_CHAR. */
413 draw_char_data(term
, x
+ i
, y
, data
);
414 for (cell
= 1; cell
< cells
; cell
++)
415 draw_char_data(term
, x
+ i
+ cell
,
421 /* The character does not fit completely.
422 * Write spaces to the cells that do fit. */
423 for (cell
= 0; cell
< cells
; cell
++) {
424 if (col_is_in_box(box
, x
+ i
+ cell
)
425 && i
+ cell
< fc
->size
)
435 /* The int_bounds calls above ensured that the
436 * insertion point cannot be at the left side
437 * of the scrolled-visible part of the text.
438 * However it can still be at the right side.
439 * Check whether we need to change fs->vpos.
441 * This implementation attempts to follow
443 * - If the insertion point is at the end of
444 * the string, leave at least one empty cell
445 * so that there is a place for the cursor.
446 * - If a character follows the insertion
447 * point, make that character fully visible;
448 * note the character may be double-width.
449 * - If fc->size < 2, it is not possible to
450 * make a double-width character fully
451 * visible. In this case, it is OK if the
452 * output is ugly, but ELinks must not fall
453 * into an infinite loop or crash.
454 * - The length of the string should not affect
455 * how long this function takes. The width
456 * of the widget naturally will.
457 * - Optimize the case where fields are drawn
458 * several times without being modified.
461 * - If the "for i" loop above hit UCS_NO_CHAR,
462 * then there is no need to scroll.
463 * - When the string ends with a double-width
464 * character that fits in only partially,
465 * then text==end, but the field may have
467 if (fs
->value
&& last_in_view
468 && last_in_view
< fs
->value
+ fs
->state
) {
469 unsigned char *ptr
= fs
->value
+ fs
->state
;
470 int cells
= fc
->size
;
471 enum utf8_step how
= (fc
->type
== FC_PASSWORD
)
472 ? utf8_step_characters
473 : utf8_step_cells_fewer
;
475 /* The insertion point is at the right
476 * side of the scrolled-visible part
477 * of the text. Decide a new fs->vpos
478 * by counting cells backwards from
479 * @ptr. But first advance @ptr past
480 * the character that follows the
481 * insertion point, so that it will be
482 * fully displayed. If there is no
483 * such character, reserve one cell
484 * for the cursor anyway. */
485 if (utf_8_to_unicode(&ptr
, end
) == UCS_NO_CHAR
)
487 ptr
= utf8_step_backward(ptr
, fs
->value
,
490 if (fs
->vpos
!= ptr
- fs
->value
) {
491 fs
->vpos
= ptr
- fs
->value
;
493 goto retry_after_scroll
;
497 #endif /* CONFIG_UTF_8 */
499 draw_textarea(term
, fs
, doc_view
, link
);
503 if (link
->npoints
< 2) break;
504 x
= link
->points
[1].x
+ dx
;
505 y
= link
->points
[1].y
+ dy
;
506 if (is_in_box(box
, x
, y
))
507 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
510 fixup_select_state(fc
, fs
);
511 if (fs
->state
< fc
->nvalues
)
512 s
= fc
->labels
[fs
->state
];
514 /* XXX: when can this happen? --pasky */
517 if (term
->utf8
) goto utf_8_select
;
518 #endif /* CONFIG_UTF_8 */
519 len
= s
? strlen(s
) : 0;
520 for (i
= 0; i
< link
->npoints
; i
++) {
521 x
= link
->points
[i
].x
+ dx
;
522 y
= link
->points
[i
].y
+ dy
;
523 if (is_in_box(box
, x
, y
))
524 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
530 end
= strchr(s
, '\0');
531 len
= utf8_ptr2cells(text
, end
);
532 for (i
= 0; i
< link
->npoints
; i
++) {
533 x
= link
->points
[i
].x
+ dx
;
534 y
= link
->points
[i
].y
+ dy
;
535 if (is_in_box(box
, x
, y
)) {
540 data
= utf_8_to_unicode(&s
, end
);
541 cell
= unicode_to_cell(data
);
542 if (i
+ 1 < len
&& cell
== 2) {
543 draw_char_data(term
, x
++, y
, data
);
547 } else if (cell
== 2) {
552 draw_char_data(term
, x
, y
, data
);
556 #endif /* CONFIG_UTF_8 */
567 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
569 struct link
*l1
, *l2
;
571 assert(term
&& doc_view
);
572 if_assert_failed
return;
574 l1
= get_first_link(doc_view
);
575 l2
= get_last_link(doc_view
);
578 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
579 /* Return path :-). */
583 struct form_control
*fc
= get_link_form_control(l1
);
586 #ifdef CONFIG_FORMHIST
587 if (fc
->type
== FC_TEXT
|| fc
->type
== FC_PASSWORD
) {
588 unsigned char *value
;
591 value
= get_form_history_value(fc
->form
->action
, fc
->name
);
594 mem_free_set(&fc
->default_value
,
597 #endif /* CONFIG_FORMHIST */
598 draw_form_entry(term
, doc_view
, l1
);
605 done_submitted_value_list(struct list_head
*list
)
607 struct submitted_value
*sv
, *svtmp
;
610 if_assert_failed
return;
612 foreach (sv
, *list
) {
615 del_from_list(svtmp
);
616 done_submitted_value(svtmp
);
621 add_submitted_value_to_list(struct form_control
*fc
,
622 struct form_state
*fs
,
623 struct list_head
*list
)
625 struct submitted_value
*sub
;
630 assert(fc
&& fs
&& list
);
633 position
= fc
->position
;
641 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
642 if (sub
) add_to_list(*list
, sub
);
647 if (!fs
->state
) break;
654 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
656 if (sub
) add_to_list(*list
, sub
);
660 if (!fc
->nvalues
) break;
662 fixup_select_state(fc
, fs
);
663 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
664 if (sub
) add_to_list(*list
, sub
);
668 name
= straconcat(fc
->name
, ".x", NULL
);
670 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
672 if (sub
) add_to_list(*list
, sub
);
674 name
= straconcat(fc
->name
, ".y", NULL
);
676 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
678 if (sub
) add_to_list(*list
, sub
);
685 sort_submitted_values(struct list_head
*list
)
688 struct submitted_value
*sub
;
691 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
692 if (sub
->next
->position
< sub
->position
) {
693 struct submitted_value
*next
= sub
->next
;
696 add_at_pos(next
, sub
);
701 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
702 if (sub
->next
->position
< sub
->position
) {
703 struct submitted_value
*next
= sub
->next
;
706 add_at_pos(next
, sub
);
716 get_successful_controls(struct document_view
*doc_view
,
717 struct form_control
*fc
, struct list_head
*list
)
719 struct form_control
*fc2
;
721 assert(doc_view
&& fc
&& fc
->form
&& list
);
722 if_assert_failed
return;
724 foreach (fc2
, fc
->form
->items
) {
725 if (((fc2
->type
!= FC_SUBMIT
&&
726 fc2
->type
!= FC_IMAGE
&&
727 fc2
->type
!= FC_RESET
&&
728 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
729 && fc2
->name
&& fc2
->name
[0]) {
730 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
734 add_submitted_value_to_list(fc2
, fs
, list
);
738 sort_submitted_values(list
);
742 encode_controls(struct list_head
*l
, struct string
*data
,
743 int cp_from
, int cp_to
)
745 struct submitted_value
*sv
;
746 struct conv_table
*convert_table
= NULL
;
750 if_assert_failed
return;
753 unsigned char *p2
= NULL
;
756 add_char_to_string(data
, '&');
760 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
761 add_char_to_string(data
, '=');
763 /* Convert back to original encoding (see html_form_control()
764 * for the original recoding). */
765 if (sv
->type
== FC_TEXTAREA
) {
768 p
= encode_textarea(sv
);
771 convert_table
= get_translation_table(cp_from
, cp_to
);
773 p2
= convert_string(convert_table
, p
,
774 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
777 } else if (sv
->type
== FC_TEXT
||
778 sv
->type
== FC_PASSWORD
) {
780 convert_table
= get_translation_table(cp_from
, cp_to
);
782 p2
= convert_string(convert_table
, sv
->value
,
783 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
785 p2
= stracpy(sv
->value
);
789 encode_uri_string(data
, p2
, strlen(p2
), 1);
797 #define BOUNDARY_LENGTH 32
798 #define realloc_bound_ptrs(bptrs, bptrs_size) \
799 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
801 struct boundary_info
{
804 unsigned char string
[BOUNDARY_LENGTH
];
808 init_boundary(struct boundary_info
*boundary
)
810 memset(boundary
, 0, sizeof(*boundary
));
811 memset(boundary
->string
, '0', BOUNDARY_LENGTH
);
814 /* Add boundary to string and save the offset */
816 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
818 add_to_string(data
, "--");
820 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
821 boundary
->offsets
[boundary
->count
++] = data
->length
;
823 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
826 static inline unsigned char *
827 increment_boundary_counter(struct boundary_info
*boundary
)
831 /* This is just a decimal string incrementation */
832 for (j
= BOUNDARY_LENGTH
- 1; j
>= 0; j
--) {
833 if (boundary
->string
[j
]++ < '9')
834 return boundary
->string
;
836 boundary
->string
[j
] = '0';
839 INTERNAL("Form data boundary counter overflow");
845 check_boundary(struct string
*data
, struct boundary_info
*boundary
)
847 unsigned char *bound
= boundary
->string
;
850 /* Search between all boundaries. There is a starting and an ending
851 * boundary so only check the range of chars after the current offset
852 * and before the next offset. If some string in the form data matches
853 * the boundary string it is changed. */
854 for (i
= 0; i
< boundary
->count
- 1; i
++) {
855 /* Start after the boundary string and also jump past the
856 * "\r\nContent-Disposition: form-data; name=\"" string added
857 * before any form data. */
858 int start_offset
= boundary
->offsets
[i
] + BOUNDARY_LENGTH
+ 40;
860 /* End so that there is atleast BOUNDARY_LENGTH chars to
861 * compare. Subtract 2 char because there is no need to also
862 * compare the '--' prefix that is part of the boundary. */
863 int end_offset
= boundary
->offsets
[i
+ 1] - BOUNDARY_LENGTH
- 2;
864 unsigned char *pos
= data
->source
+ start_offset
;
865 unsigned char *end
= data
->source
+ end_offset
;
867 for (; pos
<= end
; pos
++) {
868 if (memcmp(pos
, bound
, BOUNDARY_LENGTH
))
871 /* If incrementing causes overflow bail out. There is
872 * no need to reset the boundary string with '0' since
873 * that is already done when incrementing. */
874 if (!increment_boundary_counter(boundary
))
877 /* Else start checking all boundaries using the new
884 /* Now update all the boundaries with the unique boundary string */
885 for (i
= 0; i
< boundary
->count
; i
++)
886 memcpy(data
->source
+ boundary
->offsets
[i
], bound
, BOUNDARY_LENGTH
);
889 /* FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
891 encode_multipart(struct session
*ses
, struct list_head
*l
, struct string
*data
,
892 struct boundary_info
*boundary
, int cp_from
, int cp_to
)
894 struct conv_table
*convert_table
= NULL
;
895 struct submitted_value
*sv
;
897 assert(ses
&& l
&& data
&& boundary
);
898 if_assert_failed
return;
900 init_boundary(boundary
);
903 add_boundary(data
, boundary
);
904 add_crlf_to_string(data
);
906 /* FIXME: name is not encoded.
908 * multipart/form-data contains a series of parts.
909 * Each part is expected to contain a content-disposition
910 * header where the value is "form-data" and a name attribute
911 * specifies the field name within the form,
912 * e.g., 'content-disposition: form-data; name="xxxxx"',
913 * where xxxxx is the field name corresponding to that field.
914 * Field names originally in non-ASCII character sets may be
915 * encoded using the method outlined in RFC 1522. */
916 add_to_string(data
, "Content-Disposition: form-data; name=\"");
917 add_to_string(data
, sv
->name
);
918 add_char_to_string(data
, '"');
920 if (sv
->type
== FC_FILE
) {
921 #define F_BUFLEN 1024
923 unsigned char buffer
[F_BUFLEN
];
924 unsigned char *extension
;
926 add_to_string(data
, "; filename=\"");
927 add_to_string(data
, get_filename_position(sv
->value
));
928 /* It sends bad data if the file name contains ", but
929 Netscape does the same */
930 /* FIXME: We should follow RFCs 1522, 1867,
931 * 2047 (updated by rfc 2231), to provide correct support
932 * for non-ASCII and special characters in values. --Zas */
933 add_char_to_string(data
, '"');
935 /* Add a Content-Type header if the type is configured */
936 extension
= strrchr(sv
->value
, '.');
938 unsigned char *type
= get_extension_content_type(extension
);
941 add_crlf_to_string(data
);
942 add_to_string(data
, "Content-Type: ");
943 add_to_string(data
, type
);
948 add_crlf_to_string(data
);
949 add_crlf_to_string(data
);
952 unsigned char *filename
;
954 if (get_cmd_opt_bool("anonymous")) {
959 /* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
960 filename
= expand_tilde(sv
->value
);
961 if (!filename
) goto encode_error
;
963 fh
= open(filename
, O_RDONLY
);
966 if (fh
== -1) goto encode_error
;
969 ssize_t rd
= safe_read(fh
, buffer
, F_BUFLEN
);
977 add_bytes_to_string(data
, buffer
, rd
);
987 add_crlf_to_string(data
);
988 add_crlf_to_string(data
);
990 /* Convert back to original encoding (see
991 * html_form_control() for the original recoding). */
992 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
993 sv
->type
== FC_TEXTAREA
) {
997 convert_table
= get_translation_table(cp_from
,
1000 p
= convert_string(convert_table
, sv
->value
,
1001 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
1004 add_to_string(data
, p
);
1008 add_to_string(data
, sv
->value
);
1012 add_crlf_to_string(data
);
1016 add_boundary(data
, boundary
);
1017 add_to_string(data
, "--\r\n");
1019 check_boundary(data
, boundary
);
1021 mem_free_if(boundary
->offsets
);
1025 mem_free_if(boundary
->offsets
);
1028 /* XXX: This error message should move elsewhere. --Zas */
1029 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
1030 N_("Error while posting form"), ALIGN_CENTER
,
1031 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
1032 sv
->value
, strerror(errno
)));
1036 encode_newlines(struct string
*string
, unsigned char *data
)
1038 for (; *data
; data
++) {
1039 if (*data
== '\n' || *data
== '\r') {
1040 unsigned char buffer
[3];
1044 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
1045 buffer
[2] = hx(((int) *data
) & 0xF);
1046 add_bytes_to_string(string
, buffer
, 3);
1048 add_char_to_string(string
, *data
);
1054 encode_text_plain(struct list_head
*l
, struct string
*data
,
1055 int cp_from
, int cp_to
)
1057 struct submitted_value
*sv
;
1058 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
1061 if_assert_failed
return;
1064 unsigned char *area51
= NULL
;
1065 unsigned char *value
= sv
->value
;
1067 add_to_string(data
, sv
->name
);
1068 add_char_to_string(data
, '=');
1072 value
= area51
= encode_textarea(sv
);
1077 /* Convert back to original encoding (see
1078 * html_form_control() for the original recoding). */
1079 value
= convert_string(convert_table
, value
,
1080 strlen(value
), -1, CSM_FORM
,
1083 /* Falling right through to free that textarea stuff */
1084 mem_free_if(area51
);
1086 /* Did the conversion fail? */
1089 encode_newlines(data
, value
);
1091 /* Free if we did convert something */
1092 if (value
!= sv
->value
) mem_free(value
);
1095 add_crlf_to_string(data
);
1100 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1102 struct form_control
*fc
;
1104 assert(doc_view
&& doc_view
->document
);
1105 if_assert_failed
return;
1107 foreach (fc
, form
->items
) {
1108 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1110 if (fs
) init_form_state(fc
, fs
);
1114 enum frame_event_status
1115 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1117 struct link
*link
= get_current_link(doc_view
);
1119 if (!link
) return FRAME_EVENT_OK
;
1121 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1122 draw_forms(ses
->tab
->term
, doc_view
);
1124 /* Could be the refresh return value and then ditch the draw_forms()
1126 return FRAME_EVENT_OK
;
1130 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1131 struct form_control
*fc
)
1133 struct boundary_info boundary
;
1134 INIT_LIST_HEAD(submit
);
1141 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1142 if_assert_failed
return NULL
;
1143 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1144 if_assert_failed
return NULL
;
1148 if (fc
->type
== FC_RESET
) {
1149 do_reset_form(doc_view
, form
);
1154 || !init_string(&data
))
1157 get_successful_controls(doc_view
, fc
, &submit
);
1159 cp_from
= get_opt_codepage_tree(ses
->tab
->term
->spec
, "charset");
1160 cp_to
= doc_view
->document
->cp
;
1161 switch (form
->method
) {
1162 case FORM_METHOD_GET
:
1163 case FORM_METHOD_POST
:
1164 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1167 case FORM_METHOD_POST_MP
:
1168 encode_multipart(ses
, &submit
, &data
, &boundary
, cp_from
, cp_to
);
1171 case FORM_METHOD_POST_TEXT_PLAIN
:
1172 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1175 #ifdef CONFIG_FORMHIST
1176 /* XXX: We check data.source here because a NULL value can indicate
1177 * not only a memory allocation failure, but also an error reading
1178 * a file that is to be uploaded. TODO: Distinguish between
1179 * these two classes of errors (is it worth it?). -- Miciah */
1181 && get_opt_bool("document.browse.forms.show_formhist"))
1182 memorize_form(ses
, &submit
, form
);
1185 done_submitted_value_list(&submit
);
1188 || !init_string(&go
)) {
1193 switch (form
->method
) {
1194 case FORM_METHOD_GET
:
1196 unsigned char *pos
= strchr(form
->action
, '#');
1199 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1201 add_to_string(&go
, form
->action
);
1204 if (strchr(go
.source
, '?'))
1205 add_char_to_string(&go
, '&');
1207 add_char_to_string(&go
, '?');
1209 add_string_to_string(&go
, &data
);
1211 if (pos
) add_to_string(&go
, pos
);
1214 case FORM_METHOD_POST
:
1215 case FORM_METHOD_POST_MP
:
1216 case FORM_METHOD_POST_TEXT_PLAIN
:
1218 /* Note that we end content type here by a simple '\n',
1219 * replaced later by correct '\r\n' in http_send_header(). */
1222 add_to_string(&go
, form
->action
);
1223 add_char_to_string(&go
, POST_CHAR
);
1224 if (form
->method
== FORM_METHOD_POST
) {
1225 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1227 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1228 /* Dunno about this one but we don't want the full
1229 * hextcat thingy. --jonas */
1230 add_to_string(&go
, "text/plain\n");
1231 add_to_string(&go
, data
.source
);
1235 add_to_string(&go
, "multipart/form-data; boundary=");
1236 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1237 add_char_to_string(&go
, '\n');
1240 for (i
= 0; i
< data
.length
; i
++) {
1243 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1244 add_to_string(&go
, p
);
1251 uri
= get_uri(go
.source
, 0);
1253 if (uri
) uri
->form
= 1;
1258 #undef BOUNDARY_LENGTH
1261 enum frame_event_status
1262 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1264 goto_current_link(ses
, doc_view
, do_reload
);
1265 return FRAME_EVENT_OK
;
1269 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1270 struct form
*form
, int do_reload
)
1272 /* Added support for submitting forms in hidden
1273 * links in 1.285, commented code can safely be removed once we have made sure the new
1274 * code does the right thing. */
1277 struct document
*document
= doc_view
->document
;
1280 for (link
= 0; link
< document
->nlinks
; link
++) {
1281 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1283 if (fc
&& fc
->form
== form
) {
1284 doc_view
->vs
->current_link
= link
;
1285 submit_form(ses
, doc_view
, 0);
1290 if (!list_empty(form
->items
)) {
1291 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1293 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1296 uri
= get_form_uri(ses
, doc_view
, fc
);
1298 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1304 auto_submit_form(struct session
*ses
)
1306 struct document
*document
= ses
->doc_view
->document
;
1308 if (!list_empty(document
->forms
))
1309 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1315 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1317 unsigned char *filename
= filename_
;
1318 struct form_state
*fs
= fs_
;
1320 /* The menu code doesn't free the filename data */
1321 mem_free_set(&fs
->value
, filename
);
1322 fs
->state
= strlen(filename
);
1323 redraw_terminal(term
);
1328 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1330 unsigned char *path
= path_
;
1331 struct form_state
*fs
= fs_
;
1333 /* FIXME: It doesn't work for ../../ */
1335 int valuelen
= strlen(fs
->value
);
1336 int pathlen
= strlen(path
);
1337 int no_elevator
= 0;
1339 /* Don't add elevators for subdirs menus */
1340 /* It is not perfect at all because fs->value is not updated for each
1341 * newly opened file menu. Maybe it should be dropped. */
1342 for (; valuelen
< pathlen
; valuelen
++) {
1343 if (dir_sep(path
[valuelen
- 1])) {
1350 auto_complete_file(term
, 0 /* no_elevator */, path
,
1351 set_file_form_state
,
1352 file_form_menu
, fs
);
1356 enum frame_event_status
1357 field_op(struct session
*ses
, struct document_view
*doc_view
,
1358 struct link
*link
, struct term_event
*ev
)
1360 struct form_control
*fc
;
1361 struct form_state
*fs
;
1362 enum edit_action action_id
;
1363 unsigned char *text
;
1365 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1367 int utf8
= ses
->tab
->term
->utf8
;
1368 #endif /* CONFIG_UTF_8 */
1370 assert(ses
&& doc_view
&& link
&& ev
);
1371 if_assert_failed
return FRAME_EVENT_OK
;
1373 fc
= get_link_form_control(link
);
1374 assertm(fc
, "link has no form control");
1375 if_assert_failed
return FRAME_EVENT_OK
;
1377 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1378 || ses
->insert_mode
== INSERT_MODE_OFF
)
1379 return FRAME_EVENT_IGNORED
;
1381 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1383 fs
= find_form_state(doc_view
, fc
);
1384 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1386 switch (action_id
) {
1389 if (fc
->type
== FC_TEXTAREA
) {
1390 status
= textarea_op_left(fs
, fc
, utf8
);
1394 int old_state
= fs
->state
;
1395 unsigned char *new_value
;
1398 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1399 fs
->state
= new_value
- fs
->value
;
1401 if (old_state
!= fs
->state
) {
1402 if (fc
->type
== FC_PASSWORD
)
1405 cells
= utf8_char2cells(new_value
, NULL
);
1406 fs
->state_cell
= int_max(fs
->state_cell
- cells
, 0);
1409 #endif /* CONFIG_UTF_8 */
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');
1421 int old_state
= fs
->state
;
1422 unicode_val_T data
= utf_8_to_unicode(&text
, end
);
1424 fs
->state
= (int)(text
- fs
->value
);
1425 if (old_state
!= fs
->state
) {
1426 if (fc
->type
== FC_PASSWORD
)
1427 fs
->state_cell
= int_min(fs
->state_cell
+ 1,
1428 utf8_ptr2cells(fs
->value
, NULL
));
1430 fs
->state_cell
+= unicode_to_cell(data
);
1433 #endif /* CONFIG_UTF_8 */
1434 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1438 if (fc
->type
== FC_TEXTAREA
) {
1439 status
= textarea_op_home(fs
, fc
, utf8
);
1445 if (fc
->type
== FC_TEXTAREA
) {
1446 status
= textarea_op_home(fs
, fc
);
1451 #endif /* CONFIG_UTF_8 */
1454 if (fc
->type
!= FC_TEXTAREA
)
1455 status
= FRAME_EVENT_IGNORED
;
1458 status
= textarea_op_up(fs
, fc
, utf8
);
1460 status
= textarea_op_up(fs
, fc
);
1461 #endif /* CONFIG_UTF_8 */
1464 if (fc
->type
!= FC_TEXTAREA
)
1465 status
= FRAME_EVENT_IGNORED
;
1468 status
= textarea_op_down(fs
, fc
, utf8
);
1470 status
= textarea_op_down(fs
, fc
);
1471 #endif /* CONFIG_UTF_8 */
1474 if (fc
->type
== FC_TEXTAREA
) {
1476 status
= textarea_op_end(fs
, fc
, utf8
);
1478 status
= textarea_op_end(fs
, fc
);
1479 #endif /* CONFIG_UTF_8 */
1481 fs
->state
= strlen(fs
->value
);
1483 if (utf8
&& fc
->type
!= FC_PASSWORD
)
1484 fs
->state_cell
= utf8_ptr2cells(fs
->value
,
1485 fs
->value
+ fs
->state
);
1487 fs
->state_cell
= utf8_ptr2chars(fs
->value
,
1488 fs
->value
+ fs
->state
);
1489 #endif /* CONFIG_UTF_8 */
1492 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1493 if (fc
->type
== FC_TEXTAREA
) {
1495 status
= textarea_op_bob(fs
, fc
, utf8
);
1497 status
= textarea_op_bob(fs
, fc
);
1498 #endif /* CONFIG_UTF_8 */
1504 #endif /* CONFIG_UTF_8 */
1506 case ACT_EDIT_END_OF_BUFFER
:
1507 if (fc
->type
== FC_TEXTAREA
) {
1509 status
= textarea_op_eob(fs
, fc
, utf8
);
1511 status
= textarea_op_eob(fs
, fc
);
1512 #endif /* CONFIG_UTF_8 */
1514 fs
->state
= strlen(fs
->value
);
1516 if (utf8
&& fc
->type
!= FC_PASSWORD
)
1517 fs
->state_cell
= utf8_ptr2cells(fs
->value
,
1518 fs
->value
+ fs
->state
);
1520 fs
->state_cell
= utf8_ptr2chars(fs
->value
,
1521 fs
->value
+ fs
->state
);
1523 #endif /* CONFIG_UTF_8 */
1526 case ACT_EDIT_OPEN_EXTERNAL
:
1527 if (form_field_is_readonly(fc
))
1528 status
= FRAME_EVENT_IGNORED
;
1529 else if (fc
->type
== FC_TEXTAREA
)
1530 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1532 case ACT_EDIT_COPY_CLIPBOARD
:
1533 set_clipboard_text(fs
->value
);
1534 status
= FRAME_EVENT_OK
;
1536 case ACT_EDIT_CUT_CLIPBOARD
:
1537 set_clipboard_text(fs
->value
);
1538 if (!form_field_is_readonly(fc
))
1543 #endif /* CONFIG_UTF_8 */
1545 case ACT_EDIT_PASTE_CLIPBOARD
:
1546 if (form_field_is_readonly(fc
)) break;
1548 text
= get_clipboard_text();
1551 length
= strlen(text
);
1552 if (length
<= fc
->maxlength
) {
1553 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1557 memmove(v
, text
, length
+ 1);
1558 fs
->state
= strlen(fs
->value
);
1560 if(utf8
&& fc
->type
== FC_PASSWORD
)
1561 fs
->state_cell
= utf8_ptr2chars(fs
->value
,
1562 fs
->value
+ fs
->state
);
1563 else if (utf8
&& fc
->type
== FC_TEXTAREA
)
1566 fs
->state_cell
= utf8_ptr2cells(fs
->value
,
1567 fs
->value
+ fs
->state
);
1568 #endif /* CONFIG_UTF_8 */
1573 case ACT_EDIT_ENTER
:
1574 if (fc
->type
== FC_TEXTAREA
) {
1576 status
= textarea_op_enter(fs
, fc
, utf8
);
1578 status
= textarea_op_enter(fs
, fc
);
1579 #endif /* CONFIG_UTF_8 */
1583 /* Set status to ok if either it is not possible to
1584 * submit the form or the posting fails. */
1585 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1586 if ((has_form_submit(fc
->form
)
1587 && !get_opt_bool("document.browse.forms.auto_submit"))
1588 || goto_current_link(ses
, doc_view
, 0)) {
1589 if (ses
->insert_mode
== INSERT_MODE_ON
)
1590 ses
->insert_mode
= INSERT_MODE_OFF
;
1591 status
= FRAME_EVENT_OK
;
1594 case ACT_EDIT_BACKSPACE
:
1595 if (form_field_is_readonly(fc
)) {
1596 status
= FRAME_EVENT_IGNORED
;
1601 status
= FRAME_EVENT_OK
;
1606 int old_state
= fs
->state
;
1607 unsigned char *new_value
;
1609 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1610 fs
->state
= new_value
- fs
->value
;
1612 if (old_state
!= fs
->state
) {
1613 if (fc
->type
== FC_TEXTAREA
)
1615 else if (fc
->type
== FC_PASSWORD
)
1616 fs
->state_cell
= int_max(fs
->state_cell
- 1, 0);
1618 fs
->state_cell
-= utf8_char2cells(new_value
, NULL
);
1619 length
= strlen(fs
->value
+ old_state
) + 1;
1620 memmove(new_value
, fs
->value
+ old_state
, length
);
1623 #endif /* CONFIG_UTF_8 */
1625 length
= strlen(fs
->value
+ fs
->state
) + 1;
1626 text
= fs
->value
+ fs
->state
;
1628 memmove(text
- 1, text
, length
);
1632 case ACT_EDIT_DELETE
:
1633 if (form_field_is_readonly(fc
)) {
1634 status
= FRAME_EVENT_IGNORED
;
1638 length
= strlen(fs
->value
);
1639 if (fs
->state
>= length
) {
1640 status
= FRAME_EVENT_OK
;
1645 unsigned char *end
= fs
->value
+ length
;
1646 unsigned char *text
= fs
->value
+ fs
->state
;
1647 unsigned char *old
= text
;
1649 utf_8_to_unicode(&text
, end
);
1652 (int)(end
- text
) + 1);
1656 #endif /* CONFIG_UTF_8 */
1657 text
= fs
->value
+ fs
->state
;
1659 memmove(text
, text
+ 1, length
- fs
->state
);
1661 case ACT_EDIT_KILL_TO_BOL
:
1662 if (form_field_is_readonly(fc
)) {
1663 status
= FRAME_EVENT_IGNORED
;
1667 if (fs
->state
<= 0) {
1668 status
= FRAME_EVENT_OK
;
1672 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1674 /* Leave the new-line character if it does not
1675 * immediately precede the cursor. */
1676 if (text
!= &fs
->value
[fs
->state
- 1])
1682 length
= strlen(fs
->value
+ fs
->state
) + 1;
1683 memmove(text
, fs
->value
+ fs
->state
, length
);
1685 fs
->state
= (int) (text
- fs
->value
);
1688 if(fc
->type
== FC_PASSWORD
)
1689 fs
->state_cell
= utf8_ptr2cells(fs
->value
,
1690 fs
->value
+ fs
->state
);
1691 else if (fc
->type
== FC_TEXTAREA
)
1694 fs
->state_cell
= utf8_ptr2cells(fs
->value
,
1695 fs
->value
+ fs
->state
);
1697 #endif /* CONFIG_UTF_8 */
1699 case ACT_EDIT_KILL_TO_EOL
:
1700 if (form_field_is_readonly(fc
)) {
1701 status
= FRAME_EVENT_IGNORED
;
1705 if (!fs
->value
[fs
->state
]) {
1706 status
= FRAME_EVENT_OK
;
1710 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1712 fs
->value
[fs
->state
] = '\0';
1716 if (fs
->value
[fs
->state
] == ASCII_LF
)
1719 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1722 case ACT_EDIT_KILL_WORD_BACK
:
1723 if (form_field_is_readonly(fc
)) {
1724 status
= FRAME_EVENT_IGNORED
;
1728 if (fs
->state
<= 0) {
1729 status
= FRAME_EVENT_OK
;
1733 text
= &fs
->value
[fs
->state
];
1734 while (text
> fs
->value
&& isspace(*(text
- 1)))
1736 while (text
> fs
->value
&& !isspace(*(text
- 1)))
1738 if (*text
== ASCII_LF
1739 && text
!= &fs
->value
[fs
->state
- 1])
1742 length
= strlen(fs
->value
+ fs
->state
) + 1;
1743 memmove(text
, fs
->value
+ fs
->state
, length
);
1745 fs
->state
= (int) (text
- fs
->value
);
1748 case ACT_EDIT_MOVE_BACKWARD_WORD
:
1749 while (fs
->state
> 0
1750 && isspace(fs
->value
[fs
->state
- 1]))
1752 while (fs
->state
> 0
1753 && !isspace(fs
->value
[fs
->state
- 1]))
1757 case ACT_EDIT_MOVE_FORWARD_WORD
:
1758 while (isspace(fs
->value
[fs
->state
]))
1760 while (fs
->value
[fs
->state
]
1761 && !isspace(fs
->value
[fs
->state
]))
1763 while (isspace(fs
->value
[fs
->state
]))
1767 case ACT_EDIT_AUTO_COMPLETE
:
1768 if (fc
->type
!= FC_FILE
1769 || form_field_is_readonly(fc
)) {
1770 status
= FRAME_EVENT_IGNORED
;
1774 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1777 case ACT_EDIT_CANCEL
:
1778 if (ses
->insert_mode
== INSERT_MODE_ON
)
1779 ses
->insert_mode
= INSERT_MODE_OFF
;
1781 status
= FRAME_EVENT_IGNORED
;
1784 case ACT_EDIT_REDRAW
:
1785 redraw_terminal_cls(ses
->tab
->term
);
1786 status
= FRAME_EVENT_OK
;
1790 if (!check_kbd_textinput_key(ev
)) {
1791 status
= FRAME_EVENT_IGNORED
;
1795 if (form_field_is_readonly(fc
)
1796 #ifndef CONFIG_UTF_8
1797 || strlen(fs
->value
) >= fc
->maxlength
1798 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1799 #endif /* CONFIG_UTF_8 */
1802 status
= FRAME_EVENT_OK
;
1808 /* The charset of the terminal; we assume
1809 * fs->value is in this charset.
1811 int cp
= get_opt_codepage_tree(ses
->tab
->term
->spec
,
1814 text
= u2cp_no_nbsp(get_kbd_key(ev
), cp
);
1815 length
= strlen(text
);
1817 if (strlen(fs
->value
) + length
> fc
->maxlength
1818 || !insert_in_string(&fs
->value
, fs
->state
, text
, length
)) {
1819 status
= FRAME_EVENT_OK
;
1823 fs
->state
+= length
;
1824 if (fc
->type
== FC_PASSWORD
)
1825 fs
->state_cell
+= (is_cp_utf8(cp
) ? 1 : length
);
1826 else if (fc
->type
== FC_TEXTAREA
)
1829 fs
->state_cell
+= (is_cp_utf8(cp
) ? unicode_to_cell(get_kbd_key(ev
)) : length
);
1832 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1833 #endif /* CONFIG_UTF_8 */
1841 get_form_label(struct form_control
*fc
)
1846 return N_("Reset form");
1848 return N_("Harmless button");
1853 if (!fc
->form
->action
) return NULL
;
1855 if (fc
->form
->method
== FORM_METHOD_GET
)
1856 return N_("Submit form to");
1857 return N_("Post form to");
1859 return N_("Radio button");
1861 return N_("Checkbox");
1863 return N_("Select field");
1865 return N_("Text field");
1867 return N_("Text area");
1869 return N_("File upload");
1871 return N_("Password field");
1878 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1879 unsigned char *name
, unsigned char *value
)
1881 add_to_string(string
, ", ");
1882 add_to_string(string
, _(name
, term
));
1884 add_char_to_string(string
, ' ');
1885 add_to_string(string
, value
);
1890 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1892 struct terminal
*term
= ses
->tab
->term
;
1893 struct link
*link
= get_current_link(doc_view
);
1894 struct form_control
*fc
;
1895 unsigned char *label
, *key
;
1900 fc
= get_link_form_control(link
);
1901 label
= get_form_label(fc
);
1902 if (!label
) return NULL
;
1904 if (!init_string(&str
)) return NULL
;
1906 add_to_string(&str
, _(label
, term
));
1908 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1909 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1916 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1918 if (!fs
->value
|| !fs
->value
[0])
1921 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1931 unsigned char *uristring
;
1933 if (form_field_is_readonly(fc
)) {
1934 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1937 /* Should we add info about entering insert mode or add info
1938 * about submitting the form? */
1939 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1940 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1944 if (form_field_is_readonly(fc
))
1945 label
= N_("press %s to navigate");
1947 label
= N_("press %s to edit");
1949 add_to_string(&str
, " (");
1950 add_format_to_string(&str
, _(label
, term
), key
);
1951 add_char_to_string(&str
, ')');
1957 if (fc
->type
== FC_TEXTAREA
)
1962 if (!fc
->form
->action
1963 || (has_form_submit(fc
->form
)
1964 && !get_opt_bool("document.browse.forms.auto_submit")))
1967 uri
= get_uri(fc
->form
->action
, 0);
1970 /* Add the uri with password and post info stripped */
1971 uristring
= get_uri_string(uri
, URI_PUBLIC
);
1974 if (!uristring
) break;
1976 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1978 mem_free(uristring
);
1982 if (fc
->form
->method
== FORM_METHOD_GET
)
1983 label
= N_("press %s to submit to %s");
1985 label
= N_("press %s to post to %s");
1987 add_to_string(&str
, " (");
1988 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
1989 mem_free(uristring
);
1992 add_char_to_string(&str
, ')');
1997 add_char_to_string(&str
, ' ');
2000 /* Add the uri with password and post info stripped */
2001 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
2012 && get_opt_bool("document.browse.accesskey.display")) {
2013 add_to_string(&str
, " (");
2014 add_accesskey_to_string(&str
, link
->accesskey
);
2015 add_char_to_string(&str
, ')');
2022 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
2024 struct session
*ses
= ses_
;
2025 struct document_view
*doc_view
;
2026 int link_number
= *(int *) link_number_
;
2028 mem_free(link_number_
);
2030 assert(term
&& ses
);
2031 if_assert_failed
return;
2033 doc_view
= current_frame(ses
);
2034 if (!doc_view
) return;
2036 assert(doc_view
->vs
&& doc_view
->document
);
2037 if_assert_failed
return;
2039 jump_to_link_number(ses
, doc_view
, link_number
);
2040 refresh_view(ses
, doc_view
, 0);
2044 link_form_menu(struct session
*ses
)
2046 struct document_view
*doc_view
;
2048 struct menu_item
*mi
;
2049 struct form_control
*fc
;
2053 if_assert_failed
return;
2055 doc_view
= current_frame(ses
);
2056 if (!doc_view
) return;
2058 assert(doc_view
->vs
&& doc_view
->document
);
2059 if_assert_failed
return;
2061 link
= get_current_link(doc_view
);
2064 assert(link_is_form(link
));
2066 fc
= get_link_form_control(link
);
2071 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
2074 foreach (fc
, form
->items
) {
2075 unsigned char *text
;
2076 unsigned char *rtext
;
2087 text
= N_("Useless button");
2089 text
= N_("Submit button");
2093 text
= get_form_label(fc
);
2096 link_number
= get_form_control_link(doc_view
->document
, fc
);
2098 || !init_string(&str
))
2102 add_to_string(&str
, _(text
, ses
->tab
->term
));
2105 if (!rtext
) rtext
= fc
->alt
;
2107 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
2108 link_form_menu_func
, intdup(link_number
),
2112 do_menu(ses
->tab
->term
, mi
, ses
, 1);