mirror of
https://github.com/rkd77/elinks.git
synced 2024-12-04 14:46:47 -05: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:
commit
5499926cc0
16
NEWS
16
NEWS
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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 *
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user