mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Xen used to have single, system-wide limits for the number of grant
frames and maptrack frames a guest was allowed to create. Increasing
or decreasing this single limit on the Xen command-line would change
the limit for all guests on the system.
Later, per-domain limits for these values was created. The system-wide
limits became strict limits: domains could not be created with higher
limits, but could be created with lower limits. However, that change
also introduced a range of different "default" values into various
places in the toolstack:
- The python libxc bindings hard-coded these values to 32 and 1024,
respectively
- The libxl default values are 32 and 1024 respectively.
- xl will use the libxl default for maptrack, but does its own default
calculation for grant frames: either 32 or 64, based on the max
possible mfn.
These defaults interact poorly with the hypervisor command-line limit:
- The hypervisor command-line limit cannot be used to raise the limit
for all guests anymore, as the default in the toolstack will
effectively override this.
- If you use the hypervisor command-line limit to *reduce* the limit,
then the "default" values generated by the toolstack are too high,
and all guest creations will fail.
In other words, the toolstack defaults require any change to be
effected by having the admin explicitly specify a new value in every
guest.
In order to address this, have grant_table_init treat negative values
for max_grant_frames and max_maptrack_frames as instructions to use the
system-wide default, and have all the above toolstacks default to passing
-1 unless a different value is explicitly configured.
This restores the old behavior in that changing the hypervisor command-line
option can change the behavior for all guests, while retaining the ability
to set per-guest values. It also removes the bug that reducing the
system-wide max will cause all domains without explicit limits to fail.
NOTE: - The Ocaml bindings require the caller to always specify a value,
and the code to start a xenstored stubdomain hard-codes these to 4
and 128 respectively; this behavour will not be modified.
Signed-off-by: George Dunlap <george.dunlap@citrix.com>
Signed-off-by: Paul Durrant <pdurrant@amazon.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Release-acked-by: Juergen Gross <jgross@suse.com>
Acked-by: Wei Liu <wl@xen.org>
Acked-by: Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
714 lines
19 KiB
C
714 lines
19 KiB
C
/*
|
|
* libxlu_cfg.c - xl configuration file parsing: setup and helper functions
|
|
*
|
|
* Copyright (C) 2010 Citrix Ltd.
|
|
* Author Ian Jackson <ian.jackson@eu.citrix.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation; version 2.1 only. with the special
|
|
* exception on linking described in file LICENSE.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Lesser General Public License for more details.
|
|
*/
|
|
|
|
|
|
#include "libxl_osdeps.h" /* must come before any other headers */
|
|
|
|
#include <limits.h>
|
|
|
|
#include "libxlu_internal.h"
|
|
#include "libxlu_cfg_y.h"
|
|
#include "libxlu_cfg_l.h"
|
|
#include "libxlu_cfg_i.h"
|
|
|
|
XLU_Config *xlu_cfg_init(FILE *report, const char *report_source) {
|
|
XLU_Config *cfg;
|
|
|
|
cfg= malloc(sizeof(*cfg));
|
|
if (!cfg) return 0;
|
|
|
|
cfg->report= report;
|
|
cfg->config_source= strdup(report_source);
|
|
if (!cfg->config_source) { free(cfg); return 0; }
|
|
|
|
cfg->settings= 0;
|
|
return cfg;
|
|
}
|
|
|
|
static int ctx_prep(CfgParseContext *ctx, XLU_Config *cfg) {
|
|
int e;
|
|
|
|
ctx->cfg= cfg;
|
|
ctx->err= 0;
|
|
ctx->lexerrlineno= -1;
|
|
ctx->likely_python= 0;
|
|
ctx->scanner= 0;
|
|
|
|
e= xlu__cfg_yylex_init_extra(ctx, &ctx->scanner);
|
|
if (e) {
|
|
fprintf(cfg->report,"%s: unable to create scanner: %s\n",
|
|
cfg->config_source, strerror(e));
|
|
return e;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void ctx_dispose(CfgParseContext *ctx) {
|
|
if (ctx->scanner) xlu__cfg_yylex_destroy(ctx->scanner);
|
|
}
|
|
|
|
static void parse(CfgParseContext *ctx) {
|
|
/* On return, ctx.err will be updated with the error status. */
|
|
int r;
|
|
|
|
xlu__cfg_yyset_lineno(1, ctx->scanner);
|
|
|
|
r= xlu__cfg_yyparse(ctx);
|
|
if (r) assert(ctx->err);
|
|
|
|
if (ctx->err && ctx->likely_python) {
|
|
fputs(
|
|
"warning: Config file looks like it contains Python code.\n"
|
|
"warning: Arbitrary Python is no longer supported.\n"
|
|
"warning: See http://wiki.xen.org/wiki/PythonInXlConfig\n",
|
|
ctx->cfg->report);
|
|
}
|
|
}
|
|
|
|
int xlu_cfg_readfile(XLU_Config *cfg, const char *real_filename) {
|
|
FILE *f = 0;
|
|
int e;
|
|
|
|
CfgParseContext ctx;
|
|
e = ctx_prep(&ctx, cfg);
|
|
if (e) { ctx.err= e; goto xe; }
|
|
|
|
f= fopen(real_filename, "r");
|
|
if (!f) {
|
|
ctx.err = errno;
|
|
fprintf(cfg->report,"%s: unable to open configuration file: %s\n",
|
|
real_filename, strerror(e));
|
|
goto xe;
|
|
}
|
|
|
|
xlu__cfg_yyrestart(f, ctx.scanner);
|
|
|
|
parse(&ctx);
|
|
|
|
xe:
|
|
ctx_dispose(&ctx);
|
|
if (f) fclose(f);
|
|
|
|
return ctx.err;
|
|
}
|
|
|
|
int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
|
|
int e;
|
|
YY_BUFFER_STATE buf= 0;
|
|
|
|
CfgParseContext ctx;
|
|
e= ctx_prep(&ctx, cfg);
|
|
if (e) { ctx.err= e; goto xe; }
|
|
|
|
buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
|
|
if (!buf) {
|
|
fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
|
|
cfg->config_source);
|
|
ctx.err= ENOMEM;
|
|
goto xe;
|
|
}
|
|
|
|
parse(&ctx);
|
|
|
|
xe:
|
|
if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
|
|
ctx_dispose(&ctx);
|
|
|
|
return ctx.err;
|
|
}
|
|
|
|
void xlu__cfg_value_free(XLU_ConfigValue *value)
|
|
{
|
|
int i;
|
|
|
|
if (!value) return;
|
|
|
|
switch (value->type) {
|
|
case XLU_STRING:
|
|
free(value->u.string);
|
|
break;
|
|
case XLU_LIST:
|
|
for (i = 0; i < value->u.list.nvalues; i++)
|
|
xlu__cfg_value_free(value->u.list.values[i]);
|
|
free(value->u.list.values);
|
|
}
|
|
free(value);
|
|
}
|
|
|
|
void xlu__cfg_set_free(XLU_ConfigSetting *set) {
|
|
if (!set) return;
|
|
free(set->name);
|
|
xlu__cfg_value_free(set->value);
|
|
free(set);
|
|
}
|
|
|
|
void xlu_cfg_destroy(XLU_Config *cfg) {
|
|
XLU_ConfigSetting *set, *set_next;
|
|
|
|
if (!cfg) return;
|
|
for (set= cfg->settings;
|
|
set;
|
|
set= set_next) {
|
|
set_next= set->next;
|
|
xlu__cfg_set_free(set);
|
|
}
|
|
free(cfg->config_source);
|
|
free(cfg);
|
|
}
|
|
|
|
static XLU_ConfigSetting *find(const XLU_Config *cfg, const char *n) {
|
|
XLU_ConfigSetting *set;
|
|
|
|
for (set= cfg->settings;
|
|
set;
|
|
set= set->next)
|
|
if (!strcmp(set->name, n))
|
|
return set;
|
|
return 0;
|
|
}
|
|
|
|
static int find_atom(const XLU_Config *cfg, const char *n,
|
|
XLU_ConfigSetting **set_r, int dont_warn) {
|
|
XLU_ConfigSetting *set;
|
|
|
|
set= find(cfg,n);
|
|
if (!set) return ESRCH;
|
|
|
|
if (set->value->type!=XLU_STRING) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: parameter `%s' is"
|
|
" a list but should be a single value\n",
|
|
cfg->config_source, set->lineno, n);
|
|
return EINVAL;
|
|
}
|
|
*set_r= set;
|
|
return 0;
|
|
}
|
|
|
|
|
|
enum XLU_ConfigValueType xlu_cfg_value_type(const XLU_ConfigValue *value)
|
|
{
|
|
return value->type;
|
|
}
|
|
|
|
int xlu_cfg_value_get_string(const XLU_Config *cfg, XLU_ConfigValue *value,
|
|
char **value_r, int dont_warn)
|
|
{
|
|
if (value->type != XLU_STRING) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d:%d: warning: value is not a string\n",
|
|
cfg->config_source, value->loc.first_line,
|
|
value->loc.first_column);
|
|
*value_r = NULL;
|
|
return EINVAL;
|
|
}
|
|
|
|
*value_r = value->u.string;
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_value_get_list(const XLU_Config *cfg, XLU_ConfigValue *value,
|
|
XLU_ConfigList **value_r, int dont_warn)
|
|
{
|
|
if (value->type != XLU_LIST) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d:%d: warning: value is not a list\n",
|
|
cfg->config_source, value->loc.first_line,
|
|
value->loc.first_column);
|
|
*value_r = NULL;
|
|
return EINVAL;
|
|
}
|
|
|
|
*value_r = &value->u.list;
|
|
return 0;
|
|
}
|
|
|
|
XLU_ConfigValue *xlu_cfg_get_listitem2(const XLU_ConfigList *list,
|
|
int entry)
|
|
{
|
|
if (entry < 0 || entry >= list->nvalues) return NULL;
|
|
return list->values[entry];
|
|
}
|
|
|
|
int xlu_cfg_get_string(const XLU_Config *cfg, const char *n,
|
|
const char **value_r, int dont_warn) {
|
|
XLU_ConfigSetting *set;
|
|
int e;
|
|
|
|
e= find_atom(cfg,n,&set,dont_warn); if (e) return e;
|
|
*value_r= set->value->u.string;
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n,
|
|
char **value_r, int dont_warn) {
|
|
XLU_ConfigSetting *set;
|
|
int e;
|
|
|
|
e= find_atom(cfg,n,&set,dont_warn); if (e) return e;
|
|
free(*value_r);
|
|
*value_r= strdup(set->value->u.string);
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_get_bounded_long(const XLU_Config *cfg, const char *n,
|
|
long min, long max, long *value_r,
|
|
int dont_warn) {
|
|
long l;
|
|
XLU_ConfigSetting *set;
|
|
int e;
|
|
char *ep;
|
|
|
|
e= find_atom(cfg,n,&set,dont_warn); if (e) return e;
|
|
if (set->op == XLU_OP_ADDITION) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: can't use += with numbers"
|
|
" for parameter `%s'\n",
|
|
cfg->config_source, set->lineno, n);
|
|
return EINVAL;
|
|
}
|
|
errno= 0; l= strtol(set->value->u.string, &ep, 0);
|
|
e= errno;
|
|
if (errno) {
|
|
e= errno;
|
|
assert(e==EINVAL || e==ERANGE);
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: parameter `%s' could not be parsed"
|
|
" as a number: %s\n",
|
|
cfg->config_source, set->lineno, n, strerror(e));
|
|
return e;
|
|
}
|
|
if (*ep || ep==set->value->u.string) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: parameter `%s' is not a valid number\n",
|
|
cfg->config_source, set->lineno, n);
|
|
return EINVAL;
|
|
}
|
|
if (l < min) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: value `%ld' is smaller than minimum bound '%ld'\n",
|
|
cfg->config_source, set->lineno, l, min);
|
|
return EINVAL;
|
|
}
|
|
if (l > max) {
|
|
if (!dont_warn)
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: value `%ld' is greater than maximum bound '%ld'\n",
|
|
cfg->config_source, set->lineno, l, max);
|
|
return EINVAL;
|
|
}
|
|
|
|
*value_r= l;
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_get_long(const XLU_Config *cfg, const char *n,
|
|
long *value_r, int dont_warn) {
|
|
return xlu_cfg_get_bounded_long(cfg, n, LONG_MIN, LONG_MAX, value_r,
|
|
dont_warn);
|
|
}
|
|
|
|
int xlu_cfg_get_defbool(const XLU_Config *cfg, const char *n, libxl_defbool *b,
|
|
int dont_warn)
|
|
{
|
|
int ret;
|
|
long l;
|
|
|
|
ret = xlu_cfg_get_long(cfg, n, &l, dont_warn);
|
|
if (ret) return ret;
|
|
libxl_defbool_set(b, !!l);
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_get_list(const XLU_Config *cfg, const char *n,
|
|
XLU_ConfigList **list_r, int *entries_r, int dont_warn) {
|
|
XLU_ConfigSetting *set;
|
|
set= find(cfg,n); if (!set) return ESRCH;
|
|
if (set->value->type!=XLU_LIST) {
|
|
if (!dont_warn) {
|
|
fprintf(cfg->report,
|
|
"%s:%d: warning: parameter `%s' is a single value"
|
|
" but should be a list\n",
|
|
cfg->config_source, set->lineno, n);
|
|
}
|
|
return EINVAL;
|
|
}
|
|
if (list_r) *list_r= &set->value->u.list;
|
|
if (entries_r) *entries_r= set->value->u.list.nvalues;
|
|
return 0;
|
|
}
|
|
|
|
int xlu_cfg_get_list_as_string_list(const XLU_Config *cfg, const char *n,
|
|
libxl_string_list *psl, int dont_warn) {
|
|
int i, rc, nr;
|
|
XLU_ConfigList *list;
|
|
libxl_string_list sl;
|
|
|
|
rc = xlu_cfg_get_list(cfg, n, &list, &nr, dont_warn);
|
|
if (rc) return rc;
|
|
|
|
sl = malloc(sizeof(char*)*(nr + 1));
|
|
if (sl == NULL) return ENOMEM;
|
|
|
|
for (i=0; i<nr; i++) {
|
|
const char *a = xlu_cfg_get_listitem(list, i);
|
|
sl[i] = a ? strdup(a) : NULL;
|
|
}
|
|
|
|
sl[nr] = NULL;
|
|
|
|
*psl = sl;
|
|
return 0;
|
|
}
|
|
|
|
const char *xlu_cfg_get_listitem(const XLU_ConfigList *list, int entry) {
|
|
if (entry < 0 || entry >= list->nvalues) return 0;
|
|
if (list->values[entry]->type != XLU_STRING) return 0;
|
|
return list->values[entry]->u.string;
|
|
}
|
|
|
|
|
|
XLU_ConfigValue *xlu__cfg_string_mk(CfgParseContext *ctx, char *atom,
|
|
YYLTYPE *loc)
|
|
{
|
|
XLU_ConfigValue *value = NULL;
|
|
|
|
if (ctx->err) goto x;
|
|
|
|
value = malloc(sizeof(*value));
|
|
if (!value) goto xe;
|
|
value->type = XLU_STRING;
|
|
value->u.string = atom;
|
|
memcpy(&value->loc, loc, sizeof(*loc));
|
|
|
|
return value;
|
|
|
|
xe:
|
|
ctx->err= errno;
|
|
x:
|
|
free(value);
|
|
free(atom);
|
|
return NULL;
|
|
}
|
|
|
|
XLU_ConfigValue *xlu__cfg_list_mk(CfgParseContext *ctx,
|
|
XLU_ConfigValue *val,
|
|
YYLTYPE *loc)
|
|
{
|
|
XLU_ConfigValue *value = NULL;
|
|
XLU_ConfigValue **values = NULL;
|
|
|
|
if (ctx->err) goto x;
|
|
|
|
values = malloc(sizeof(*values));
|
|
if (!values) goto xe;
|
|
values[0] = val;
|
|
|
|
value = malloc(sizeof(*value));
|
|
if (!value) goto xe;
|
|
value->type = XLU_LIST;
|
|
value->u.list.nvalues = !!val;
|
|
value->u.list.avalues = 1;
|
|
value->u.list.values = values;
|
|
memcpy(&value->loc, loc, sizeof(*loc));
|
|
|
|
return value;
|
|
|
|
xe:
|
|
ctx->err= errno;
|
|
x:
|
|
free(value);
|
|
free(values);
|
|
xlu__cfg_value_free(val);
|
|
return NULL;
|
|
}
|
|
|
|
void xlu__cfg_list_append(CfgParseContext *ctx,
|
|
XLU_ConfigValue *list,
|
|
XLU_ConfigValue *val)
|
|
{
|
|
if (ctx->err) return;
|
|
|
|
assert(val);
|
|
assert(list->type == XLU_LIST);
|
|
|
|
if (list->u.list.nvalues >= list->u.list.avalues) {
|
|
int new_avalues;
|
|
XLU_ConfigValue **new_values = NULL;
|
|
|
|
if (list->u.list.avalues > INT_MAX / 100) {
|
|
ctx->err = ERANGE;
|
|
xlu__cfg_value_free(val);
|
|
return;
|
|
}
|
|
|
|
new_avalues = list->u.list.avalues * 4;
|
|
new_values = realloc(list->u.list.values,
|
|
sizeof(*new_values) * new_avalues);
|
|
if (!new_values) {
|
|
ctx->err = errno;
|
|
xlu__cfg_value_free(val);
|
|
return;
|
|
}
|
|
|
|
list->u.list.avalues = new_avalues;
|
|
list->u.list.values = new_values;
|
|
}
|
|
|
|
list->u.list.values[list->u.list.nvalues] = val;
|
|
list->u.list.nvalues++;
|
|
}
|
|
|
|
static int xlu__cfg_concat_vals(CfgParseContext *ctx,
|
|
XLU_ConfigValue *prev,
|
|
XLU_ConfigValue *to_add)
|
|
{
|
|
int r;
|
|
|
|
if (prev->type != to_add->type) {
|
|
xlu__cfgl_lexicalerror(ctx,
|
|
"can't add [list] to \"string\" or vice versa");
|
|
return EINVAL;
|
|
}
|
|
|
|
switch (to_add->type) {
|
|
case XLU_STRING: {
|
|
char *new_string = NULL;
|
|
|
|
r = asprintf(&new_string, "%s%s", prev->u.string,
|
|
to_add->u.string);
|
|
if (r < 0) {
|
|
return errno;
|
|
}
|
|
free(to_add->u.string);
|
|
to_add->u.string = new_string;
|
|
return 0;
|
|
}
|
|
case XLU_LIST: {
|
|
XLU_ConfigList *const prev_list = &prev->u.list;
|
|
XLU_ConfigList *const cur_list = &to_add->u.list;
|
|
int nvalues;
|
|
|
|
if (prev->u.list.nvalues > INT_MAX - to_add->u.list.nvalues) {
|
|
return ERANGE;
|
|
}
|
|
nvalues = prev->u.list.nvalues + to_add->u.list.nvalues;
|
|
|
|
if (nvalues >= cur_list->avalues) {
|
|
XLU_ConfigValue **new_vals;
|
|
new_vals = realloc(cur_list->values,
|
|
nvalues * sizeof(*new_vals));
|
|
if (!new_vals) {
|
|
return ENOMEM;
|
|
}
|
|
cur_list->avalues = nvalues;
|
|
cur_list->values = new_vals;
|
|
}
|
|
|
|
/* make space for `prev' into `to_add' */
|
|
memmove(cur_list->values + prev_list->nvalues,
|
|
cur_list->values,
|
|
cur_list->nvalues * sizeof(XLU_ConfigValue *));
|
|
/* move values from `prev' to `to_add' as the list in `prev' will
|
|
* not be reachable by find(). */
|
|
memcpy(cur_list->values,
|
|
prev_list->values,
|
|
prev_list->nvalues * sizeof(XLU_ConfigValue *));
|
|
cur_list->nvalues = nvalues;
|
|
prev_list->nvalues = 0;
|
|
memset(prev_list->values, 0,
|
|
prev_list->nvalues * sizeof(XLU_ConfigValue *));
|
|
return 0;
|
|
}
|
|
default:
|
|
abort();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void xlu__cfg_set_store(CfgParseContext *ctx, char *name,
|
|
enum XLU_Operation op,
|
|
XLU_ConfigValue *val, int lineno) {
|
|
XLU_ConfigSetting *set;
|
|
int r;
|
|
|
|
if (ctx->err) goto out;
|
|
|
|
assert(name);
|
|
|
|
if (op == XLU_OP_ADDITION) {
|
|
/* If we have += concatenate with previous value with same name */
|
|
XLU_ConfigSetting *prev_set = find(ctx->cfg, name);
|
|
if (prev_set) {
|
|
r = xlu__cfg_concat_vals(ctx, prev_set->value, val);
|
|
if (r) {
|
|
ctx->err = r;
|
|
goto out;
|
|
}
|
|
}
|
|
}
|
|
|
|
set = malloc(sizeof(*set));
|
|
if (!set) {
|
|
ctx->err = errno;
|
|
goto out;
|
|
}
|
|
set->name= name;
|
|
set->value = val;
|
|
set->op = op;
|
|
set->lineno= lineno;
|
|
set->next= ctx->cfg->settings;
|
|
ctx->cfg->settings= set;
|
|
return;
|
|
out:
|
|
assert(ctx->err);
|
|
free(name);
|
|
xlu__cfg_value_free(val);
|
|
}
|
|
|
|
char *xlu__cfgl_strdup(CfgParseContext *ctx, const char *src) {
|
|
char *result;
|
|
|
|
if (ctx->err) return 0;
|
|
result= strdup(src);
|
|
if (!result) ctx->err= errno;
|
|
return result;
|
|
}
|
|
|
|
char *xlu__cfgl_dequote(CfgParseContext *ctx, const char *src) {
|
|
char *result;
|
|
const char *p;
|
|
char *q;
|
|
int len, c, nc;
|
|
|
|
if (ctx->err) return 0;
|
|
|
|
len= strlen(src);
|
|
assert(len>=2 && src[0]==src[len-1]);
|
|
|
|
result= malloc(len-1);
|
|
if (!result) { ctx->err= errno; return 0; }
|
|
|
|
q= result;
|
|
|
|
for (p= src+1;
|
|
p < src+len-1;
|
|
) {
|
|
c= *p++;
|
|
if (c=='\\') {
|
|
assert(p < src+len-1);
|
|
nc= *p++;
|
|
if (nc=='"' || nc=='\'' || nc=='\\') {
|
|
*q++= nc;
|
|
} else if (nc=='a') { *q++= '\007';
|
|
} else if (nc=='b') { *q++= '\010';
|
|
} else if (nc=='f') { *q++= '\014';
|
|
} else if (nc=='n') { *q++= '\n';
|
|
} else if (nc=='r') { *q++= '\r';
|
|
} else if (nc=='t') { *q++= '\t';
|
|
} else if (nc=='v') { *q++= '\013';
|
|
} else if (nc=='x') {
|
|
|
|
#define NUMERIC_CHAR(minlen,maxlen,base,basetext) do{ \
|
|
char numbuf[(maxlen)+1], *ep; \
|
|
unsigned long val; \
|
|
\
|
|
strncpy(numbuf,p,(maxlen)); \
|
|
numbuf[(maxlen)]= 0; \
|
|
val= strtoul(numbuf, &ep, (base)); \
|
|
if (ep <= numbuf+(minlen)) { \
|
|
xlu__cfgl_lexicalerror(ctx,"invalid digit after" \
|
|
" backslash " basetext "numerical character escape" \
|
|
" in quoted string"); \
|
|
ctx->err= EINVAL; \
|
|
goto x; \
|
|
} \
|
|
p += (ep - numbuf); \
|
|
}while(0)
|
|
|
|
p++;
|
|
NUMERIC_CHAR(2,2,16,"hex");
|
|
} else if (nc>='0' && nc<='7') {
|
|
NUMERIC_CHAR(1,3,10,"octal");
|
|
} else {
|
|
xlu__cfgl_lexicalerror(ctx,
|
|
"invalid character after backlash in quoted string");
|
|
ctx->err= EINVAL;
|
|
goto x;
|
|
}
|
|
assert(p <= src+len-1);
|
|
} else {
|
|
*q++= c;
|
|
}
|
|
}
|
|
|
|
x:
|
|
*q++= 0;
|
|
return result;
|
|
}
|
|
|
|
void xlu__cfgl_lexicalerror(CfgParseContext *ctx, char const *msg) {
|
|
YYLTYPE loc;
|
|
loc.first_line= xlu__cfg_yyget_lineno(ctx->scanner);
|
|
xlu__cfg_yyerror(&loc, ctx, msg);
|
|
ctx->lexerrlineno= loc.first_line;
|
|
}
|
|
|
|
void xlu__cfg_yyerror(YYLTYPE *loc, CfgParseContext *ctx, char const *msg) {
|
|
const char *text, *newline;
|
|
int len, lineno;
|
|
|
|
lineno= loc->first_line;
|
|
if (lineno <= ctx->lexerrlineno) return;
|
|
|
|
text= xlu__cfg_yyget_text(ctx->scanner);
|
|
len= xlu__cfg_yyget_leng(ctx->scanner);
|
|
newline= "";
|
|
if (len>0 && text[len-1]=='\n') {
|
|
len--;
|
|
lineno--;
|
|
if (!len) {
|
|
newline= "<newline>";
|
|
}
|
|
}
|
|
while (len>0 && (text[len-1]=='\t' || text[len-1]==' ')) {
|
|
len--;
|
|
}
|
|
|
|
fprintf(ctx->cfg->report,
|
|
"%s:%d: config parsing error near %s%.*s%s%s: %s\n",
|
|
ctx->cfg->config_source, lineno,
|
|
len?"`":"", len, text, len?"'":"", newline,
|
|
msg);
|
|
if (!ctx->err) ctx->err= EINVAL;
|
|
}
|
|
|
|
/*
|
|
* Local variables:
|
|
* mode: C
|
|
* c-basic-offset: 4
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*/
|