On Thu, 2011-03-24 at 17:49 +0000, Ian Jackson wrote:
> From: Ian Jackson <ijackson@xxxxxxxxxxxxxxxxxxxxxx>
>
> Introduce new flex/regexp-based parser for disk configuration strings.
>
> Callers will be updated in following patches.
>
> Signed-off-by: Ian Jackson <ijackson@xxxxxxxxxxxxxxxxxxxxxx>
> ---
> config/StdGNU.mk | 1 +
> tools/libxl/Makefile | 9 +-
> tools/libxl/libxlu_cfg.c | 42 +++++++---
> tools/libxl/libxlu_disk.c | 164
> +++++++++++++++++++++++++++++++++++++++++
> tools/libxl/libxlu_disk_i.h | 19 +++++
> tools/libxl/libxlu_disk_l.l | 54 ++++++++++++++
> tools/libxl/libxlu_internal.h | 20 +++++
> tools/libxl/libxlutil.h | 12 +++
> 8 files changed, 304 insertions(+), 17 deletions(-)
> create mode 100644 tools/libxl/libxlu_disk.c
> create mode 100644 tools/libxl/libxlu_disk_i.h
> create mode 100644 tools/libxl/libxlu_disk_l.l
>
> diff --git a/config/StdGNU.mk b/config/StdGNU.mk
> index 25aeb4d..9b331f0 100644
> --- a/config/StdGNU.mk
> +++ b/config/StdGNU.mk
> @@ -67,6 +67,7 @@ CURSES_LIBS = -lncurses
> PTHREAD_LIBS = -lpthread
> UTIL_LIBS = -lutil
> DLOPEN_LIBS = -ldl
> +PCRE_LIBS = -lpcre
>
> SONAME_LDFLAG = -soname
> SHLIB_LDFLAGS = -shared
> diff --git a/tools/libxl/Makefile b/tools/libxl/Makefile
> index a7b1d51..f0c473f 100644
> --- a/tools/libxl/Makefile
> +++ b/tools/libxl/Makefile
> @@ -22,7 +22,7 @@ endif
> LIBXL_LIBS =
> LIBXL_LIBS = $(LDLIBS_libxenctrl) $(LDLIBS_libxenguest)
> $(LDLIBS_libxenstore) $(LDLIBS_libblktapctl) $(UTIL_LIBS) $(LIBUUID_LIBS)
>
> -LIBXLU_LIBS =
> +LIBXLU_LIBS = $(PCRE_LIBS)
>
> LIBXL_OBJS-y = osdeps.o libxl_paths.o libxl_bootloader.o flexarray.o
> ifeq ($(LIBXL_BLKTAP),y)
> @@ -38,9 +38,10 @@ LIBXL_OBJS = flexarray.o libxl.o libxl_create.o libxl_dm.o
> libxl_pci.o \
> libxl_internal.o libxl_utils.o $(LIBXL_OBJS-y)
> LIBXL_OBJS += _libxl_types.o
>
> -AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h
> -AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c
> -LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o
> +AUTOINCS= libxlu_cfg_y.h libxlu_cfg_l.h libxlu_disk_l.h
> +AUTOSRCS= libxlu_cfg_y.c libxlu_cfg_l.c libxlu_disk_l.c
> +LIBXLU_OBJS = libxlu_cfg_y.o libxlu_cfg_l.o libxlu_cfg.o \
> + libxlu_disk_l.o libxlu_disk.o
>
> CLIENTS = xl
>
> diff --git a/tools/libxl/libxlu_cfg.c b/tools/libxl/libxlu_cfg.c
> index f947c21..fd3e102 100644
> --- a/tools/libxl/libxlu_cfg.c
> +++ b/tools/libxl/libxlu_cfg.c
> @@ -15,6 +15,7 @@ XLU_Config *xlu_cfg_init(FILE *report, const char
> *report_filename) {
> if (!cfg->filename) { free(cfg); return 0; }
>
> cfg->settings= 0;
> + cfg->disk_re= 0;
> return cfg;
> }
>
> @@ -38,6 +39,29 @@ static int ctx_prep(CfgParseContext *ctx, XLU_Config *cfg)
> {
>
> static void ctx_dispose(CfgParseContext *ctx) {
> if (ctx->scanner) xlu__cfg_yylex_destroy(ctx->scanner);
> + if (ctx->disk_re) pcre_free(ctx->disk_re);
> +}
> +
> +int xlu__scanner_string_prep(CfgParseContext *ctx, YY_BUFFER_STATE *buf,
> + XLU_Config *cfg, const char *data, int length) {
> + int e;
> +
> + e= ctx_prep(ctx, cfg);
> + if (e) return e;
> +
> + *buf = xlu__cfg_yy_scan_bytes(data, length, ctx->scanner);
> + if (!*buf) {
> + fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
> + cfg->filename);
> + return ENOMEM;
> + }
> +
> + return 0;
> +}
> +
> +void xlu__scanner_string_dispose(CfgParseContext *ctx, YY_BUFFER_STATE *buf)
> {
> + if (*buf) xlu__cfg_yy_delete_buffer(*buf, ctx->scanner);
> + ctx_dispose(ctx);
> }
>
> static void parse(CfgParseContext *ctx) {
> @@ -87,25 +111,17 @@ int xlu_cfg_readfile(XLU_Config *cfg, const char
> *real_filename) {
>
> int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
> int e;
> - YY_BUFFER_STATE buf= 0;
> -
> + YY_BUFFER_STATE buf=0;
> CfgParseContext ctx;
> - e= ctx_prep(&ctx, cfg);
> - if (e) { ctx.err= e; goto xe; }
> + ctx.scanner=0;
>
> - buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
> - if (!buf) {
> - fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
> - cfg->filename);
> - ctx.err= ENOMEM;
> - goto xe;
> - }
> + e = xlu__scanner_string_prep(&ctx,&buf, cfg,data,length);
> + if (e) { ctx.err = e; goto xe; }
>
> parse(&ctx);
>
> xe:
> - if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
> - ctx_dispose(&ctx);
> + xlu__scanner_string_dispose(&ctx,&buf);
>
> return ctx.err;
> }
> diff --git a/tools/libxl/libxlu_disk.c b/tools/libxl/libxlu_disk.c
> new file mode 100644
> index 0000000..be523f7
> --- /dev/null
> +++ b/tools/libxl/libxlu_disk.c
> @@ -0,0 +1,164 @@
> +
> +/* Parsing the disk spec a tricky problem, because the target
> + * string might contain "," which is the delimiter we use for
> + * stripping off things on the RHS, and ":", which is the delimiter
> + * we use for stripping off things on the LHS.
> + *
> + * For the LHS we use a flex scanner, see libxlu_disk_l.l.
> + * That chops prefixes like "phy:" from the front of the string,
> + * accumulating information into the disk structure.
> + *
> + * For the RHS, we use a regexp using pcre.
> + */
> +
> +#include "libxlu_internal.h"
> +#include "libxlu_disk_l.h"
> +#include "libxlu_disk_i.h"
> +#include "libxlu_cfg_i.h"
> +
> +static void err_core(DiskParseContext *dpc, const char *message,
> + const char *in, int inlen) {
> + fprintf(dpc->ctx.cfg->report,
> + "%s: config parsing error in disk specification:"
> + " %s: near `%s' in `%.*s'\n",
> + dpc->ctx.cfg->filename, message, dpc->spec, inlen, in);
> + if (!dpc->ctx.err) dpc->ctx.err= EINVAL;
> +}
> +
> +void xlu__disk_err(DiskParseContext *dpc, const char *message) {
> + err_core(dpc, message,
> + xlu__disk_yyget_text(dpc->ctx.scanner),
> + xlu__disk_yyget_leng(dpc->ctx.scanner));
> +}
> +
> +void xlu__disk_rhs(DiskParseContext *dpc) {
> +
> + /* variables used by pcre */
> +#define WORKSPACE_SIZE 20 /* wtf no manifest constant from pcre */
> +#define OVECTOR_SIZE 10
> + int ovector[OVECTOR_SIZE];
> + int workspace[WORKSPACE_SIZE];
> +
> + int rc, len;
> + const char *text;
> + libxl_device_disk *disk = dpc->disk;
> +
> + static const char *re_text=
> + "(.*),(?:/dev/)?([0-9a-zA-Z]*)(:[0-9a-z]+)?(,[0-9a-z]*)?"
> + /* 1:target 2:vdev 3:type 4:attrib (r/w) */;
> +
> + /* compile the regexp if we haven't got one already */
> + if (!dpc->ctx.disk_re) {
> + const char *re_errstr;
> + int re_errcode, re_erroffset;
> +
> + dpc->ctx.disk_re = pcre_compile2(re_text, PCRE_DOTALL,
> + &re_errcode, &re_errstr,
> &re_erroffset,
> + 0);
> + if (!dpc->ctx.disk_re) {
> + if (re_errcode == 21 /* wtf, no manifest constant */) {
> + dpc->ctx.err = ENOMEM;
> + return;
> + }
> + fprintf(dpc->ctx.cfg->report, "config parsing:"
> + " unable to compile regexp: %s [%d] (at `%s')",
> + re_errstr, re_errcode, re_text+re_erroffset);
> + dpc->ctx.err = EIO;
> + return;
> + }
> + }
> +
> + /* actually run the regexp match */
> +
> + text = xlu__disk_yyget_text(dpc->ctx.scanner);
> + len = xlu__disk_yyget_leng(dpc->ctx.scanner);
> +
> + rc = pcre_dfa_exec(dpc->ctx.disk_re, 0,
> + text, len, 0,
> + PCRE_ANCHORED,
> + ovector, OVECTOR_SIZE,
> + workspace, WORKSPACE_SIZE);
> + switch (rc) {
> + case PCRE_ERROR_NOMATCH:
> + xlu__disk_err(dpc, "bad syntax for target, or missing vdev");
> + return;
case 1: ??
> + case 2:
> + case 3:
> + case 4:
> + break;
Or does it belong here? In which case aborting on a parse error is bad
juju.
case 1:
> + default:
> + abort();
> + }
> +
> + /* macros for processing info from capturing parens; see pcreapi(3) */
> +
> +#define CAPTURED(cap) (START((cap)) >= 0)
> +#define START(cap) (ovector[(cap)*2])
> +#define END(cap) (ovector[(cap)*2+1])
> +#define LEN(cap) (END((cap)) - START((cap)))
> +#define TEXT(cap) (text + START((cap)))
> +
> +#define STORE(cap, member) do{ \
> + assert(CAPTURED((cap))); \
> + free(disk->member); \
> + disk->member = malloc(LEN((cap)) + 1); \
> + if (!disk->member) { dpc->ctx.err = ENOMEM; return; } \
> + memcpy(disk->member, TEXT((cap)), LEN((cap))); \
> + disk->member[LEN((cap))] = 0; \
> +}while(0)
> +
> +#define EXACTLY(cap, string) \
> + (CAPTURED((cap)) && \
> + LEN((cap))==strlen((string)) && \
> + !memcmp(TEXT((cap)), (string), LEN((cap))))
> +
> +#define ERR(cap, msg) \
> + err_core(dpc, (msg), TEXT((cap)), LEN((cap)))
> +
> + /* actually process the four capturing parens in our regexp */
> +
> + STORE(1, pdev_path);
> +
> + STORE(2, vdev);
> +
> + if (!CAPTURED(3) || EXACTLY(3, ":")) {
> + disk->is_cdrom = 0;
> + } else if (EXACTLY(3, ":cdrom")) {
> + disk->is_cdrom = 1;
> + } else {
> + ERR(3, "unknown type (only `:' and `:cdrom' supported)");
> + }
> +
> + if (!CAPTURED(4) || EXACTLY(4, ",w") || EXACTLY(4, ",")) {
> + disk->readwrite = 1;
> + } else if (EXACTLY(4, ",r")) {
> + disk->readwrite = 0;
> + } else {
> + ERR(4, "unknown read/write attribute");
> + }
> +}
Hmm, I'm not sure this is nicer than the code it's replacing, it's
certainly a lot longer. I don't like the idea of it being full of things
comparing for ",w" or ":cdrom" etc, rather than tokenising it fully and
evaluating what the tokens are separately.
> +int xlu_disk_parse(XLU_Config *cfg, const char *spec,
> + libxl_device_disk *disk) {
> + YY_BUFFER_STATE buf = 0;
> + DiskParseContext dpc;
> + int e, r;
> +
> + dpc.disk = disk;
> + dpc.spec = spec;
> + dpc.ctx.scanner = 0;
> +
> + e = xlu__scanner_string_prep(&dpc.ctx,&buf, cfg,spec,strlen(spec));
> + if (e) { dpc.ctx.err = e; goto xe; }
> +
> + r = xlu__disk_yylex(dpc.ctx.scanner);
> + if (r) assert(dpc.ctx.err);
> + if (dpc.ctx.err) goto xe;
> +
> + if (disk->format == DISK_FORMAT_UNKNOWN) disk->format = DISK_FORMAT_RAW;
> +
> + xe:
> + xlu__scanner_string_dispose(&dpc.ctx,&buf);
> + return dpc.ctx.err;
> +}
> +
> diff --git a/tools/libxl/libxlu_disk_i.h b/tools/libxl/libxlu_disk_i.h
> new file mode 100644
> index 0000000..deb429f
> --- /dev/null
> +++ b/tools/libxl/libxlu_disk_i.h
> @@ -0,0 +1,19 @@
> +#ifndef LIBXLU_DISK_I_H
> +#define LIBXLU_DISK_I_H
> +
> +#include "libxlu_internal.h"
> +#include "libxl_internal.h"
> +
> +
> +typedef struct {
> + CfgParseContext ctx;
> + const char *spec;
> + libxl_device_disk *disk;
> +} DiskParseContext;
> +
> +void xlu__disk_err(DiskParseContext *dpc, const char *message);
> +
> +void xlu__disk_rhs(DiskParseContext *dpc);
> +
> +
> +#endif /*LIBXLU_DISK_I_H*/
> diff --git a/tools/libxl/libxlu_disk_l.l b/tools/libxl/libxlu_disk_l.l
> new file mode 100644
> index 0000000..82e2248
> --- /dev/null
> +++ b/tools/libxl/libxlu_disk_l.l
> @@ -0,0 +1,54 @@
> +/* -*- fundamental -*- */
> +
> +%{
> +#include "libxlu_disk_i.h"
> +
> +#define dpc ((DiskParseContext*)yyextra)
> +#define YY_NO_INPUT
> +
> +#define DSET(member,enumname,valname) do{ \
> + if (dpc->disk->member != DISK_##enumname##_UNKNOWN && \
> + dpc->disk->member != DISK_##enumname##_##valname) { \
> + xlu__disk_err(dpc, TOSTRING(member) " respecified"); \
> + return 0; \
> + } else { \
> + dpc->disk->member = DISK_##enumname##_##valname; \
> + } \
> + }while(0)
> +
> +/* Some versions of flex have a bug (Fedora bugzilla 612465) which causes
> + * it to fail to declare these functions, which it defines. So declare
> + * them ourselves. Hopefully we won't have to simultaneously support
> + * a flex version which declares these differently somehow. */
> +int xlu__disk_yyget_column(yyscan_t yyscanner);
> +void xlu__disk_yyset_column(int column_no, yyscan_t yyscanner);
> +
> +%}
> +
> +%option warn
> +%option nodefault
> +%option batch
> +%option 8bit
> +%option noyywrap
> +%option reentrant
> +%option prefix="xlu__disk_yy"
> +%option nounput
> +
> +%%
> +
> +raw: { DSET(format,FORMAT,RAW); }
> +qcow: { DSET(format,FORMAT,QCOW); }
> +qcow2: { DSET(format,FORMAT,QCOW2); }
> +vhd: { DSET(format,FORMAT,QCOW2); }
> +
> +phy: { DSET(format,FORMAT,RAW); DSET(backend,BACKEND,PHY); }
> +file: { DSET(format,FORMAT,RAW); DSET(backend,BACKEND,TAP); }
> +tapdisk:|tap2?: { DSET(backend,BACKEND,TAP); }
> +aio: { }
> +ioemu: { }
This bit is quite nice though. We could probably just tidy up the
existing parser using arrays of values and things rather than a lot of
if/else statements though.
> +[a-z][a-z0-9-]*: { xlu__disk_err(dpc,"unknown disk format/method"); return
> 0; }
> +
> +[^:]*(\/.*)? { xlu__disk_rhs(dpc); return 0; }
> +
> +.* { xlu__disk_err(dpc,"bad disk syntax"); return 0; }
> diff --git a/tools/libxl/libxlu_internal.h b/tools/libxl/libxlu_internal.h
> index e251a63..d681fba 100644
> --- a/tools/libxl/libxlu_internal.h
> +++ b/tools/libxl/libxlu_internal.h
> @@ -21,10 +21,15 @@
> #include <string.h>
> #include <assert.h>
>
> +#include <pcre.h>
> +
> #define XLU_ConfigList XLU_ConfigSetting
>
> #include "libxlutil.h"
>
> +
> +
> +
> struct XLU_ConfigSetting { /* transparent */
> struct XLU_ConfigSetting *next;
> char *name;
> @@ -37,12 +42,27 @@ struct XLU_Config {
> XLU_ConfigSetting *settings;
> FILE *report;
> char *filename;
> + pcre *disk_re;
> };
>
> typedef struct {
> XLU_Config *cfg;
> int err, lexerrlineno, likely_python;
> void *scanner;
> + pcre *disk_re;
> } CfgParseContext;
>
> +
> +#ifndef YY_TYPEDEF_YY_BUFFER_STATE
> +#define YY_TYPEDEF_YY_BUFFER_STATE
> +typedef struct yy_buffer_state *YY_BUFFER_STATE;
> +#endif
> +
> +int xlu__scanner_string_prep(CfgParseContext *ctx, YY_BUFFER_STATE *buf,
> + XLU_Config *cfg, const char *data, int length);
> + /* Caller must initialise ctx->scanner=0 and buf=0 beforehand. */
> +
> +void xlu__scanner_string_dispose(CfgParseContext *ctx, YY_BUFFER_STATE *buf);
> +
> +
> #endif /*LIBXLU_INTERNAL_H*/
> diff --git a/tools/libxl/libxlutil.h b/tools/libxl/libxlutil.h
> index 8a6fcbd..acf96fd 100644
> --- a/tools/libxl/libxlutil.h
> +++ b/tools/libxl/libxlutil.h
> @@ -58,4 +58,16 @@ const char *xlu_cfg_get_listitem(const XLU_ConfigList*,
> int entry);
> /* xlu_cfg_get_listitem cannot fail, except that if entry is
> * out of range it returns 0 (not setting errno) */
>
> +
> +/*
> + * Disk specification parsing.
> + */
> +
> +int xlu_disk_parse(XLU_Config *cfg, const char *spec,
> + libxl_device_disk *disk);
> + /* disk must have been initialised; on error, returns errno value;
> + * bad strings, returns EINVAL and prints a message to cfg's report.
> + * That's all cfg is used for. */
> +
> +
> #endif /* LIBXLUTIL_H */
> --
> 1.5.6.5
Gianni
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|