2007-07-27 07:13:27 -04:00
|
|
|
/** Forms viewing/manipulation handling
|
|
|
|
* @file */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _GNU_SOURCE
|
|
|
|
#define _GNU_SOURCE /* XXX: we want memrchr() ! */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_FCNTL_H
|
|
|
|
#include <fcntl.h> /* OS/2 needs this after sys/types.h */
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "elinks.h"
|
|
|
|
|
|
|
|
#include "bfu/listmenu.h"
|
|
|
|
#include "bfu/dialog.h"
|
|
|
|
#include "config/kbdbind.h"
|
|
|
|
#include "dialogs/menu.h"
|
|
|
|
#include "document/document.h"
|
|
|
|
#include "document/forms.h"
|
|
|
|
#include "document/view.h"
|
2008-07-18 12:16:34 -04:00
|
|
|
#include "ecmascript/ecmascript.h"
|
2021-08-08 15:25:08 -04:00
|
|
|
#include "intl/libintl.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "formhist/formhist.h"
|
|
|
|
#include "mime/mime.h"
|
|
|
|
#include "osdep/ascii.h"
|
|
|
|
#include "osdep/osdep.h"
|
|
|
|
#include "protocol/uri.h"
|
|
|
|
#include "session/session.h"
|
|
|
|
#include "session/task.h"
|
|
|
|
#include "terminal/kbd.h"
|
|
|
|
#include "terminal/terminal.h"
|
|
|
|
#include "terminal/window.h"
|
|
|
|
#include "util/conv.h"
|
|
|
|
#include "util/error.h"
|
|
|
|
#include "util/file.h"
|
|
|
|
#include "util/memory.h"
|
2008-05-25 11:31:49 -04:00
|
|
|
#include "util/random.h"
|
2005-09-15 09:58:31 -04:00
|
|
|
#include "util/string.h"
|
|
|
|
#include "viewer/action.h"
|
|
|
|
#include "viewer/text/draw.h"
|
|
|
|
#include "viewer/text/form.h"
|
|
|
|
#include "viewer/text/link.h"
|
|
|
|
#include "viewer/text/textarea.h"
|
|
|
|
#include "viewer/text/view.h"
|
|
|
|
#include "viewer/text/vs.h"
|
|
|
|
|
|
|
|
|
|
|
|
/* TODO: Some of these (particulary those encoding routines) would feel better
|
|
|
|
* in viewer/common/. --pasky */
|
|
|
|
|
2008-05-12 06:51:53 -04:00
|
|
|
struct files_offset {
|
|
|
|
LIST_HEAD(struct files_offset);
|
2008-05-12 07:01:51 -04:00
|
|
|
/* offset of the filename in the data generated by encode_multipart.
|
|
|
|
* data[begin] is the FILE_CHAR, data + begin + 1 is the filename. */
|
2008-05-12 06:42:43 -04:00
|
|
|
int begin;
|
2008-05-12 07:01:51 -04:00
|
|
|
/* end of filename. data[end] is the FILE_CHAR. In normal strings
|
|
|
|
* it would be a nul char. */
|
2008-05-12 06:42:43 -04:00
|
|
|
int end;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** @relates submitted_value */
|
2008-05-02 14:48:10 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
struct submitted_value *
|
2021-01-02 10:20:27 -05:00
|
|
|
init_submitted_value(char *name, char *value, enum form_type type,
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc, int position)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct submitted_value *sv;
|
|
|
|
|
|
|
|
sv = mem_alloc(sizeof(*sv));
|
|
|
|
if (!sv) return NULL;
|
|
|
|
|
|
|
|
sv->value = stracpy(value);
|
|
|
|
if (!sv->value) { mem_free(sv); return NULL; }
|
|
|
|
|
|
|
|
sv->name = stracpy(name);
|
|
|
|
if (!sv->name) { mem_free(sv->value); mem_free(sv); return NULL; }
|
|
|
|
|
|
|
|
sv->type = type;
|
|
|
|
sv->form_control = fc;
|
|
|
|
sv->position = position;
|
|
|
|
|
|
|
|
return sv;
|
|
|
|
}
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** @relates submitted_value */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
|
|
|
done_submitted_value(struct submitted_value *sv)
|
|
|
|
{
|
|
|
|
if (!sv) return;
|
|
|
|
mem_free_if(sv->value);
|
|
|
|
mem_free_if(sv->name);
|
|
|
|
mem_free(sv);
|
|
|
|
}
|
|
|
|
|
2006-05-02 06:56:05 -04:00
|
|
|
static void
|
2018-09-09 13:18:53 -04:00
|
|
|
fixup_select_state(struct el_form_control *fc, struct form_state *fs)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(fc && fs);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
if (fs->state >= 0
|
|
|
|
&& fs->state < fc->nvalues
|
|
|
|
&& !strcmp(fc->values[fs->state], fs->value))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < fc->nvalues; i++)
|
|
|
|
if (!strcmp(fc->values[i], fs->value)) {
|
|
|
|
fs->state = i;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs->state = 0;
|
|
|
|
|
|
|
|
mem_free_set(&fs->value, stracpy(fc->nvalues
|
|
|
|
? fc->values[0]
|
2021-01-02 10:20:27 -05:00
|
|
|
: (char *) ""));
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* menu_func_T */
|
|
|
|
void
|
|
|
|
selected_item(struct terminal *term, void *item_, void *ses_)
|
|
|
|
{
|
|
|
|
struct session *ses = ses_;
|
|
|
|
int item = (long) item_;
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct link *link;
|
|
|
|
struct form_state *fs;
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(term && ses);
|
|
|
|
if_assert_failed return;
|
|
|
|
doc_view = current_frame(ses);
|
|
|
|
|
|
|
|
assert(doc_view && doc_view->vs && doc_view->document);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
link = get_current_link(doc_view);
|
|
|
|
if (!link || link->type != LINK_SELECT) return;
|
|
|
|
|
|
|
|
fc = get_link_form_control(link);
|
|
|
|
fs = find_form_state(doc_view, fc);
|
|
|
|
if (fs) {
|
|
|
|
if (item >= 0 && item < fc->nvalues) {
|
|
|
|
fs->state = item;
|
|
|
|
mem_free_set(&fs->value, stracpy(fc->values[item]));
|
|
|
|
}
|
|
|
|
fixup_select_state(fc, fs);
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh_view(ses, doc_view, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
init_form_state(struct document_view *doc_view,
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc, struct form_state *fs)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
struct terminal *term;
|
|
|
|
int doc_cp, viewer_cp;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
assert(fc && fs);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
doc_cp = doc_view->document->cp;
|
|
|
|
term = doc_view->session->tab->term;
|
2008-12-28 10:42:29 -05:00
|
|
|
viewer_cp = get_terminal_codepage(term);
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
mem_free_set(&fs->value, NULL);
|
|
|
|
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
2007-04-29 14:39:28 -04:00
|
|
|
#ifdef CONFIG_FORMHIST
|
|
|
|
fs->value = null_or_stracpy(
|
|
|
|
get_form_history_value(
|
|
|
|
fc->form->action, fc->name));
|
|
|
|
#endif /* CONFIG_FORMHIST */
|
|
|
|
/* fall through */
|
2005-09-15 09:58:31 -04:00
|
|
|
case FC_TEXTAREA:
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
if (fs->value == NULL) {
|
|
|
|
fs->value = convert_string(
|
|
|
|
get_translation_table(doc_cp, viewer_cp),
|
|
|
|
fc->default_value,
|
|
|
|
strlen(fc->default_value),
|
|
|
|
viewer_cp, CSM_FORM,
|
|
|
|
&fs->state, NULL, NULL);
|
|
|
|
}
|
2007-04-29 14:39:28 -04:00
|
|
|
fs->state = fs->value ? strlen(fs->value) : 0;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-05-06 16:36:48 -04:00
|
|
|
if (fc->type == FC_TEXTAREA)
|
|
|
|
fs->state_cell = 0;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
fs->vpos = 0;
|
|
|
|
break;
|
|
|
|
case FC_FILE:
|
|
|
|
fs->value = stracpy("");
|
|
|
|
fs->state = 0;
|
|
|
|
fs->vpos = 0;
|
|
|
|
break;
|
|
|
|
case FC_SELECT:
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
fs->value = convert_string(
|
|
|
|
get_translation_table(doc_cp, viewer_cp),
|
|
|
|
fc->default_value,
|
|
|
|
strlen(fc->default_value),
|
|
|
|
viewer_cp, CSM_FORM,
|
|
|
|
&fs->state, NULL, NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
fs->state = fc->default_state;
|
|
|
|
fixup_select_state(fc, fs);
|
|
|
|
break;
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
case FC_RADIO:
|
|
|
|
fs->state = fc->default_state;
|
|
|
|
/* Fall-through */
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
case FC_RESET:
|
|
|
|
case FC_BUTTON:
|
|
|
|
case FC_HIDDEN:
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
/* We don't want to recode hidden fields. */
|
2005-09-15 09:58:31 -04:00
|
|
|
fs->value = stracpy(fc->default_value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct form_state *
|
2018-09-09 13:18:53 -04:00
|
|
|
find_form_state(struct document_view *doc_view, struct el_form_control *fc)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct view_state *vs;
|
|
|
|
struct form_state *fs;
|
|
|
|
int n;
|
|
|
|
|
|
|
|
assert(doc_view && doc_view->vs && fc);
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
|
|
|
|
vs = doc_view->vs;
|
|
|
|
n = fc->g_ctrl_num;
|
|
|
|
|
|
|
|
if (n >= vs->form_info_len) {
|
|
|
|
int nn = n + 1;
|
2021-10-17 12:17:48 -04:00
|
|
|
#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS)
|
2008-07-18 12:16:34 -04:00
|
|
|
const struct form_state *const old_form_info = vs->form_info;
|
|
|
|
#endif
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-02-17 12:32:59 -05:00
|
|
|
fs = mem_align_alloc(&vs->form_info, vs->form_info_len, nn, 0);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!fs) return NULL;
|
|
|
|
vs->form_info = fs;
|
|
|
|
vs->form_info_len = nn;
|
2008-07-18 12:16:34 -04:00
|
|
|
|
2021-10-17 12:17:48 -04:00
|
|
|
#if defined(CONFIG_ECMASCRIPT_SMJS) || defined(CONFIG_QUICKJS)
|
2008-07-18 12:16:34 -04:00
|
|
|
/* TODO: Standard C does not allow this comparison;
|
|
|
|
* if the memory to which old_form_info pointed has
|
|
|
|
* been freed, then the value of the pointer itself is
|
|
|
|
* indeterminate. Fixing this would require changing
|
|
|
|
* mem_align_alloc to tell the caller whether it did
|
|
|
|
* realloc or not. */
|
|
|
|
if (vs->form_info != old_form_info) {
|
|
|
|
/* vs->form_info[] was moved to a different address.
|
|
|
|
* Update all the ECMAScript objects that have
|
|
|
|
* pointers to its elements. */
|
|
|
|
for (nn = 0; nn < vs->form_info_len; nn++)
|
|
|
|
ecmascript_moved_form_state(&vs->form_info[nn]);
|
|
|
|
}
|
2020-10-27 09:53:24 -04:00
|
|
|
#endif /* CONFIG_ECMASCRIPT_SMJS */
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
fs = &vs->form_info[n];
|
|
|
|
|
|
|
|
if (fs->form_view && fs->form_view->form_num == fc->form->form_num
|
|
|
|
&& fs->g_ctrl_num == fc->g_ctrl_num
|
|
|
|
&& fs->position == fc->position
|
|
|
|
&& fs->type == fc->type)
|
|
|
|
return fs;
|
|
|
|
|
|
|
|
mem_free_if(fs->value);
|
|
|
|
memset(fs, 0, sizeof(*fs));
|
|
|
|
fs->form_view = find_form_view(doc_view, fc->form);
|
|
|
|
fs->g_ctrl_num = fc->g_ctrl_num;
|
|
|
|
fs->position = fc->position;
|
|
|
|
fs->type = fc->type;
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
init_form_state(doc_view, fc, fs);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
return fs;
|
|
|
|
}
|
|
|
|
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *
|
2005-09-15 09:58:31 -04:00
|
|
|
find_form_control(struct document *document, struct form_state *fs)
|
|
|
|
{
|
|
|
|
struct form *form = find_form_by_form_view(document, fs->form_view);
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
foreach (fc, form->items) {
|
|
|
|
if (fs->g_ctrl_num == fc->g_ctrl_num
|
|
|
|
&& fs->position == fc->position
|
|
|
|
&& fs->type == fc->type)
|
|
|
|
return fc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct form_view *
|
|
|
|
find_form_view_in_vs(struct view_state *vs, int form_num)
|
|
|
|
{
|
|
|
|
struct form_view *fv;
|
|
|
|
|
|
|
|
assert(vs);
|
|
|
|
|
|
|
|
foreach (fv, vs->forms)
|
|
|
|
if (fv->form_num == form_num)
|
|
|
|
return fv;
|
|
|
|
|
|
|
|
fv = mem_calloc(1, sizeof(*fv));
|
|
|
|
fv->form_num = form_num;
|
|
|
|
add_to_list(vs->forms, fv);
|
|
|
|
return fv;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct form_view *
|
|
|
|
find_form_view(struct document_view *doc_view, struct form *form)
|
|
|
|
{
|
|
|
|
return find_form_view_in_vs(doc_view->vs, form->form_num);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct form *
|
|
|
|
find_form_by_form_view(struct document *document, struct form_view *fv)
|
|
|
|
{
|
|
|
|
struct form *form;
|
|
|
|
|
|
|
|
foreach (form, document->forms) {
|
|
|
|
if (form->form_num == fv->form_num)
|
|
|
|
return form;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-07-18 12:16:34 -04:00
|
|
|
/** Free any data owned by @a fs, but not the struct form_state
|
|
|
|
* itself, because that is normally allocated as part of an array.
|
|
|
|
* @relates form_state */
|
|
|
|
void
|
|
|
|
done_form_state(struct form_state *fs)
|
|
|
|
{
|
2021-09-16 15:43:28 -04:00
|
|
|
#if defined(CONFIG_ECMASCRIPT) || defined(CONFIG_ECMASCRIPT_SMJS)
|
2008-07-18 12:16:34 -04:00
|
|
|
ecmascript_detach_form_state(fs);
|
|
|
|
#endif
|
|
|
|
mem_free_if(fs->value);
|
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2008-07-18 12:43:30 -04:00
|
|
|
/** Free @a fv and any data owned by it. This does not call
|
|
|
|
* del_from_list(fv), so the caller must usually do that first.
|
|
|
|
* @relates form_view */
|
|
|
|
void
|
|
|
|
done_form_view(struct form_view *fv)
|
|
|
|
{
|
2021-09-16 15:43:28 -04:00
|
|
|
#if defined(CONFIG_ECMASCRIPT) || defined(CONFIG_ECMASCRIPT_SMJS)
|
2008-07-18 12:43:30 -04:00
|
|
|
ecmascript_detach_form_view(fv);
|
|
|
|
#endif
|
|
|
|
mem_free(fv);
|
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
int
|
|
|
|
get_current_state(struct session *ses)
|
|
|
|
{
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct link *link;
|
|
|
|
struct form_state *fs;
|
|
|
|
|
|
|
|
assert(ses);
|
|
|
|
if_assert_failed return -1;
|
|
|
|
doc_view = current_frame(ses);
|
|
|
|
|
|
|
|
assert(doc_view && doc_view->vs && doc_view->document);
|
|
|
|
if_assert_failed return -1;
|
|
|
|
|
|
|
|
link = get_current_link(doc_view);
|
|
|
|
if (!link || link->type != LINK_SELECT) return -1;
|
|
|
|
|
|
|
|
fs = find_form_state(doc_view, get_link_form_control(link));
|
|
|
|
if (fs) return fs->state;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
draw_form_entry(struct terminal *term, struct document_view *doc_view,
|
|
|
|
struct link *link)
|
|
|
|
{
|
|
|
|
struct form_state *fs;
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct view_state *vs;
|
2018-09-09 13:14:56 -04:00
|
|
|
struct el_box *box;
|
2005-09-15 09:58:31 -04:00
|
|
|
int dx, dy;
|
|
|
|
|
|
|
|
assert(term && doc_view && doc_view->document && doc_view->vs && link);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
fc = get_link_form_control(link);
|
2007-03-11 06:22:02 -04:00
|
|
|
assertm(fc != NULL, "link %d has no form control", (int) (link - doc_view->document->links));
|
2005-09-15 09:58:31 -04:00
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
fs = find_form_state(doc_view, fc);
|
|
|
|
if (!fs) return;
|
|
|
|
|
|
|
|
box = &doc_view->box;
|
|
|
|
vs = doc_view->vs;
|
|
|
|
dx = box->x - vs->x;
|
|
|
|
dy = box->y - vs->y;
|
|
|
|
switch (fc->type) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *s;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2021-01-02 10:20:27 -05:00
|
|
|
char *text, *end, *last_in_view;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
int len;
|
|
|
|
int i, x, y;
|
|
|
|
|
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
|
|
|
case FC_FILE:
|
|
|
|
if (!link->npoints) break;
|
|
|
|
|
|
|
|
y = link->points[0].y + dy;
|
|
|
|
if (!row_is_in_box(box, y))
|
|
|
|
break;
|
|
|
|
|
|
|
|
x = link->points[0].x + dx;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2007-05-20 08:31:02 -04:00
|
|
|
if (term->utf8_cp) goto utf8;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-05-06 16:03:26 -04:00
|
|
|
int_bounds(&fs->vpos, fs->state - fc->size + 1, fs->state);
|
|
|
|
len = strlen(fs->value) - fs->vpos;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
for (i = 0; i < fc->size; i++, x++) {
|
|
|
|
unsigned char data;
|
|
|
|
|
|
|
|
if (!col_is_in_box(box, x)) continue;
|
|
|
|
|
|
|
|
if (fs->value && i >= -fs->vpos && i < len)
|
|
|
|
data = fc->type != FC_PASSWORD
|
|
|
|
? fs->value[i + fs->vpos] : '*';
|
|
|
|
else
|
|
|
|
data = '_';
|
|
|
|
|
|
|
|
draw_char_data(term, x, y, data);
|
|
|
|
}
|
|
|
|
break;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-09-17 09:06:22 -04:00
|
|
|
utf8:
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2006-08-27 16:41:41 -04:00
|
|
|
retry_after_scroll:
|
|
|
|
text = fs->value;
|
|
|
|
if (!text) text = "";
|
|
|
|
len = strlen(text);
|
|
|
|
int_bounds(&fs->state, 0, len);
|
|
|
|
int_bounds(&fs->vpos, 0, fs->state);
|
|
|
|
end = text + len;
|
|
|
|
text += fs->vpos;
|
|
|
|
last_in_view = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < fc->size; ) {
|
2006-05-06 16:03:26 -04:00
|
|
|
unicode_val_T data;
|
2006-08-27 16:41:41 -04:00
|
|
|
int cells, cell;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *maybe_in_view = text;
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2006-09-17 09:06:22 -04:00
|
|
|
data = utf8_to_unicode(&text, end);
|
2006-08-27 16:41:41 -04:00
|
|
|
if (data == UCS_NO_CHAR) /* end of string */
|
2006-01-14 16:44:00 -05:00
|
|
|
data = '_';
|
2006-08-27 16:41:41 -04:00
|
|
|
else if (fc->type == FC_PASSWORD)
|
|
|
|
data = '*';
|
|
|
|
|
|
|
|
cells = unicode_to_cell(data);
|
|
|
|
if (i + cells <= fc->size) {
|
|
|
|
last_in_view = maybe_in_view;
|
|
|
|
if (colspan_is_in_box(box, x + i, cells)) {
|
|
|
|
/* The character fits completely.
|
|
|
|
* Draw the character, and mark any
|
|
|
|
* further cells with UCS_NO_CHAR. */
|
|
|
|
draw_char_data(term, x + i, y, data);
|
|
|
|
for (cell = 1; cell < cells; cell++)
|
|
|
|
draw_char_data(term, x + i + cell,
|
|
|
|
y, UCS_NO_CHAR);
|
|
|
|
goto drew_char;
|
|
|
|
}
|
|
|
|
}
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2006-08-27 16:41:41 -04:00
|
|
|
/* The character does not fit completely.
|
2006-11-12 12:53:31 -05:00
|
|
|
* Write UCS_ORPHAN_CELL to the cells that
|
|
|
|
* do fit. */
|
2006-08-27 16:41:41 -04:00
|
|
|
for (cell = 0; cell < cells; cell++) {
|
|
|
|
if (col_is_in_box(box, x + i + cell)
|
|
|
|
&& i + cell < fc->size)
|
|
|
|
draw_char_data(term,
|
2006-11-12 12:53:31 -05:00
|
|
|
x + i + cell, y,
|
|
|
|
UCS_ORPHAN_CELL);
|
2006-08-27 16:41:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
drew_char:
|
|
|
|
i += cells;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The int_bounds calls above ensured that the
|
|
|
|
* insertion point cannot be at the left side
|
|
|
|
* of the scrolled-visible part of the text.
|
|
|
|
* However it can still be at the right side.
|
|
|
|
* Check whether we need to change fs->vpos.
|
|
|
|
*
|
|
|
|
* This implementation attempts to follow
|
|
|
|
* these rules:
|
|
|
|
* - If the insertion point is at the end of
|
|
|
|
* the string, leave at least one empty cell
|
|
|
|
* so that there is a place for the cursor.
|
|
|
|
* - If a character follows the insertion
|
|
|
|
* point, make that character fully visible;
|
|
|
|
* note the character may be double-width.
|
|
|
|
* - If fc->size < 2, it is not possible to
|
|
|
|
* make a double-width character fully
|
|
|
|
* visible. In this case, it is OK if the
|
|
|
|
* output is ugly, but ELinks must not fall
|
|
|
|
* into an infinite loop or crash.
|
|
|
|
* - The length of the string should not affect
|
|
|
|
* how long this function takes. The width
|
|
|
|
* of the widget naturally will.
|
|
|
|
* - Optimize the case where fields are drawn
|
|
|
|
* several times without being modified.
|
|
|
|
*
|
|
|
|
* It follows that:
|
|
|
|
* - If the "for i" loop above hit UCS_NO_CHAR,
|
|
|
|
* then there is no need to scroll.
|
|
|
|
* - When the string ends with a double-width
|
|
|
|
* character that fits in only partially,
|
|
|
|
* then text==end, but the field may have
|
|
|
|
* to be scrolled. */
|
|
|
|
if (fs->value && last_in_view
|
|
|
|
&& last_in_view < fs->value + fs->state) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *ptr = fs->value + fs->state;
|
2006-08-27 16:41:41 -04:00
|
|
|
int cells = fc->size;
|
|
|
|
enum utf8_step how = (fc->type == FC_PASSWORD)
|
2006-11-12 07:51:18 -05:00
|
|
|
? UTF8_STEP_CHARACTERS
|
|
|
|
: UTF8_STEP_CELLS_FEWER;
|
2006-08-27 16:41:41 -04:00
|
|
|
|
|
|
|
/* The insertion point is at the right
|
|
|
|
* side of the scrolled-visible part
|
|
|
|
* of the text. Decide a new fs->vpos
|
|
|
|
* by counting cells backwards from
|
|
|
|
* @ptr. But first advance @ptr past
|
|
|
|
* the character that follows the
|
|
|
|
* insertion point, so that it will be
|
|
|
|
* fully displayed. If there is no
|
|
|
|
* such character, reserve one cell
|
|
|
|
* for the cursor anyway. */
|
2006-09-17 09:06:22 -04:00
|
|
|
if (utf8_to_unicode(&ptr, end) == UCS_NO_CHAR)
|
2006-08-27 16:41:41 -04:00
|
|
|
--cells;
|
|
|
|
ptr = utf8_step_backward(ptr, fs->value,
|
|
|
|
cells, how, NULL);
|
|
|
|
|
|
|
|
if (fs->vpos != ptr - fs->value) {
|
|
|
|
fs->vpos = ptr - fs->value;
|
|
|
|
goto retry_after_scroll;
|
2006-05-06 16:03:26 -04:00
|
|
|
}
|
2006-01-14 16:44:00 -05:00
|
|
|
}
|
|
|
|
break;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
case FC_TEXTAREA:
|
|
|
|
draw_textarea(term, fs, doc_view, link);
|
|
|
|
break;
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
case FC_RADIO:
|
|
|
|
if (link->npoints < 2) break;
|
|
|
|
x = link->points[1].x + dx;
|
|
|
|
y = link->points[1].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
|
|
|
draw_char_data(term, x, y, fs->state ? 'X' : ' ');
|
|
|
|
break;
|
|
|
|
case FC_SELECT:
|
|
|
|
fixup_select_state(fc, fs);
|
|
|
|
if (fs->state < fc->nvalues)
|
|
|
|
s = fc->labels[fs->state];
|
|
|
|
else
|
|
|
|
/* XXX: when can this happen? --pasky */
|
|
|
|
s = "";
|
2009-05-01 15:39:30 -04:00
|
|
|
|
|
|
|
/* insert preceding '[_' */
|
|
|
|
i = 0;
|
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
|
|
|
draw_char_data(term, x, y, '[');
|
|
|
|
i++;
|
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
|
|
|
draw_char_data(term, x, y, '_');
|
|
|
|
i++;
|
|
|
|
|
|
|
|
#ifndef CONFIG_UTF8
|
2005-09-15 09:58:31 -04:00
|
|
|
len = s ? strlen(s) : 0;
|
2009-05-01 15:39:30 -04:00
|
|
|
for (; i < link->npoints-2; i++) {
|
2005-09-15 09:58:31 -04:00
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
2009-05-01 15:39:30 -04:00
|
|
|
draw_char_data(term, x, y, i-2 < len ? s[i] : '_');
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
2009-05-01 15:39:30 -04:00
|
|
|
#else
|
2006-01-14 16:44:00 -05:00
|
|
|
text = s;
|
2016-04-20 13:43:37 -04:00
|
|
|
end = strchr((const char *)s, '\0');
|
2006-05-06 16:03:26 -04:00
|
|
|
len = utf8_ptr2cells(text, end);
|
2009-05-01 15:39:30 -04:00
|
|
|
for (; i < link->npoints-2; i++) {
|
2006-01-14 16:44:00 -05:00
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
2006-05-06 16:03:26 -04:00
|
|
|
if (is_in_box(box, x, y)) {
|
|
|
|
unicode_val_T data;
|
2009-05-01 15:39:30 -04:00
|
|
|
if (i-2 < len) {
|
2006-05-06 16:03:26 -04:00
|
|
|
int cell;
|
|
|
|
|
2006-09-17 09:06:22 -04:00
|
|
|
data = utf8_to_unicode(&s, end);
|
2006-05-06 16:03:26 -04:00
|
|
|
cell = unicode_to_cell(data);
|
2021-01-17 15:56:40 -05:00
|
|
|
if (i - 1 < len && cell == 2) {
|
2006-05-06 16:03:26 -04:00
|
|
|
draw_char_data(term, x++, y, data);
|
|
|
|
|
|
|
|
data = UCS_NO_CHAR;
|
|
|
|
i++;
|
|
|
|
} else if (cell == 2) {
|
2006-11-12 12:53:31 -05:00
|
|
|
data = UCS_ORPHAN_CELL;
|
2006-05-06 16:03:26 -04:00
|
|
|
}
|
|
|
|
} else
|
|
|
|
data = '_';
|
|
|
|
draw_char_data(term, x, y, data);
|
|
|
|
}
|
2006-01-14 16:44:00 -05:00
|
|
|
}
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2009-05-01 15:39:30 -04:00
|
|
|
/* insert trailing ' ]'. */
|
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
|
|
|
draw_char_data(term, x, y, '_');
|
|
|
|
i++;
|
|
|
|
x = link->points[i].x + dx;
|
|
|
|
y = link->points[i].y + dy;
|
|
|
|
if (is_in_box(box, x, y))
|
|
|
|
draw_char_data(term, x, y, ']');
|
|
|
|
break;
|
2005-09-15 09:58:31 -04:00
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
case FC_RESET:
|
|
|
|
case FC_BUTTON:
|
|
|
|
case FC_HIDDEN:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
draw_forms(struct terminal *term, struct document_view *doc_view)
|
|
|
|
{
|
|
|
|
struct link *l1, *l2;
|
|
|
|
|
|
|
|
assert(term && doc_view);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
l1 = get_first_link(doc_view);
|
|
|
|
l2 = get_last_link(doc_view);
|
|
|
|
|
|
|
|
if (!l1 || !l2) {
|
|
|
|
assertm(!l1 && !l2, "get_first_link == %p, get_last_link == %p", l1, l2);
|
|
|
|
/* Return path :-). */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
do {
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc = get_link_form_control(l1);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!fc) continue;
|
|
|
|
draw_form_entry(term, doc_view, l1);
|
|
|
|
|
|
|
|
} while (l1++ < l2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** @relates submitted_value */
|
2005-09-15 09:58:31 -04:00
|
|
|
void
|
2007-07-26 15:39:08 -04:00
|
|
|
done_submitted_value_list(LIST_OF(struct submitted_value) *list)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct submitted_value *sv, *svtmp;
|
|
|
|
|
|
|
|
assert(list);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (sv, *list) {
|
|
|
|
svtmp = sv;
|
|
|
|
sv = sv->prev;
|
|
|
|
del_from_list(svtmp);
|
|
|
|
done_submitted_value(svtmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2018-09-09 13:18:53 -04:00
|
|
|
add_submitted_value_to_list(struct el_form_control *fc,
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_state *fs,
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct submitted_value) *list)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct submitted_value *sub;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *name;
|
2005-09-15 09:58:31 -04:00
|
|
|
enum form_type type;
|
|
|
|
int position;
|
|
|
|
|
|
|
|
assert(fc && fs && list);
|
|
|
|
|
|
|
|
name = fc->name;
|
|
|
|
position = fc->position;
|
|
|
|
type = fc->type;
|
|
|
|
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
|
|
|
case FC_FILE:
|
|
|
|
case FC_TEXTAREA:
|
|
|
|
sub = init_submitted_value(name, fs->value, type, fc, position);
|
|
|
|
if (sub) add_to_list(*list, sub);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
case FC_RADIO:
|
|
|
|
if (!fs->state) break;
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_HIDDEN:
|
|
|
|
case FC_RESET:
|
|
|
|
case FC_BUTTON:
|
|
|
|
sub = init_submitted_value(name, fs->value, type, fc,
|
|
|
|
position);
|
|
|
|
if (sub) add_to_list(*list, sub);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FC_SELECT:
|
|
|
|
if (!fc->nvalues) break;
|
|
|
|
|
|
|
|
fixup_select_state(fc, fs);
|
|
|
|
sub = init_submitted_value(name, fs->value, type, fc, position);
|
|
|
|
if (sub) add_to_list(*list, sub);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FC_IMAGE:
|
2021-01-02 10:20:27 -05:00
|
|
|
name = straconcat(fc->name, ".x", (char *) NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!name) break;
|
|
|
|
sub = init_submitted_value(name, "0", type, fc, position);
|
|
|
|
mem_free(name);
|
|
|
|
if (sub) add_to_list(*list, sub);
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
name = straconcat(fc->name, ".y", (char *) NULL);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!name) break;
|
|
|
|
sub = init_submitted_value(name, "0", type, fc, position);
|
|
|
|
mem_free(name);
|
|
|
|
if (sub) add_to_list(*list, sub);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2007-07-26 15:39:08 -04:00
|
|
|
sort_submitted_values(LIST_OF(struct submitted_value) *list)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2005-12-13 11:01:45 -05:00
|
|
|
while (1) {
|
|
|
|
struct submitted_value *sub;
|
|
|
|
int changed = 0;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
foreach (sub, *list) if (list_has_next(*list, sub))
|
|
|
|
if (sub->next->position < sub->position) {
|
2005-12-13 11:01:45 -05:00
|
|
|
struct submitted_value *next = sub->next;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
del_from_list(sub);
|
|
|
|
add_at_pos(next, sub);
|
|
|
|
sub = next;
|
|
|
|
changed = 1;
|
|
|
|
}
|
2005-12-13 11:01:45 -05:00
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
foreachback (sub, *list) if (list_has_next(*list, sub))
|
|
|
|
if (sub->next->position < sub->position) {
|
2005-12-13 11:01:45 -05:00
|
|
|
struct submitted_value *next = sub->next;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
del_from_list(sub);
|
|
|
|
add_at_pos(next, sub);
|
|
|
|
sub = next;
|
|
|
|
changed = 1;
|
|
|
|
}
|
2005-12-13 11:01:45 -05:00
|
|
|
|
|
|
|
if (!changed) break;
|
|
|
|
};
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
get_successful_controls(struct document_view *doc_view,
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc,
|
2007-07-26 15:39:08 -04:00
|
|
|
LIST_OF(struct submitted_value) *list)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc2;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(doc_view && fc && fc->form && list);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (fc2, fc->form->items) {
|
|
|
|
if (((fc2->type != FC_SUBMIT &&
|
|
|
|
fc2->type != FC_IMAGE &&
|
|
|
|
fc2->type != FC_RESET &&
|
|
|
|
fc2->type != FC_BUTTON) || fc2 == fc)
|
|
|
|
&& fc2->name && fc2->name[0]) {
|
|
|
|
struct form_state *fs = find_form_state(doc_view, fc2);
|
|
|
|
|
|
|
|
if (!fs) continue;
|
|
|
|
|
|
|
|
add_submitted_value_to_list(fc2, fs, list);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort_submitted_values(list);
|
|
|
|
}
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
char *
|
2008-11-03 16:56:49 -05:00
|
|
|
encode_crlf(struct submitted_value *sv)
|
|
|
|
{
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string newtext;
|
2008-11-03 16:56:49 -05:00
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(sv && sv->value);
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
|
|
|
|
if (!init_string(&newtext)) return NULL;
|
|
|
|
|
|
|
|
for (i = 0; sv->value[i]; i++) {
|
|
|
|
if (sv->value[i] == '\r') {
|
|
|
|
if (sv->value[i+1] != '\n')
|
|
|
|
add_crlf_to_string(&newtext);
|
|
|
|
} else if (sv->value[i] == '\n')
|
|
|
|
add_crlf_to_string(&newtext);
|
|
|
|
else
|
|
|
|
add_char_to_string(&newtext, sv->value[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return newtext.source;
|
|
|
|
}
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2019-04-21 06:27:40 -04:00
|
|
|
encode_controls(LIST_OF(struct submitted_value) *l, struct string *data,
|
2005-09-15 09:58:31 -04:00
|
|
|
int cp_from, int cp_to)
|
|
|
|
{
|
|
|
|
struct submitted_value *sv;
|
|
|
|
struct conv_table *convert_table = NULL;
|
|
|
|
int lst = 0;
|
|
|
|
|
|
|
|
assert(l && data);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (sv, *l) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *p2 = NULL;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (lst)
|
|
|
|
add_char_to_string(data, '&');
|
|
|
|
else
|
|
|
|
lst = 1;
|
|
|
|
|
|
|
|
encode_uri_string(data, sv->name, strlen(sv->name), 1);
|
|
|
|
add_char_to_string(data, '=');
|
|
|
|
|
|
|
|
/* Convert back to original encoding (see html_form_control()
|
|
|
|
* for the original recoding). */
|
|
|
|
if (sv->type == FC_TEXTAREA) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *p;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
p = encode_textarea(sv);
|
|
|
|
if (p) {
|
|
|
|
if (!convert_table)
|
|
|
|
convert_table = get_translation_table(cp_from, cp_to);
|
|
|
|
|
|
|
|
p2 = convert_string(convert_table, p,
|
|
|
|
strlen(p), -1, CSM_FORM, NULL, NULL, NULL);
|
|
|
|
mem_free(p);
|
|
|
|
}
|
|
|
|
} else if (sv->type == FC_TEXT ||
|
|
|
|
sv->type == FC_PASSWORD) {
|
|
|
|
if (!convert_table)
|
|
|
|
convert_table = get_translation_table(cp_from, cp_to);
|
|
|
|
|
|
|
|
p2 = convert_string(convert_table, sv->value,
|
|
|
|
strlen(sv->value), -1, CSM_FORM, NULL, NULL, NULL);
|
2008-11-03 16:56:49 -05:00
|
|
|
} else if (sv->type == FC_HIDDEN) {
|
|
|
|
p2 = encode_crlf(sv);
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
|
|
|
p2 = stracpy(sv->value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p2) {
|
|
|
|
encode_uri_string(data, p2, strlen(p2), 1);
|
|
|
|
mem_free(p2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define BOUNDARY_LENGTH 32
|
|
|
|
#define realloc_bound_ptrs(bptrs, bptrs_size) \
|
2006-02-17 12:32:59 -05:00
|
|
|
mem_align_alloc(bptrs, bptrs_size, bptrs_size + 1, 0xFF)
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
struct boundary_info {
|
|
|
|
int count;
|
|
|
|
int *offsets;
|
2021-01-02 10:20:27 -05:00
|
|
|
char string[BOUNDARY_LENGTH];
|
2005-09-15 09:58:31 -04:00
|
|
|
};
|
|
|
|
|
2008-05-17 12:20:13 -04:00
|
|
|
/** @relates boundary_info */
|
|
|
|
static void
|
2021-01-02 10:20:27 -05:00
|
|
|
randomize_boundary(char *data, int length)
|
2008-05-17 12:20:13 -04:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
random_nonce((unsigned char *)data, length);
|
2008-05-17 12:20:13 -04:00
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
/* Only [0-9A-Za-z]. */
|
|
|
|
data[i] = data[i] & 63;
|
|
|
|
if (data[i] < 10) data[i] += '0';
|
|
|
|
else if (data[i] < 36) data[i] = data[i] - 10 + 'A';
|
|
|
|
else if (data[i] < 62) data[i] = data[i] - 36 + 'a';
|
|
|
|
else data[i] = '0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** @relates boundary_info */
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline void
|
|
|
|
init_boundary(struct boundary_info *boundary)
|
|
|
|
{
|
|
|
|
memset(boundary, 0, sizeof(*boundary));
|
2008-05-17 12:20:13 -04:00
|
|
|
randomize_boundary(boundary->string, BOUNDARY_LENGTH);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** Add boundary to string and save the offset
|
|
|
|
* @relates boundary_info */
|
2005-09-15 09:58:31 -04:00
|
|
|
static inline void
|
2019-04-21 06:27:40 -04:00
|
|
|
add_boundary(struct string *data, struct boundary_info *boundary)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
add_to_string(data, "--");
|
|
|
|
|
|
|
|
if (realloc_bound_ptrs(&boundary->offsets, boundary->count))
|
|
|
|
boundary->offsets[boundary->count++] = data->length;
|
|
|
|
|
|
|
|
add_bytes_to_string(data, boundary->string, BOUNDARY_LENGTH);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-11 04:27:46 -04:00
|
|
|
/** Format a multipart/form-data body for a POST request.
|
|
|
|
*
|
|
|
|
* @param ses
|
|
|
|
* Display an info_box() in the terminal of this session if an error
|
|
|
|
* occurs.
|
|
|
|
* @param[in] l
|
|
|
|
* List of values to be sent to the server.
|
|
|
|
* @param[out] data
|
|
|
|
* Append the body here. This is in the same format as uri.post,
|
2008-07-11 07:15:32 -04:00
|
|
|
* except this never has a Content-Type at the beginning, the
|
|
|
|
* literal parts are not encoded in hexadecimal, and the file names
|
|
|
|
* are not percent-encoded. Therefore the result would be ambiguous
|
|
|
|
* without @a bfs.
|
2008-07-11 04:27:46 -04:00
|
|
|
* @param[out] boundary
|
|
|
|
* A random boundary %string, and a list of offsets where the
|
|
|
|
* boundary was used, so that the caller can in principle change the
|
|
|
|
* %string and update all of its uses if the original one conflicts
|
|
|
|
* with some of the submitted values. However, the caller does not
|
|
|
|
* do that nowadays because reading through the attached files would
|
|
|
|
* be too expensive. It just assumes the boundary is random enough.
|
|
|
|
* @param[out] bfs
|
|
|
|
* List of offsets of names of files to be uploaded. This is how
|
|
|
|
* the caller knows which occurrences of ::FILE_CHAR in @a data
|
|
|
|
* should be encoded and which ones should not.
|
|
|
|
* @param[in] cp_from
|
|
|
|
* Codepage of the submitted-value strings in @a l.
|
|
|
|
* @param[in] cp_to
|
|
|
|
* Codepage wanted by the server.
|
|
|
|
*
|
|
|
|
* @todo FIXME: shouldn't we encode data at send time (in http.c) ? --Zas */
|
2005-09-15 09:58:31 -04:00
|
|
|
static void
|
2007-07-26 15:39:08 -04:00
|
|
|
encode_multipart(struct session *ses, LIST_OF(struct submitted_value) *l,
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string *data, struct boundary_info *boundary,
|
2008-05-12 06:51:53 -04:00
|
|
|
LIST_OF(struct files_offset) *bfs, int cp_from, int cp_to)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct conv_table *convert_table = NULL;
|
|
|
|
struct submitted_value *sv;
|
|
|
|
|
|
|
|
assert(ses && l && data && boundary);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
init_boundary(boundary);
|
|
|
|
|
|
|
|
foreach (sv, *l) {
|
|
|
|
add_boundary(data, boundary);
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
|
2007-07-27 07:13:27 -04:00
|
|
|
/** @bug FIXME: name is not encoded.
|
2008-07-11 04:27:46 -04:00
|
|
|
* From RFC 1867:
|
2005-09-15 09:58:31 -04:00
|
|
|
* multipart/form-data contains a series of parts.
|
2008-07-11 04:27:46 -04:00
|
|
|
* Each %part is expected to contain a content-disposition
|
2005-09-15 09:58:31 -04:00
|
|
|
* header where the value is "form-data" and a name attribute
|
|
|
|
* specifies the field name within the form,
|
|
|
|
* e.g., 'content-disposition: form-data; name="xxxxx"',
|
|
|
|
* where xxxxx is the field name corresponding to that field.
|
|
|
|
* Field names originally in non-ASCII character sets may be
|
|
|
|
* encoded using the method outlined in RFC 1522. */
|
|
|
|
add_to_string(data, "Content-Disposition: form-data; name=\"");
|
|
|
|
add_to_string(data, sv->name);
|
|
|
|
add_char_to_string(data, '"');
|
|
|
|
|
|
|
|
if (sv->type == FC_FILE) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *extension;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
add_to_string(data, "; filename=\"");
|
|
|
|
add_to_string(data, get_filename_position(sv->value));
|
|
|
|
/* It sends bad data if the file name contains ", but
|
|
|
|
Netscape does the same */
|
|
|
|
/* FIXME: We should follow RFCs 1522, 1867,
|
|
|
|
* 2047 (updated by rfc 2231), to provide correct support
|
|
|
|
* for non-ASCII and special characters in values. --Zas */
|
|
|
|
add_char_to_string(data, '"');
|
|
|
|
|
|
|
|
/* Add a Content-Type header if the type is configured */
|
2016-04-20 15:03:27 -04:00
|
|
|
extension = strrchr((const char *)sv->value, '.');
|
2005-09-15 09:58:31 -04:00
|
|
|
if (extension) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *type = get_extension_content_type(extension);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (type) {
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
add_to_string(data, "Content-Type: ");
|
|
|
|
add_to_string(data, type);
|
|
|
|
mem_free(type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
|
|
|
|
if (*sv->value) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *filename;
|
2008-05-12 06:51:53 -04:00
|
|
|
struct files_offset *bfs_new;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (get_cmd_opt_bool("anonymous")) {
|
|
|
|
errno = EPERM;
|
|
|
|
goto encode_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
filename = expand_tilde(sv->value);
|
|
|
|
if (!filename) goto encode_error;
|
|
|
|
|
2008-05-12 06:37:00 -04:00
|
|
|
if (access(filename, R_OK)) {
|
2008-05-02 14:48:10 -04:00
|
|
|
mem_free(filename);
|
|
|
|
goto encode_error;
|
|
|
|
}
|
2008-07-11 07:15:32 -04:00
|
|
|
|
2008-05-12 06:37:00 -04:00
|
|
|
bfs_new = mem_calloc(1, sizeof(*bfs_new));
|
|
|
|
if (!bfs_new) {
|
2008-05-02 14:48:10 -04:00
|
|
|
mem_free(filename);
|
2008-05-12 06:37:00 -04:00
|
|
|
goto encode_error;
|
2008-05-02 14:48:10 -04:00
|
|
|
}
|
2008-05-12 06:37:00 -04:00
|
|
|
bfs_new->begin = data->length;
|
2008-05-12 06:51:53 -04:00
|
|
|
add_char_to_string(data, FILE_CHAR);
|
2008-05-12 06:37:00 -04:00
|
|
|
add_to_string(data, filename);
|
2008-05-12 06:51:53 -04:00
|
|
|
add_char_to_string(data, FILE_CHAR);
|
2008-05-12 06:37:00 -04:00
|
|
|
bfs_new->end = data->length;
|
|
|
|
add_to_list_end(*bfs, bfs_new);
|
2005-09-15 09:58:31 -04:00
|
|
|
mem_free(filename);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
|
|
|
|
/* Convert back to original encoding (see
|
2007-08-12 07:23:49 -04:00
|
|
|
* html_special_form_control() for the original
|
|
|
|
* recoding). */
|
2005-09-15 09:58:31 -04:00
|
|
|
if (sv->type == FC_TEXT || sv->type == FC_PASSWORD ||
|
|
|
|
sv->type == FC_TEXTAREA) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *p;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!convert_table)
|
|
|
|
convert_table = get_translation_table(cp_from,
|
|
|
|
cp_to);
|
|
|
|
|
|
|
|
p = convert_string(convert_table, sv->value,
|
|
|
|
strlen(sv->value), -1, CSM_FORM, NULL,
|
|
|
|
NULL, NULL);
|
|
|
|
if (p) {
|
|
|
|
add_to_string(data, p);
|
|
|
|
mem_free(p);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
add_to_string(data, sv->value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* End-boundary */
|
|
|
|
add_boundary(data, boundary);
|
|
|
|
add_to_string(data, "--\r\n");
|
|
|
|
|
|
|
|
mem_free_if(boundary->offsets);
|
|
|
|
return;
|
|
|
|
|
|
|
|
encode_error:
|
2008-05-12 06:37:00 -04:00
|
|
|
free_list(*bfs);
|
2005-09-15 09:58:31 -04:00
|
|
|
mem_free_if(boundary->offsets);
|
|
|
|
done_string(data);
|
|
|
|
|
|
|
|
/* XXX: This error message should move elsewhere. --Zas */
|
|
|
|
info_box(ses->tab->term, MSGBOX_FREE_TEXT,
|
|
|
|
N_("Error while posting form"), ALIGN_CENTER,
|
|
|
|
msg_text(ses->tab->term, N_("Could not load file %s: %s"),
|
|
|
|
sv->value, strerror(errno)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2021-01-02 10:20:27 -05:00
|
|
|
encode_newlines(struct string *string, char *data)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
for (; *data; data++) {
|
|
|
|
if (*data == '\n' || *data == '\r') {
|
2021-01-02 10:20:27 -05:00
|
|
|
char buffer[3];
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
/* Hex it. */
|
|
|
|
buffer[0] = '%';
|
|
|
|
buffer[1] = hx((((int) *data) & 0xF0) >> 4);
|
|
|
|
buffer[2] = hx(((int) *data) & 0xF);
|
|
|
|
add_bytes_to_string(string, buffer, 3);
|
|
|
|
} else {
|
|
|
|
add_char_to_string(string, *data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-04-21 06:27:40 -04:00
|
|
|
encode_text_plain(LIST_OF(struct submitted_value) *l, struct string *data,
|
2005-09-15 09:58:31 -04:00
|
|
|
int cp_from, int cp_to)
|
|
|
|
{
|
|
|
|
struct submitted_value *sv;
|
|
|
|
struct conv_table *convert_table = get_translation_table(cp_from, cp_to);
|
|
|
|
|
|
|
|
assert(l && data);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (sv, *l) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *area51 = NULL;
|
|
|
|
char *value = sv->value;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
add_to_string(data, sv->name);
|
|
|
|
add_char_to_string(data, '=');
|
|
|
|
|
|
|
|
switch (sv->type) {
|
|
|
|
case FC_TEXTAREA:
|
|
|
|
value = area51 = encode_textarea(sv);
|
|
|
|
if (!area51) break;
|
|
|
|
/* Fall through */
|
2008-11-03 16:56:49 -05:00
|
|
|
case FC_HIDDEN:
|
|
|
|
if (!area51) value = area51 = encode_crlf(sv);
|
|
|
|
if (!area51) break;
|
|
|
|
/* Fall through */
|
2005-09-15 09:58:31 -04:00
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
|
|
|
/* Convert back to original encoding (see
|
|
|
|
* html_form_control() for the original recoding). */
|
|
|
|
value = convert_string(convert_table, value,
|
|
|
|
strlen(value), -1, CSM_FORM,
|
|
|
|
NULL, NULL, NULL);
|
|
|
|
default:
|
|
|
|
/* Falling right through to free that textarea stuff */
|
|
|
|
mem_free_if(area51);
|
|
|
|
|
|
|
|
/* Did the conversion fail? */
|
|
|
|
if (!value) break;
|
|
|
|
|
|
|
|
encode_newlines(data, value);
|
|
|
|
|
|
|
|
/* Free if we did convert something */
|
|
|
|
if (value != sv->value) mem_free(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
add_crlf_to_string(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
do_reset_form(struct document_view *doc_view, struct form *form)
|
|
|
|
{
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(doc_view && doc_view->document);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
foreach (fc, form->items) {
|
|
|
|
struct form_state *fs = find_form_state(doc_view, fc);
|
|
|
|
|
Bug 784: Keep form_control.default_value in the document charset.
Previously, html_special_form_control converted
form_control.default_value to the terminal charset, and init_form_state
then copied the value to form_state.value. However, when CONFIG_UTF8
is defined and UTF-8 I/O is enabled, form_state.value is supposed to
be in UTF-8, rather than in the terminal charset.
This mismatch could not be conveniently fixed in
html_special_form_control because that does not know which terminal is
being used and whether UTF-8 I/O is enabled there. Also, constructing
a conversion table from the document charset to form_state.value could
have ruined renderer_context.convert_table, because src/intl/charsets.c
does not support multiple concurrent conversion tables.
So instead, we now keep form_control.default_value in the document
charset, and convert it in the viewer each time it is needed. Because
the result of the conversion is kept in form_state.value between
incremental renderings, this shouldn't even slow things down too much.
I am not implementing the proper charset conversions for the DOM
defaultValue property yet, because the current code doesn't have
them for other string properties either, and bug 805 is already open
for that.
2007-04-29 15:01:13 -04:00
|
|
|
if (fs) init_form_state(doc_view, fc, fs);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
enum frame_event_status
|
|
|
|
reset_form(struct session *ses, struct document_view *doc_view, int a)
|
|
|
|
{
|
|
|
|
struct link *link = get_current_link(doc_view);
|
|
|
|
|
|
|
|
if (!link) return FRAME_EVENT_OK;
|
|
|
|
|
|
|
|
do_reset_form(doc_view, get_link_form_control(link)->form);
|
|
|
|
draw_forms(ses->tab->term, doc_view);
|
|
|
|
|
|
|
|
/* Could be the refresh return value and then ditch the draw_forms()
|
|
|
|
* call. */
|
|
|
|
return FRAME_EVENT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct uri *
|
|
|
|
get_form_uri(struct session *ses, struct document_view *doc_view,
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
struct boundary_info boundary;
|
2007-07-26 15:39:08 -04:00
|
|
|
INIT_LIST_OF(struct submitted_value, submit);
|
2008-05-12 06:51:53 -04:00
|
|
|
INIT_LIST_OF(struct files_offset, bfs);
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string data;
|
|
|
|
struct string go;
|
2005-09-15 09:58:31 -04:00
|
|
|
int cp_from, cp_to;
|
|
|
|
struct uri *uri;
|
|
|
|
struct form *form;
|
|
|
|
|
|
|
|
assert(ses && ses->tab && ses->tab->term);
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
assert(doc_view && doc_view->document && fc && fc->form);
|
|
|
|
if_assert_failed return NULL;
|
|
|
|
|
|
|
|
form = fc->form;
|
|
|
|
|
|
|
|
if (fc->type == FC_RESET) {
|
|
|
|
do_reset_form(doc_view, form);
|
|
|
|
return NULL;
|
2006-05-31 13:34:49 -04:00
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!form->action
|
|
|
|
|| !init_string(&data))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
get_successful_controls(doc_view, fc, &submit);
|
|
|
|
|
2008-12-28 10:42:29 -05:00
|
|
|
cp_from = get_terminal_codepage(ses->tab->term);
|
2005-09-15 09:58:31 -04:00
|
|
|
cp_to = doc_view->document->cp;
|
|
|
|
switch (form->method) {
|
|
|
|
case FORM_METHOD_GET:
|
|
|
|
case FORM_METHOD_POST:
|
|
|
|
encode_controls(&submit, &data, cp_from, cp_to);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FORM_METHOD_POST_MP:
|
2008-05-02 14:48:10 -04:00
|
|
|
encode_multipart(ses, &submit, &data, &boundary,
|
|
|
|
&bfs, cp_from, cp_to);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
case FORM_METHOD_POST_TEXT_PLAIN:
|
|
|
|
encode_text_plain(&submit, &data, cp_from, cp_to);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_FORMHIST
|
|
|
|
/* XXX: We check data.source here because a NULL value can indicate
|
|
|
|
* not only a memory allocation failure, but also an error reading
|
|
|
|
* a file that is to be uploaded. TODO: Distinguish between
|
|
|
|
* these two classes of errors (is it worth it?). -- Miciah */
|
|
|
|
if (data.source
|
2007-08-30 17:11:51 -04:00
|
|
|
&& get_opt_bool("document.browse.forms.show_formhist", ses))
|
2005-09-15 09:58:31 -04:00
|
|
|
memorize_form(ses, &submit, form);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
done_submitted_value_list(&submit);
|
|
|
|
|
|
|
|
if (!data.source
|
|
|
|
|| !init_string(&go)) {
|
|
|
|
done_string(&data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (form->method) {
|
|
|
|
case FORM_METHOD_GET:
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *pos = strchr((const char *)form->action, '#');
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (pos) {
|
|
|
|
add_bytes_to_string(&go, form->action, pos - form->action);
|
|
|
|
} else {
|
|
|
|
add_to_string(&go, form->action);
|
|
|
|
}
|
|
|
|
|
2016-04-20 13:43:37 -04:00
|
|
|
if (strchr((const char *)go.source, '?'))
|
2005-09-15 09:58:31 -04:00
|
|
|
add_char_to_string(&go, '&');
|
|
|
|
else
|
|
|
|
add_char_to_string(&go, '?');
|
|
|
|
|
|
|
|
add_string_to_string(&go, &data);
|
|
|
|
|
|
|
|
if (pos) add_to_string(&go, pos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FORM_METHOD_POST:
|
|
|
|
case FORM_METHOD_POST_MP:
|
|
|
|
case FORM_METHOD_POST_TEXT_PLAIN:
|
|
|
|
{
|
|
|
|
/* Note that we end content type here by a simple '\n',
|
|
|
|
* replaced later by correct '\r\n' in http_send_header(). */
|
|
|
|
|
|
|
|
add_to_string(&go, form->action);
|
|
|
|
add_char_to_string(&go, POST_CHAR);
|
|
|
|
if (form->method == FORM_METHOD_POST) {
|
|
|
|
add_to_string(&go, "application/x-www-form-urlencoded\n");
|
|
|
|
|
|
|
|
} else if (form->method == FORM_METHOD_POST_TEXT_PLAIN) {
|
|
|
|
/* Dunno about this one but we don't want the full
|
|
|
|
* hextcat thingy. --jonas */
|
|
|
|
add_to_string(&go, "text/plain\n");
|
|
|
|
add_to_string(&go, data.source);
|
|
|
|
break;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
add_to_string(&go, "multipart/form-data; boundary=");
|
|
|
|
add_bytes_to_string(&go, boundary.string, BOUNDARY_LENGTH);
|
|
|
|
add_char_to_string(&go, '\n');
|
|
|
|
}
|
|
|
|
|
2008-05-02 14:48:10 -04:00
|
|
|
if (list_empty(bfs)) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < data.length; i++) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char p[3];
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2008-05-02 14:48:10 -04:00
|
|
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
|
|
|
add_to_string(&go, p);
|
|
|
|
}
|
|
|
|
} else {
|
2008-05-12 06:51:53 -04:00
|
|
|
struct files_offset *b;
|
2008-05-02 14:48:10 -04:00
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
foreach (b, bfs) {
|
|
|
|
for (; i < b->begin; i++) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char p[3];
|
2008-05-02 14:48:10 -04:00
|
|
|
|
|
|
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
|
|
|
add_to_string(&go, p);
|
|
|
|
}
|
2008-07-11 07:15:32 -04:00
|
|
|
assert(i == b->begin);
|
|
|
|
assert(b->end - b->begin >= 2);
|
|
|
|
assert(data.source[b->begin] == FILE_CHAR);
|
|
|
|
assert(data.source[b->end - 1] == FILE_CHAR);
|
|
|
|
add_char_to_string(&go, FILE_CHAR);
|
|
|
|
encode_uri_string(&go, data.source + i + 1,
|
|
|
|
b->end - b->begin - 2, 0);
|
|
|
|
add_char_to_string(&go, FILE_CHAR);
|
2008-05-02 14:48:10 -04:00
|
|
|
i = b->end;
|
|
|
|
}
|
|
|
|
for (; i < data.length; i++) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char p[3];
|
2008-05-02 14:48:10 -04:00
|
|
|
|
|
|
|
ulonghexcat(p, NULL, (int) data.source[i], 2, '0', 0);
|
|
|
|
add_to_string(&go, p);
|
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
done_string(&data);
|
|
|
|
|
|
|
|
uri = get_uri(go.source, 0);
|
|
|
|
done_string(&go);
|
2008-05-02 14:48:10 -04:00
|
|
|
if (uri) {
|
|
|
|
uri->form = 1;
|
|
|
|
}
|
|
|
|
free_list(bfs);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
return uri;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef BOUNDARY_LENGTH
|
|
|
|
|
|
|
|
|
|
|
|
enum frame_event_status
|
|
|
|
submit_form(struct session *ses, struct document_view *doc_view, int do_reload)
|
|
|
|
{
|
|
|
|
goto_current_link(ses, doc_view, do_reload);
|
|
|
|
return FRAME_EVENT_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2006-01-28 05:17:22 -05:00
|
|
|
submit_given_form(struct session *ses, struct document_view *doc_view,
|
|
|
|
struct form *form, int do_reload)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
/* Added support for submitting forms in hidden
|
|
|
|
* links in 1.285, commented code can safely be removed once we have made sure the new
|
|
|
|
* code does the right thing. */
|
|
|
|
#if 0
|
|
|
|
|
|
|
|
struct document *document = doc_view->document;
|
|
|
|
int link;
|
|
|
|
|
|
|
|
for (link = 0; link < document->nlinks; link++) {
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc = get_link_form_control(&document->links[link]);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (fc && fc->form == form) {
|
|
|
|
doc_view->vs->current_link = link;
|
|
|
|
submit_form(ses, doc_view, 0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!list_empty(form->items)) {
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc = (struct el_form_control *)form->items.next;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct uri *uri;
|
2006-01-28 05:17:22 -05:00
|
|
|
enum cache_mode mode = do_reload ? CACHE_MODE_FORCE_RELOAD : CACHE_MODE_NORMAL;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (!fc) return;
|
|
|
|
uri = get_form_uri(ses, doc_view, fc);
|
|
|
|
if (!uri) return;
|
2006-01-28 05:17:22 -05:00
|
|
|
goto_uri_frame(ses, uri, form->target, mode);
|
2005-09-15 09:58:31 -04:00
|
|
|
done_uri(uri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
auto_submit_form(struct session *ses)
|
|
|
|
{
|
|
|
|
struct document *document = ses->doc_view->document;
|
|
|
|
|
|
|
|
if (!list_empty(document->forms))
|
2006-01-28 05:17:22 -05:00
|
|
|
submit_given_form(ses, ses->doc_view, document->forms.next, 0);
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* menu_func_T */
|
|
|
|
static void
|
|
|
|
set_file_form_state(struct terminal *term, void *filename_, void *fs_)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *filename = filename_;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_state *fs = fs_;
|
|
|
|
|
|
|
|
/* The menu code doesn't free the filename data */
|
|
|
|
mem_free_set(&fs->value, filename);
|
|
|
|
fs->state = strlen(filename);
|
|
|
|
redraw_terminal(term);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* menu_func_T */
|
|
|
|
static void
|
|
|
|
file_form_menu(struct terminal *term, void *path_, void *fs_)
|
|
|
|
{
|
2021-01-02 10:20:27 -05:00
|
|
|
char *path = path_;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_state *fs = fs_;
|
|
|
|
|
|
|
|
/* FIXME: It doesn't work for ../../ */
|
|
|
|
#if 0
|
|
|
|
int valuelen = strlen(fs->value);
|
|
|
|
int pathlen = strlen(path);
|
|
|
|
int no_elevator = 0;
|
|
|
|
|
|
|
|
/* Don't add elevators for subdirs menus */
|
|
|
|
/* It is not perfect at all because fs->value is not updated for each
|
|
|
|
* newly opened file menu. Maybe it should be dropped. */
|
|
|
|
for (; valuelen < pathlen; valuelen++) {
|
|
|
|
if (dir_sep(path[valuelen - 1])) {
|
|
|
|
no_elevator = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
auto_complete_file(term, 0 /* no_elevator */, path,
|
|
|
|
set_file_form_state,
|
|
|
|
file_form_menu, fs);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
enum frame_event_status
|
|
|
|
field_op(struct session *ses, struct document_view *doc_view,
|
|
|
|
struct link *link, struct term_event *ev)
|
|
|
|
{
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-09-15 09:58:31 -04:00
|
|
|
struct form_state *fs;
|
|
|
|
enum edit_action action_id;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *text;
|
2005-09-15 09:58:31 -04:00
|
|
|
int length;
|
|
|
|
enum frame_event_status status = FRAME_EVENT_REFRESH;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2021-01-02 10:20:27 -05:00
|
|
|
const char *ctext;
|
2007-05-20 08:31:02 -04:00
|
|
|
int utf8 = ses->tab->term->utf8_cp;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(ses && doc_view && link && ev);
|
|
|
|
if_assert_failed return FRAME_EVENT_OK;
|
|
|
|
|
|
|
|
fc = get_link_form_control(link);
|
2007-03-11 06:22:02 -04:00
|
|
|
assertm(fc != NULL, "link has no form control");
|
2005-09-15 09:58:31 -04:00
|
|
|
if_assert_failed return FRAME_EVENT_OK;
|
|
|
|
|
|
|
|
if (fc->mode == FORM_MODE_DISABLED || ev->ev != EVENT_KBD
|
2008-01-19 11:12:57 -05:00
|
|
|
|| (ses->insert_mode == INSERT_MODE_OFF
|
|
|
|
&& !(get_kbd_modifier(ev) & KBD_MOD_PASTE)))
|
2005-09-15 09:58:31 -04:00
|
|
|
return FRAME_EVENT_IGNORED;
|
|
|
|
|
|
|
|
action_id = kbd_action(KEYMAP_EDIT, ev, NULL);
|
|
|
|
|
|
|
|
fs = find_form_state(doc_view, fc);
|
|
|
|
if (!fs || !fs->value) return FRAME_EVENT_OK;
|
|
|
|
|
|
|
|
switch (action_id) {
|
|
|
|
case ACT_EDIT_LEFT:
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-05-06 16:36:48 -04:00
|
|
|
if (fc->type == FC_TEXTAREA) {
|
|
|
|
status = textarea_op_left(fs, fc, utf8);
|
|
|
|
break;
|
|
|
|
}
|
2006-01-14 16:44:00 -05:00
|
|
|
if (utf8) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *new_value;
|
2006-07-27 03:51:10 -04:00
|
|
|
|
2006-05-06 16:03:26 -04:00
|
|
|
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
|
|
|
|
fs->state = new_value - fs->value;
|
2006-01-14 16:44:00 -05:00
|
|
|
} else
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-01-14 16:44:00 -05:00
|
|
|
fs->state = int_max(fs->state - 1, 0);
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_RIGHT:
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-07-23 09:23:19 -04:00
|
|
|
if (fc->type == FC_TEXTAREA) {
|
|
|
|
status = textarea_op_right(fs, fc, utf8);
|
|
|
|
break;
|
|
|
|
}
|
2006-01-14 16:44:00 -05:00
|
|
|
if (utf8) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *text = fs->value + fs->state;
|
|
|
|
char *end = strchr((const char *)text, '\0');
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2006-09-17 09:06:22 -04:00
|
|
|
utf8_to_unicode(&text, end);
|
2006-01-14 16:44:00 -05:00
|
|
|
fs->state = (int)(text - fs->value);
|
|
|
|
} else
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-01-14 16:44:00 -05:00
|
|
|
fs->state = int_min(fs->state + 1, strlen(fs->value));
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_HOME:
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-05-06 16:36:48 -04:00
|
|
|
if (fc->type == FC_TEXTAREA) {
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_home(fs, fc, utf8);
|
2006-05-06 16:36:48 -04:00
|
|
|
} else {
|
|
|
|
fs->state = 0;
|
|
|
|
}
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
if (fc->type == FC_TEXTAREA) {
|
|
|
|
status = textarea_op_home(fs, fc);
|
|
|
|
} else {
|
|
|
|
fs->state = 0;
|
|
|
|
}
|
2006-05-06 16:36:48 -04:00
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_UP:
|
|
|
|
if (fc->type != FC_TEXTAREA)
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
else
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_up(fs, fc, utf8);
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_up(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_DOWN:
|
|
|
|
if (fc->type != FC_TEXTAREA)
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
else
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_down(fs, fc, utf8);
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_down(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_END:
|
|
|
|
if (fc->type == FC_TEXTAREA) {
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_end(fs, fc, utf8);
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_end(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
|
|
|
fs->state = strlen(fs->value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_BEGINNING_OF_BUFFER:
|
|
|
|
if (fc->type == FC_TEXTAREA) {
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_bob(fs, fc, utf8);
|
2006-09-02 16:16:32 -04:00
|
|
|
fs->state_cell = 0;
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_bob(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
|
|
|
fs->state = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_END_OF_BUFFER:
|
|
|
|
if (fc->type == FC_TEXTAREA) {
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_eob(fs, fc, utf8);
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_eob(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
} else {
|
|
|
|
fs->state = strlen(fs->value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_OPEN_EXTERNAL:
|
|
|
|
if (form_field_is_readonly(fc))
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
else if (fc->type == FC_TEXTAREA)
|
|
|
|
textarea_edit(0, ses->tab->term, fs, doc_view, link);
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_COPY_CLIPBOARD:
|
|
|
|
set_clipboard_text(fs->value);
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_CUT_CLIPBOARD:
|
|
|
|
set_clipboard_text(fs->value);
|
|
|
|
if (!form_field_is_readonly(fc))
|
|
|
|
fs->value[0] = 0;
|
|
|
|
fs->state = 0;
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-09-02 16:16:32 -04:00
|
|
|
if (fc->type == FC_TEXTAREA)
|
|
|
|
fs->state_cell = 0;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_PASTE_CLIPBOARD:
|
|
|
|
if (form_field_is_readonly(fc)) break;
|
|
|
|
|
|
|
|
text = get_clipboard_text();
|
|
|
|
if (!text) break;
|
|
|
|
|
|
|
|
length = strlen(text);
|
|
|
|
if (length <= fc->maxlength) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *v = mem_realloc(fs->value, length + 1);
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (v) {
|
|
|
|
fs->value = v;
|
|
|
|
memmove(v, text, length + 1);
|
|
|
|
fs->state = strlen(fs->value);
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-09-02 16:16:32 -04:00
|
|
|
if (utf8 && fc->type == FC_TEXTAREA)
|
2006-05-06 16:36:48 -04:00
|
|
|
fs->state_cell = 0;
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
mem_free(text);
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_ENTER:
|
|
|
|
if (fc->type == FC_TEXTAREA) {
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
status = textarea_op_enter(fs, fc, utf8);
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
status = textarea_op_enter(fs, fc);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Set status to ok if either it is not possible to
|
|
|
|
* submit the form or the posting fails. */
|
|
|
|
/* FIXME: We should maybe have ACT_EDIT_ENTER_RELOAD */
|
|
|
|
if ((has_form_submit(fc->form)
|
2007-08-30 17:11:51 -04:00
|
|
|
&& !get_opt_bool("document.browse.forms.auto_submit", ses))
|
2008-08-01 20:17:50 -04:00
|
|
|
|| goto_link(ses, doc_view, link, 0)) {
|
2005-09-15 09:58:31 -04:00
|
|
|
if (ses->insert_mode == INSERT_MODE_ON)
|
|
|
|
ses->insert_mode = INSERT_MODE_OFF;
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_BACKSPACE:
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fs->state) {
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
if (utf8) {
|
2006-05-06 16:03:26 -04:00
|
|
|
int old_state = fs->state;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *new_value;
|
2006-07-27 03:51:10 -04:00
|
|
|
|
2006-05-06 16:03:26 -04:00
|
|
|
new_value = utf8_prevchar(fs->value + fs->state, 1, fs->value);
|
|
|
|
fs->state = new_value - fs->value;
|
|
|
|
|
|
|
|
if (old_state != fs->state) {
|
2006-05-06 16:36:48 -04:00
|
|
|
if (fc->type == FC_TEXTAREA)
|
|
|
|
fs->state_cell = 0;
|
2006-05-06 16:03:26 -04:00
|
|
|
length = strlen(fs->value + old_state) + 1;
|
|
|
|
memmove(new_value, fs->value + old_state, length);
|
|
|
|
}
|
|
|
|
} else
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-05-06 16:03:26 -04:00
|
|
|
{
|
|
|
|
length = strlen(fs->value + fs->state) + 1;
|
|
|
|
text = fs->value + fs->state;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
2006-05-06 16:03:26 -04:00
|
|
|
memmove(text - 1, text, length);
|
|
|
|
fs->state--;
|
|
|
|
}
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_DELETE:
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = strlen(fs->value);
|
|
|
|
if (fs->state >= length) {
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-01-14 16:44:00 -05:00
|
|
|
if (utf8) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *end = fs->value + length;
|
|
|
|
char *text = fs->value + fs->state;
|
|
|
|
char *old = text;
|
2006-01-14 16:44:00 -05:00
|
|
|
|
2006-09-17 09:06:22 -04:00
|
|
|
utf8_to_unicode(&text, end);
|
2006-01-14 16:44:00 -05:00
|
|
|
if (old != text) {
|
|
|
|
memmove(old, text,
|
|
|
|
(int)(end - text) + 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
text = fs->value + fs->state;
|
|
|
|
|
|
|
|
memmove(text, text + 1, length - fs->state);
|
|
|
|
break;
|
|
|
|
case ACT_EDIT_KILL_TO_BOL:
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs->state <= 0) {
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text = memrchr(fs->value, ASCII_LF, fs->state);
|
|
|
|
if (text) {
|
|
|
|
/* Leave the new-line character if it does not
|
|
|
|
* immediately precede the cursor. */
|
|
|
|
if (text != &fs->value[fs->state - 1])
|
|
|
|
text++;
|
|
|
|
} else {
|
|
|
|
text = fs->value;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = strlen(fs->value + fs->state) + 1;
|
|
|
|
memmove(text, fs->value + fs->state, length);
|
|
|
|
|
|
|
|
fs->state = (int) (text - fs->value);
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2006-05-06 16:03:26 -04:00
|
|
|
if (utf8) {
|
2006-09-02 16:16:32 -04:00
|
|
|
if (fc->type == FC_TEXTAREA)
|
2006-05-06 16:36:48 -04:00
|
|
|
fs->state_cell = 0;
|
2006-01-14 16:44:00 -05:00
|
|
|
}
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
case ACT_EDIT_KILL_TO_EOL:
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fs->value[fs->state]) {
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-04-20 13:43:37 -04:00
|
|
|
text = strchr((const char *)(fs->value + fs->state), ASCII_LF);
|
2005-09-15 09:58:31 -04:00
|
|
|
if (!text) {
|
|
|
|
fs->value[fs->state] = '\0';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs->value[fs->state] == ASCII_LF)
|
|
|
|
++text;
|
|
|
|
|
|
|
|
memmove(fs->value + fs->state, text, strlen(text) + 1);
|
|
|
|
break;
|
|
|
|
|
2006-06-23 00:07:52 -04:00
|
|
|
case ACT_EDIT_KILL_WORD_BACK:
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fs->state <= 0) {
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text = &fs->value[fs->state];
|
|
|
|
while (text > fs->value && isspace(*(text - 1)))
|
|
|
|
--text;
|
|
|
|
while (text > fs->value && !isspace(*(text - 1)))
|
|
|
|
--text;
|
|
|
|
if (*text == ASCII_LF
|
|
|
|
&& text != &fs->value[fs->state - 1])
|
|
|
|
text++;
|
|
|
|
|
|
|
|
length = strlen(fs->value + fs->state) + 1;
|
|
|
|
memmove(text, fs->value + fs->state, length);
|
|
|
|
|
|
|
|
fs->state = (int) (text - fs->value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACT_EDIT_MOVE_BACKWARD_WORD:
|
|
|
|
while (fs->state > 0
|
|
|
|
&& isspace(fs->value[fs->state - 1]))
|
|
|
|
--fs->state;
|
2006-07-27 03:51:10 -04:00
|
|
|
while (fs->state > 0
|
2006-06-23 00:07:52 -04:00
|
|
|
&& !isspace(fs->value[fs->state - 1]))
|
|
|
|
--fs->state;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACT_EDIT_MOVE_FORWARD_WORD:
|
|
|
|
while (isspace(fs->value[fs->state]))
|
|
|
|
++fs->state;
|
|
|
|
while (fs->value[fs->state]
|
|
|
|
&& !isspace(fs->value[fs->state]))
|
|
|
|
++fs->state;
|
|
|
|
while (isspace(fs->value[fs->state]))
|
|
|
|
++fs->state;
|
|
|
|
break;
|
|
|
|
|
2005-09-15 09:58:31 -04:00
|
|
|
case ACT_EDIT_AUTO_COMPLETE:
|
|
|
|
if (fc->type != FC_FILE
|
|
|
|
|| form_field_is_readonly(fc)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
file_form_menu(ses->tab->term, fs->value, fs);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACT_EDIT_CANCEL:
|
|
|
|
if (ses->insert_mode == INSERT_MODE_ON)
|
|
|
|
ses->insert_mode = INSERT_MODE_OFF;
|
|
|
|
else
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ACT_EDIT_REDRAW:
|
|
|
|
redraw_terminal_cls(ses->tab->term);
|
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (!check_kbd_textinput_key(ev)) {
|
|
|
|
status = FRAME_EVENT_IGNORED;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (form_field_is_readonly(fc)
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifndef CONFIG_UTF8
|
2006-08-05 12:38:15 -04:00
|
|
|
|| strlen(fs->value) >= fc->maxlength
|
2006-02-02 18:27:01 -05:00
|
|
|
|| !insert_in_string(&fs->value, fs->state, "?", 1)
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2006-02-02 18:27:01 -05:00
|
|
|
)
|
|
|
|
{
|
2005-09-15 09:58:31 -04:00
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
|
|
|
}
|
2006-08-05 12:38:15 -04:00
|
|
|
|
2006-09-17 09:12:47 -04:00
|
|
|
#ifdef CONFIG_UTF8
|
2007-05-20 08:31:02 -04:00
|
|
|
/* fs->value is in the charset of the terminal. */
|
|
|
|
ctext = u2cp_no_nbsp(get_kbd_key(ev),
|
2008-12-28 10:42:29 -05:00
|
|
|
get_terminal_codepage(ses->tab->term));
|
2007-01-02 01:45:02 -05:00
|
|
|
length = strlen(ctext);
|
2006-02-03 20:18:00 -05:00
|
|
|
|
2006-10-23 16:01:47 -04:00
|
|
|
if (strlen(fs->value) + length > fc->maxlength
|
2007-01-02 01:45:02 -05:00
|
|
|
|| !insert_in_string(&fs->value, fs->state, ctext, length)) {
|
2006-10-23 16:01:47 -04:00
|
|
|
status = FRAME_EVENT_OK;
|
|
|
|
break;
|
2006-01-14 16:44:00 -05:00
|
|
|
}
|
2006-10-23 16:01:47 -04:00
|
|
|
|
|
|
|
fs->state += length;
|
|
|
|
if (fc->type == FC_TEXTAREA)
|
|
|
|
fs->state_cell = 0;
|
2006-02-02 18:27:01 -05:00
|
|
|
#else
|
2005-09-15 09:58:31 -04:00
|
|
|
fs->value[fs->state++] = get_kbd_key(ev);
|
2006-09-17 09:12:47 -04:00
|
|
|
#endif /* CONFIG_UTF8 */
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
static char *
|
2018-09-09 13:18:53 -04:00
|
|
|
get_form_label(struct el_form_control *fc)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
assert(fc->form);
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_RESET:
|
|
|
|
return N_("Reset form");
|
|
|
|
case FC_BUTTON:
|
|
|
|
return N_("Harmless button");
|
|
|
|
case FC_HIDDEN:
|
|
|
|
return NULL;
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
if (!fc->form->action) return NULL;
|
|
|
|
|
|
|
|
if (fc->form->method == FORM_METHOD_GET)
|
|
|
|
return N_("Submit form to");
|
|
|
|
return N_("Post form to");
|
|
|
|
case FC_RADIO:
|
|
|
|
return N_("Radio button");
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
return N_("Checkbox");
|
|
|
|
case FC_SELECT:
|
|
|
|
return N_("Select field");
|
|
|
|
case FC_TEXT:
|
|
|
|
return N_("Text field");
|
|
|
|
case FC_TEXTAREA:
|
|
|
|
return N_("Text area");
|
|
|
|
case FC_FILE:
|
|
|
|
return N_("File upload");
|
|
|
|
case FC_PASSWORD:
|
|
|
|
return N_("Password field");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void
|
2019-04-21 06:27:40 -04:00
|
|
|
add_form_attr_to_string(struct string *string, struct terminal *term,
|
2021-01-02 10:20:27 -05:00
|
|
|
char *name, char *value)
|
2005-09-15 09:58:31 -04:00
|
|
|
{
|
|
|
|
add_to_string(string, ", ");
|
|
|
|
add_to_string(string, _(name, term));
|
|
|
|
if (value) {
|
|
|
|
add_char_to_string(string, ' ');
|
|
|
|
add_to_string(string, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-02 10:20:27 -05:00
|
|
|
char *
|
2005-09-15 09:58:31 -04:00
|
|
|
get_form_info(struct session *ses, struct document_view *doc_view)
|
|
|
|
{
|
|
|
|
struct terminal *term = ses->tab->term;
|
|
|
|
struct link *link = get_current_link(doc_view);
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *label, *key;
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string str;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
assert(link);
|
|
|
|
|
|
|
|
fc = get_link_form_control(link);
|
|
|
|
label = get_form_label(fc);
|
|
|
|
if (!label) return NULL;
|
|
|
|
|
|
|
|
if (!init_string(&str)) return NULL;
|
|
|
|
|
|
|
|
add_to_string(&str, _(label, term));
|
|
|
|
|
|
|
|
if (link->type != LINK_BUTTON && fc->name && fc->name[0]) {
|
|
|
|
add_form_attr_to_string(&str, term, N_("name"), fc->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_CHECKBOX:
|
|
|
|
case FC_RADIO:
|
|
|
|
{
|
|
|
|
struct form_state *fs = find_form_state(doc_view, fc);
|
|
|
|
|
|
|
|
if (!fs->value || !fs->value[0])
|
|
|
|
break;
|
|
|
|
|
|
|
|
add_form_attr_to_string(&str, term, N_("value"), fs->value);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case FC_TEXT:
|
|
|
|
case FC_PASSWORD:
|
|
|
|
case FC_FILE:
|
|
|
|
case FC_TEXTAREA:
|
|
|
|
{
|
|
|
|
struct uri *uri;
|
2021-01-02 10:20:27 -05:00
|
|
|
char *uristring;
|
2005-09-15 09:58:31 -04:00
|
|
|
|
|
|
|
if (form_field_is_readonly(fc)) {
|
|
|
|
add_form_attr_to_string(&str, term, N_("read only"), NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should we add info about entering insert mode or add info
|
|
|
|
* about submitting the form? */
|
|
|
|
if (ses->insert_mode == INSERT_MODE_OFF) {
|
|
|
|
key = get_keystroke(ACT_EDIT_ENTER, KEYMAP_EDIT);
|
|
|
|
|
|
|
|
if (!key) break;
|
|
|
|
|
|
|
|
if (form_field_is_readonly(fc))
|
|
|
|
label = N_("press %s to navigate");
|
|
|
|
else
|
|
|
|
label = N_("press %s to edit");
|
|
|
|
|
|
|
|
add_to_string(&str, " (");
|
|
|
|
add_format_to_string(&str, _(label, term), key);
|
|
|
|
add_char_to_string(&str, ')');
|
|
|
|
mem_free(key);
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fc->type == FC_TEXTAREA)
|
|
|
|
break;
|
|
|
|
|
|
|
|
assert(fc->form);
|
|
|
|
|
|
|
|
if (!fc->form->action
|
|
|
|
|| (has_form_submit(fc->form)
|
2007-08-28 12:41:18 -04:00
|
|
|
&& !get_opt_bool("document.browse.forms.auto_submit",
|
2007-08-30 17:11:51 -04:00
|
|
|
ses)))
|
2005-09-15 09:58:31 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
uri = get_uri(fc->form->action, 0);
|
|
|
|
if (!uri) break;
|
|
|
|
|
|
|
|
/* Add the uri with password and post info stripped */
|
|
|
|
uristring = get_uri_string(uri, URI_PUBLIC);
|
|
|
|
done_uri(uri);
|
|
|
|
|
|
|
|
if (!uristring) break;
|
|
|
|
|
|
|
|
key = get_keystroke(ACT_EDIT_ENTER, KEYMAP_EDIT);
|
|
|
|
if (!key) {
|
|
|
|
mem_free(uristring);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fc->form->method == FORM_METHOD_GET)
|
|
|
|
label = N_("press %s to submit to %s");
|
|
|
|
else
|
|
|
|
label = N_("press %s to post to %s");
|
|
|
|
|
|
|
|
add_to_string(&str, " (");
|
|
|
|
add_format_to_string(&str, _(label, term), key, uristring);
|
|
|
|
mem_free(uristring);
|
|
|
|
mem_free(key);
|
|
|
|
|
|
|
|
add_char_to_string(&str, ')');
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
add_char_to_string(&str, ' ');
|
|
|
|
|
|
|
|
assert(fc->form);
|
|
|
|
/* Add the uri with password and post info stripped */
|
|
|
|
add_string_uri_to_string(&str, fc->form->action, URI_PUBLIC);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case FC_HIDDEN:
|
|
|
|
case FC_RESET:
|
|
|
|
case FC_BUTTON:
|
|
|
|
case FC_SELECT:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (link->accesskey
|
2007-08-30 17:11:51 -04:00
|
|
|
&& get_opt_bool("document.browse.accesskey.display", ses)) {
|
2005-09-15 09:58:31 -04:00
|
|
|
add_to_string(&str, " (");
|
|
|
|
add_accesskey_to_string(&str, link->accesskey);
|
|
|
|
add_char_to_string(&str, ')');
|
|
|
|
}
|
|
|
|
|
|
|
|
return str.source;
|
|
|
|
}
|
2005-12-25 19:27:44 -05:00
|
|
|
|
|
|
|
static void
|
|
|
|
link_form_menu_func(struct terminal *term, void *link_number_, void *ses_)
|
|
|
|
{
|
|
|
|
struct session *ses = ses_;
|
|
|
|
struct document_view *doc_view;
|
2005-12-26 06:25:03 -05:00
|
|
|
int link_number = *(int *) link_number_;
|
|
|
|
|
|
|
|
mem_free(link_number_);
|
2005-12-25 19:27:44 -05:00
|
|
|
|
|
|
|
assert(term && ses);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
doc_view = current_frame(ses);
|
|
|
|
if (!doc_view) return;
|
|
|
|
|
|
|
|
assert(doc_view->vs && doc_view->document);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
jump_to_link_number(ses, doc_view, link_number);
|
2005-12-26 06:31:05 -05:00
|
|
|
refresh_view(ses, doc_view, 0);
|
2005-12-25 19:27:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2005-12-26 16:28:38 -05:00
|
|
|
link_form_menu(struct session *ses)
|
2005-12-25 19:27:44 -05:00
|
|
|
{
|
|
|
|
struct document_view *doc_view;
|
|
|
|
struct link *link;
|
|
|
|
struct menu_item *mi;
|
2018-09-09 13:18:53 -04:00
|
|
|
struct el_form_control *fc;
|
2005-12-25 19:27:44 -05:00
|
|
|
struct form *form;
|
|
|
|
|
2005-12-26 16:28:38 -05:00
|
|
|
assert(ses);
|
2005-12-25 19:27:44 -05:00
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
doc_view = current_frame(ses);
|
|
|
|
if (!doc_view) return;
|
|
|
|
|
|
|
|
assert(doc_view->vs && doc_view->document);
|
|
|
|
if_assert_failed return;
|
|
|
|
|
|
|
|
link = get_current_link(doc_view);
|
|
|
|
if (!link) return;
|
|
|
|
|
|
|
|
assert(link_is_form(link));
|
|
|
|
|
|
|
|
fc = get_link_form_control(link);
|
|
|
|
if (!fc) return;
|
|
|
|
|
|
|
|
form = fc->form;
|
|
|
|
|
|
|
|
mi = new_menu(FREE_LIST | FREE_TEXT | NO_INTL);
|
|
|
|
if (!mi) return;
|
|
|
|
|
|
|
|
foreach (fc, form->items) {
|
2021-01-02 10:20:27 -05:00
|
|
|
char *text;
|
|
|
|
char *rtext;
|
2005-12-25 19:27:44 -05:00
|
|
|
int link_number;
|
2019-04-21 06:27:40 -04:00
|
|
|
struct string str;
|
2005-12-25 19:27:44 -05:00
|
|
|
|
|
|
|
switch (fc->type) {
|
|
|
|
case FC_HIDDEN:
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case FC_SUBMIT:
|
|
|
|
case FC_IMAGE:
|
|
|
|
if (!form->action)
|
|
|
|
text = N_("Useless button");
|
|
|
|
else
|
|
|
|
text = N_("Submit button");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
text = get_form_label(fc);
|
|
|
|
}
|
|
|
|
|
|
|
|
link_number = get_form_control_link(doc_view->document, fc);
|
|
|
|
if (link_number < 0
|
|
|
|
|| !init_string(&str))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
assert(text);
|
2005-12-26 16:28:38 -05:00
|
|
|
add_to_string(&str, _(text, ses->tab->term));
|
2005-12-25 19:27:44 -05:00
|
|
|
|
|
|
|
rtext = fc->name;
|
|
|
|
if (!rtext) rtext = fc->alt;
|
|
|
|
|
|
|
|
add_to_menu(&mi, str.source, rtext, ACT_MAIN_NONE,
|
2005-12-26 06:25:03 -05:00
|
|
|
link_form_menu_func, intdup(link_number),
|
|
|
|
FREE_DATA);
|
2005-12-25 19:27:44 -05:00
|
|
|
}
|
|
|
|
|
2005-12-26 16:28:38 -05:00
|
|
|
do_menu(ses->tab->term, mi, ses, 1);
|
2005-12-25 19:27:44 -05:00
|
|
|
}
|