1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-06-28 01:35:32 +00:00

Merge branch 'elinks-0.12' into elinks-0.13

Conflicts:

	po/pl.po
	src/config/conf.c
	src/terminal/kbd.c
This commit is contained in:
Kalle Olavi Niemitalo 2008-02-03 22:22:00 +02:00 committed by Kalle Olavi Niemitalo
commit 5499926cc0
14 changed files with 719 additions and 341 deletions

16
NEWS
View File

@ -31,7 +31,7 @@ To be released as ELinks 0.12.0.
//////////////////////////////////////////////////////////////////////
This list now contains all the important changes from ELinks 0.11.0 to
ELinks 0.12.GIT (21052fafb0be0b87b7f376fb4edfa70304e365cc) and related
ELinks 0.12.GIT (054852ae234342765ab69da0eb95a18f96bd6028) and related
bug numbers. Each section is sorted by severity and grouped by topic.
The list no doubt includes several changes that are not really
@ -82,6 +82,7 @@ Miscellaneous:
* critical bug 869: long mailcap entry buffer overflow (non-security)
when downloading
* tabs opened by -remote now go behind existing dialogs
* major bug 503: various fixes in parsing and updating of elinks.conf
* Debian bug 257762: turn terminal transparency off by default
* bug 724: better parsing of escape sequences and control
sequences from the terminal
@ -131,10 +132,14 @@ Miscellaneous:
on enter
* enhancement: add support for parsing space separated CSS class
attribute values
* enhancement: make meta refresh content attribute parsing more tolerant
* enhancement: recognize meta http-equiv="cache-control" even if no
refresh
* enhancement: mouse wheel support over GPM (contrib/gpm-wheel.patch),
and on BSD via moused -z 4
* enhancement: 24-bit truecolor mode
* enhancement 622: -dump-color-mode
* enhancement 994: treat only termios.c_cc[VERASE] as "Backspace"
* enhancement: support Ctrl+Alt+letter key combinations
* enhancement 381: reduce memory consumption of codepages and some
other arrays
@ -166,6 +171,12 @@ Changes in the experimental ECMAScript support:
* enhancement: added document.location.href, input.selectedIndex,
window.setTimeout, window.status
Changes in the experimental NNTP client:
* HTML escape header field values
* Add support for handling RFC2047 encoded words
* Improve listing of articles for groups
Changes in the experimental SGML/DOM implementation:
* enhancement: minimalistic RSS renderer
@ -231,6 +242,8 @@ have already been considered.
memory
- (bugfix 920) assertion priority >= prev_priority failed: queue is
not sorted
- (bugfix 968) assertion width > 0 failed in copy_chars called from
justify_line
* Already backported to a previous release but not listed there:
- (enhancement) Activate link only when onClick returns true.
Fixed bug 786 in ELinks 0.11.2.
@ -296,6 +309,7 @@ have already been considered.
this as part of the UTF-8 support.
* enhancement in user SMJS: elinks.alert no longer displays as an
"error"
* Really retry forever when connection.retries = 0
//////////////////////////////////////////////////////////////////////
ELinks 0.11.3.GIT now:

View File

