[augeas-devel] [PATCH] Add aug_span API function V2

Francis Giraldeau francis.giraldeau at gmail.com
Sun Feb 6 20:42:29 UTC 2011


aug_span API function provides information about the node of the specified
path. It sets the absolute path of the file name and indexes of label, value
and span inside the node. The span includes all matching chars of the actual
lens and it's descendant. Hence, the top level node will span the entire file.
It returns 0 in case of success and -1 if the node is not associated to a file
or if the path is invalid.  The main use case is to make possible to display
the related file with the node elements hightlighted in a UI.

The API option AUG_NO_NODE_INDEX provided to aug_init disable the allocation
structures to keep node indexes. It saves about 5% of memory for a typical
tree.

  * src/augeas.c : add aug_span implementation and option handling to aug_in
    according to the option /augeas/indez in the tree
  * src/augeas.h : add declaration of aug_span and constants for option
  * src/augeas_sym.version : make the new function visible in libaugeas
  * src/augtool.c : add the "nodepos" command to augtool
  * src/get.c : gather filename, label, value and span indexes if any
  * src/info.c : create and release functions for struct node_span
  * src/info.h : add definition of struct node_info
  * src/internal.h : add node_info to struct tree
  * src/transform.c : duplicate filename string
  * tests/test-load.c : 3 basic unit tests for general correct behavior

Recursive lens and regular lens are supported.

New in patch V2:
  * Fix handling of span for top-level nodes corresponding to a file.
  * Option is now /augeas/span and flag is AUG_ENABLE_SPAN. The client has to
    enable explicitly the span.
  * Renamed to span everywhere.
  * Don't use ref for span struct, always one reference.
  * Use ERR_BAIL in aug_span after pathx operations.
  * Require specific value AUG_ENABLE enable span option.
  * Added error message corresponding to AUG_ENOSPAN.
  * Reduced the size of span struct by removing ref count and is_first_update
    flag. A special case is added, where span_start is set to UINT_MAX when
    struct is initialized.
  * Proper error handling for callers of make_span.
  * Proper error handling for path query.
  * Add tests for error handling.
  * Updated aug_span API documentation.
  * Replaced uint by unsigned int in augeas.h, uint only defined in stdlib.h
    that is not included.
  * Add --span option to augtool to explicitly load span positions.
---
 src/augeas.c           |   80 +++++++++++++++++++++++++++++++++-
 src/augeas.h           |   27 +++++++++++-
 src/augeas_sym.version |    5 ++
 src/augtool.c          |   57 ++++++++++++++++++++++++-
 src/get.c              |   43 +++++++++++++++++-
 src/info.c             |   43 ++++++++++++++++++
 src/info.h             |   15 ++++++
 src/internal.h         |    9 ++++
 src/transform.c        |   23 ++++++++--
 tests/cutest.h         |    4 ++
 tests/test-api.c       |  113 ++++++++++++++++++++++++++++++++++++++++++++++++
 tests/test-load.c      |    5 --
 12 files changed, 409 insertions(+), 15 deletions(-)

