diff --git a/src/Makefile.am b/src/Makefile.am index 5616796ea..9b1ce595b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,7 +23,8 @@ libaugeas_la_SOURCES = augeas.h augeas.c augrun.c pathx.c \ memory.h memory.c ref.h ref.c \ syntax.c syntax.h parser.y builtin.c lens.c lens.h regexp.c regexp.h \ transform.h transform.c ast.c get.c put.c list.h \ - info.c info.h errcode.c errcode.h jmt.h jmt.c + info.c info.h errcode.c errcode.h jmt.h jmt.c \ + schema.h schema.c if USE_VERSION_SCRIPT AUGEAS_VERSION_SCRIPT = $(VERSION_SCRIPT_FLAGS)$(srcdir)/augeas_sym.version diff --git a/src/augrun.c b/src/augrun.c index 15b198449..68c23e877 100644 --- a/src/augrun.c +++ b/src/augrun.c @@ -1202,6 +1202,43 @@ static const struct command_def cmd_retrieve_def = { .help = cmd_retrieve_help }; +#include "syntax.h" +#include "schema.h" + +static void cmd_schema(struct command *cmd) { + const char *lens_name = arg_value(cmd, "lens"); + struct augeas *aug = cmd->aug; + struct lens *lens = NULL; + struct schema *schema = NULL; + + lens = lens_lookup(aug, lens_name); + if (lens == NULL) { + ERR_REPORT(cmd, AUG_ECMDRUN, "Lens %s does not exist", lens_name); + return; + } + + schema = schema_from_lens(lens); + ERR_BAIL(aug); + simplify_schema(schema); + dump_schema(cmd->out, schema); + error: + free_schema(schema); +} + +static const struct command_opt_def cmd_schema_opts[] = { + { .type = CMD_STR, .name = "lens", .optional = false, + .help = "show schema for this lens" }, + CMD_OPT_DEF_LAST +}; + +static const struct command_def cmd_schema_def = { + .name = "schema", + .opts = cmd_schema_opts, + .handler = cmd_schema, + .synopsis = "show the schema for a lens", + .help = "show the schema for a lens" +}; + /* Given a path "/augeas/files/FILENAME/error", return FILENAME */ static char *err_filename(const char *match) { int noise = strlen(AUGEAS_META_FILES) + strlen("/error"); @@ -1324,6 +1361,7 @@ static const struct command_grp_def cmd_grp_admin_def = { &cmd_store_def, &cmd_transform_def, &cmd_load_file_def, + &cmd_schema_def, &cmd_def_last } }; diff --git a/src/augtool.c b/src/augtool.c index b6ab4a737..3ebf51ea7 100644 --- a/src/augtool.c +++ b/src/augtool.c @@ -180,7 +180,7 @@ static char *readline_command_generator(const char *text, int state) { "get", "label", "ins", "load", "ls", "match", "mv", "cp", "rename", "print", "dump-xml", "rm", "save", "set", "setm", "clearm", "span", "store", "retrieve", "transform", "load-file", - "help", "touch", "insert", "move", "copy", "errors", NULL }; + "help", "touch", "insert", "move", "copy", "errors", "schema", NULL }; static int current = 0; const char *name; diff --git a/src/schema.c b/src/schema.c new file mode 100644 index 000000000..df12bffc0 --- /dev/null +++ b/src/schema.c @@ -0,0 +1,292 @@ +/* + * schema.c: support for generating a tree schema from a lens + * + * Copyright (C) 2016 David Lutterkort + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: David Lutterkort + */ + +#include "schema.h" + +#include "internal.h" +#include "errcode.h" +#include "memory.h" + +static struct schema* make_schema(enum schema_tag tag, struct info *info) { + struct schema *result = NULL; + int r; + + r = ALLOC(result); + ERR_NOMEM(r < 0, info); + + result->tag = tag; + result->info = ref(info); + + return result; + error: + return NULL; +} + +static struct schema *make_schema_concat_or_union(struct lens *lens) { + struct schema *result = NULL; + enum schema_tag tag; + + int r; + + ensure0(lens->tag == L_CONCAT || lens->tag == L_UNION, lens->info); + + tag = lens->tag == L_CONCAT ? S_CONCAT : S_UNION; + + result = make_schema(tag, lens->info); + ERR_BAIL(lens->info); + + r = ALLOC_N(result->children, lens->nchildren); + ERR_NOMEM(r < 0, lens->info); + result->nchildren = lens->nchildren; + for (int i=0; i < result->nchildren; i++) { + result->children[i] = schema_from_lens(lens->children[i]); + } + return result; + error: + free_schema(result); + return NULL; +} + +void simplify_schema(struct schema *schema) { + if (schema == NULL) + return; + + switch(schema->tag) { + case S_REC: + case S_UNIT: + break; + case S_CONCAT: + case S_UNION: + { + int nunit = 0; + for (int i=0; i < schema->nchildren; i++) { + simplify_schema(schema->children[i]); + if (schema->children[i]->tag == S_UNIT) + nunit += 1; + } + if (nunit == schema->nchildren) { + for (int i=0; i < schema->nchildren; i++) { + free_schema(schema->children[i]); + } + FREE(schema->children); + schema->tag = S_UNIT; + } else if (nunit == schema->nchildren - 1) { + struct schema tmp; + for (int i=0; i < schema->nchildren; i++) { + if (schema->children[i]->tag != S_UNIT) { + tmp = *schema->children[i]; + free(schema->children[i]); + } else { + free_schema(schema->children[i]); + } + } + free(schema->children); + unref(schema->info, info); + *schema = tmp; + } + } + break; + case S_SUBTREE: + simplify_schema(schema->child); + break; + case S_STAR: + case S_MAYBE: + case S_SQUARE: + simplify_schema(schema->child); + if (schema->child->tag == S_UNIT) { + free_schema(schema->child); + schema->child = NULL; + schema->tag = S_UNIT; + } + break; + default: + BUG_ON(true, schema->info, "Unexpected schema type %d", schema->tag); + break; + } + error: + return; +} + +struct schema *schema_from_lens(struct lens *lens) { + struct schema *result = NULL; + + if (lens == NULL) + return NULL; + + switch (lens->tag) { + case L_DEL: + case L_STORE: + case L_VALUE: + case L_KEY: + case L_LABEL: + case L_SEQ: + case L_COUNTER: + result = make_schema(S_UNIT, lens->info); + ERR_BAIL(lens->info); + break; + case L_SUBTREE: + result = make_schema(S_SUBTREE, lens->info); + ERR_BAIL(lens->info); + result->ktype = ref(lens->child->ktype); + result->vtype = ref(lens->child->vtype); + result->child = schema_from_lens(lens->child); + break; + case L_STAR: + result = make_schema(S_STAR, lens->info); + ERR_BAIL(lens->info); + result->child = schema_from_lens(lens->child); + break; + case L_MAYBE: + result = make_schema(S_MAYBE, lens->info); + ERR_BAIL(lens->info); + result->child = schema_from_lens(lens->child); + break; + case L_SQUARE: + result = make_schema(S_SQUARE, lens->info); + ERR_BAIL(lens->info); + result->child = schema_from_lens(lens->child); + break; + case L_CONCAT: + case L_UNION: + result = make_schema_concat_or_union(lens); + ERR_BAIL(lens->info); + break; + case L_REC: + result = make_schema(S_REC, lens->info); + ERR_BAIL(lens->info); + break; + default: + BUG_ON(true, lens->info, "Unexpected lens tag %d", lens->tag); + break; + } + + return result; + error: + free_schema(result); + return NULL; +} + +void free_schema(struct schema *schema) { + if (schema == NULL) + return; + + switch(schema->tag) { + case S_REC: + case S_UNIT: + break; + case S_CONCAT: + case S_UNION: + for (int i=0; i < schema->nchildren; i++) { + free_schema(schema->children[i]); + } + free(schema->children); + break; + case S_SUBTREE: + unref(schema->ktype, regexp); + unref(schema->vtype, regexp); + free_schema(schema->child); + break; + case S_STAR: + case S_MAYBE: + case S_SQUARE: + free_schema(schema->child); + break; + default: + BUG_ON(true, schema->info, "Unexpected schema type %d", schema->tag); + break; + } + unref(schema->info, info); + free(schema); + error: + return; +} + +static void print_indent(FILE *out, int indent) { + for (int i=0; i < indent; i++) + fputc(' ', out); +} + +static void dump(FILE *out, struct schema *schema, int indent) { + if (schema == NULL) { + print_indent(out, indent); + fprintf(out, "<>\n"); + return; + } + + switch(schema->tag) { + case S_REC: + print_indent(out, indent); + fprintf(out, "<>\n"); + case S_UNIT: + print_indent(out, indent); + fprintf(out, "()\n"); + break; + case S_CONCAT: + case S_UNION: + print_indent(out, indent); + if (schema->tag == S_CONCAT) { + fprintf(out, ".\n"); + } else { + fprintf(out, "|\n"); + } + for (int i=0; i < schema->nchildren; i++) { + dump(out, schema->children[i], indent+2); + } + break; + case S_SUBTREE: + { + bool unit = schema->child->tag == S_UNIT; + print_indent(out, indent); + fprintf(out, unit ? "{ " : "[ "); + print_regexp(out, schema->ktype); + fprintf(out, " = "); + print_regexp(out, schema->vtype); + fprintf(out, unit ? " }\n" : " ]\n"); + if (! unit) + dump(out, schema->child, indent+2); + } + break; + case S_STAR: + case S_MAYBE: + case S_SQUARE: + print_indent(out, indent); + if (schema->tag == S_STAR) { + fputc('*', out); + } else if (schema->tag == S_MAYBE) { + fputc('?', out); + } else { + fputc('#', out); + } + fputc('\n', out); + dump(out, schema->child, indent+2); + break; + default: + BUG_ON(true, schema->info, "Unexpected schema type %d", schema->tag); + break; + } + error: + return; +} + +void dump_schema(FILE *out, struct schema *schema) { + dump(out, schema, 0); +} diff --git a/src/schema.h b/src/schema.h new file mode 100644 index 000000000..9c2f93468 --- /dev/null +++ b/src/schema.h @@ -0,0 +1,74 @@ +/* + * schema.h: support for generating a tree schema from a lens + * + * Copyright (C) 2016 David Lutterkort + * + * This library 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: David Lutterkort + */ + +#ifndef SCHEMA_H_ +#define SCHEMA_H_ + +#include "stdio.h" + +#include "config.h" +#include "lens.h" + +/* What we are trying to represent in OCaml notation: + + type schema = Concat schema * schema + | Union schema * schema + | Subtree rx * rx * schema + | Square schema + | Star schema + | Maybe schema + | Rec + | Unit +*/ + +enum schema_tag { + S_UNIT, + S_CONCAT, + S_UNION, + S_SUBTREE, + S_STAR, + S_MAYBE, + S_SQUARE, + S_REC +}; + +struct schema { + enum schema_tag tag; + struct info *info; + union { + struct { /* L_CONCAT, L_UNION */ + unsigned int nchildren; + struct schema **children; + }; + struct { /* L_SUBTREE */ + struct regexp *ktype; + struct regexp *vtype; + struct schema *child; /* L_STAR, L_MAYBE, L_SQUARE */ + }; + }; +}; + +struct schema *schema_from_lens(struct lens *lens); +void simplify_schema(struct schema *); +void dump_schema(FILE *out, struct schema *); +void free_schema(struct schema *); +#endif