@ -57,16 +57,63 @@
* value to an option, but sometimes you may want to first create the option
* ;). Then this will come handy. */
struct conf_parsing_state {
/** This part may be copied to a local variable as a bookmark
* and restored later. So it must not contain any pointers
* that would have to be freed in that situation. */
struct conf_parsing_pos {
/** Points to the next character to be parsed from the
* configuration file. */
unsigned char *look;
/* Skip comments and whitespace,
* setting *@line to the number of lines skipped. */
static unsigned char *
skip_white(unsigned char *start, int *line)
/** The line number corresponding to #look. This is
* shown in error messages. */
int line;
} pos;
/** When ELinks is rewriting the configuration file, @c mirrored
* indicates the end of the part that has already been copied
* to the mirror string. Otherwise, @c mirrored is not used.
*
* @invariant @c mirrored @<= @c pos.look */
unsigned char *mirrored;
/** File name for error messages. If NULL then do not display
* error messages. */
const unsigned char *filename;
};
/** Tell the user about an error in the configuration file.
* @return @a err, for convenience. */
static enum parse_error
show_parse_error(const struct conf_parsing_state *state, enum parse_error err)
{
static const unsigned char error_msg[][40] = {
"no error", /* ERROR_NONE */
"unknown command", /* ERROR_COMMAND */
"parse error", /* ERROR_PARSE */
"unknown option", /* ERROR_OPTION */
"bad value", /* ERROR_VALUE */
"no memory left", /* ERROR_NOMEM */
};
if (state->filename) {
fprintf(stderr, "%s:%d: %s\n",
state->filename, state->pos.line, error_msg[err]);
}
return err;
}
/** Skip comments and whitespace. */
static void
skip_white(struct conf_parsing_pos *pos)
{
unsigned char *start = pos->look;
while (*start) {
while (isspace(*start)) {
if (*start == '\n') {
(*line)++;
pos->line++;
}
start++;
}
@ -74,128 +121,261 @@ skip_white(unsigned char *start, int *line)
if (*start == '#') {
start += strcspn(start, "\n");
} else {
return start;
pos->look = start;
return;
}
}
return start;
pos->look = start;
}
/** Skip a quoted string.
* This function allows "mismatching quotes' because str_rd() does so. */
static void
skip_quoted(struct conf_parsing_pos *pos)
{
assert(isquote(*pos->look));
if_assert_failed return;
pos->look++;
for (;;) {
if (!*pos->look)
return;
if (isquote(*pos->look)) {
pos->look++;
return;
}
if (*pos->look == '\\' && pos->look[1])
pos->look++;
if (*pos->look == '\n')
pos->line++;
pos->look++;
}
}
/** Skip the value of an option.
*
* This job is normally done by the reader function that corresponds
* to the type of the option. However, if ELinks does not recognize
* the name of the option, it cannot look up the type and has to use
* this function instead. */
static void
skip_option_value(struct conf_parsing_pos *pos)
{
if (isquote(*pos->look)) {
/* Looks like OPT_STRING, OPT_CODEPAGE, OPT_LANGUAGE,
* or OPT_COLOR. */
skip_quoted(pos);
} else {
/* Looks like OPT_BOOL, OPT_INT, or OPT_LONG. */
while (isasciialnum(*pos->look) || *pos->look == '.'
|| *pos->look == '+' || *pos->look == '-')
pos->look++;
}
}
/** Skip to the next newline or comment that is not part of a quoted
* string. When ELinks hits a parse error in the configuration file,
* it calls this in order to find the place where is should resume
* parsing. This is intended to prevent ELinks from treating words
* in strings as commands. */
static void
skip_to_unquoted_newline_or_comment(struct conf_parsing_pos *pos)
{
while (*pos->look && *pos->look != '#' && *pos->look != '\n') {
if (isquote(*pos->look))
skip_quoted(pos);
else
pos->look++;
}
}
/* Parse a command. Returns error code. */
/* If dynamic string credentials are supplied, we will mirror the command at
* the end of the string; however, we won't load the option value to the tree,
* and we will even write option value from the tree to the output string. We
* will only possibly set OPT_WATERMARK flag to the option (if enabled). */
* and we will even write option value from the tree to the output string.
* We will only possibly set or clear OPT_MUST_SAVE flag in the option. */
static enum parse_error
parse_set_common(struct option *opt_tree, unsigned char **file, int *line,
struct string *mirror, int is_system_conf, int want_domain)
parse_set_common(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf, int want_domain)
{
unsigned char *orig_pos = *file;
unsigned char *domain_name = NULL;
unsigned char *optname;
unsigned char bin;
const unsigned char *domain_orig = NULL;
size_t domain_len = 0;
unsigned char *domain_copy = NULL;
const unsigned char *optname_orig;
size_t optname_len;
unsigned char *optname_copy;
*file = skip_white(*file, line);
if (!**file) return ERROR_PARSE;
skip_white(&state->pos);
if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
if (want_domain) {
domain_name = *file;
while (isident(**file) || **file == '*' || **file == '.' || **file == '+')
(*file)++;
domain_orig = state->pos.look;
while (isident(*state->pos.look) || *state->pos.look == '*'
|| *state->pos.look == '.' || *state->pos.look == '+')
state->pos.look++;
domain_len = state->pos.look - domain_orig;
bin = **file;
**file = '\0';
domain_name = stracpy(domain_name);
**file = bin;
if (!domain_name) return ERROR_NOMEM;
*file = skip_white(*file, line);
skip_white(&state->pos);
}
/* Option name */
optname = *file;
while (isident(**file) || **file == '*' || **file == '.' || **file == '+')
(*file)++;
optname_orig = state->pos.look;
while (isident(*state->pos.look) || *state->pos.look == '*'
|| *state->pos.look == '.' || *state->pos.look == '+')
state->pos.look++;
optname_len = state->pos.look - optname_orig;
bin = **file;
**file = '\0';
optname = stracpy(optname);
**file = bin;
if (!optname) return ERROR_NOMEM;
*file = skip_white(*file, line);
skip_white(&state->pos);
/* Equal sign */
if (**file != '=') { mem_free(optname); return ERROR_PARSE; }
(*file)++; /* '=' */
*file = skip_white(*file, line);
if (!**file) { mem_free(optname); return ERROR_VALUE; }
if (*state->pos.look != '=')
return show_parse_error(state, ERROR_PARSE);
state->pos.look++; /* '=' */
skip_white(&state->pos);
if (!*state->pos.look)
return show_parse_error(state, ERROR_VALUE);
/* Mirror what we already have */
if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos);
optname_copy = memacpy(optname_orig, optname_len);
if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
if (want_domain) {
domain_copy = memacpy(domain_orig, domain_len);
if (!domain_copy) {
mem_free(optname_copy);
return show_parse_error(state, ERROR_NOMEM);
}
}
/* Option value */
{
struct option *opt;
unsigned char *val;
const struct conf_parsing_pos pos_before_value = state->pos;
if (want_domain && *domain_name) {
if (want_domain && *domain_copy) {
struct option *domain_tree;
domain_tree = get_domain_tree(domain_name);
domain_tree = get_domain_tree(domain_copy);
if (!domain_tree) {
mem_free(domain_name);
mem_free(optname);
return ERROR_NOMEM;
mem_free(domain_copy);
mem_free(optname_copy);
skip_option_value(&state->pos);
return show_parse_error(state, ERROR_NOMEM);
}
if (mirror) {
opt = get_opt_rec_real(domain_tree, optname);
opt = get_opt_rec_real(domain_tree,
optname_copy);
} else {
opt = get_opt_rec(opt_tree, optname);
opt = get_opt_rec(opt_tree, optname_copy);
if (opt) {
opt = get_option_shadow(opt, opt_tree,
domain_tree);
if (!opt) {
mem_free(domain_name);
mem_free(optname);
return ERROR_NOMEM;
mem_free(domain_copy);
mem_free(optname_copy);
skip_option_value(&state->pos);
return show_parse_error(state,
ERROR_NOMEM);
}
}
}
} else {
opt = mirror ? get_opt_rec_real(opt_tree, optname) : get_opt_rec(opt_tree, optname);
opt = mirror
? get_opt_rec_real(opt_tree, optname_copy)
: get_opt_rec(opt_tree, optname_copy);
}
if (want_domain)
mem_free(domain_name);
mem_free(optname);
mem_free(domain_copy);
domain_copy = NULL;
mem_free(optname_copy);
optname_copy = NULL;
if (!opt || (opt->flags & OPT_HIDDEN))
if (!opt || (opt->flags & OPT_HIDDEN)) {
show_parse_error(state, ERROR_OPTION);
skip_option_value(&state->pos);
return ERROR_OPTION;
/* TODO: Distinguish between two scenarios:
* - A newer version of ELinks has saved an
* option that this version does not recognize.
* The option must be preserved. (This works.)
* - The user has added an option, saved
* elinks.conf, restarted ELinks, deleted the
* option, and is now saving elinks.conf again.
* The option should be rewritten to "unset".
* (This does not work yet.)
* In both cases, ELinks has no struct option
* for that name. Possible fixes:
* - If the tree has OPT_AUTOCREATE, then
* assume the user had created that option,
* and rewrite it to "unset". Otherwise,
* keep it.
* - When the user deletes an option, just mark
* it with OPT_DELETED, and keep it in memory
* as long as OPT_TOUCHED is set. */
}
if (!option_types[opt->type].read)
return ERROR_VALUE;
val = option_types[opt->type].read(opt, file, line);
if (!val) return ERROR_VALUE;
if (mirror) {
if (opt->flags & OPT_DELETED)
opt->flags &= ~OPT_WATERMARK;
else
opt->flags |= OPT_WATERMARK;
if (option_types[opt->type].write) {
option_types[opt->type].write(opt, mirror);
}
} else if (!option_types[opt->type].set
|| !option_types[opt->type].set(opt, val)) {
mem_free(val);
if (!option_types[opt->type].read) {
show_parse_error(state, ERROR_VALUE);
skip_option_value(&state->pos);
return ERROR_VALUE;
}
/* This is not needed since this will be WATERMARK'd when
* saving it. We won't need to save it as touched. */
/* if (!str) opt->flags |= OPT_TOUCHED; */
val = option_types[opt->type].read(opt, &state->pos.look,
&state->pos.line);
if (!val) {
/* The reader function failed. Jump back to
* the beginning of the value and skip it with
* the generic code. For the error message,
* use the line number at the beginning of the
* value, because the ending position is not
* interesting if there is an unclosed quote. */
state->pos = pos_before_value;
show_parse_error(state, ERROR_VALUE);
skip_option_value(&state->pos);
return ERROR_VALUE;
}
if (!mirror) {
/* loading a configuration file */
if (!option_types[opt->type].set
|| !option_types[opt->type].set(opt, val)) {
mem_free(val);
return show_parse_error(state, ERROR_VALUE);
}
} else if (is_system_conf) {
/* scanning a file that will not be rewritten */
struct option *flagsite = indirect_option(opt);
if (!(flagsite->flags & OPT_DELETED)
&& option_types[opt->type].equals
&& option_types[opt->type].equals(opt, val))
flagsite->flags &= ~OPT_MUST_SAVE;
else
flagsite->flags |= OPT_MUST_SAVE;
} else {
/* rewriting a configuration file */
struct option *flagsite = indirect_option(opt);
if (flagsite->flags & OPT_DELETED) {
/* Replace the "set" command with an
* "unset" command. */
add_to_string(mirror, "unset ");
add_bytes_to_string(mirror, optname_orig,
optname_len);
state->mirrored = state->pos.look;
} else if (option_types[opt->type].write) {
add_bytes_to_string(mirror, state->mirrored,
pos_before_value.look
- state->mirrored);
option_types[opt->type].write(opt, mirror);
state->mirrored = state->pos.look;
}
/* Remember that the option need not be
* written to the end of the file. */
flagsite->flags &= ~OPT_MUST_SAVE;
}
mem_free(val);
}
@ -203,64 +383,91 @@ parse_set_common(struct option *opt_tree, unsigned char **file, int *line,
}
static enum parse_error
parse_set_domain(struct option *opt_tree, unsigned char **file, int *line,
parse_set_domain(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
return parse_set_common(opt_tree, file, line, mirror, is_system_conf, 1);
return parse_set_common(opt_tree, state, mirror, is_system_conf, 1);
}
static enum parse_error
parse_set(struct option *opt_tree, unsigned char **file, int *line,
parse_set(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
return parse_set_common(opt_tree, file, line, mirror, is_system_conf, 0);
return parse_set_common(opt_tree, state, mirror, is_system_conf, 0);
}
static enum parse_error
parse_unset(struct option *opt_tree, unsigned char **file, int *line,
parse_unset(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
unsigned char *orig_pos = *file;
unsigned char *optname;
unsigned char bin;
const unsigned char *optname_orig;
size_t optname_len;
unsigned char *optname_copy;
/* XXX: This does not handle the autorewriting well and is mostly a
* quick hack than anything now. --pasky */
*file = skip_white(*file, line);
if (!**file) return ERROR_PARSE;
skip_white(&state->pos);
if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
/* Option name */
optname = *file;
while (isident(**file) || **file == '*' || **file == '.' || **file == '+')
(*file)++;
optname_orig = state->pos.look;
while (isident(*state->pos.look) || *state->pos.look == '*'
|| *state->pos.look == '.' || *state->pos.look == '+')
state->pos.look++;
optname_len = state->pos.look - optname_orig;
bin = **file;
**file = '\0';
optname = stracpy(optname);
**file = bin;
if (!optname) return ERROR_NOMEM;
/* Mirror what we have */
if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos);
optname_copy = memacpy(optname_orig, optname_len);
if (!optname_copy) return show_parse_error(state, ERROR_NOMEM);
{
struct option *opt;
opt = get_opt_rec_real(opt_tree, optname);
mem_free(optname);
opt = get_opt_rec_real(opt_tree, optname_copy);
mem_free(optname_copy);
optname_copy = NULL;
if (!opt || (opt->flags & OPT_HIDDEN))
return ERROR_OPTION;
if (!opt || (opt->flags & OPT_HIDDEN)) {
/* The user wanted to delete the option, and
* it has already been deleted; this is not an
* error. This might happen if a version of
* ELinks has a built-in URL rewriting rule,
* the user disables it, and a later version
* no longer has it. */
return ERROR_NONE;
}
if (!mirror) {
/* loading a configuration file */
if (opt->flags & OPT_ALLOC) delete_option(opt);
} else {
if (opt->flags & OPT_DELETED)
opt->flags |= OPT_WATERMARK;
else mark_option_as_deleted(opt);
} else if (is_system_conf) {
/* scanning a file that will not be rewritten */
struct option *flagsite = indirect_option(opt);
if (flagsite->flags & OPT_DELETED)
flagsite->flags &= ~OPT_MUST_SAVE;
else
opt->flags &= ~OPT_WATERMARK;
flagsite->flags |= OPT_MUST_SAVE;
} else {
/* rewriting a configuration file */
struct option *flagsite = indirect_option(opt);
if (flagsite->flags & OPT_DELETED) {
/* The "unset" command is already in the file,
* and unlike with "set", there is no value
* to be updated. */
} else if (option_types[opt->type].write) {
/* Replace the "unset" command with a
* "set" command. */
add_to_string(mirror, "set ");
add_bytes_to_string(mirror, optname_orig,
optname_len);
add_to_string(mirror, " = ");
option_types[opt->type].write(opt, mirror);
state->mirrored = state->pos.look;
}
/* Remember that the option need not be
* written to the end of the file. */
flagsite->flags &= ~OPT_MUST_SAVE;
}
}
@ -268,73 +475,90 @@ parse_unset(struct option *opt_tree, unsigned char **file, int *line,
}
static enum parse_error
parse_bind(struct option *opt_tree, unsigned char **file, int *line,
parse_bind(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
unsigned char *orig_pos = *file, *next_pos;
unsigned char *keymap, *keystroke, *action;
enum parse_error err = ERROR_NONE;
struct conf_parsing_pos before_error;
*file = skip_white(*file, line);
if (!*file) return ERROR_PARSE;
skip_white(&state->pos);
if (!*state->pos.look) return show_parse_error(state, ERROR_PARSE);
/* Keymap */
keymap = option_types[OPT_STRING].read(NULL, file, line);
*file = skip_white(*file, line);
if (!keymap || !**file)
return ERROR_OPTION;
before_error = state->pos;
keymap = option_types[OPT_STRING].read(NULL, &state->pos.look,
&state->pos.line);
skip_white(&state->pos);
if (!keymap || !*state->pos.look) {
state->pos = before_error;
return show_parse_error(state, ERROR_PARSE);
}
/* Keystroke */
keystroke = option_types[OPT_STRING].read(NULL, file, line);
*file = skip_white(*file, line);
if (!keystroke || !**file) {
mem_free(keymap);
return ERROR_OPTION;
before_error = state->pos;
keystroke = option_types[OPT_STRING].read(NULL, &state->pos.look,
&state->pos.line);
skip_white(&state->pos);
if (!keystroke || !*state->pos.look) {
mem_free(keymap); mem_free_if(keystroke);
state->pos = before_error;
return show_parse_error(state, ERROR_PARSE);
}
/* Equal sign */
*file = skip_white(*file, line);
if (**file != '=') {
skip_white(&state->pos);
if (*state->pos.look != '=') {
mem_free(keymap); mem_free(keystroke);
return ERROR_PARSE;
return show_parse_error(state, ERROR_PARSE);
}
(*file)++; /* '=' */
state->pos.look++; /* '=' */
*file = skip_white(*file, line);
if (!**file) {
skip_white(&state->pos);
if (!*state->pos.look) {
mem_free(keymap); mem_free(keystroke);
return ERROR_PARSE;
return show_parse_error(state, ERROR_PARSE);
}
/* Action */
next_pos = *file;
action = option_types[OPT_STRING].read(NULL, file, line);
before_error = state->pos;
action = option_types[OPT_STRING].read(NULL, &state->pos.look,
&state->pos.line);
if (!action) {
mem_free(keymap);
return ERROR_VALUE;
mem_free(keymap); mem_free(keystroke);
state->pos = before_error;
return show_parse_error(state, ERROR_PARSE);
}
if (mirror) {
/* Mirror what we already have */
unsigned char *act_str = bind_act(keymap, keystroke);
if (act_str) {
add_bytes_to_string(mirror, orig_pos,
next_pos - orig_pos);
add_to_string(mirror, act_str);
mem_free(act_str);
} else {
err = ERROR_VALUE;
}
} else {
if (!mirror) {
/* loading a configuration file */
/* We don't bother to bind() if -default-keys. */
if (!get_cmd_opt_bool("default-keys")
&& bind_do(keymap, keystroke, action, is_system_conf)) {
/* bind_do() tried but failed. */
err = ERROR_VALUE;
err = show_parse_error(state, ERROR_VALUE);
} else {
err = ERROR_NONE;
}
} else if (is_system_conf) {
/* scanning a file that will not be rewritten */
/* TODO */
} else {
/* rewriting a configuration file */
/* Mirror what we already have. If the keystroke has
* been unbound, then act_str is simply "none" and
* this does not require special handling. */
unsigned char *act_str = bind_act(keymap, keystroke);
if (act_str) {
add_bytes_to_string(mirror, state->mirrored,
before_error.look - state->mirrored);
add_to_string(mirror, act_str);
mem_free(act_str);
state->mirrored = state->pos.look;
} else {
err = show_parse_error(state, ERROR_VALUE);
}
}
mem_free(keymap); mem_free(keystroke); mem_free(action);
return err;
@ -344,27 +568,31 @@ static int load_config_file(unsigned char *, unsigned char *, struct option *,
struct string *, int);
static enum parse_error
parse_include(struct option *opt_tree, unsigned char **file, int *line,
parse_include(struct option *opt_tree, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
unsigned char *orig_pos = *file;
unsigned char *fname;
struct string dumbstring;
struct conf_parsing_pos before_error;
if (!init_string(&dumbstring)) return ERROR_NOMEM;
if (!init_string(&dumbstring))
return show_parse_error(state, ERROR_NOMEM);
*file = skip_white(*file, line);
if (!*file) return ERROR_PARSE;
/* File name */
fname = option_types[OPT_STRING].read(NULL, file, line);
if (!fname) {
skip_white(&state->pos);
if (!*state->pos.look) {
done_string(&dumbstring);
return ERROR_VALUE;
return show_parse_error(state, ERROR_PARSE);
}
/* Mirror what we already have */
if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos);
/* File name */
before_error = state->pos;
fname = option_types[OPT_STRING].read(NULL, &state->pos.look,
&state->pos.line);
if (!fname) {
done_string(&dumbstring);
state->pos = before_error;
return show_parse_error(state, ERROR_PARSE);
}
/* We want load_config_file() to watermark stuff, but not to load
* anything, polluting our beloved options tree - thus, we will feed it
@ -375,10 +603,11 @@ parse_include(struct option *opt_tree, unsigned char **file, int *line,
* CONFDIR/<otherfile> ;). --pasky */
if (load_config_file(fname[0] == '/' ? (unsigned char *) ""
: elinks_home,
fname, opt_tree, &dumbstring, is_system_conf)) {
fname, opt_tree,
mirror ? &dumbstring : NULL, 1)) {
done_string(&dumbstring);
mem_free(fname);
return ERROR_VALUE;
return show_parse_error(state, ERROR_VALUE);
}
done_string(&dumbstring);
@ -388,13 +617,13 @@ parse_include(struct option *opt_tree, unsigned char **file, int *line,
struct parse_handler {
unsigned char *command;
const unsigned char *command;
enum parse_error (*handler)(struct option *opt_tree,
unsigned char **file, int *line,
struct conf_parsing_state *state,
struct string *mirror, int is_system_conf);
};
static struct parse_handler parse_handlers[] = {
static const struct parse_handler parse_handlers[] = {
{ "set_domain", parse_set_domain },
{ "set", parse_set },
{ "unset", parse_unset },
@ -404,102 +633,113 @@ static struct parse_handler parse_handlers[] = {
};
enum parse_error
parse_config_command(struct option *options, unsigned char **file, int *line,
static enum parse_error
parse_config_command(struct option *options, struct conf_parsing_state *state,
struct string *mirror, int is_system_conf)
{
struct parse_handler *handler;
const struct parse_handler *handler;
/* If we're mirroring, then everything up to this point must
* have already been mirrored. */
assert(mirror == NULL || state->mirrored == state->pos.look);
if_assert_failed return show_parse_error(state, ERROR_PARSE);
for (handler = parse_handlers; handler->command;
handler++) {
int cmdlen = strlen(handler->command);
if (!strncmp(*file, handler->command, cmdlen)
&& isspace((*file)[cmdlen])) {
if (!strncmp(state->pos.look, handler->command, cmdlen)
&& isspace(state->pos.look[cmdlen])) {
enum parse_error err;
struct string mirror2 = NULL_STRING;
struct string *m2 = NULL;
/* Mirror what we already have */
if (mirror && init_string(&mirror2)) {
m2 = &mirror2;
add_bytes_to_string(m2, *file, cmdlen);
}
*file += cmdlen;
err = handler->handler(options, file, line, m2,
state->pos.look += cmdlen;
err = handler->handler(options, state, mirror,
is_system_conf);
if (!err && mirror && m2) {
add_string_to_string(mirror, m2);
if (mirror) {
/* Mirror any characters that the handler
* consumed but did not already mirror. */
add_bytes_to_string(mirror, state->mirrored,
state->pos.look - state->mirrored);
state->mirrored = state->pos.look;
}
if (m2) done_string(m2);
return err;
}
}
return ERROR_COMMAND;
return show_parse_error(state, ERROR_COMMAND);
}
#ifdef CONFIG_EXMODE
enum parse_error
parse_config_exmode_command(unsigned char *cmd)
{
struct conf_parsing_state state = {{ 0 }};
state.pos.look = cmd;
state.pos.line = 0;
state.mirrored = NULL; /* not read because mirror is NULL too */
state.filename = NULL; /* prevent error messages */
return parse_config_command(config_options, &state, NULL, 0);
}
#endif /* CONFIG_EXMODE */
void
parse_config_file(struct option *options, unsigned char *name,
unsigned char *file, struct string *mirror,
int is_system_conf)
{
int line = 1;
struct conf_parsing_state state = {{ 0 }};
int error_occurred = 0;
enum parse_error err = 0;
enum verbose_level verbose = get_cmd_opt_int("verbose");
unsigned char error_msg[][40] = {
"no error",
"parse error",
"unknown command",
"unknown option",
"bad value",
"no memory left",
};
while (file && *file) {
unsigned char *orig_pos = file;
state.pos.look = file;
state.pos.line = 1;
state.mirrored = file;
if (!mirror && get_cmd_opt_int("verbose") >= VERBOSE_WARNINGS)
state.filename = name;
while (state.pos.look && *state.pos.look) {
enum parse_error err = ERROR_NONE;
/* Skip all possible comments and whitespace. */
file = skip_white(file, &line);
skip_white(&state.pos);
/* Mirror what we already have */
if (mirror)
add_bytes_to_string(mirror, orig_pos, file - orig_pos);
/* Second chance to escape from the hell. */
if (!*file) break;
err = parse_config_command(options, &file, &line, mirror,
is_system_conf);
if (err == ERROR_COMMAND) {
orig_pos = file;
/* Jump over this crap we can't understand. */
while (!isspace(*file) && *file != '#' && *file)
file++;
/* Mirror what we already have */
if (mirror) add_bytes_to_string(mirror, orig_pos,
file - orig_pos);
if (mirror) {
add_bytes_to_string(mirror, state.mirrored,
state.pos.look - state.mirrored);
state.mirrored = state.pos.look;
}
if (!mirror && err) {
/* TODO: Make this a macro and report error directly
* as it's stumbled upon; line info may not be accurate
* anymore now (?). --pasky */
if (verbose >= VERBOSE_WARNINGS) {
fprintf(stderr, "%s:%d: %s\n",
name, line, error_msg[err]);
error_occurred = 1;
/* Second chance to escape from the hell. */
if (!*state.pos.look) break;
err = parse_config_command(options, &state, mirror,
is_system_conf);
switch (err) {
case ERROR_NONE:
break;
case ERROR_COMMAND:
case ERROR_PARSE:
/* Jump over this crap we can't understand. */
skip_to_unquoted_newline_or_comment(&state.pos);
/* Mirror what we already have */
if (mirror) {
add_bytes_to_string(mirror, state.mirrored,
state.pos.look - state.mirrored);
state.mirrored = state.pos.look;
}
err = 0;
/* fall through */
default:
error_occurred = 1;
break;
}
}
if (!error_occurred) return;
if (!error_occurred || !state.filename) return;
/* If an error occurred make sure that the user is notified and is able
* to see it. First sound the bell. Then, if the text viewer is going to
@ -605,7 +845,6 @@ load_config(void)
static int indentation = 2;
/* 0 -> none, 1 -> only option full name+type, 2 -> only desc, 3 -> both */
static int comments = 3;
static int touching = 0;
static inline unsigned char *
conf_i18n(unsigned char *s, int i18n)
@ -634,12 +873,6 @@ smart_config_output_fn(struct string *string, struct option *option,
if (option->type == OPT_ALIAS)
return;
/* XXX: OPT_LANGUAGE shouldn't have any bussiness here, but we're just
* weird in that area. */
if (touching && !(option->flags & OPT_TOUCHED)
&& option->type != OPT_LANGUAGE)
return;
switch (action) {
case 0:
if (!(comments & 1)) break;
@ -765,25 +998,26 @@ create_config_string(unsigned char *prefix, unsigned char *name)
if (!init_string(&config)) return NULL;
if (savestyle == 3) {
touching = 1;
savestyle = 1;
} else {
touching = 0;
{
int set_all = (savestyle == 1 || savestyle == 2);
struct domain_tree *domain;
prepare_mustsave_flags(options->value.tree, set_all);
foreach (domain, domain_trees) {
prepare_mustsave_flags(domain->tree->value.tree,
set_all);
}
}
if (savestyle == 2) watermark_deleted_options(options->value.tree);
/* Scaring. */
if (savestyle == 2
|| (savestyle < 2
&& (load_config_file(prefix, name, options, &config, 0)
|| !config.length))) {
|| load_config_file(prefix, name, options, &config, 0)
|| !config.length) {
/* At first line, and in English, write ELinks version, may be
* of some help in future. Please keep that format for it.
* --Zas */
add_to_string(&config, "## ELinks " VERSION " configuration file\n\n");
assert(savestyle >= 0 && savestyle <= 2);
assert(savestyle >= 0 && savestyle <= 3);
switch (savestyle) {
case 0:
add_to_string(&config, conf_i18n(N_(
@ -793,7 +1027,7 @@ create_config_string(unsigned char *prefix, unsigned char *name)
"## and all your formatting, own comments etc will be kept as-is.\n"),
i18n));
break;
case 1:
case 1: case 3:
add_to_string(&config, conf_i18n(N_(
"## This is ELinks configuration file. You can edit it manually,\n"
"## if you wish so; this file is edited by ELinks when you save\n"
@ -844,7 +1078,6 @@ create_config_string(unsigned char *prefix, unsigned char *name)
domain->tree->value.tree,
NULL, 0,
smart_config_output_fn);
unmark_options_tree(domain->tree->value.tree);
}
smart_config_output_fn_domain = NULL;
@ -866,8 +1099,6 @@ create_config_string(unsigned char *prefix, unsigned char *name)
done_string(&tmpstring);
get_me_out:
unmark_options_tree(options->value.tree);
return config.source;
}
@ -895,6 +1126,13 @@ write_config_file(unsigned char *prefix, unsigned char *name,
if (ssi) {
secure_fputs(ssi, cfg_str);
ret = secure_close(ssi);
if (!ret) {
struct domain_tree *domain;
untouch_options(config_options->value.tree);
foreach (domain, domain_trees)
untouch_options(domain->tree->value.tree);
}
}
write_config_dialog(term, config_file, secsave_errno, ret);

View File

@ -14,10 +14,9 @@ enum parse_error {
};
void load_config(void);
enum parse_error parse_config_command(struct option *options,
unsigned char **file, int *line,
struct string *mirror,
int is_system_conf);
#ifdef CONFIG_EXMODE
enum parse_error parse_config_exmode_command(unsigned char *cmd);
#endif
void parse_config_file(struct option *options, unsigned char *name,
unsigned char *file, struct string *mirror,
int is_system_conf);

View File

@ -158,7 +158,14 @@ debug_check_option_syntax(struct option *option)
/* Ugly kludge */
static int no_autocreate = 0;
/* Get record of option of given name, or NULL if there's no such option. */
/** Get record of option of given name, or NULL if there's no such option.
*
* If the specified option is an ::OPT_ALIAS, this function returns the
* alias, rather than the option to which the alias refers. It must
* work this way because the alias may have the ::OPT_ALIAS_NEGATE flag.
* Instead, if the caller tries to read or set the value of the alias,
* the functions associated with ::OPT_ALIAS will forward the operation
* to the underlying option. However, see indirect_option(). */
struct option *
get_opt_rec(struct option *tree, const unsigned char *name_)
{
@ -242,6 +249,27 @@ get_opt_rec_real(struct option *tree, const unsigned char *name)
return opt;
}
/** If @a opt is an alias, return the option to which it refers.
*
* @warning Because the alias may have the ::OPT_ALIAS_NEGATE flag,
* the caller must not access the value of the returned option as if
* it were also the value of the alias. However, it is safe to access
* flags such as ::OPT_MUST_SAVE and ::OPT_DELETED. */
struct option *
indirect_option(struct option *alias)
{
struct option *real;
if (alias->type != OPT_ALIAS) return alias; /* not an error */
real = get_opt_rec(config_options, alias->value.string);
assertm(real != NULL, "%s aliased to unknown option %s!",
alias->name, alias->value.string);
if_assert_failed return alias;
return real;
}
/* Fetch pointer to value of certain option. It is guaranteed to never return
* NULL. Note that you are supposed to use wrapper get_opt(). */
union option_value *
@ -793,27 +821,35 @@ register_change_hooks(const struct change_hook_info *change_hooks)
}
void
unmark_options_tree(LIST_OF(struct option) *tree)
prepare_mustsave_flags(LIST_OF(struct option) *tree, int set_all)
{
struct option *option;
foreach (option, *tree) {
option->flags &= ~OPT_WATERMARK;
/* XXX: OPT_LANGUAGE shouldn't have any bussiness
* here, but we're just weird in that area. */
if (set_all
|| (option->flags & (OPT_TOUCHED | OPT_DELETED))
|| option->type == OPT_LANGUAGE)
option->flags |= OPT_MUST_SAVE;
else
option->flags &= ~OPT_MUST_SAVE;
if (option->type == OPT_TREE)
unmark_options_tree(option->value.tree);
prepare_mustsave_flags(option->value.tree, set_all);
}
}
void
watermark_deleted_options(LIST_OF(struct option) *tree)
untouch_options(LIST_OF(struct option) *tree)
{
struct option *option;
foreach (option, *tree) {
if (option->flags & OPT_DELETED)
option->flags |= OPT_WATERMARK;
else if (option->type == OPT_TREE)
watermark_deleted_options(option->value.tree);
option->flags &= ~OPT_TOUCHED;
if (option->type == OPT_TREE)
untouch_options(option->value.tree);
}
}
@ -826,7 +862,7 @@ check_nonempty_tree(LIST_OF(struct option) *options)
if (opt->type == OPT_TREE) {
if (check_nonempty_tree(opt->value.tree))
return 1;
} else if (!(opt->flags & OPT_WATERMARK)) {
} else if (opt->flags & OPT_MUST_SAVE) {
return 1;
}
}
@ -847,14 +883,14 @@ smart_config_string(struct string *str, int print_comment, int i18n,
int do_print_comment = 1;
if (option->flags & OPT_HIDDEN ||
option->flags & OPT_WATERMARK ||
option->type == OPT_ALIAS ||
!strcmp(option->name, "_template_"))
continue;
/* Is there anything to be printed anyway? */
if (option->type == OPT_TREE
&& !check_nonempty_tree(option->value.tree))
? !check_nonempty_tree(option->value.tree)
: !(option->flags & OPT_MUST_SAVE))
continue;
/* We won't pop out the description when we're in autocreate
@ -920,10 +956,6 @@ smart_config_string(struct string *str, int print_comment, int i18n,
fn(str, option, path, depth, /*pc*/1, 3, i18n);
}
/* TODO: We should maybe clear the touched flag only when really
* saving the stuff...? --pasky */
option->flags &= ~OPT_TOUCHED;
}
}

View File

@ -24,15 +24,33 @@ enum option_flags {
* this category when adding an option. The 'template' for the added
* hiearchy piece (category) is stored as "_template_" category. */
OPT_AUTOCREATE = 2,
/* This is used just for marking various options for some very dark,
* nasty and dirty purposes. This watermarking should be kept inside
* some very closed and clearly bounded piece of ELinks module, not
* spreaded along whole ELinks code, and you should clear it everytime
* when sneaking outside of the module (except some trivial common
* utility functions). Basically, you don't want to use this flag
* normally ;). It doesn't affect how the option is handled by common
* option handling functions in any way. */
OPT_WATERMARK = 4,
/* The option has been modified in some way and must be saved
* to elinks.conf. ELinks uses this flag only while it is
* saving the options. When the config.saving_style option
* has value 3, saving works like this:
* - First, ELinks sets OPT_MUST_SAVE in the options that have
* OPT_TOUCHED or OPT_DELETED, and clears it in the rest.
* - ELinks then parses the old configuration file and any
* files named in "include" commands.
* - If the old configuration file contains a "set" or "unset"
* command for this option, ELinks rewrites the command and
* clears OPT_MUST_SAVE.
* - If an included file contains a "set" or "unset" command
* for this option, ELinks compares the value of the option
* to the value given in the command. ELinks clears
* OPT_MUST_SAVE if the values match, or sets it if they
* differ.
* - After ELinks has rewritten the configuration file and
* parsed the included files, it appends the options that
* still have the OPT_MUST_SAVE flag.
* Other saving styles are variants of this:
* - 0: ELinks does not append any options to the
* configuration file. So OPT_MUST_SAVE has no effect.
* - 1: ELinks initially sets OPT_MUST_SAVE in all options,
* regardless of OPT_TOUCHED and OPT_DELETED.
* - 2: ELinks initially sets OPT_MUST_SAVE in all options,
* and does not read any configuration files. */
OPT_MUST_SAVE = 4,
/* This is used to mark options modified after the last save. That's
* being useful if you want to save only the options whose value
* changed. */
@ -161,8 +179,8 @@ extern void register_change_hooks(const struct change_hook_info *change_hooks);
extern LIST_OF(struct option) *init_options_tree(void);
extern void unmark_options_tree(LIST_OF(struct option) *);
void watermark_deleted_options(LIST_OF(struct option) *);
extern void prepare_mustsave_flags(LIST_OF(struct option) *, int set_all);
extern void untouch_options(LIST_OF(struct option) *);
extern void smart_config_string(struct string *, int, int,
LIST_OF(struct option) *, unsigned char *, int,
@ -224,6 +242,7 @@ extern void checkout_option_values(struct option_resolver *resolvers,
extern struct option *get_opt_rec(struct option *, const unsigned char *);
extern struct option *get_opt_rec_real(struct option *, const unsigned char *);
struct option *indirect_option(struct option *);
#ifdef CONFIG_DEBUG
extern union option_value *get_opt_(unsigned char *, int, enum option_type, struct option *, unsigned char *, struct session *);
#define get_opt(tree, name, ses, type) get_opt_(__FILE__, __LINE__, type, tree, name, ses)

View File

@ -90,19 +90,6 @@ exec_cmd(struct option *o, unsigned char ***argv, int *argc)
* possibly changing ptr to structure containing target name and pointer to
* options list? --pasky */
#define wrap_or_(name_, call_, ret_) \
{ \
struct option *real = get_opt_rec(config_options, opt->value.string); \
\
assertm(real != NULL, "%s aliased to unknown option %s!", opt->name, opt->value.string); \
if_assert_failed { return ret_; } \
\
if (option_types[real->type].name_) \
return option_types[real->type].call_; \
\
return ret_; \
}
static unsigned char *
redir_cmd(struct option *opt, unsigned char ***argv, int *argc)
{
@ -124,7 +111,22 @@ redir_cmd(struct option *opt, unsigned char ***argv, int *argc)
static unsigned char *
redir_rd(struct option *opt, unsigned char **file, int *line)
wrap_or_(read, read(real, file, line), NULL);
{
struct option *real = get_opt_rec(config_options, opt->value.string);
unsigned char *ret = NULL;
assertm(real != NULL, "%s aliased to unknown option %s!", opt->name, opt->value.string);
if_assert_failed { return ret; }
if (option_types[real->type].read) {
ret = option_types[real->type].read(real, file, line);
if (ret && (opt->flags & OPT_ALIAS_NEGATE) && real->type == OPT_BOOL) {
*(long *) ret = !*(long *) ret;
}
}
return ret;
}
static void
redir_wr(struct option *opt, struct string *string)
@ -148,10 +150,35 @@ redir_set(struct option *opt, unsigned char *str)
if_assert_failed { return ret; }
if (option_types[real->type].set) {
ret = option_types[real->type].set(real, str);
long negated;
if ((opt->flags & OPT_ALIAS_NEGATE) && real->type == OPT_BOOL) {
real->value.number = !real->value.number;
negated = !*(long *) str;
str = (unsigned char *) &negated;
}
ret = option_types[real->type].set(real, str);
}
return ret;
}
static int
redir_eq(struct option *opt, const unsigned char *str)
{
struct option *real = get_opt_rec(config_options, opt->value.string);
int ret = 0;
assertm(real != NULL, "%s aliased to unknown option %s!", opt->name, opt->value.string);
if_assert_failed { return ret; }
if (option_types[real->type].equals) {
long negated;
if ((opt->flags & OPT_ALIAS_NEGATE) && real->type == OPT_BOOL) {
negated = !*(const long *) str;
str = (unsigned char *) &negated;
}
ret = option_types[real->type].equals(real, str);
}
return ret;
@ -202,6 +229,12 @@ num_set(struct option *opt, unsigned char *str)
return 1;
}
static int
num_eq(struct option *opt, const unsigned char *str)
{
return str && opt->value.number == *(const long *) str;
}
static void
num_wr(struct option *option, struct string *string)
{
@ -216,6 +249,12 @@ long_set(struct option *opt, unsigned char *str)
return 1;
}
static int
long_eq(struct option *opt, const unsigned char *str)
{
return str && opt->value.big_number == *(const long *) str;
}
static void
long_wr(struct option *option, struct string *string)
{
@ -248,9 +287,9 @@ str_rd(struct option *opt, unsigned char **file, int *line)
* thus we will never test for it in while () condition
* and we will treat it just as '"', ignoring the
* backslash itself. */
if (isquote(str[1])) str++;
else if (isquote(str[1])) str++;
/* \\ means \. */
if (str[1] == '\\') str++;
else if (str[1] == '\\') str++;
}
if (*str == '\n') (*line)++;
@ -285,6 +324,12 @@ str_set(struct option *opt, unsigned char *str)
return 1;
}
static int
str_eq(struct option *opt, const unsigned char *str)
{
return str && strcmp(opt->value.string, str) == 0;
}
static void
str_wr(struct option *o, struct string *s)
{
@ -315,6 +360,12 @@ cp_set(struct option *opt, unsigned char *str)
return 1;
}
static int
cp_eq(struct option *opt, const unsigned char *str)
{
return str && get_cp_index(str) == opt->value.number;
}
static void
cp_wr(struct option *o, struct string *s)
{
@ -334,6 +385,16 @@ lang_set(struct option *opt, unsigned char *str)
return 1;
}
static int
lang_eq(struct option *opt, const unsigned char *str)
{
#ifdef CONFIG_NLS
return str && name_to_language(str) == opt->value.number;
#else
return 1; /* All languages are the same. */
#endif
}
static void
lang_wr(struct option *o, struct string *s)
{
@ -355,6 +416,15 @@ color_set(struct option *opt, unsigned char *str)
return !decode_color(str, strlen(str), &opt->value.color);
}
static int
color_eq(struct option *opt, const unsigned char *str)
{
color_T color;
return str && !decode_color(str, strlen(str), &color)
&& color == opt->value.color;
}
static void
color_wr(struct option *opt, struct string *str)
{
@ -402,29 +472,29 @@ const struct option_type_info option_types[] = {
/* The OPT_ comments below are here to be found by grep. */
/* OPT_BOOL */
{ N_("Boolean"), bool_cmd, num_rd, num_wr, NULL, num_set, N_("[0|1]") },
{ N_("Boolean"), bool_cmd, num_rd, num_wr, NULL, num_set, num_eq, N_("[0|1]") },
/* OPT_INT */
{ N_("Integer"), gen_cmd, num_rd, num_wr, NULL, num_set, N_("<num>") },
{ N_("Integer"), gen_cmd, num_rd, num_wr, NULL, num_set, num_eq, N_("<num>") },
/* OPT_LONG */
{ N_("Longint"), gen_cmd, num_rd, long_wr, NULL, long_set, N_("<num>") },
{ N_("Longint"), gen_cmd, num_rd, long_wr, NULL, long_set, long_eq, N_("<num>") },
/* OPT_STRING */
{ N_("String"), gen_cmd, str_rd, str_wr, str_dup, str_set, N_("<str>") },
{ N_("String"), gen_cmd, str_rd, str_wr, str_dup, str_set, str_eq, N_("<str>") },
/* OPT_CODEPAGE */
{ N_("Codepage"), gen_cmd, str_rd, cp_wr, NULL, cp_set, N_("<codepage>") },
{ N_("Codepage"), gen_cmd, str_rd, cp_wr, NULL, cp_set, cp_eq, N_("<codepage>") },
/* OPT_LANGUAGE */
{ N_("Language"), gen_cmd, str_rd, lang_wr, NULL, lang_set, N_("<language>") },
{ N_("Language"), gen_cmd, str_rd, lang_wr, NULL, lang_set, lang_eq, N_("<language>") },
/* OPT_COLOR */
{ N_("Color"), gen_cmd, str_rd, color_wr, NULL, color_set, N_("<color|#rrggbb>") },
{ N_("Color"), gen_cmd, str_rd, color_wr, NULL, color_set, color_eq, N_("<color|#rrggbb>") },
/* OPT_COMMAND */
{ N_("Special"), exec_cmd, NULL, NULL, NULL, NULL, "" },
{ N_("Special"), exec_cmd, NULL, NULL, NULL, NULL, NULL, "" },
/* OPT_ALIAS */
{ N_("Alias"), redir_cmd, redir_rd, redir_wr, NULL, redir_set, "" },
{ N_("Alias"), redir_cmd, redir_rd, redir_wr, NULL, redir_set, redir_eq, "" },
/* OPT_TREE */
{ N_("Folder"), NULL, NULL, NULL, tree_dup, NULL, "" },
{ N_("Folder"), NULL, NULL, NULL, tree_dup, NULL, NULL, "" },
};
unsigned char *

View File

@ -11,6 +11,7 @@ struct option_type_info {
void (*write)(struct option *, struct string *);
void (*dup)(struct option *, struct option *, int);
int (*set)(struct option *, unsigned char *);
int (*equals)(struct option *, const unsigned char *);
unsigned char *help_str;
};

View File

@ -63,7 +63,6 @@ static int
exmode_confcmd_handler(struct session *ses, unsigned char *command,
unsigned char *args)
{
int dummyline = 0;
enum parse_error err;
assert(ses && command && args);
@ -74,8 +73,7 @@ exmode_confcmd_handler(struct session *ses, unsigned char *command,
/* Undo the arguments separation. */
if (*args) *(--args) = ' ';
err = parse_config_command(config_options, &command, &dummyline, NULL,
0);
err = parse_config_exmode_command(command);
return err;
}

View File

@ -1371,7 +1371,7 @@ flush:
#ifndef USE_FASTFIND
int
get_cp_index(unsigned char *name)
get_cp_index(const unsigned char *name)
{
int i, a;
int syscp = 0;
@ -1452,7 +1452,7 @@ static struct fastfind_index ff_charsets_index
/* It searchs for a charset named @name or one of its aliases and
* returns index for it or -1 if not found. */
int
get_cp_index(unsigned char *name)
get_cp_index(const unsigned char *name)
{
const struct codepage_desc *codepage;
int syscp = 0;

View File

@ -117,7 +117,7 @@ unsigned char *convert_string(struct conv_table *convert_table,
void (*callback)(void *data, unsigned char *buf, int buflen),
void *callback_data);
int get_cp_index(unsigned char *);
int get_cp_index(const unsigned char *);
unsigned char *get_cp_name(int);
unsigned char *get_cp_config_name(int);
unsigned char *get_cp_mime_name(int);

View File

@ -147,7 +147,7 @@ language_to_iso639(int language)
}
int
name_to_language(unsigned char *name)
name_to_language(const unsigned char *name)
{
int i;

View File

@ -170,7 +170,7 @@ struct language {
extern struct language languages[];
/* These two calls return 1 (english) if the code/name wasn't found. */
extern int name_to_language(unsigned char *name);
extern int name_to_language(const unsigned char *name);
extern int iso639_to_language(unsigned char *iso639);
extern unsigned char *language_to_name(int language);

View File

@ -100,6 +100,7 @@ struct itrm {
void *mouse_h; /**< Mouse handle */
unsigned char *orig_title; /**< For restoring window title */
int verase; /**< Byte to map to KBD_BS, or -1 */
unsigned int blocked:1; /**< Whether it was blocked */
unsigned int altscreen:1; /**< Whether to use alternate screen */
unsigned int touched_title:1; /**< Whether the term title was changed */

View File

@ -231,14 +231,25 @@ get_terminal_name(unsigned char name[MAX_TERM_LEN])
static int
setraw(int fd, struct termios *p)
setraw(struct itrm *itrm, int save_orig)
{
struct termios t;
long vdisable;
memset(&t, 0, sizeof(t));
if (tcgetattr(fd, &t)) return -1;
if (tcgetattr(itrm->in.ctl, &t)) return -1;
if (p) copy_struct(p, &t);
if (save_orig) copy_struct(&itrm->t, &t);
#ifdef _POSIX_VDISABLE
vdisable = _POSIX_VDISABLE;
#else
vdisable = fpathconf(itrm->in.ctl, _PC_VDISABLE);
#endif
if (vdisable != -1 && t.c_cc[VERASE] == vdisable)
itrm->verase = -1;
else
itrm->verase = (unsigned char) t.c_cc[VERASE];
elinks_cfmakeraw(&t);
t.c_lflag |= ISIG;
@ -246,7 +257,7 @@ setraw(int fd, struct termios *p)
t.c_lflag |= TOSTOP;
#endif
t.c_oflag |= OPOST;
if (tcsetattr(fd, TCSANOW, &t)) return -1;
if (tcsetattr(itrm->in.ctl, TCSANOW, &t)) return -1;
return 0;
}
@ -326,7 +337,7 @@ handle_trm(int std_in, int std_out, int sock_in, int sock_out, int ctl_in,
itrm->altscreen = 1;
if (!remote) {
if (ctl_in >= 0) setraw(ctl_in, &itrm->t);
if (ctl_in >= 0) setraw(itrm, 1);
send_init_sequence(std_out, itrm->altscreen);
handle_terminal_resize(ctl_in, resize_terminal);
#ifdef CONFIG_MOUSE
@ -373,7 +384,7 @@ unblock_itrm(void)
{
if (!ditrm) return -1;
if (ditrm->in.ctl >= 0 && setraw(ditrm->in.ctl, NULL)) return -1;
if (ditrm->in.ctl >= 0 && setraw(ditrm, 0)) return -1;
ditrm->blocked = 0;
send_init_sequence(ditrm->out.std, ditrm->altscreen);
@ -949,26 +960,18 @@ decode_terminal_application_key(struct itrm *itrm, struct interlink_event *ev)
/** Initialize @a *ev to match the byte @a key received from the terminal.
* @a key must not be a value from enum term_event_special_key. */
static void
set_kbd_event(struct interlink_event *ev,
set_kbd_event(const struct itrm *itrm, struct interlink_event *ev,
int key, term_event_modifier_T modifier)
{
switch (key) {
if (key == itrm->verase)
key = KBD_BS;
else switch (key) {
case ASCII_TAB:
key = KBD_TAB;
break;
#if defined(HAVE_SYS_CONSIO_H) || defined(HAVE_MACHINE_CONSOLE_H) /* BSD */
case ASCII_BS:
key = KBD_BS;
break;
case ASCII_DEL:
case ASCII_DEL: /* often overridden by itrm->verase above */
key = KBD_DEL;
break;
#else
case ASCII_BS:
case ASCII_DEL:
key = KBD_BS;
break;
#endif
case ASCII_LF:
case ASCII_CR:
key = KBD_ENTER;
@ -978,6 +981,7 @@ set_kbd_event(struct interlink_event *ev,
key = KBD_ESC;
break;
case ASCII_BS: /* often overridden by itrm->verase above */
default:
if (key < ' ') {
key += 'A' - 1;
@ -1010,10 +1014,10 @@ kbd_timeout(struct itrm *itrm)
if (itrm->in.queue.len >= 2 && itrm->in.queue.data[0] == ASCII_ESC) {
/* This is used for ESC [ and ESC O. */
set_kbd_event(&ev, itrm->in.queue.data[1], KBD_MOD_ALT);
set_kbd_event(itrm, &ev, itrm->in.queue.data[1], KBD_MOD_ALT);
el = 2;
} else {
set_kbd_event(&ev, itrm->in.queue.data[0], KBD_MOD_NONE);
set_kbd_event(itrm, &ev, itrm->in.queue.data[0], KBD_MOD_NONE);
el = 1;
}
itrm->bracketed_pasting = 0;
@ -1098,20 +1102,22 @@ process_queue(struct itrm *itrm)
* by an escape sequence. Treat it as
* a standalone Esc. */
el = 1;
set_kbd_event(&ev, itrm->in.queue.data[0],
set_kbd_event(itrm, &ev,
itrm->in.queue.data[0],
KBD_MOD_NONE);
} else {
/* The second ESC of ESC ESC is not the
* beginning of any known escape sequence.
* This must be Alt-Esc, then. */
el = 2;
set_kbd_event(&ev, itrm->in.queue.data[1],
set_kbd_event(itrm, &ev,
itrm->in.queue.data[1],
KBD_MOD_ALT);
}
}
if (el == 0) { /* Begins with ESC, but none of the above */
el = 2;
set_kbd_event(&ev, itrm->in.queue.data[1],
set_kbd_event(itrm, &ev, itrm->in.queue.data[1],
KBD_MOD_ALT);
}
@ -1132,7 +1138,7 @@ process_queue(struct itrm *itrm)
if (el == 0) {
el = 1;
set_kbd_event(&ev, itrm->in.queue.data[0],
set_kbd_event(itrm, &ev, itrm->in.queue.data[0],
itrm->bracketed_pasting ? KBD_MOD_PASTE : KBD_MOD_NONE);
}