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/html/parser.h"
33 #include "document/view.h"
34 #include "intl/gettext/libintl.h"
35 #include "formhist/formhist.h"
36 #include "mime/mime.h"
37 #include "osdep/ascii.h"
38 #include "osdep/osdep.h"
39 #include "protocol/uri.h"
40 #include "session/session.h"
41 #include "session/task.h"
42 #include "terminal/kbd.h"
43 #include "terminal/terminal.h"
44 #include "terminal/window.h"
45 #include "util/conv.h"
46 #include "util/error.h"
47 #include "util/file.h"
48 #include "util/memory.h"
49 #include "util/string.h"
50 #include "viewer/action.h"
51 #include "viewer/text/draw.h"
52 #include "viewer/text/form.h"
53 #include "viewer/text/link.h"
54 #include "viewer/text/textarea.h"
55 #include "viewer/text/view.h"
56 #include "viewer/text/vs.h"
59 /* TODO: Some of these (particulary those encoding routines) would feel better
60 * in viewer/common/. --pasky */
62 /** @relates submitted_value */
63 struct submitted_value
*
64 init_submitted_value(unsigned char *name
, unsigned char *value
, enum form_type type
,
65 struct form_control
*fc
, int position
)
67 struct submitted_value
*sv
;
69 sv
= mem_alloc(sizeof(*sv
));
72 sv
->value
= stracpy(value
);
73 if (!sv
->value
) { mem_free(sv
); return NULL
; }
75 sv
->name
= stracpy(name
);
76 if (!sv
->name
) { mem_free(sv
->value
); mem_free(sv
); return NULL
; }
79 sv
->form_control
= fc
;
80 sv
->position
= position
;
85 /** @relates submitted_value */
87 done_submitted_value(struct submitted_value
*sv
)
90 mem_free_if(sv
->value
);
91 mem_free_if(sv
->name
);
96 fixup_select_state(struct form_control
*fc
, struct form_state
*fs
)
101 if_assert_failed
return;
104 && fs
->state
< fc
->nvalues
105 && !strcmp(fc
->values
[fs
->state
], fs
->value
))
108 for (i
= 0; i
< fc
->nvalues
; i
++)
109 if (!strcmp(fc
->values
[i
], fs
->value
)) {
116 mem_free_set(&fs
->value
, stracpy(fc
->nvalues
118 : (unsigned char *) ""));
123 selected_item(struct terminal
*term
, void *item_
, void *ses_
)
125 struct session
*ses
= ses_
;
126 int item
= (long) item_
;
127 struct document_view
*doc_view
;
129 struct form_state
*fs
;
130 struct form_control
*fc
;
133 if_assert_failed
return;
134 doc_view
= current_frame(ses
);
136 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
137 if_assert_failed
return;
139 link
= get_current_link(doc_view
);
140 if (!link
|| link
->type
!= LINK_SELECT
) return;
142 fc
= get_link_form_control(link
);
143 fs
= find_form_state(doc_view
, fc
);
145 if (item
>= 0 && item
< fc
->nvalues
) {
147 mem_free_set(&fs
->value
, stracpy(fc
->values
[item
]));
149 fixup_select_state(fc
, fs
);
152 refresh_view(ses
, doc_view
, 0);
156 init_form_state(struct document_view
*doc_view
,
157 struct form_control
*fc
, struct form_state
*fs
)
159 struct terminal
*term
;
160 int doc_cp
, viewer_cp
;
163 if_assert_failed
return;
165 doc_cp
= doc_view
->document
->cp
;
166 term
= doc_view
->session
->tab
->term
;
167 viewer_cp
= get_opt_codepage_tree(term
->spec
, "charset");
169 mem_free_set(&fs
->value
, NULL
);
174 #ifdef CONFIG_FORMHIST
175 fs
->value
= null_or_stracpy(
176 get_form_history_value(
177 fc
->form
->action
, fc
->name
));
178 #endif /* CONFIG_FORMHIST */
181 if (fs
->value
== NULL
) {
182 fs
->value
= convert_string(
183 get_translation_table(doc_cp
, viewer_cp
),
185 strlen(fc
->default_value
),
187 &fs
->state
, NULL
, NULL
);
189 fs
->state
= fs
->value
? strlen(fs
->value
) : 0;
191 if (fc
->type
== FC_TEXTAREA
)
193 #endif /* CONFIG_UTF8 */
197 fs
->value
= stracpy("");
202 fs
->value
= convert_string(
203 get_translation_table(doc_cp
, viewer_cp
),
205 strlen(fc
->default_value
),
207 &fs
->state
, NULL
, NULL
);
208 fs
->state
= fc
->default_state
;
209 fixup_select_state(fc
, fs
);
213 fs
->state
= fc
->default_state
;
220 /* We don't want to recode hidden fields. */
221 fs
->value
= stracpy(fc
->default_value
);
228 find_form_state(struct document_view
*doc_view
, struct form_control
*fc
)
230 struct view_state
*vs
;
231 struct form_state
*fs
;
234 assert(doc_view
&& doc_view
->vs
&& fc
);
235 if_assert_failed
return NULL
;
240 if (n
>= vs
->form_info_len
) {
243 fs
= mem_align_alloc(&vs
->form_info
, vs
->form_info_len
, nn
, 0);
244 if (!fs
) return NULL
;
246 vs
->form_info_len
= nn
;
248 fs
= &vs
->form_info
[n
];
250 if (fs
->form_view
&& fs
->form_view
->form_num
== fc
->form
->form_num
251 && fs
->g_ctrl_num
== fc
->g_ctrl_num
252 && fs
->position
== fc
->position
253 && fs
->type
== fc
->type
)
256 mem_free_if(fs
->value
);
257 memset(fs
, 0, sizeof(*fs
));
258 fs
->form_view
= find_form_view(doc_view
, fc
->form
);
259 fs
->g_ctrl_num
= fc
->g_ctrl_num
;
260 fs
->position
= fc
->position
;
262 init_form_state(doc_view
, fc
, fs
);
267 struct form_control
*
268 find_form_control(struct document
*document
, struct form_state
*fs
)
270 struct form
*form
= find_form_by_form_view(document
, fs
->form_view
);
271 struct form_control
*fc
;
273 foreach (fc
, form
->items
) {
274 if (fs
->g_ctrl_num
== fc
->g_ctrl_num
275 && fs
->position
== fc
->position
276 && fs
->type
== fc
->type
)
284 find_form_view_in_vs(struct view_state
*vs
, int form_num
)
286 struct form_view
*fv
;
290 foreach (fv
, vs
->forms
)
291 if (fv
->form_num
== form_num
)
294 fv
= mem_calloc(1, sizeof(*fv
));
295 fv
->form_num
= form_num
;
296 add_to_list(vs
->forms
, fv
);
301 find_form_view(struct document_view
*doc_view
, struct form
*form
)
303 return find_form_view_in_vs(doc_view
->vs
, form
->form_num
);
307 find_form_by_form_view(struct document
*document
, struct form_view
*fv
)
311 foreach (form
, document
->forms
) {
312 if (form
->form_num
== fv
->form_num
)
320 get_current_state(struct session
*ses
)
322 struct document_view
*doc_view
;
324 struct form_state
*fs
;
327 if_assert_failed
return -1;
328 doc_view
= current_frame(ses
);
330 assert(doc_view
&& doc_view
->vs
&& doc_view
->document
);
331 if_assert_failed
return -1;
333 link
= get_current_link(doc_view
);
334 if (!link
|| link
->type
!= LINK_SELECT
) return -1;
336 fs
= find_form_state(doc_view
, get_link_form_control(link
));
337 if (fs
) return fs
->state
;
342 draw_form_entry(struct terminal
*term
, struct document_view
*doc_view
,
345 struct form_state
*fs
;
346 struct form_control
*fc
;
347 struct view_state
*vs
;
351 assert(term
&& doc_view
&& doc_view
->document
&& doc_view
->vs
&& link
);
352 if_assert_failed
return;
354 fc
= get_link_form_control(link
);
355 assertm(fc
!= NULL
, "link %d has no form control", (int) (link
- doc_view
->document
->links
));
356 if_assert_failed
return;
358 fs
= find_form_state(doc_view
, fc
);
361 box
= &doc_view
->box
;
368 unsigned char *text
, *end
, *last_in_view
;
370 #endif /* CONFIG_UTF8 */
377 if (!link
->npoints
) break;
379 y
= link
->points
[0].y
+ dy
;
380 if (!row_is_in_box(box
, y
))
383 x
= link
->points
[0].x
+ dx
;
385 if (term
->utf8_cp
) goto utf8
;
386 #endif /* CONFIG_UTF8 */
387 int_bounds(&fs
->vpos
, fs
->state
- fc
->size
+ 1, fs
->state
);
388 len
= strlen(fs
->value
) - fs
->vpos
;
390 for (i
= 0; i
< fc
->size
; i
++, x
++) {
393 if (!col_is_in_box(box
, x
)) continue;
395 if (fs
->value
&& i
>= -fs
->vpos
&& i
< len
)
396 data
= fc
->type
!= FC_PASSWORD
397 ? fs
->value
[i
+ fs
->vpos
] : '*';
401 draw_char_data(term
, x
, y
, data
);
410 if (!text
) text
= "";
412 int_bounds(&fs
->state
, 0, len
);
413 int_bounds(&fs
->vpos
, 0, fs
->state
);
418 for (i
= 0; i
< fc
->size
; ) {
421 unsigned char *maybe_in_view
= text
;
423 data
= utf8_to_unicode(&text
, end
);
424 if (data
== UCS_NO_CHAR
) /* end of string */
426 else if (fc
->type
== FC_PASSWORD
)
429 cells
= unicode_to_cell(data
);
430 if (i
+ cells
<= fc
->size
) {
431 last_in_view
= maybe_in_view
;
432 if (colspan_is_in_box(box
, x
+ i
, cells
)) {
433 /* The character fits completely.
434 * Draw the character, and mark any
435 * further cells with UCS_NO_CHAR. */
436 draw_char_data(term
, x
+ i
, y
, data
);
437 for (cell
= 1; cell
< cells
; cell
++)
438 draw_char_data(term
, x
+ i
+ cell
,
444 /* The character does not fit completely.
445 * Write UCS_ORPHAN_CELL to the cells that
447 for (cell
= 0; cell
< cells
; cell
++) {
448 if (col_is_in_box(box
, x
+ i
+ cell
)
449 && i
+ cell
< fc
->size
)
459 /* The int_bounds calls above ensured that the
460 * insertion point cannot be at the left side
461 * of the scrolled-visible part of the text.
462 * However it can still be at the right side.
463 * Check whether we need to change fs->vpos.
465 * This implementation attempts to follow
467 * - If the insertion point is at the end of
468 * the string, leave at least one empty cell
469 * so that there is a place for the cursor.
470 * - If a character follows the insertion
471 * point, make that character fully visible;
472 * note the character may be double-width.
473 * - If fc->size < 2, it is not possible to
474 * make a double-width character fully
475 * visible. In this case, it is OK if the
476 * output is ugly, but ELinks must not fall
477 * into an infinite loop or crash.
478 * - The length of the string should not affect
479 * how long this function takes. The width
480 * of the widget naturally will.
481 * - Optimize the case where fields are drawn
482 * several times without being modified.
485 * - If the "for i" loop above hit UCS_NO_CHAR,
486 * then there is no need to scroll.
487 * - When the string ends with a double-width
488 * character that fits in only partially,
489 * then text==end, but the field may have
491 if (fs
->value
&& last_in_view
492 && last_in_view
< fs
->value
+ fs
->state
) {
493 unsigned char *ptr
= fs
->value
+ fs
->state
;
494 int cells
= fc
->size
;
495 enum utf8_step how
= (fc
->type
== FC_PASSWORD
)
496 ? UTF8_STEP_CHARACTERS
497 : UTF8_STEP_CELLS_FEWER
;
499 /* The insertion point is at the right
500 * side of the scrolled-visible part
501 * of the text. Decide a new fs->vpos
502 * by counting cells backwards from
503 * @ptr. But first advance @ptr past
504 * the character that follows the
505 * insertion point, so that it will be
506 * fully displayed. If there is no
507 * such character, reserve one cell
508 * for the cursor anyway. */
509 if (utf8_to_unicode(&ptr
, end
) == UCS_NO_CHAR
)
511 ptr
= utf8_step_backward(ptr
, fs
->value
,
514 if (fs
->vpos
!= ptr
- fs
->value
) {
515 fs
->vpos
= ptr
- fs
->value
;
517 goto retry_after_scroll
;
521 #endif /* CONFIG_UTF8 */
523 draw_textarea(term
, fs
, doc_view
, link
);
527 if (link
->npoints
< 2) break;
528 x
= link
->points
[1].x
+ dx
;
529 y
= link
->points
[1].y
+ dy
;
530 if (is_in_box(box
, x
, y
))
531 draw_char_data(term
, x
, y
, fs
->state
? 'X' : ' ');
534 fixup_select_state(fc
, fs
);
535 if (fs
->state
< fc
->nvalues
)
536 s
= fc
->labels
[fs
->state
];
538 /* XXX: when can this happen? --pasky */
541 if (term
->utf8_cp
) goto utf8_select
;
542 #endif /* CONFIG_UTF8 */
543 len
= s
? strlen(s
) : 0;
544 for (i
= 0; i
< link
->npoints
; i
++) {
545 x
= link
->points
[i
].x
+ dx
;
546 y
= link
->points
[i
].y
+ dy
;
547 if (is_in_box(box
, x
, y
))
548 draw_char_data(term
, x
, y
, i
< len
? s
[i
] : '_');
554 end
= strchr(s
, '\0');
555 len
= utf8_ptr2cells(text
, end
);
556 for (i
= 0; i
< link
->npoints
; i
++) {
557 x
= link
->points
[i
].x
+ dx
;
558 y
= link
->points
[i
].y
+ dy
;
559 if (is_in_box(box
, x
, y
)) {
564 data
= utf8_to_unicode(&s
, end
);
565 cell
= unicode_to_cell(data
);
566 if (i
+ 1 < len
&& cell
== 2) {
567 draw_char_data(term
, x
++, y
, data
);
571 } else if (cell
== 2) {
572 data
= UCS_ORPHAN_CELL
;
576 draw_char_data(term
, x
, y
, data
);
580 #endif /* CONFIG_UTF8 */
591 draw_forms(struct terminal
*term
, struct document_view
*doc_view
)
593 struct link
*l1
, *l2
;
595 assert(term
&& doc_view
);
596 if_assert_failed
return;
598 l1
= get_first_link(doc_view
);
599 l2
= get_last_link(doc_view
);
602 assertm(!l1
&& !l2
, "get_first_link == %p, get_last_link == %p", l1
, l2
);
603 /* Return path :-). */
607 struct form_control
*fc
= get_link_form_control(l1
);
610 draw_form_entry(term
, doc_view
, l1
);
616 /** @relates submitted_value */
618 done_submitted_value_list(LIST_OF(struct submitted_value
) *list
)
620 struct submitted_value
*sv
, *svtmp
;
623 if_assert_failed
return;
625 foreach (sv
, *list
) {
628 del_from_list(svtmp
);
629 done_submitted_value(svtmp
);
634 add_submitted_value_to_list(struct form_control
*fc
,
635 struct form_state
*fs
,
636 LIST_OF(struct submitted_value
) *list
)
638 struct submitted_value
*sub
;
643 assert(fc
&& fs
&& list
);
646 position
= fc
->position
;
654 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
655 if (sub
) add_to_list(*list
, sub
);
660 if (!fs
->state
) break;
667 sub
= init_submitted_value(name
, fs
->value
, type
, fc
,
669 if (sub
) add_to_list(*list
, sub
);
673 if (!fc
->nvalues
) break;
675 fixup_select_state(fc
, fs
);
676 sub
= init_submitted_value(name
, fs
->value
, type
, fc
, position
);
677 if (sub
) add_to_list(*list
, sub
);
681 name
= straconcat(fc
->name
, ".x", (unsigned char *) NULL
);
683 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
685 if (sub
) add_to_list(*list
, sub
);
687 name
= straconcat(fc
->name
, ".y", (unsigned char *) NULL
);
689 sub
= init_submitted_value(name
, "0", type
, fc
, position
);
691 if (sub
) add_to_list(*list
, sub
);
698 sort_submitted_values(LIST_OF(struct submitted_value
) *list
)
701 struct submitted_value
*sub
;
704 foreach (sub
, *list
) if (list_has_next(*list
, sub
))
705 if (sub
->next
->position
< sub
->position
) {
706 struct submitted_value
*next
= sub
->next
;
709 add_at_pos(next
, sub
);
714 foreachback (sub
, *list
) if (list_has_next(*list
, sub
))
715 if (sub
->next
->position
< sub
->position
) {
716 struct submitted_value
*next
= sub
->next
;
719 add_at_pos(next
, sub
);
729 get_successful_controls(struct document_view
*doc_view
,
730 struct form_control
*fc
,
731 LIST_OF(struct submitted_value
) *list
)
733 struct form_control
*fc2
;
735 assert(doc_view
&& fc
&& fc
->form
&& list
);
736 if_assert_failed
return;
738 foreach (fc2
, fc
->form
->items
) {
739 if (((fc2
->type
!= FC_SUBMIT
&&
740 fc2
->type
!= FC_IMAGE
&&
741 fc2
->type
!= FC_RESET
&&
742 fc2
->type
!= FC_BUTTON
) || fc2
== fc
)
743 && fc2
->name
&& fc2
->name
[0]) {
744 struct form_state
*fs
= find_form_state(doc_view
, fc2
);
748 add_submitted_value_to_list(fc2
, fs
, list
);
752 sort_submitted_values(list
);
756 encode_controls(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
757 int cp_from
, int cp_to
)
759 struct submitted_value
*sv
;
760 struct conv_table
*convert_table
= NULL
;
764 if_assert_failed
return;
767 unsigned char *p2
= NULL
;
770 add_char_to_string(data
, '&');
774 encode_uri_string(data
, sv
->name
, strlen(sv
->name
), 1);
775 add_char_to_string(data
, '=');
777 /* Convert back to original encoding (see html_form_control()
778 * for the original recoding). */
779 if (sv
->type
== FC_TEXTAREA
) {
782 p
= encode_textarea(sv
);
785 convert_table
= get_translation_table(cp_from
, cp_to
);
787 p2
= convert_string(convert_table
, p
,
788 strlen(p
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
791 } else if (sv
->type
== FC_TEXT
||
792 sv
->type
== FC_PASSWORD
) {
794 convert_table
= get_translation_table(cp_from
, cp_to
);
796 p2
= convert_string(convert_table
, sv
->value
,
797 strlen(sv
->value
), -1, CSM_FORM
, NULL
, NULL
, NULL
);
799 p2
= stracpy(sv
->value
);
803 encode_uri_string(data
, p2
, strlen(p2
), 1);
811 #define BOUNDARY_LENGTH 32
812 #define realloc_bound_ptrs(bptrs, bptrs_size) \
813 mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
815 struct boundary_info
{
818 unsigned char string
[BOUNDARY_LENGTH
];
821 /** @relates boundary_info */
823 init_boundary(struct boundary_info
*boundary
)
825 memset(boundary
, 0, sizeof(*boundary
));
826 memset(boundary
->string
, '0', BOUNDARY_LENGTH
);
829 /** Add boundary to string and save the offset
830 * @relates boundary_info */
832 add_boundary(struct string
*data
, struct boundary_info
*boundary
)
834 add_to_string(data
, "--");
836 if (realloc_bound_ptrs(&boundary
->offsets
, boundary
->count
))
837 boundary
->offsets
[boundary
->count
++] = data
->length
;
839 add_bytes_to_string(data
, boundary
->string
, BOUNDARY_LENGTH
);
842 /** @relates boundary_info */
843 static inline unsigned char *
844 increment_boundary_counter(struct boundary_info
*boundary
)
848 /* This is just a decimal string incrementation */
849 for (j
= BOUNDARY_LENGTH
- 1; j
>= 0; j
--) {
850 if (boundary
->string
[j
]++ < '9')
851 return boundary
->string
;
853 boundary
->string
[j
] = '0';
856 INTERNAL("Form data boundary counter overflow");
861 /** @relates boundary_info */
863 check_boundary(struct string
*data
, struct boundary_info
*boundary
)
865 unsigned char *bound
= boundary
->string
;
868 /* Search between all boundaries. There is a starting and an ending
869 * boundary so only check the range of chars after the current offset
870 * and before the next offset. If some string in the form data matches
871 * the boundary string it is changed. */
872 for (i
= 0; i
< boundary
->count
- 1; i
++) {
873 /* Start after the boundary string and also jump past the
874 * "\r\nContent-Disposition: form-data; name=\"" string added
875 * before any form data. */
876 int start_offset
= boundary
->offsets
[i
] + BOUNDARY_LENGTH
+ 40;
878 /* End so that there is atleast BOUNDARY_LENGTH chars to
879 * compare. Subtract 2 char because there is no need to also
880 * compare the '--' prefix that is part of the boundary. */
881 int end_offset
= boundary
->offsets
[i
+ 1] - BOUNDARY_LENGTH
- 2;
882 unsigned char *pos
= data
->source
+ start_offset
;
883 unsigned char *end
= data
->source
+ end_offset
;
885 for (; pos
<= end
; pos
++) {
886 if (memcmp(pos
, bound
, BOUNDARY_LENGTH
))
889 /* If incrementing causes overflow bail out. There is
890 * no need to reset the boundary string with '0' since
891 * that is already done when incrementing. */
892 if (!increment_boundary_counter(boundary
))
895 /* Else start checking all boundaries using the new
902 /* Now update all the boundaries with the unique boundary string */
903 for (i
= 0; i
< boundary
->count
; i
++)
904 memcpy(data
->source
+ boundary
->offsets
[i
], bound
, BOUNDARY_LENGTH
);
907 /** @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
909 encode_multipart(struct session
*ses
, LIST_OF(struct submitted_value
) *l
,
911 struct boundary_info
*boundary
, int cp_from
, int cp_to
)
913 struct conv_table
*convert_table
= NULL
;
914 struct submitted_value
*sv
;
916 assert(ses
&& l
&& data
&& boundary
);
917 if_assert_failed
return;
919 init_boundary(boundary
);
922 add_boundary(data
, boundary
);
923 add_crlf_to_string(data
);
925 /** @bug FIXME: name is not encoded.
927 * multipart/form-data contains a series of parts.
928 * Each part is expected to contain a content-disposition
929 * header where the value is "form-data" and a name attribute
930 * specifies the field name within the form,
931 * e.g., 'content-disposition: form-data; name="xxxxx"',
932 * where xxxxx is the field name corresponding to that field.
933 * Field names originally in non-ASCII character sets may be
934 * encoded using the method outlined in RFC 1522. */
935 add_to_string(data
, "Content-Disposition: form-data; name=\"");
936 add_to_string(data
, sv
->name
);
937 add_char_to_string(data
, '"');
939 if (sv
->type
== FC_FILE
) {
940 #define F_BUFLEN 1024
942 unsigned char buffer
[F_BUFLEN
];
943 unsigned char *extension
;
945 add_to_string(data
, "; filename=\"");
946 add_to_string(data
, get_filename_position(sv
->value
));
947 /* It sends bad data if the file name contains ", but
948 Netscape does the same */
949 /* FIXME: We should follow RFCs 1522, 1867,
950 * 2047 (updated by rfc 2231), to provide correct support
951 * for non-ASCII and special characters in values. --Zas */
952 add_char_to_string(data
, '"');
954 /* Add a Content-Type header if the type is configured */
955 extension
= strrchr(sv
->value
, '.');
957 unsigned char *type
= get_extension_content_type(extension
);
960 add_crlf_to_string(data
);
961 add_to_string(data
, "Content-Type: ");
962 add_to_string(data
, type
);
967 add_crlf_to_string(data
);
968 add_crlf_to_string(data
);
971 unsigned char *filename
;
973 if (get_cmd_opt_bool("anonymous")) {
978 /* FIXME: DO NOT COPY FILE IN MEMORY !! --Zas */
979 filename
= expand_tilde(sv
->value
);
980 if (!filename
) goto encode_error
;
982 fh
= open(filename
, O_RDONLY
);
985 if (fh
== -1) goto encode_error
;
988 ssize_t rd
= safe_read(fh
, buffer
, F_BUFLEN
);
996 add_bytes_to_string(data
, buffer
, rd
);
1006 add_crlf_to_string(data
);
1007 add_crlf_to_string(data
);
1009 /* Convert back to original encoding (see
1010 * html_form_control() for the original recoding). */
1011 if (sv
->type
== FC_TEXT
|| sv
->type
== FC_PASSWORD
||
1012 sv
->type
== FC_TEXTAREA
) {
1016 convert_table
= get_translation_table(cp_from
,
1019 p
= convert_string(convert_table
, sv
->value
,
1020 strlen(sv
->value
), -1, CSM_FORM
, NULL
,
1023 add_to_string(data
, p
);
1027 add_to_string(data
, sv
->value
);
1031 add_crlf_to_string(data
);
1035 add_boundary(data
, boundary
);
1036 add_to_string(data
, "--\r\n");
1038 check_boundary(data
, boundary
);
1040 mem_free_if(boundary
->offsets
);
1044 mem_free_if(boundary
->offsets
);
1047 /* XXX: This error message should move elsewhere. --Zas */
1048 info_box(ses
->tab
->term
, MSGBOX_FREE_TEXT
,
1049 N_("Error while posting form"), ALIGN_CENTER
,
1050 msg_text(ses
->tab
->term
, N_("Could not load file %s: %s"),
1051 sv
->value
, strerror(errno
)));
1055 encode_newlines(struct string
*string
, unsigned char *data
)
1057 for (; *data
; data
++) {
1058 if (*data
== '\n' || *data
== '\r') {
1059 unsigned char buffer
[3];
1063 buffer
[1] = hx((((int) *data
) & 0xF0) >> 4);
1064 buffer
[2] = hx(((int) *data
) & 0xF);
1065 add_bytes_to_string(string
, buffer
, 3);
1067 add_char_to_string(string
, *data
);
1073 encode_text_plain(LIST_OF(struct submitted_value
) *l
, struct string
*data
,
1074 int cp_from
, int cp_to
)
1076 struct submitted_value
*sv
;
1077 struct conv_table
*convert_table
= get_translation_table(cp_from
, cp_to
);
1080 if_assert_failed
return;
1083 unsigned char *area51
= NULL
;
1084 unsigned char *value
= sv
->value
;
1086 add_to_string(data
, sv
->name
);
1087 add_char_to_string(data
, '=');
1091 value
= area51
= encode_textarea(sv
);
1096 /* Convert back to original encoding (see
1097 * html_form_control() for the original recoding). */
1098 value
= convert_string(convert_table
, value
,
1099 strlen(value
), -1, CSM_FORM
,
1102 /* Falling right through to free that textarea stuff */
1103 mem_free_if(area51
);
1105 /* Did the conversion fail? */
1108 encode_newlines(data
, value
);
1110 /* Free if we did convert something */
1111 if (value
!= sv
->value
) mem_free(value
);
1114 add_crlf_to_string(data
);
1119 do_reset_form(struct document_view
*doc_view
, struct form
*form
)
1121 struct form_control
*fc
;
1123 assert(doc_view
&& doc_view
->document
);
1124 if_assert_failed
return;
1126 foreach (fc
, form
->items
) {
1127 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1129 if (fs
) init_form_state(doc_view
, fc
, fs
);
1133 enum frame_event_status
1134 reset_form(struct session
*ses
, struct document_view
*doc_view
, int a
)
1136 struct link
*link
= get_current_link(doc_view
);
1138 if (!link
) return FRAME_EVENT_OK
;
1140 do_reset_form(doc_view
, get_link_form_control(link
)->form
);
1141 draw_forms(ses
->tab
->term
, doc_view
);
1143 /* Could be the refresh return value and then ditch the draw_forms()
1145 return FRAME_EVENT_OK
;
1149 get_form_uri(struct session
*ses
, struct document_view
*doc_view
,
1150 struct form_control
*fc
)
1152 struct boundary_info boundary
;
1153 INIT_LIST_OF(struct submitted_value
, submit
);
1160 assert(ses
&& ses
->tab
&& ses
->tab
->term
);
1161 if_assert_failed
return NULL
;
1162 assert(doc_view
&& doc_view
->document
&& fc
&& fc
->form
);
1163 if_assert_failed
return NULL
;
1167 if (fc
->type
== FC_RESET
) {
1168 do_reset_form(doc_view
, form
);
1173 || !init_string(&data
))
1176 get_successful_controls(doc_view
, fc
, &submit
);
1178 cp_from
= get_opt_codepage_tree(ses
->tab
->term
->spec
, "charset");
1179 cp_to
= doc_view
->document
->cp
;
1180 switch (form
->method
) {
1181 case FORM_METHOD_GET
:
1182 case FORM_METHOD_POST
:
1183 encode_controls(&submit
, &data
, cp_from
, cp_to
);
1186 case FORM_METHOD_POST_MP
:
1187 encode_multipart(ses
, &submit
, &data
, &boundary
, cp_from
, cp_to
);
1190 case FORM_METHOD_POST_TEXT_PLAIN
:
1191 encode_text_plain(&submit
, &data
, cp_from
, cp_to
);
1194 #ifdef CONFIG_FORMHIST
1195 /* XXX: We check data.source here because a NULL value can indicate
1196 * not only a memory allocation failure, but also an error reading
1197 * a file that is to be uploaded. TODO: Distinguish between
1198 * these two classes of errors (is it worth it?). -- Miciah */
1200 && get_opt_bool("document.browse.forms.show_formhist"))
1201 memorize_form(ses
, &submit
, form
);
1204 done_submitted_value_list(&submit
);
1207 || !init_string(&go
)) {
1212 switch (form
->method
) {
1213 case FORM_METHOD_GET
:
1215 unsigned char *pos
= strchr(form
->action
, '#');
1218 add_bytes_to_string(&go
, form
->action
, pos
- form
->action
);
1220 add_to_string(&go
, form
->action
);
1223 if (strchr(go
.source
, '?'))
1224 add_char_to_string(&go
, '&');
1226 add_char_to_string(&go
, '?');
1228 add_string_to_string(&go
, &data
);
1230 if (pos
) add_to_string(&go
, pos
);
1233 case FORM_METHOD_POST
:
1234 case FORM_METHOD_POST_MP
:
1235 case FORM_METHOD_POST_TEXT_PLAIN
:
1237 /* Note that we end content type here by a simple '\n',
1238 * replaced later by correct '\r\n' in http_send_header(). */
1241 add_to_string(&go
, form
->action
);
1242 add_char_to_string(&go
, POST_CHAR
);
1243 if (form
->method
== FORM_METHOD_POST
) {
1244 add_to_string(&go
, "application/x-www-form-urlencoded\n");
1246 } else if (form
->method
== FORM_METHOD_POST_TEXT_PLAIN
) {
1247 /* Dunno about this one but we don't want the full
1248 * hextcat thingy. --jonas */
1249 add_to_string(&go
, "text/plain\n");
1250 add_to_string(&go
, data
.source
);
1254 add_to_string(&go
, "multipart/form-data; boundary=");
1255 add_bytes_to_string(&go
, boundary
.string
, BOUNDARY_LENGTH
);
1256 add_char_to_string(&go
, '\n');
1259 for (i
= 0; i
< data
.length
; i
++) {
1262 ulonghexcat(p
, NULL
, (int) data
.source
[i
], 2, '0', 0);
1263 add_to_string(&go
, p
);
1270 uri
= get_uri(go
.source
, 0);
1272 if (uri
) uri
->form
= 1;
1277 #undef BOUNDARY_LENGTH
1280 enum frame_event_status
1281 submit_form(struct session
*ses
, struct document_view
*doc_view
, int do_reload
)
1283 goto_current_link(ses
, doc_view
, do_reload
);
1284 return FRAME_EVENT_OK
;
1288 submit_given_form(struct session
*ses
, struct document_view
*doc_view
,
1289 struct form
*form
, int do_reload
)
1291 /* Added support for submitting forms in hidden
1292 * links in 1.285, commented code can safely be removed once we have made sure the new
1293 * code does the right thing. */
1296 struct document
*document
= doc_view
->document
;
1299 for (link
= 0; link
< document
->nlinks
; link
++) {
1300 struct form_control
*fc
= get_link_form_control(&document
->links
[link
]);
1302 if (fc
&& fc
->form
== form
) {
1303 doc_view
->vs
->current_link
= link
;
1304 submit_form(ses
, doc_view
, 0);
1309 if (!list_empty(form
->items
)) {
1310 struct form_control
*fc
= (struct form_control
*)form
->items
.next
;
1312 enum cache_mode mode
= do_reload
? CACHE_MODE_FORCE_RELOAD
: CACHE_MODE_NORMAL
;
1315 uri
= get_form_uri(ses
, doc_view
, fc
);
1317 goto_uri_frame(ses
, uri
, form
->target
, mode
);
1323 auto_submit_form(struct session
*ses
)
1325 struct document
*document
= ses
->doc_view
->document
;
1327 if (!list_empty(document
->forms
))
1328 submit_given_form(ses
, ses
->doc_view
, document
->forms
.next
, 0);
1334 set_file_form_state(struct terminal
*term
, void *filename_
, void *fs_
)
1336 unsigned char *filename
= filename_
;
1337 struct form_state
*fs
= fs_
;
1339 /* The menu code doesn't free the filename data */
1340 mem_free_set(&fs
->value
, filename
);
1341 fs
->state
= strlen(filename
);
1342 redraw_terminal(term
);
1347 file_form_menu(struct terminal
*term
, void *path_
, void *fs_
)
1349 unsigned char *path
= path_
;
1350 struct form_state
*fs
= fs_
;
1352 /* FIXME: It doesn't work for ../../ */
1354 int valuelen
= strlen(fs
->value
);
1355 int pathlen
= strlen(path
);
1356 int no_elevator
= 0;
1358 /* Don't add elevators for subdirs menus */
1359 /* It is not perfect at all because fs->value is not updated for each
1360 * newly opened file menu. Maybe it should be dropped. */
1361 for (; valuelen
< pathlen
; valuelen
++) {
1362 if (dir_sep(path
[valuelen
- 1])) {
1369 auto_complete_file(term
, 0 /* no_elevator */, path
,
1370 set_file_form_state
,
1371 file_form_menu
, fs
);
1375 enum frame_event_status
1376 field_op(struct session
*ses
, struct document_view
*doc_view
,
1377 struct link
*link
, struct term_event
*ev
)
1379 struct form_control
*fc
;
1380 struct form_state
*fs
;
1381 enum edit_action action_id
;
1382 unsigned char *text
;
1384 enum frame_event_status status
= FRAME_EVENT_REFRESH
;
1386 const unsigned char *ctext
;
1387 int utf8
= ses
->tab
->term
->utf8_cp
;
1388 #endif /* CONFIG_UTF8 */
1390 assert(ses
&& doc_view
&& link
&& ev
);
1391 if_assert_failed
return FRAME_EVENT_OK
;
1393 fc
= get_link_form_control(link
);
1394 assertm(fc
!= NULL
, "link has no form control");
1395 if_assert_failed
return FRAME_EVENT_OK
;
1397 if (fc
->mode
== FORM_MODE_DISABLED
|| ev
->ev
!= EVENT_KBD
1398 || ses
->insert_mode
== INSERT_MODE_OFF
)
1399 return FRAME_EVENT_IGNORED
;
1401 action_id
= kbd_action(KEYMAP_EDIT
, ev
, NULL
);
1403 fs
= find_form_state(doc_view
, fc
);
1404 if (!fs
|| !fs
->value
) return FRAME_EVENT_OK
;
1406 switch (action_id
) {
1409 if (fc
->type
== FC_TEXTAREA
) {
1410 status
= textarea_op_left(fs
, fc
, utf8
);
1414 unsigned char *new_value
;
1416 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1417 fs
->state
= new_value
- fs
->value
;
1419 #endif /* CONFIG_UTF8 */
1420 fs
->state
= int_max(fs
->state
- 1, 0);
1422 case ACT_EDIT_RIGHT
:
1424 if (fc
->type
== FC_TEXTAREA
) {
1425 status
= textarea_op_right(fs
, fc
, utf8
);
1429 unsigned char *text
= fs
->value
+ fs
->state
;
1430 unsigned char *end
= strchr(text
, '\0');
1432 utf8_to_unicode(&text
, end
);
1433 fs
->state
= (int)(text
- fs
->value
);
1435 #endif /* CONFIG_UTF8 */
1436 fs
->state
= int_min(fs
->state
+ 1, strlen(fs
->value
));
1440 if (fc
->type
== FC_TEXTAREA
) {
1441 status
= textarea_op_home(fs
, fc
, utf8
);
1446 if (fc
->type
== FC_TEXTAREA
) {
1447 status
= textarea_op_home(fs
, fc
);
1452 #endif /* CONFIG_UTF8 */
1455 if (fc
->type
!= FC_TEXTAREA
)
1456 status
= FRAME_EVENT_IGNORED
;
1459 status
= textarea_op_up(fs
, fc
, utf8
);
1461 status
= textarea_op_up(fs
, fc
);
1462 #endif /* CONFIG_UTF8 */
1465 if (fc
->type
!= FC_TEXTAREA
)
1466 status
= FRAME_EVENT_IGNORED
;
1469 status
= textarea_op_down(fs
, fc
, utf8
);
1471 status
= textarea_op_down(fs
, fc
);
1472 #endif /* CONFIG_UTF8 */
1475 if (fc
->type
== FC_TEXTAREA
) {
1477 status
= textarea_op_end(fs
, fc
, utf8
);
1479 status
= textarea_op_end(fs
, fc
);
1480 #endif /* CONFIG_UTF8 */
1482 fs
->state
= strlen(fs
->value
);
1485 case ACT_EDIT_BEGINNING_OF_BUFFER
:
1486 if (fc
->type
== FC_TEXTAREA
) {
1488 status
= textarea_op_bob(fs
, fc
, utf8
);
1491 status
= textarea_op_bob(fs
, fc
);
1492 #endif /* CONFIG_UTF8 */
1497 case ACT_EDIT_END_OF_BUFFER
:
1498 if (fc
->type
== FC_TEXTAREA
) {
1500 status
= textarea_op_eob(fs
, fc
, utf8
);
1502 status
= textarea_op_eob(fs
, fc
);
1503 #endif /* CONFIG_UTF8 */
1505 fs
->state
= strlen(fs
->value
);
1508 case ACT_EDIT_OPEN_EXTERNAL
:
1509 if (form_field_is_readonly(fc
))
1510 status
= FRAME_EVENT_IGNORED
;
1511 else if (fc
->type
== FC_TEXTAREA
)
1512 textarea_edit(0, ses
->tab
->term
, fs
, doc_view
, link
);
1514 case ACT_EDIT_COPY_CLIPBOARD
:
1515 set_clipboard_text(fs
->value
);
1516 status
= FRAME_EVENT_OK
;
1518 case ACT_EDIT_CUT_CLIPBOARD
:
1519 set_clipboard_text(fs
->value
);
1520 if (!form_field_is_readonly(fc
))
1524 if (fc
->type
== FC_TEXTAREA
)
1526 #endif /* CONFIG_UTF8 */
1528 case ACT_EDIT_PASTE_CLIPBOARD
:
1529 if (form_field_is_readonly(fc
)) break;
1531 text
= get_clipboard_text();
1534 length
= strlen(text
);
1535 if (length
<= fc
->maxlength
) {
1536 unsigned char *v
= mem_realloc(fs
->value
, length
+ 1);
1540 memmove(v
, text
, length
+ 1);
1541 fs
->state
= strlen(fs
->value
);
1543 if (utf8
&& fc
->type
== FC_TEXTAREA
)
1545 #endif /* CONFIG_UTF8 */
1550 case ACT_EDIT_ENTER
:
1551 if (fc
->type
== FC_TEXTAREA
) {
1553 status
= textarea_op_enter(fs
, fc
, utf8
);
1555 status
= textarea_op_enter(fs
, fc
);
1556 #endif /* CONFIG_UTF8 */
1560 /* Set status to ok if either it is not possible to
1561 * submit the form or the posting fails. */
1562 /* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
1563 if ((has_form_submit(fc
->form
)
1564 && !get_opt_bool("document.browse.forms.auto_submit"))
1565 || goto_current_link(ses
, doc_view
, 0)) {
1566 if (ses
->insert_mode
== INSERT_MODE_ON
)
1567 ses
->insert_mode
= INSERT_MODE_OFF
;
1568 status
= FRAME_EVENT_OK
;
1571 case ACT_EDIT_BACKSPACE
:
1572 if (form_field_is_readonly(fc
)) {
1573 status
= FRAME_EVENT_IGNORED
;
1578 status
= FRAME_EVENT_OK
;
1583 int old_state
= fs
->state
;
1584 unsigned char *new_value
;
1586 new_value
= utf8_prevchar(fs
->value
+ fs
->state
, 1, fs
->value
);
1587 fs
->state
= new_value
- fs
->value
;
1589 if (old_state
!= fs
->state
) {
1590 if (fc
->type
== FC_TEXTAREA
)
1592 length
= strlen(fs
->value
+ old_state
) + 1;
1593 memmove(new_value
, fs
->value
+ old_state
, length
);
1596 #endif /* CONFIG_UTF8 */
1598 length
= strlen(fs
->value
+ fs
->state
) + 1;
1599 text
= fs
->value
+ fs
->state
;
1601 memmove(text
- 1, text
, length
);
1605 case ACT_EDIT_DELETE
:
1606 if (form_field_is_readonly(fc
)) {
1607 status
= FRAME_EVENT_IGNORED
;
1611 length
= strlen(fs
->value
);
1612 if (fs
->state
>= length
) {
1613 status
= FRAME_EVENT_OK
;
1618 unsigned char *end
= fs
->value
+ length
;
1619 unsigned char *text
= fs
->value
+ fs
->state
;
1620 unsigned char *old
= text
;
1622 utf8_to_unicode(&text
, end
);
1625 (int)(end
- text
) + 1);
1629 #endif /* CONFIG_UTF8 */
1630 text
= fs
->value
+ fs
->state
;
1632 memmove(text
, text
+ 1, length
- fs
->state
);
1634 case ACT_EDIT_KILL_TO_BOL
:
1635 if (form_field_is_readonly(fc
)) {
1636 status
= FRAME_EVENT_IGNORED
;
1640 if (fs
->state
<= 0) {
1641 status
= FRAME_EVENT_OK
;
1645 text
= memrchr(fs
->value
, ASCII_LF
, fs
->state
);
1647 /* Leave the new-line character if it does not
1648 * immediately precede the cursor. */
1649 if (text
!= &fs
->value
[fs
->state
- 1])
1655 length
= strlen(fs
->value
+ fs
->state
) + 1;
1656 memmove(text
, fs
->value
+ fs
->state
, length
);
1658 fs
->state
= (int) (text
- fs
->value
);
1661 if (fc
->type
== FC_TEXTAREA
)
1664 #endif /* CONFIG_UTF8 */
1666 case ACT_EDIT_KILL_TO_EOL
:
1667 if (form_field_is_readonly(fc
)) {
1668 status
= FRAME_EVENT_IGNORED
;
1672 if (!fs
->value
[fs
->state
]) {
1673 status
= FRAME_EVENT_OK
;
1677 text
= strchr(fs
->value
+ fs
->state
, ASCII_LF
);
1679 fs
->value
[fs
->state
] = '\0';
1683 if (fs
->value
[fs
->state
] == ASCII_LF
)
1686 memmove(fs
->value
+ fs
->state
, text
, strlen(text
) + 1);
1689 case ACT_EDIT_KILL_WORD_BACK
:
1690 if (form_field_is_readonly(fc
)) {
1691 status
= FRAME_EVENT_IGNORED
;
1695 if (fs
->state
<= 0) {
1696 status
= FRAME_EVENT_OK
;
1700 text
= &fs
->value
[fs
->state
];
1701 while (text
> fs
->value
&& isspace(*(text
- 1)))
1703 while (text
> fs
->value
&& !isspace(*(text
- 1)))
1705 if (*text
== ASCII_LF
1706 && text
!= &fs
->value
[fs
->state
- 1])
1709 length
= strlen(fs
->value
+ fs
->state
) + 1;
1710 memmove(text
, fs
->value
+ fs
->state
, length
);
1712 fs
->state
= (int) (text
- fs
->value
);
1715 case ACT_EDIT_MOVE_BACKWARD_WORD
:
1716 while (fs
->state
> 0
1717 && isspace(fs
->value
[fs
->state
- 1]))
1719 while (fs
->state
> 0
1720 && !isspace(fs
->value
[fs
->state
- 1]))
1724 case ACT_EDIT_MOVE_FORWARD_WORD
:
1725 while (isspace(fs
->value
[fs
->state
]))
1727 while (fs
->value
[fs
->state
]
1728 && !isspace(fs
->value
[fs
->state
]))
1730 while (isspace(fs
->value
[fs
->state
]))
1734 case ACT_EDIT_AUTO_COMPLETE
:
1735 if (fc
->type
!= FC_FILE
1736 || form_field_is_readonly(fc
)) {
1737 status
= FRAME_EVENT_IGNORED
;
1741 file_form_menu(ses
->tab
->term
, fs
->value
, fs
);
1744 case ACT_EDIT_CANCEL
:
1745 if (ses
->insert_mode
== INSERT_MODE_ON
)
1746 ses
->insert_mode
= INSERT_MODE_OFF
;
1748 status
= FRAME_EVENT_IGNORED
;
1751 case ACT_EDIT_REDRAW
:
1752 redraw_terminal_cls(ses
->tab
->term
);
1753 status
= FRAME_EVENT_OK
;
1757 if (!check_kbd_textinput_key(ev
)) {
1758 status
= FRAME_EVENT_IGNORED
;
1762 if (form_field_is_readonly(fc
)
1764 || strlen(fs
->value
) >= fc
->maxlength
1765 || !insert_in_string(&fs
->value
, fs
->state
, "?", 1)
1766 #endif /* CONFIG_UTF8 */
1769 status
= FRAME_EVENT_OK
;
1774 /* fs->value is in the charset of the terminal. */
1775 ctext
= u2cp_no_nbsp(get_kbd_key(ev
),
1776 get_opt_codepage_tree(ses
->tab
->term
->spec
,
1778 length
= strlen(ctext
);
1780 if (strlen(fs
->value
) + length
> fc
->maxlength
1781 || !insert_in_string(&fs
->value
, fs
->state
, ctext
, length
)) {
1782 status
= FRAME_EVENT_OK
;
1786 fs
->state
+= length
;
1787 if (fc
->type
== FC_TEXTAREA
)
1790 fs
->value
[fs
->state
++] = get_kbd_key(ev
);
1791 #endif /* CONFIG_UTF8 */
1798 static unsigned char *
1799 get_form_label(struct form_control
*fc
)
1804 return N_("Reset form");
1806 return N_("Harmless button");
1811 if (!fc
->form
->action
) return NULL
;
1813 if (fc
->form
->method
== FORM_METHOD_GET
)
1814 return N_("Submit form to");
1815 return N_("Post form to");
1817 return N_("Radio button");
1819 return N_("Checkbox");
1821 return N_("Select field");
1823 return N_("Text field");
1825 return N_("Text area");
1827 return N_("File upload");
1829 return N_("Password field");
1836 add_form_attr_to_string(struct string
*string
, struct terminal
*term
,
1837 unsigned char *name
, unsigned char *value
)
1839 add_to_string(string
, ", ");
1840 add_to_string(string
, _(name
, term
));
1842 add_char_to_string(string
, ' ');
1843 add_to_string(string
, value
);
1848 get_form_info(struct session
*ses
, struct document_view
*doc_view
)
1850 struct terminal
*term
= ses
->tab
->term
;
1851 struct link
*link
= get_current_link(doc_view
);
1852 struct form_control
*fc
;
1853 unsigned char *label
, *key
;
1858 fc
= get_link_form_control(link
);
1859 label
= get_form_label(fc
);
1860 if (!label
) return NULL
;
1862 if (!init_string(&str
)) return NULL
;
1864 add_to_string(&str
, _(label
, term
));
1866 if (link
->type
!= LINK_BUTTON
&& fc
->name
&& fc
->name
[0]) {
1867 add_form_attr_to_string(&str
, term
, N_("name"), fc
->name
);
1874 struct form_state
*fs
= find_form_state(doc_view
, fc
);
1876 if (!fs
->value
|| !fs
->value
[0])
1879 add_form_attr_to_string(&str
, term
, N_("value"), fs
->value
);
1889 unsigned char *uristring
;
1891 if (form_field_is_readonly(fc
)) {
1892 add_form_attr_to_string(&str
, term
, N_("read only"), NULL
);
1895 /* Should we add info about entering insert mode or add info
1896 * about submitting the form? */
1897 if (ses
->insert_mode
== INSERT_MODE_OFF
) {
1898 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1902 if (form_field_is_readonly(fc
))
1903 label
= N_("press %s to navigate");
1905 label
= N_("press %s to edit");
1907 add_to_string(&str
, " (");
1908 add_format_to_string(&str
, _(label
, term
), key
);
1909 add_char_to_string(&str
, ')');
1915 if (fc
->type
== FC_TEXTAREA
)
1920 if (!fc
->form
->action
1921 || (has_form_submit(fc
->form
)
1922 && !get_opt_bool("document.browse.forms.auto_submit")))
1925 uri
= get_uri(fc
->form
->action
, 0);
1928 /* Add the uri with password and post info stripped */
1929 uristring
= get_uri_string(uri
, URI_PUBLIC
);
1932 if (!uristring
) break;
1934 key
= get_keystroke(ACT_EDIT_ENTER
, KEYMAP_EDIT
);
1936 mem_free(uristring
);
1940 if (fc
->form
->method
== FORM_METHOD_GET
)
1941 label
= N_("press %s to submit to %s");
1943 label
= N_("press %s to post to %s");
1945 add_to_string(&str
, " (");
1946 add_format_to_string(&str
, _(label
, term
), key
, uristring
);
1947 mem_free(uristring
);
1950 add_char_to_string(&str
, ')');
1955 add_char_to_string(&str
, ' ');
1958 /* Add the uri with password and post info stripped */
1959 add_string_uri_to_string(&str
, fc
->form
->action
, URI_PUBLIC
);
1970 && get_opt_bool("document.browse.accesskey.display")) {
1971 add_to_string(&str
, " (");
1972 add_accesskey_to_string(&str
, link
->accesskey
);
1973 add_char_to_string(&str
, ')');
1980 link_form_menu_func(struct terminal
*term
, void *link_number_
, void *ses_
)
1982 struct session
*ses
= ses_
;
1983 struct document_view
*doc_view
;
1984 int link_number
= *(int *) link_number_
;
1986 mem_free(link_number_
);
1988 assert(term
&& ses
);
1989 if_assert_failed
return;
1991 doc_view
= current_frame(ses
);
1992 if (!doc_view
) return;
1994 assert(doc_view
->vs
&& doc_view
->document
);
1995 if_assert_failed
return;
1997 jump_to_link_number(ses
, doc_view
, link_number
);
1998 refresh_view(ses
, doc_view
, 0);
2002 link_form_menu(struct session
*ses
)
2004 struct document_view
*doc_view
;
2006 struct menu_item
*mi
;
2007 struct form_control
*fc
;
2011 if_assert_failed
return;
2013 doc_view
= current_frame(ses
);
2014 if (!doc_view
) return;
2016 assert(doc_view
->vs
&& doc_view
->document
);
2017 if_assert_failed
return;
2019 link
= get_current_link(doc_view
);
2022 assert(link_is_form(link
));
2024 fc
= get_link_form_control(link
);
2029 mi
= new_menu(FREE_LIST
| FREE_TEXT
| NO_INTL
);
2032 foreach (fc
, form
->items
) {
2033 unsigned char *text
;
2034 unsigned char *rtext
;
2045 text
= N_("Useless button");
2047 text
= N_("Submit button");
2051 text
= get_form_label(fc
);
2054 link_number
= get_form_control_link(doc_view
->document
, fc
);
2056 || !init_string(&str
))
2060 add_to_string(&str
, _(text
, ses
->tab
->term
));
2063 if (!rtext
) rtext
= fc
->alt
;
2065 add_to_menu(&mi
, str
.source
, rtext
, ACT_MAIN_NONE
,
2066 link_form_menu_func
, intdup(link_number
),
2070 do_menu(ses
->tab
->term
, mi
, ses
, 1);