diff --git a/src/augeas.c b/src/augeas.c
index 25ca016..a3a89e2 100644
--- a/src/augeas.c
+++ b/src/augeas.c
@@ -63,7 +63,8 @@ static const char *const errcodes[] = {
     "Too many matches for path expression",             /* AUG_EMMATCH */
     "Syntax error in lens definition",                  /* AUG_ESYNTAX */
     "Lens not found",                                   /* AUG_ENOLENS */
-    "Multiple transforms"                               /* AUG_EMXFM */
+    "Multiple transforms",                              /* AUG_EMXFM */
+    "Node has no span info"                             /* AUG_ENOSPAN */
 };
 
 static void tree_mark_dirty(struct tree *tree) {
@@ -421,6 +422,13 @@ struct augeas *aug_init(const char *root, const char *loadpath,
     } else {
         aug_set(result, AUGEAS_META_SAVE_MODE, AUG_SAVE_OVERWRITE_TEXT);
     }
+
+    if (flags & AUG_ENABLE_SPAN) {
+        aug_set(result, AUGEAS_SPAN_OPTION, AUG_ENABLE);
+    } else {
+        aug_set(result, AUGEAS_SPAN_OPTION, AUG_DISABLE);
+    }
+
     /* Make sure we always have /files and /augeas/variables */
     tree_path_cr(result->origin, 1, s_files);
     tree_path_cr(result->origin, 2, s_augeas, s_vars);
@@ -503,6 +511,7 @@ static void tree_rm_dirty_leaves(struct augeas *aug, struct tree *tree,
 }
 
 int aug_load(struct augeas *aug) {
+    const char *option = NULL;
     struct tree *meta = tree_child_cr(aug->origin, s_augeas);
     struct tree *meta_files = tree_child_cr(meta, s_files);
     struct tree *files = tree_child_cr(aug->origin, s_files);
@@ -527,6 +536,16 @@ int aug_load(struct augeas *aug) {
      * (4) Remove entries from /augeas/files and /files that correspond
      *     to directories without any files of interest
      */
+
+    /* update flags according to option value */
+    if (aug_get(aug, AUGEAS_SPAN_OPTION, &option) == 1) {
+        if (strcmp(option, AUG_ENABLE) == 0) {
+            aug->flags |= AUG_ENABLE_SPAN;
+        } else {
+            aug->flags &= ~AUG_ENABLE_SPAN;
+        }
+    }
+
     tree_clean(meta_files);
     tree_mark_files(meta_files);
 
@@ -860,6 +879,8 @@ static void free_tree_node(struct tree *tree) {
     if (tree == NULL)
         return;
 
+    if (tree->span != NULL)
+        free_span(tree->span);
     free(tree->label);
     free(tree->value);
     free(tree);
@@ -943,6 +964,63 @@ int aug_rm(struct augeas *aug, const char *path) {
     return -1;
 }
 
+int aug_span(struct augeas *aug, const char *path, char **filename,
+        uint *label_start, uint *label_end, uint *value_start, uint *value_end,
+        uint *span_start, uint *span_end) {
+    struct pathx *p = NULL;
+    int result = -1;
+    struct tree *tree = NULL;
+    struct span *span;
+
+    api_entry(aug);
+
+    p = pathx_aug_parse(aug, aug->origin, path, true);
+    ERR_BAIL(aug);
+
+    tree = pathx_first(p);
+    ERR_BAIL(aug);
+
+    ERR_THROW(tree == NULL, aug, AUG_ENOMATCH, "No node matching %s", path);
+    ERR_THROW(tree->span == NULL, aug, AUG_ENOSPAN, "No span info for %s", path);
+    ERR_THROW(pathx_next(p) != NULL, aug, AUG_EMMATCH, "Multiple nodes match %s", path);
+
+    span = tree->span;
+
+    if (label_start != NULL)
+        *label_start = span->label_start;
+
+    if (label_end != NULL)
+        *label_end = span->label_end;
+
+    if (value_start != NULL)
+        *value_start = span->value_start;
+
+    if (value_end != NULL)
+        *value_end = span->value_end;
+
+    if (span_start != NULL)
+        *span_start = span->span_start;
+
+    if (span_end != NULL)
+        *span_end = span->span_end;
+
+    /* We are safer here, make sure we have a filename */
+    if (filename != NULL) {
+        if (span->filename == NULL || span->filename->str == NULL) {
+            *filename = strdup("");
+        } else {
+            *filename = strdup(span->filename->str);
+        }
+        ERR_NOMEM(*filename == NULL, aug);
+    }
+
+    result = 0;
+ error:
+    free_pathx(p);
+    api_exit(aug);
+    return result;
+}
+
 int tree_replace(struct augeas *aug, const char *path, struct tree *sub) {
     struct tree *parent;
     struct pathx *p = NULL;
diff --git a/src/augeas.h b/src/augeas.h
index b4848cb..cd16ba3 100644
--- a/src/augeas.h
+++ b/src/augeas.h
@@ -47,7 +47,8 @@ enum aug_flags {
     AUG_SAVE_NOOP    = (1 << 4),  /* Make save a no-op process, just record
                                      what would have changed */
     AUG_NO_LOAD      = (1 << 5),  /* Do not load the tree from AUG_INIT */
-    AUG_NO_MODL_AUTOLOAD = (1 << 6)
+    AUG_NO_MODL_AUTOLOAD = (1 << 6),
+    AUG_ENABLE_SPAN    = (1 << 7) /* Do not keep track of node indexes */
 };
 
 /* Function: aug_init
@@ -149,6 +150,27 @@ int aug_set(augeas *aug, const char *path, const char *value);
  */
 int aug_setm(augeas *aug, const char *base, const char *sub, const char *value);
 
+/* Function: aug_span
+ *
+ * Get the span according to input file of the node associated with PATH. If
+ * the node is associated with a file, the filename, label and value start and
+ * end positions are set, and return value is 0. The caller is responsible for
+ * freeing returned filename. If an argument for return value is NULL, then the
+ * corresponding value is not set. If the node associated with PATH doesn't
+ * belong to a file or is doesn't exists, filename and span are not set and
+ * return value is -1.
+ *
+ * Returns:
+ * 0 on success with filename, label_start, label_stop, value_start, value_end,
+ *   span_start, span_end
+ * -1 on error
+ */
+
+int aug_span(augeas *aug, const char *path, char **filename,
+        unsigned int *label_start, unsigned int *label_end,
+        unsigned int *value_start, unsigned int *value_end,
+        unsigned int *span_start, unsigned int *span_end);
+
 /* Function: aug_insert
  *
  * Create a new sibling LABEL for PATH by inserting into the tree just
@@ -297,7 +319,8 @@ typedef enum {
     AUG_EMMATCH,        /* Too many matches for path expression */
     AUG_ESYNTAX,        /* Syntax error in lens file */
     AUG_ENOLENS,        /* Lens lookup failed */
-    AUG_EMXFM           /* Multiple transforms */
+    AUG_EMXFM,          /* Multiple transforms */
+    AUG_ENOSPAN         /* No span for this node */
 } aug_errcode_t;
 
 /* Return the error code from the last API call */
diff --git a/src/augeas_sym.version b/src/augeas_sym.version
index 542f5fa..3f065f1 100644
--- a/src/augeas_sym.version
+++ b/src/augeas_sym.version
@@ -34,3 +34,8 @@ AUGEAS_0.11.0 {
     global:
       aug_setm;
 } AUGEAS_0.10.0;
+
+AUGEAS_0.12.0 {
+    global:
+      aug_span;
+} AUGEAS_0.11.0;
diff --git a/src/augtool.c b/src/augtool.c
index 7de8e9d..ae6456d 100644
--- a/src/augtool.c
+++ b/src/augtool.c
@@ -661,6 +661,54 @@ static const struct command_def cmd_clearm_def = {
     "BASE will be modified."
 };
 
+static void cmd_span(struct command *cmd) {
+    const char *path = arg_value(cmd, "path");
+    int r;
+    uint label_start, label_end, value_start, value_end, span_start, span_end;
+    char *filename;
+    const char *option;
+    // FIXME: add check to see if AUG_ENABLE_SPAN is set
+
+    if (aug_get(aug, AUGEAS_SPAN_OPTION, &option) != 1) {
+        printf("Error: option " AUGEAS_SPAN_OPTION " not found\n");
+        return;
+    }
+    if (strcmp(AUG_DISABLE, option) == 0) {
+        printf("Span is not enabled. To enable, run commands:\n");
+        printf("set %s %s\n", AUGEAS_SPAN_OPTION, AUG_ENABLE);
+        printf("rm %s\n", AUGEAS_FILES_TREE);
+        printf("load\n");
+        return;
+    } else if (strcmp(AUG_ENABLE, option) != 0) {
+        printf("Error: option %s must be %s or %s\n", AUGEAS_SPAN_OPTION,
+               AUG_ENABLE, AUG_DISABLE);
+        return;
+    }
+    r = aug_span(aug, path, &filename, &label_start, &label_end, &value_start, 
+                 &value_end, &span_start, &span_end);
+    err_check(cmd);
+    if (r == -1){
+        printf ("Failed\n");
+        return;
+    }
+    printf("%s label=(%i:%i) value=(%i:%i) span=(%i,%i)\n", filename, 
+           label_start, label_end, value_start, value_end, span_start, span_end);
+}
+
+static const struct command_opt_def cmd_span_opts[] = {
+    { .type = CMD_PATH, .name = "path", .optional = false,
+      .help = "node path" },
+    CMD_OPT_DEF_LAST
+};
+
+static const struct command_def cmd_span_def = {
+    .name = "span",
+    .opts = cmd_span_opts,
+    .handler = cmd_span,
+    .synopsis = "get the filename, label and value position in the text of this node",
+    .help = "get the filename, label and value position in the text of this node"
+};
+
 static void cmd_defvar(struct command *cmd) {
     const char *name = arg_value(cmd, "name");
     const char *path = arg_value(cmd, "expr");
@@ -971,6 +1019,7 @@ static const struct command_def const *commands[] = {
     &cmd_set_def,
     &cmd_setm_def,
     &cmd_clearm_def,
+    &cmd_span_def,
     &cmd_help_def,
     &cmd_def_last
 };
@@ -1035,6 +1084,7 @@ static void usage(void) {
     fprintf(stderr, "  --nostdinc         do not search the builtin default directories for modules\n");
     fprintf(stderr, "  --noload           do not load any files into the tree on startup\n");
     fprintf(stderr, "  --noautoload       do not autoload modules from the search path\n");
+    fprintf(stderr, "  --span             load span positions for nodes related to a file\n");
     fprintf(stderr, "  --version          print version information and exit.\n");
 
     exit(EXIT_FAILURE);
@@ -1047,7 +1097,8 @@ static void parse_opts(int argc, char **argv) {
         VAL_NO_STDINC = CHAR_MAX + 1,
         VAL_NO_LOAD = VAL_NO_STDINC + 1,
         VAL_NO_AUTOLOAD = VAL_NO_LOAD + 1,
-        VAL_VERSION = VAL_NO_AUTOLOAD + 1
+        VAL_VERSION = VAL_NO_AUTOLOAD + 1,
+        VAL_SPAN = VAL_VERSION + 1
     };
     struct option options[] = {
         { "help",      0, 0, 'h' },
@@ -1062,6 +1113,7 @@ static void parse_opts(int argc, char **argv) {
         { "nostdinc",  0, 0, VAL_NO_STDINC },
         { "noload",    0, 0, VAL_NO_LOAD },
         { "noautoload", 0, 0, VAL_NO_AUTOLOAD },
+        { "span",      0, 0, VAL_SPAN },
         { "version",   0, 0, VAL_VERSION },
         { 0, 0, 0, 0}
     };
@@ -1109,6 +1161,9 @@ static void parse_opts(int argc, char **argv) {
             flags |= AUG_NO_MODL_AUTOLOAD;
             print_version = true;
             break;
+        case VAL_SPAN:
+            flags |= AUG_ENABLE_SPAN;
+            break;
         default:
             usage();
             break;
diff --git a/src/get.c b/src/get.c
index dbab6fa..9fa236b 100644
--- a/src/get.c
+++ b/src/get.c
@@ -45,6 +45,7 @@ struct seq {
 
 struct state {
     struct info      *info;
+    struct span      *span;
     const char       *text;
     struct seq       *seqs;
     char             *key;
@@ -73,6 +74,7 @@ struct frame {
     struct lens     *lens;
     char            *key;
     char            *square;
+    struct span     *span;
     union {
         struct { /* MGET */
             char        *value;
@@ -388,6 +390,7 @@ static struct tree *get_del(struct lens *lens, struct state *state) {
     if (lens->string == NULL) {
         state->square = token(state);
     }
+    update_span(state->span, REG_START(state), REG_END(state));
     return NULL;
 }
 
@@ -413,8 +416,14 @@ static struct tree *get_store(struct lens *lens, struct state *state) {
         get_error(state, lens, "More than one store in a subtree");
     else if (! REG_MATCHED(state))
         no_match_error(state, lens);
-    else
+    else {
         state->value = token(state);
+        if (state->span) {
+            state->span->value_start = REG_START(state);
+            state->span->value_end = REG_END(state);
+            update_span(state->span, REG_START(state), REG_END(state));
+        }
+    }
     return tree;
 }
 
@@ -440,8 +449,14 @@ static struct tree *get_key(struct lens *lens, struct state *state) {
     ensure0(lens->tag == L_KEY, state->info);
     if (! REG_MATCHED(state))
         no_match_error(state, lens);
-    else
+    else {
         state->key = token(state);
+        if (state->span) {
+            state->span->label_start = REG_START(state);
+            state->span->label_end = REG_END(state);
+            update_span(state->span, REG_START(state), REG_END(state));
+        }
+    }
     return NULL;
 }
 
@@ -661,17 +676,32 @@ static struct skel *parse_quant_maybe(struct lens *lens, struct state *state,
 static struct tree *get_subtree(struct lens *lens, struct state *state) {
     char *key = state->key;
     char *value = state->value;
+    struct span *span = state->span;
+
     struct tree *tree = NULL, *children;
 
     state->key = NULL;
     state->value = NULL;
+    if (state->info->flags & AUG_ENABLE_SPAN) {
+        state->span = make_span(state->info);
+        ERR_NOMEM(state->span == NULL, state->info);
+    }
+
     children = get_lens(lens->child, state);
 
     tree = make_tree(state->key, state->value, NULL, children);
+    tree->span = state->span;
+
+    if (state->span != NULL) {
+        update_span(span, state->span->span_start, state->span->span_end);
+    }
 
     state->key = key;
     state->value = value;
+    state->span = span;
     return tree;
+ error:
+    return NULL;
 }
 
 static struct skel *parse_subtree(struct lens *lens, struct state *state,
@@ -855,9 +885,16 @@ static void visit_enter(struct lens *lens,
         struct frame *f = push_frame(rec_state, lens);
         f->key = state->key;
         f->value = state->value;
+        f->span = state->span;
         state->key = NULL;
         state->value = NULL;
+        if (state->info->flags & AUG_ENABLE_SPAN) {
+            state->span = make_span(state->info);
+            ERR_NOMEM(state->span == NULL, state->info);
+        }
     }
+ error:
+    return;
 }
 
 static void get_combine(struct rec_state *rec_state,
@@ -948,11 +985,13 @@ static void visit_exit(struct lens *lens,
             struct tree *tree;
             // FIXME: tree may leak if pop_frame ensure0 fail
             tree = make_tree(top->key, top->value, NULL, top->tree);
+            tree->span = state->span;
             ERR_NOMEM(tree == NULL, lens->info);
             top = pop_frame(rec_state);
             ensure(lens == top->lens, state->info);
             state->key = top->key;
             state->value = top->value;
+            state->span = top->span;
             pop_frame(rec_state);
             top = push_frame(rec_state, lens);
             top->tree = tree;
diff --git a/src/info.c b/src/info.c
index 9363477..288290d 100644
--- a/src/info.c
+++ b/src/info.c
@@ -23,6 +23,7 @@
 #include <config.h>
 #include "info.h"
 #include "internal.h"
+#include "memory.h"
 #include "ref.h"
 
 /*
@@ -113,6 +114,48 @@ void free_info(struct info *info) {
     free(info);
 }
 
+struct span *make_span(struct info *info) {
+    struct span *span = NULL;
+    if (ALLOC(span) < 0) {
+        return NULL;
+    }
+    /* UINT_MAX means span is not initialized yet */
+    span->span_start = UINT_MAX;
+    span->filename = ref(info->filename);
+    return span;
+}
+
+void free_span(struct span *span) {
+    if (span == NULL)
+        return;
+    unref(span->filename, string);
+    free(span);
+}
+
+void print_span(struct span *span) {
+    if (span == NULL)
+        return;
+    printf("%s label=(%i:%i) value=(%i:%i) span=(%i,%i)\n",
+            span->filename->str,
+            span->label_start, span->label_end,
+            span->value_start, span->value_end,
+            span->span_start, span->span_end);
+}
+
+void update_span(struct span *node_info, int x, int y) {
+    if (node_info == NULL)
+        return;
+    if (node_info->span_start == UINT_MAX) {
+        node_info->span_start = x;
+        node_info->span_end = y;
+    } else {
+        if (node_info->span_start > x)
+            node_info->span_start = x;
+        if (node_info->span_end < y)
+            node_info->span_end = y;
+    }
+}
+
 /*
  * Local variables:
  *  indent-tabs-mode: nil
diff --git a/src/info.h b/src/info.h
index cc5d2ce..4c27cfb 100644
--- a/src/info.h
+++ b/src/info.h
@@ -51,6 +51,17 @@ struct info {
     uint16_t last_line;
     uint16_t last_column;
     ref_t    ref;
+    int flags;
+};
+
+struct span {
+    struct string *filename;
+    uint label_start;
+    uint label_end;
+    uint value_start;
+    uint value_end;
+    uint span_start;
+    uint span_end;
 };
 
 char *format_info(struct info *info);
@@ -60,6 +71,10 @@ void print_info(FILE *out, struct info *info);
 /* Do not call directly, use UNREF instead */
 void free_info(struct info *info);
 
+struct span *make_span(struct info *info);
+void free_span(struct span *node_info);
+void update_span(struct span *node_info, int x, int y);
+void print_span(struct span *node_info);
 #endif
 
 
diff --git a/src/internal.h b/src/internal.h
index 71c5d42..b649e23 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -90,6 +90,10 @@
 /* Where to put information about parsing of path expressions */
 #define AUGEAS_META_PATHX AUGEAS_META_TREE "/pathx"
 
+/* Define: AUGEAS_SPAN_OPTION
+ * Enable or disable node indexes */
+#define AUGEAS_SPAN_OPTION AUGEAS_META_TREE "/span"
+
 /* Define: AUGEAS_LENS_ENV
  * Name of env var that contains list of paths to search for additional
    spec files */
@@ -111,6 +115,10 @@
 #define AUG_SAVE_NOOP_TEXT "noop"
 #define AUG_SAVE_OVERWRITE_TEXT "overwrite"
 
+/* constants for options in the tree */
+#define AUG_ENABLE "enable"
+#define AUG_DISABLE "disable"
+
 #ifdef __GNUC__
 
 #ifndef __GNUC_PREREQ
@@ -344,6 +352,7 @@ struct tree {
     struct tree *children;   /* List of children through NEXT */
     char        *value;
     int          dirty;
+    struct span *span;
 };
 
 /* The opaque structure used to represent path expressions. API's
diff --git a/src/transform.c b/src/transform.c
index 521414b..48eb67f 100644
--- a/src/transform.c
+++ b/src/transform.c
@@ -484,7 +484,8 @@ static int load_file(struct augeas *aug, struct lens *lens,
     struct tree *tree = NULL;
     char *path = NULL;
     struct lns_error *err = NULL;
-    int result = -1, r;
+    struct span *span = NULL;
+    int result = -1, r, text_len = 0;
 
     path = file_name_path(aug, filename);
     ERR_NOMEM(path == NULL, aug);
@@ -498,18 +499,24 @@ static int load_file(struct augeas *aug, struct lens *lens,
         err_status = "read_failed";
         goto done;
     }
-    text = append_newline(text, strlen(text));
+    text_len = strlen(text);
+    text = append_newline(text, text_len);
 
     struct info *info;
     make_ref(info);
     make_ref(info->filename);
-    info->filename->str = filename;
+    info->filename->str = strdup(filename);
     info->error = aug->error;
+    info->flags = aug->flags;
     info->first_line = 1;
 
+    if (aug->flags & AUG_ENABLE_SPAN) {
+        span = make_span(info);
+        ERR_NOMEM(span == NULL, info);
+    }
+
     tree = lns_get(info, lens, text, &err);
 
-    info->filename->str = NULL;
     unref(info, info);
 
     if (err != NULL) {
@@ -518,6 +525,14 @@ static int load_file(struct augeas *aug, struct lens *lens,
     }
 
     tree_replace(aug, path, tree);
+
+    /* top level node span entire file length */
+    if (span != NULL) {
+        tree->parent->span = span;
+        tree->parent->span->span_start = 0;
+        tree->parent->span->span_end = text_len;
+    }
+
     tree = NULL;
 
     result = 0;
diff --git a/tests/cutest.h b/tests/cutest.h
index 616fb1f..99e2d34 100644
--- a/tests/cutest.h
+++ b/tests/cutest.h
@@ -37,6 +37,10 @@ void die_oom(void);
 
 /* CuTest */
 
+#define CuAssertPositive(tc, n) CuAssertTrue(tc, (n) > 0)
+#define CuAssertZero(tc, n) CuAssertIntEquals(tc, 0, (n))
+#define CuAssertRetSuccess(tc, n) CuAssertIntEquals(tc, 0, (n))
+
 typedef struct CuTest CuTest;
 
 typedef void (*TestFunction)(CuTest *);
diff --git a/tests/test-api.c b/tests/test-api.c
index 322f4a9..54b4c7d 100644
--- a/tests/test-api.c
+++ b/tests/test-api.c
@@ -27,6 +27,8 @@
 #include "cutest.h"
 #include "internal.h"
 
+#include <unistd.h>
+
 static const char *abs_top_srcdir;
 static char *root;
 static char *loadpath;
@@ -222,6 +224,116 @@ static void testDefNodeCreateMeta(CuTest *tc) {
     aug_close(aug);
 }
 
+static void reset_indexes(uint *a, uint *b, uint *c, uint *d, uint *e, uint *f) {
+    *a = 0; *b = 0; *c = 0; *d = 0; *e = 0; *f = 0;
+}
+
+#define SPAN_TEST_DEF_LAST { .expr = NULL, .ls = 0, .le = 0, \
+        .vs = 0, .ve = 0, .ss = 0, .se = 0 }
+
+struct span_test_def {
+    const char *expr;
+    const char *f;
+    int ret;
+    int ls;
+    int le;
+    int vs;
+    int ve;
+    int ss;
+    int se;
+};
+
+static const struct span_test_def span_test[] = {
+    { .expr = "/files/etc/hosts/1/ipaddr", .f = "hosts", .ret = 0, .ls = 0, .le = 0, .vs = 104, .ve = 113, .ss = 104, .se = 113 },
+    { .expr = "/files/etc/hosts/1", .f = "hosts", .ret = 0, .ls = 0, .le = 0, .vs = 0, .ve = 0, .ss = 104, .se = 171 },
+    { .expr = "/files/etc/hosts/*[last()]", .f = "hosts", .ret = 0, .ls = 0, .le = 0, .vs = 0, .ve = 0, .ss = 266, .se = 309 },
+    { .expr = "/files/etc/hosts/#comment[2]", .f = "hosts", .ret = 0, .ls = 0, .le = 0, .vs = 58, .ve = 103, .ss = 56, .se = 104 },
+    { .expr = "/files/etc/hosts", .f = "hosts", .ret = 0, .ls = 0, .le = 0, .vs = 0, .ve = 0, .ss = 0, .se = 309 },
+    { .expr = "/files", .f = NULL, .ret = -1, .ls = 0, .le = 0, .vs = 0, .ve = 0, .ss = 0, .se = 0 },
+    { .expr = "/random", .f = NULL, .ret = -1, .ls = 0, .le = 0, .vs = 0, .ve = 0, .ss = 0, .se = 0 },
+    SPAN_TEST_DEF_LAST
+};
+
+static void testNodeInfo(CuTest *tc) {
+    int ret;
+    int i = 0;
+    struct augeas *aug;
+    struct span_test_def test;
+    char *fbase;
+    char msg[1024];
+    static const char *const expr = "/files/etc/hosts/1/ipaddr";
+
+    char *filename_ac;
+    uint label_start, label_end, value_start, value_end, span_start, span_end;
+
+    aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD|AUG_ENABLE_SPAN);
+    ret = aug_load(aug);
+    CuAssertRetSuccess(tc, ret);
+
+    while(span_test[i].expr != NULL) {
+        test = span_test[i];
+        i++;
+        ret = aug_span(aug, test.expr, &filename_ac, &label_start, &label_end,
+                     &value_start, &value_end, &span_start, &span_end);
+        sprintf(msg, "span_test %d ret\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.ret, ret);
+        sprintf(msg, "span_test %d label_start\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.ls, label_start);
+        sprintf(msg, "span_test %d label_end\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.le, label_end);
+        sprintf(msg, "span_test %d value_start\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.vs, value_start);
+        sprintf(msg, "span_test %d value_end\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.ve, value_end);
+        sprintf(msg, "span_test %d span_start\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.ss, span_start);
+        sprintf(msg, "span_test %d span_end\n", i);
+        CuAssertIntEquals_Msg(tc, msg, test.se, span_end);
+        if (filename_ac != NULL) {
+            fbase = basename(filename_ac);
+        } else {
+            fbase = NULL;
+        }
+        sprintf(msg, "span_test %d filename\n", i);
+        CuAssertStrEquals_Msg(tc, msg, test.f, fbase);
+        free(filename_ac);
+        filename_ac = NULL;
+        reset_indexes(&label_start, &label_end, &value_start, &value_end,
+                      &span_start, &span_end);
+    }
+
+    /* aug_span returns -1 and when no node matches */
+    ret = aug_span(aug, "/files/etc/hosts/*[ last() + 1 ]", &filename_ac,
+            &label_start, &label_end, &value_start, &value_end,
+            &span_start, &span_end);
+    CuAssertIntEquals(tc, -1, ret);
+    CuAssertPtrEquals(tc, NULL, filename_ac);
+    CuAssertIntEquals(tc, AUG_ENOMATCH, aug_error(aug));
+
+    /* aug_span should return an error when multiple nodes match */
+    ret = aug_span(aug, "/files/etc/hosts/*", &filename_ac,
+            &label_start, &label_end, &value_start, &value_end,
+            &span_start, &span_end);
+    CuAssertIntEquals(tc, -1, ret);
+    CuAssertPtrEquals(tc, NULL, filename_ac);
+    CuAssertIntEquals(tc, AUG_EMMATCH, aug_error(aug));
+
+    /* aug_span returns -1 if nodes span are not loaded */
+    aug_close(aug);
+    aug = aug_init(root, loadpath, AUG_NO_STDINC|AUG_NO_LOAD);
+    ret = aug_load(aug);
+    CuAssertRetSuccess(tc, ret);
+    ret = aug_span(aug, expr, &filename_ac, &label_start, &label_end,
+                 &value_start, &value_end, &span_start, &span_end);
+    CuAssertIntEquals(tc, -1, ret);
+    CuAssertPtrEquals(tc, NULL, filename_ac);
+    CuAssertIntEquals(tc, AUG_ENOSPAN, aug_error(aug));
+    reset_indexes(&label_start, &label_end, &value_start, &value_end,
+                  &span_start, &span_end);
+
+    aug_close(aug);
+}
+
 int main(void) {
     char *output = NULL;
     CuSuite* suite = CuSuiteNew();
@@ -232,6 +344,7 @@ int main(void) {
     SUITE_ADD_TEST(suite, testDefVarMeta);
     SUITE_ADD_TEST(suite, testDefNodeExistingMeta);
     SUITE_ADD_TEST(suite, testDefNodeCreateMeta);
+    SUITE_ADD_TEST(suite, testNodeInfo);
 
     abs_top_srcdir = getenv("abs_top_srcdir");
     if (abs_top_srcdir == NULL)
diff --git a/tests/test-load.c b/tests/test-load.c
index 6110ebc..d025900 100644
--- a/tests/test-load.c
+++ b/tests/test-load.c
@@ -27,10 +27,6 @@
 #include "cutest.h"
 #include "internal.h"
 
-#define CuAssertPositive(tc, n) CuAssertTrue(tc, (n) > 0)
-#define CuAssertZero(tc, n) CuAssertIntEquals(tc, 0, (n))
-#define CuAssertRetSuccess(tc, n) CuAssertIntEquals(tc, 0, (n))
-
 static const char *abs_top_srcdir;
 static const char *abs_top_builddir;
 static char *root = NULL;
@@ -488,7 +484,6 @@ int main(void) {
     char *output = NULL;
     CuSuite* suite = CuSuiteNew();
     CuSuiteSetup(suite, NULL, NULL);
-
     SUITE_ADD_TEST(suite, testDefault);
     SUITE_ADD_TEST(suite, testNoLoad);
     SUITE_ADD_TEST(suite, testNoAutoload);
-- 
1.7.1




More information about the augeas-devel mailing list