📄 conf.c
字号:
/* Config file manipulation *//* $Id: conf.c,v 1.152.2.3 2005/05/02 14:44:34 jonas Exp $ */#ifdef HAVE_CONFIG_H#include "config.h"#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h> /* OS/2 needs this after sys/types.h */#ifdef HAVE_FCNTL_H#include <fcntl.h> /* OS/2 needs this after sys/types.h */#endif#ifdef HAVE_UNISTD_H#include <unistd.h>#endif#include "elinks.h"#include "config/conf.h"#include "config/dialogs.h"#include "config/kbdbind.h"#include "config/options.h"#include "config/opttypes.h"#include "intl/gettext/libintl.h"#include "lowlevel/home.h"#include "osdep/osdep.h"#include "terminal/terminal.h"#include "util/error.h"#include "util/memory.h"#include "util/secsave.h"#include "util/string.h"/* Config file has only very simple grammar: * * /set *option *= *value/ * /bind *keymap *keystroke *= *action/ * /include *file/ * /#.*$/ * * Where option consists from any number of categories separated by dots and * name of the option itself. Both category and option name consists from * [a-zA-Z0-9_-*] - using uppercase letters is not recommended, though. '*' is * reserved and is used only as escape character in place of '.' originally in * option name. * * Value can consist from: * - number (it will be converted to int/long) * - enum (like on, off; true, fake, last_url; etc ;) - in planning state yet * - string - "blah blah" (keymap, keystroke and action and file looks like that too) * * "set" command is parsed first, and then type-specific function is called, * with option as one parameter and value as a second. Usually it just assigns * value to an option, but sometimes you may want to first create the option * ;). Then this will come handy. *//* Skip comments and whitespace, * setting *@line to the number of lines skipped. */static unsigned char *skip_white(unsigned char *start, int *line){ while (*start) { while (isspace(*start)) { if (*start == '\n') { (*line)++; } start++; } if (*start == '#') { start += strcspn(start, "\n"); } else { return start; } } return start;}/* 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). */static enum parse_errorparse_set(struct option *opt_tree, unsigned char **file, int *line, struct string *mirror){ unsigned char *orig_pos = *file; unsigned char *optname; unsigned char bin; *file = skip_white(*file, line); if (!**file) return ERROR_PARSE; /* Option name */ optname = *file; while (isident(**file) || **file == '*' || **file == '.') (*file)++; bin = **file; **file = '\0'; optname = stracpy(optname); if (!optname) return ERROR_NOMEM; **file = bin; *file = skip_white(*file, line); /* Equal sign */ if (**file != '=') { mem_free(optname); return ERROR_PARSE; } (*file)++; /* '=' */ *file = skip_white(*file, line); if (!**file) { mem_free(optname); return ERROR_VALUE; } /* Mirror what we already have */ if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos); /* Option value */ { struct option *opt; unsigned char *val; opt = mirror ? get_opt_rec_real(opt_tree, optname) : get_opt_rec(opt_tree, optname); mem_free(optname); if (!opt || (opt->flags & OPT_HIDDEN)) return ERROR_OPTION; 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); 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; */ mem_free(val); } return ERROR_NONE;}static enum parse_errorparse_unset(struct option *opt_tree, unsigned char **file, int *line, struct string *mirror){ unsigned char *orig_pos = *file; unsigned char *optname; unsigned char bin; /* 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; /* Option name */ optname = *file; while (isident(**file) || **file == '*' || **file == '.') (*file)++; bin = **file; **file = '\0'; optname = stracpy(optname); if (!optname) return ERROR_NOMEM; **file = bin; /* Mirror what we have */ if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos); { struct option *opt; opt = get_opt_rec_real(opt_tree, optname); mem_free(optname); if (!opt || (opt->flags & OPT_HIDDEN)) return ERROR_OPTION; if (!mirror) { if (opt->flags & OPT_ALLOC) delete_option(opt); } else { if (opt->flags & OPT_DELETED) opt->flags |= OPT_WATERMARK; else opt->flags &= ~OPT_WATERMARK; } } return ERROR_NONE;}static enum parse_errorparse_bind(struct option *opt_tree, unsigned char **file, int *line, struct string *mirror){ unsigned char *orig_pos = *file, *next_pos; unsigned char *keymap, *keystroke, *action; enum parse_error err = ERROR_NONE; *file = skip_white(*file, line); if (!*file) return ERROR_PARSE; /* Keymap */ keymap = option_types[OPT_STRING].read(NULL, file, line); *file = skip_white(*file, line); if (!keymap || !**file) return ERROR_OPTION; /* Keystroke */ keystroke = option_types[OPT_STRING].read(NULL, file, line); *file = skip_white(*file, line); if (!keystroke || !**file) { mem_free(keymap); return ERROR_OPTION; } /* Equal sign */ *file = skip_white(*file, line); if (**file != '=') { mem_free(keymap); mem_free(keystroke); return ERROR_PARSE; } (*file)++; /* '=' */ *file = skip_white(*file, line); if (!**file) { mem_free(keymap); mem_free(keystroke); return ERROR_PARSE; } /* Action */ next_pos = *file; action = option_types[OPT_STRING].read(NULL, file, line); if (!action) { mem_free(keymap); return ERROR_VALUE; } 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 { /* We don't bother to bind() if -default-keys. */ if (!get_cmd_opt_bool("default-keys") && bind_do(keymap, keystroke, action)) { /* bind_do() tried but failed. */ err = ERROR_VALUE; } else { err = ERROR_NONE; } } mem_free(keymap); mem_free(keystroke); mem_free(action); return err;}static int load_config_file(unsigned char *, unsigned char *, struct option *, struct string *);static enum parse_errorparse_include(struct option *opt_tree, unsigned char **file, int *line, struct string *mirror){ unsigned char *orig_pos = *file; unsigned char *fname; struct string dumbstring; if (!init_string(&dumbstring)) return 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) return ERROR_VALUE; /* Mirror what we already have */ if (mirror) add_bytes_to_string(mirror, orig_pos, *file - orig_pos); /* We want load_config_file() to watermark stuff, but not to load * anything, polluting our beloved options tree - thus, we will feed it * with some dummy string which we will destroy later; still better * than cloning whole options tree or polluting interface with another * rarely-used option ;). */ /* XXX: We should try CONFDIR/<file> when proceeding * CONFDIR/<otherfile> ;). --pasky */ if (load_config_file(fname[0] == '/' ? (unsigned char *) "" : elinks_home, fname, opt_tree, &dumbstring)) { done_string(&dumbstring); mem_free(fname); return ERROR_VALUE; } done_string(&dumbstring); mem_free(fname); return ERROR_NONE;}struct parse_handler { unsigned char *command; enum parse_error (*handler)(struct option *opt_tree, unsigned char **file, int *line, struct string *mirror);};static struct parse_handler parse_handlers[] = { { "set", parse_set }, { "unset", parse_unset }, { "bind", parse_bind }, { "include", parse_include }, { NULL, NULL }};enum parse_errorparse_config_command(struct option *options, unsigned char **file, int *line, struct string *mirror){ struct parse_handler *handler; for (handler = parse_handlers; handler->command; handler++) { int cmdlen = strlen(handler->command); if (!strncmp(*file, handler->command, cmdlen) && isspace((*file)[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); if (!err && mirror && m2) { add_string_to_string(mirror, m2); } if (m2) done_string(m2); return err; } } return ERROR_COMMAND;}voidparse_config_file(struct option *options, unsigned char *name, unsigned char *file, struct string *mirror){ int line = 1; int error_occured = 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; /* Skip all possible comments and whitespace. */ file = skip_white(file, &line); /* 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); if (err == ERROR_COMMAND) { orig_pos = file; /* Jump over this crap we can't understand. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -