Makefile.in: Set the myfulldir and mydir variables (which are relative
authorTheodore Tso <tytso@mit.edu>
Sat, 14 Nov 1998 03:45:05 +0000 (03:45 +0000)
committerTheodore Tso <tytso@mit.edu>
Sat, 14 Nov 1998 03:45:05 +0000 (03:45 +0000)
to buildtop and thisconfigdir, respectively.)

configure.in: Build the test script prtest for doing regression test
suites of the profile library.

prof_err.et (PROF_MAGIC_ITERATOR): Add a new error code for the magic
number for the iterator structure.

prof_file.c (profile_update_file): Increment the update serial number
when the profile file is re-read.

prof_tree.c (profile_make_node_final, profile_is_node_final): Add a
new attribute for a node, which is whether or not the node is "final".
This controls whether or not the next profile file should be searched
when looking up a key which matches the section named by the node.
(profile_node_iterator_create, profile_node_iterator_free,
profile_node_iterator): New functions which take a profile_t and
returns all of the names or values for a particular search key.  This
iterator follows the rules of doing multiple profile file lookups
using the "final node" marker to stop searching subsequent profile
files.

prof_parse.c (parse_std_line): Add support for marking top level
sections, subsections, and individual nodes as final, using the '*'
character.
(dump_profile_to_file): Print finalized sections with the '*'
character.

prof_get.c: Update routines to use the iterators provided by
prof_tree.c.

prof_int.c: Add upd_serial member to the prf_file_t structure.  Define
the symbolic flags used by the profile node iterator.  Add function
declarations for profile_make_node_final, profile_is_node_final,
profile_node_iterator_create, profile_node_iterator_free,
profile_node_iterator, and profile_get_value.

test_profile.c: Add the query1 command which tests profile_get_value.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@11038 dc483132-0cff-0310-8789-dd5450dbe970

13 files changed:
src/util/profile/ChangeLog
src/util/profile/Makefile.in
src/util/profile/configure.in
src/util/profile/prof_err.et
src/util/profile/prof_file.c
src/util/profile/prof_get.c
src/util/profile/prof_int.h
src/util/profile/prof_parse.c
src/util/profile/prof_tree.c
src/util/profile/profile.5 [new file with mode: 0644]
src/util/profile/prtest.in [new file with mode: 0644]
src/util/profile/prtest.script [new file with mode: 0644]
src/util/profile/test_profile.c

index e3345397c49ef53980bb8e7cc7770c0c1e0495c8..21dd99016d1e040f831a9d42dc35df85215c1a0e 100644 (file)
@@ -1,3 +1,48 @@
+1998-11-13  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+       * Makefile.in: Set the myfulldir and mydir variables (which are
+               relative to buildtop and thisconfigdir, respectively.)
+
+       * configure.in: Build the test script prtest for doing regression
+               test suites of the profile library.
+
+       * prof_err.et (PROF_MAGIC_ITERATOR): Add a new error code for the
+               magic number for the iterator structure.
+
+       * prof_file.c (profile_update_file): Increment the update serial
+               number when the profile file is re-read.
+
+       * prof_tree.c (profile_make_node_final, profile_is_node_final):
+               Add a new attribute for a node, which is whether or not
+               the node is "final".  This controls whether or not the
+               next profile file should be searched when looking up a key
+               which matches the section named by the node.
+               (profile_node_iterator_create, profile_node_iterator_free,
+               profile_node_iterator): New functions which take a
+               profile_t and returns all of the names or values for a
+               particular search key.  This iterator follows the rules of
+               doing multiple profile file lookups using the "final node"
+               marker to stop searching subsequent profile files.
+       
+       * prof_parse.c (parse_std_line): Add support for marking top level
+               sections, subsections, and individual nodes as final,
+               using the '*' character.
+               (dump_profile_to_file): Print finalized sections with the '*'
+               character.
+
+       * prof_get.c: Update routines to use the iterators provided by
+               prof_tree.c.
+
+       * prof_int.c: Add upd_serial member to the prf_file_t structure.
+               Define the symbolic flags used by the profile node
+               iterator.  Add function declarations for
+               profile_make_node_final, profile_is_node_final,
+               profile_node_iterator_create, profile_node_iterator_free,
+               profile_node_iterator, and profile_get_value.
+
+       * test_profile.c: Add the query1 command which tests
+               profile_get_value. 
+
 1998-11-05  Geoffrey King  <gjking@mit.edu>
 
        * prof_init.c (profile_init): Fix a problem whereby if the last
index 0889e343c5c1b87a270d3fad14f970a33c780706..b17ef17e6ca44d6c7e0cc7d633b9bad095d453e3 100644 (file)
@@ -1,4 +1,6 @@
 thisconfigdir=.
+myfulldir=util/profile
+mydir=.
 BUILDTOP=$(REL)$(U)$(S)$(U)
 PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
index 0594dbaa917b9d7d987c2d0667ae91f6f0be39bd..850f3befbb732cfa4324ed909cf54614044535ce 100644 (file)
@@ -10,5 +10,8 @@ AC_PROG_AWK
 KRB5_BUILD_LIBOBJS
 KRB5_BUILD_PROGRAM
 KRB5_BUILD_LIBRARY_WITH_DEPS
-V5_AC_OUTPUT_MAKEFILE
+K5_GEN_MAKEFILE(., lib libobj)
+K5_GEN_FILE(prtest)
+K5_AC_OUTPUT
+
 
index 6e1dac68f4f5e97a78e797c3df6e662b0580ca49..e424042cb350770f47e5c3e957f4ce42905be091 100644 (file)
@@ -16,6 +16,7 @@ error_code    PROF_BAD_LINK_LIST,     "Bad linked list in profile structures"
 error_code     PROF_BAD_GROUP_LVL,     "Bad group level in profile strctures"
 error_code     PROF_BAD_PARENT_PTR,    
        "Bad parent pointer in profile strctures"
+error_code     PROF_MAGIC_ITERATOR,    "Bad magic value in profile iterator"
 
 #
 # generated by prof_parse.c
index d2b427e559ddd36d7311e559146d6740483eb1f5..070cbeffc0c80174a378f3952a917ebee05d35e5 100644 (file)
@@ -86,6 +86,7 @@ errcode_t profile_update_file(prf)
        f = fopen(prf->filename, "r");
        if (f == NULL)
                return errno;
+       prf->upd_serial++;
        retval = profile_parse_file(f, &prf->root);
        fclose(f);
        if (retval)
index adb175afaed053144f635a6d5ef6659bdd8f8164..2613127b7e33a473ae8dad50cb513e12ced010b0 100644 (file)
@@ -2,85 +2,6 @@
  * prof_get.c --- routines that expose the public interfaces for
  *     querying items from the profile.
  *
- * A profile object can contain multiple profile files; each profile
- * is composed of hierarchical sections.  Sections can contain
- * sections, or relations, both of which are named.  (Sections roughly
- * correspond to directories, and relations to files.)
- * 
- * Relations may contain multiple values; profile_get_values() will
- * return all of the values for a particular relation,
- * profile_get_value() will return the first such value for a
- * relation.
- *
- * When there are multiple profile files open for a particular
- * profile object, the searching algorithms will find the first
- * profile file which contains the full section-path, and only look in
- * that profile file for the named relation.
- *
- * An example here may be useful.  Consider a profile which is
- * initialied to search to profile files, ~/.samplerc and
- * /etc/sample.conf, in that order.  Let us suppose that the
- * system-wide /etc/sample.conf contains the following information:
- *
- * [realms]
- *     ATHENA.MIT.EDU = {
- *             kdc = kerberos.mit.edu:88
- *             kdc = kerberos-1.mit.edu:88
- *             kdc = kerberos-2.mit.edu:88
- *             admin_server = kerberos.mit.edu:88
- *             default_domain = mit.edu
- *     }
- *
- * [DNS]
- *     MIT.EDU = {
- *             strawb = {
- *                     version = 4.8.3
- *                     location = E40
- *             }
- *             bitsy = {
- *                     version = 4.8.3
- *                     location = E40
- *             }
- *     }
- *
- * ... and the user's ~/.samplerc contains the following:
- *
- * [realms]
- *     ATHENA.MIT.EDU = {
- *             kdc = kerberos-test.mit.edu
- *             admin_server = kerberos-test.mit.edu
- *     }
- *
- * [DNS]
- *     MIT.EDU = {
- *             w20-ns = {
- *                     version = 4.8.3
- *                     location = W20
- *             }
- *             bitsy = {
- *                     version = 4.9.4
- *             }
- *     }
- * 
- * In this example, the values for realms/ATHENA.MIT.EDU/kdc and
- * realms/ATHENA.MIT.EDU/admin_server will be taken from ~/.samplrc
- * exclusively, since the section realms/ATHENA.MIT.EDU was found
- * first in ~/.samplerc.
- * 
- * However, in the case of the [DNS] section, queries for
- * DNS/MIT.EDU/w20-ns/<*> will be taken from ~/.samplrc, and
- * DNS/MIT.EDU/strawb/<*> will be taken from /etc/sample.rc.
- * 
- * DNS/MIT.EDU/BITSY/version will return 4.9.4, since the entry
- * in ~/.samplerc will override the one in /etc/sample.conf.  Less
- * intuitively, a query for DNS/bitsy/location will return no value,
- * since the DNS/bitsy section exists in ~/.samplerc.
- * 
- * This can all be summed up using the following rule: a section found
- * in an earlier profile file completely shadows a section in a later
- * profile file for the purposes of looking up relations, but not when
- * looking up subsections contained in the section.
- * 
  */
 
 #include <stdio.h>
@@ -215,75 +136,6 @@ KRB5_DLLIMP void KRB5_CALLCONV profile_free_list(list)
     free(list);
 }
 
-/*
- * This function searches the profile for a named section, looking in
- * each file in the profile.  If ret_name is NULL, then this
- * function looks at the entire names array; if ret_name is non-NULL,
- * then the last entry in the names array is assumed to be the name of
- * the relation desired by profile_get_values(), and is returned in
- * ret_name.  The section looked up in that case will not include the
- * last entry in the names array.
- */
-static errcode_t lookup_section(profile, names, ret_name, ret_section)
-       profile_t       profile;
-       const char      **names;
-       const char      **ret_name;
-       struct profile_node **ret_section;
-{
-       prf_file_t      file;
-       errcode_t       retval;
-       int             done_idx = 0;
-       const char      **cpp;
-       void            *state;
-       struct profile_node *section;
-
-       if (profile == 0)
-               return PROF_NO_PROFILE;
-
-       if (names == 0 || names[0] == 0 || (ret_name && names[1] == 0))
-               return PROF_BAD_NAMESET;
-
-       if (ret_name)
-               done_idx = 1;
-
-       file = profile->first_file;
-       if ((retval = profile_update_file(file)))
-               return retval;
-
-       section = file->root;
-       cpp = names;
-
-       while (cpp[done_idx]) {
-               state = 0;
-               retval = profile_find_node_subsection(section, *cpp,
-                                                     &state, 0, &section);
-               if (retval == PROF_NO_SECTION) {
-                       /*
-                        * OK, we didn't find the section in this
-                        * file; let's try the next file.
-                        */
-                       file = file->next;
-                       if (!file)
-                               return retval;
-                       if ((retval = profile_update_file(file)))
-                               return retval;
-                       section = file->root;
-                       cpp = names;
-                       continue;
-               } else if (retval)
-                       return retval;
-               cpp++;
-       }
-       *ret_section = section;
-       if (ret_name)
-               *ret_name = *cpp;
-       return 0;
-}
-
-/*
- * This function finds a relation from the profile, and returns all of
- * the values from that relation.  
- */
 KRB5_DLLIMP errcode_t KRB5_CALLCONV
 profile_get_values(profile, names, ret_values)
        profile_t       profile;
@@ -291,24 +143,23 @@ profile_get_values(profile, names, ret_values)
        char    ***ret_values;
 {
        errcode_t               retval;
-       struct profile_node     *section;
        void                    *state;
-       const char              *name;
        char                    *value;
        struct profile_string_list values;
 
-       retval = lookup_section(profile, names, &name, &section);
-       if (retval)
+       if ((retval = profile_node_iterator_create(profile, names,
+                                                  PROFILE_ITER_RELATIONS_ONLY,
+                                                  &state)))
                return retval;
 
-       init_list(&values);
+       if ((retval = init_list(&values)))
+               return retval;
 
-       state = 0;
        do {
-               if ((retval = profile_find_node_relation(section, name,
-                                                        &state, 0, &value)))
+               if ((retval = profile_node_iterator(&state, 0, 0, &value)))
                        goto cleanup;
-               add_to_list(&values, value);
+               if (value)
+                       add_to_list(&values, value);
        } while (state);
 
        end_list(&values, ret_values);
@@ -317,34 +168,37 @@ profile_get_values(profile, names, ret_values)
 cleanup:
        end_list(&values, 0);
        return retval;
-}      
+}
 
 /*
  * This function only gets the first value from the file; it is a
  * helper function for profile_get_string, profile_get_integer, etc.
  */
-static errcode_t profile_get_value(profile, names, ret_value)
+errcode_t profile_get_value(profile, names, ret_value)
        profile_t       profile;
        const char      **names;
-       char    **ret_value;
+       const char      **ret_value;
 {
        errcode_t               retval;
-       struct profile_node     *section;
        void                    *state;
-       const char              *name;
        char                    *value;
 
-       retval = lookup_section(profile, names, &name, &section);
-       if (retval)
+       if ((retval = profile_node_iterator_create(profile, names,
+                                                  PROFILE_ITER_RELATIONS_ONLY,
+                                                  &state)))
                return retval;
 
-       state = 0;
-       if ((retval = profile_find_node_relation(section, name,
-                                                &state, 0, &value)))
-               return retval;
+       if ((retval = profile_node_iterator(&state, 0, 0, &value)))
+               goto cleanup;
+
+       if (value)
+               *ret_value = value;
+       else
+               retval = PROF_NO_RELATION;
        
-       *ret_value = value;
-       return 0;
+cleanup:
+       profile_node_iterator_free(&state);
+       return retval;
 }
 
 errcode_t profile_get_string(profile, name, subname, subsubname,
@@ -388,7 +242,7 @@ errcode_t profile_get_integer(profile, name, subname, subsubname,
        int             def_val;
        int             *ret_int;
 {
-       char    *value;
+       const char      *value;
        errcode_t       retval;
        const char      *names[4];
 
@@ -421,59 +275,29 @@ errcode_t profile_get_subsection_names(profile, names, ret_names)
        const char      **names;
        char            ***ret_names;
 {
-       prf_file_t      file;
-       errcode_t       retval;
-       char            *name;
-       const char      **cpp;
-       void            *state;
-       struct profile_node *section;
+       errcode_t               retval;
+       void                    *state;
+       char                    *name;
        struct profile_string_list values;
 
-       if (profile == 0)
-               return PROF_NO_PROFILE;
+       if ((retval = profile_node_iterator_create(profile, names,
+                  PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY,
+                  &state)))
+               return retval;
 
-       if (names == 0)
-               return PROF_BAD_NAMESET;
+       if ((retval = init_list(&values)))
+               return retval;
+
+       do {
+               if ((retval = profile_node_iterator(&state, 0, &name, 0)))
+                       goto cleanup;
+               if (name)
+                       add_to_list(&values, name);
+       } while (state);
 
-       init_list(&values);
-       for (file = profile->first_file; file; file = file->next) {
-               if ((retval = profile_update_file(file)))
-                       return retval;
-               section = file->root;
-               cpp = names;
-               /*
-                * Find the correct section in this file, if it
-                * exists.
-                */
-               while (*cpp) {
-                       state = 0;
-                       retval = profile_find_node_subsection(section, *cpp,
-                                                     &state, 0, &section);
-                       if (retval == PROF_NO_SECTION)
-                               continue;
-                       else if (retval)
-                               goto cleanup;
-                       cpp++;
-               }
-               /*
-                * Now find all of the subsections and append them to
-                * the list.
-                */
-               state = 0;
-               do {
-                       retval = profile_find_node_subsection(section, 0, 
-                                                     &state, &name, 0);
-                       if (retval == PROF_NO_SECTION)
-                               break;
-                       else if (retval)
-                               goto cleanup;
-                       if (!is_list_member(&values, name))
-                               add_to_list(&values, name);
-               } while (state);
-       }
-       
        end_list(&values, ret_names);
        return 0;
+       
 cleanup:
        end_list(&values, 0);
        return retval;
@@ -489,35 +313,29 @@ errcode_t profile_get_relation_names(profile, names, ret_names)
        char            ***ret_names;
 {
        errcode_t               retval;
-       struct profile_node     *section;
        void                    *state;
        char                    *name;
        struct profile_string_list values;
 
-       retval = lookup_section(profile, names, 0, &section);
-       if (retval)
+       if ((retval = profile_node_iterator_create(profile, names,
+                  PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY,
+                  &state)))
                return retval;
 
-       init_list(&values);
+       if ((retval = init_list(&values)))
+               return retval;
 
-       state = 0;
        do {
-               retval = profile_find_node_relation(section, 0,
-                                                   &state, &name, 0);
-               if (retval == PROF_NO_RELATION)
-                       break;
-               else if (retval)
+               if ((retval = profile_node_iterator(&state, 0, &name, 0)))
                        goto cleanup;
-               if (!is_list_member(&values, name))
+               if (name && !is_list_member(&values, name))
                        add_to_list(&values, name);
        } while (state);
 
        end_list(&values, ret_names);
        return 0;
+       
 cleanup:
        end_list(&values, 0);
        return retval;
 }
-
-
-
index 8a45ec423eedd71b2ab339b8aa184a7ef98e525b..99d7fc1ae65bf03540fbcc302d9819982e2d283c 100644 (file)
@@ -43,6 +43,7 @@ struct _prf_file_t {
        struct profile_node *root;
        time_t          timestamp;
        int             flags;
+       int             upd_serial;
        struct _prf_file_t *next;
 };
 
@@ -60,6 +61,16 @@ struct _profile_t {
 
 typedef struct _profile_t *profile_t;
 
+/*
+ * Used by the profile node iterator in prof_tre.c
+ */
+#define PROFILE_ITER_LIST_SECTION      0x0001
+#define PROFILE_ITER_SECTIONS_ONLY     0x0002
+#define PROFILE_ITER_RELATIONS_ONLY    0x0004
+
+#define PROFILE_ITER_FINAL_SEEN                0x0100
+
+
 /* profile_parse.c */
 
 errcode_t profile_parse_file
@@ -82,6 +93,12 @@ errcode_t profile_add_node
                    const char *name, const char *value,
                    struct profile_node **ret_node));
 
+errcode_t profile_make_node_final
+       PROTOTYPE((struct profile_node *node));
+       
+int profile_is_node_final
+       PROTOTYPE((struct profile_node *node));
+       
 errcode_t profile_find_node_relation
        PROTOTYPE ((struct profile_node *section,
                    const char *name, void **state,
@@ -103,6 +120,17 @@ errcode_t profile_find_node_name
        PROTOTYPE ((struct profile_node *section, void **state,
                    char **ret_name));
 
+errcode_t profile_node_iterator_create
+       PROTOTYPE((profile_t profile, const char **names,
+                  int flags, void **ret_iter));
+
+void profile_node_iterator_free
+       PROTOTYPE((void **iter_p));
+
+errcode_t profile_node_iterator
+       PROTOTYPE((void **iter_p, struct profile_node **ret_node,
+                  char **ret_name, char **ret_value));
+
 /* prof_file.c */
 
 errcode_t profile_open_file
@@ -134,6 +162,10 @@ KRB5_DLLIMP errcode_t KRB5_CALLCONV profile_get_values
        PROTOTYPE ((profile_t profile, const char **names,
                    char ***ret_values));
 
+errcode_t profile_get_value
+       PROTOTYPE ((profile_t profile, const char **names,
+                   const char  **ret_value));
+       
 errcode_t profile_get_string
        PROTOTYPE((profile_t profile, const char *name, const char *subname, 
                        const char *subsubname, const char *def_val,
index cc7e36b8f3f9887ef1bf50d3f9f89ca0c47efc2e..833f80d358e54b4c2fc543b528ef314e94b90688 100644 (file)
@@ -89,6 +89,7 @@ static errcode_t parse_std_line(line, state)
        char    *cp, ch, *tag, *value;
        char    *p;
        errcode_t retval;
+       struct profile_node     *node;
        int do_subsection = 0;
        void *iter = 0;
        
@@ -125,6 +126,10 @@ static errcode_t parse_std_line(line, state)
                 * Finish off the rest of the line.
                 */
                cp = p+1;
+               if (*cp == '*') {
+                       profile_make_node_final(state->current_section);
+                       cp++;
+               }
                if (*cp)
                        return PROF_SECTION_SYNTAX;
                return 0;
@@ -132,6 +137,8 @@ static errcode_t parse_std_line(line, state)
        if (ch == '}') {
                if (state->group_level == 0)
                        return PROF_EXTRA_CBRACE;
+               if (*(cp+1) == '*')
+                       profile_make_node_final(state->current_section);
                retval = profile_get_node_parent(state->current_section,
                                                 &state->current_section);
                if (retval)
@@ -170,15 +177,24 @@ static errcode_t parse_std_line(line, state)
                        *cp-- = 0;
        }
        if (do_subsection) {
+               p = strchr(tag, '*');
+               if (p)
+                       *p = '\0';
                retval = profile_add_node(state->current_section,
                                          tag, 0, &state->current_section);
                if (retval)
                        return retval;
-
+               if (p)
+                       profile_make_node_final(state->current_section);
                state->group_level++;
                return 0;
        }
-       profile_add_node(state->current_section, tag, value, 0);
+       p = strchr(tag, '*');
+       if (p)
+               *p = '\0';
+       profile_add_node(state->current_section, tag, value, &node);
+       if (p)
+               profile_make_node_final(node);
        return 0;
 }
 
@@ -384,7 +400,8 @@ void dump_profile_to_file(root, level, dstfile)
                if (level == 0) { /* [xxx] */
                        for (i=0; i < level; i++)
                                fprintf(dstfile, "\t");
-                       fprintf(dstfile, "[%s]%s", name, EOL);
+                       fprintf(dstfile, "[%s]%s%s", name,
+                               profile_is_node_final(p) ? "*" : "", EOL);
                        dump_profile_to_file(p, level+1, dstfile);
                        fprintf(dstfile, EOL);
                } else {        /* xxx = { ... } */
@@ -394,7 +411,8 @@ void dump_profile_to_file(root, level, dstfile)
                        dump_profile_to_file(p, level+1, dstfile);
                        for (i=0; i < level; i++)
                                fprintf(dstfile, "\t");
-                       fprintf(dstfile, "}%s", EOL);
+                       fprintf(dstfile, "}%s%s",
+                               profile_is_node_final(p) ? "*" : "", EOL);
                }
        } while (iter != 0);
 }
index 80d633dedd89a4b14f47c55369b2ae0d0bc60f7d..3bb05d29ff99a2606f71eceb589d77f645a5f319 100644 (file)
@@ -32,6 +32,7 @@ struct profile_node {
        char *name;
        char *value;
        int group_level;
+       int final:1;            /* Indicate don't search next file */
        struct profile_node *first_child;
        struct profile_node *parent;
        struct profile_node *next, *prev;
@@ -174,6 +175,27 @@ errcode_t profile_add_node(section, name, value, ret_node)
        return 0;
 }
 
+/*
+ * Set the final flag on a particular node.
+ */
+errcode_t profile_make_node_final(node)
+       struct profile_node *node;
+{
+       CHECK_MAGIC(node);
+
+       node->final = 1;
+       return 0;
+}
+
+/*
+ * Check the final flag on a node
+ */
+int profile_is_node_final(node)
+       struct profile_node *node;
+{
+       return (node->final != 0);
+}
+
 /*
  * Iterate through the section, returning the relations which match
  * the given name.  If name is NULL, then interate through all the
@@ -340,3 +362,174 @@ errcode_t profile_get_node_parent(section, parent)
        *parent = section->parent;
        return 0;
 }
+
+/*
+ * This is a general-purpose iterator for returning all nodes that
+ * match the specified name array.  
+ */
+struct profile_iterator {
+       prf_magic_t             magic;
+       profile_t               profile;
+       int                     flags;
+       const char              **names;
+       const char              *name;
+       prf_file_t              file;
+       int                     file_serial;
+       int                     done_idx;
+       struct profile_node     *node;
+       int                     num;
+};
+
+errcode_t profile_node_iterator_create(profile, names, flags, ret_iter)
+       profile_t       profile;
+       const char      **names;
+       int             flags;
+       void            **ret_iter;
+{
+       struct profile_iterator *iter;
+       int     done_idx = 0;
+
+       if (profile == 0)
+               return PROF_NO_PROFILE;
+       if (profile->magic != PROF_MAGIC_PROFILE)
+               return PROF_MAGIC_PROFILE;
+       if (!names)
+               return PROF_BAD_NAMESET;
+       if (!(flags & PROFILE_ITER_LIST_SECTION)) {
+               if (!names[0])
+                       return PROF_BAD_NAMESET;
+               done_idx = 1;
+       }
+
+       if ((iter = malloc(sizeof(struct profile_iterator))) == NULL)
+               return ENOMEM;
+
+       iter->magic = PROF_MAGIC_ITERATOR;
+       iter->profile = profile;
+       iter->names = names;
+       iter->flags = flags;
+       iter->file = profile->first_file;
+       iter->done_idx = done_idx;
+       iter->node = 0;
+       iter->num = 0;
+       *ret_iter = iter;
+       return 0;
+}
+
+void profile_node_iterator_free(iter_p)
+       void    **iter_p;
+{
+       struct profile_iterator *iter;
+
+       if (!iter_p)
+               return;
+       iter = *iter_p;
+       if (!iter || iter->magic != PROF_MAGIC_ITERATOR)
+               return;
+       free(iter);
+       *iter_p = 0;
+}
+
+errcode_t profile_node_iterator(iter_p, ret_node, ret_name, ret_value)
+       void    **iter_p;
+       struct profile_node     **ret_node;
+       char **ret_name, **ret_value;
+{
+       struct profile_iterator         *iter = *iter_p;
+       struct profile_node             *section, *p;
+       const char                      **cpp;
+       errcode_t                       retval;
+       int                             skip_num = 0;
+
+       if (iter->magic != PROF_MAGIC_ITERATOR)
+               return PROF_MAGIC_ITERATOR;
+       /*
+        * If the file has changed, then the node pointer is invalid,
+        * so we'll have search the file again looking for it.
+        */
+       if (iter->node && (iter->file->upd_serial != iter->file_serial)) {
+               iter->flags &= ~PROFILE_ITER_FINAL_SEEN;
+               skip_num = iter->num;
+               iter->node = 0;
+       }
+get_new_file:
+       while (iter->node == 0) {
+               if (iter->file == 0 ||
+                   (iter->flags & PROFILE_ITER_FINAL_SEEN)) {
+                       profile_node_iterator_free(iter_p);
+                       if (ret_node)
+                               *ret_node = 0;
+                       if (ret_name)
+                               *ret_name = 0;
+                       if (ret_value)
+                               *ret_value =0;
+                       return 0;
+               }
+               if ((retval = profile_update_file(iter->file))) {
+                       profile_node_iterator_free(iter_p);
+                       return retval;
+               }
+               iter->file_serial = iter->file->upd_serial;
+               /*
+                * Find the section to list if we are a LIST_SECTION,
+                * or find the containing section if not.
+                */
+               section = iter->file->root;
+               for (cpp = iter->names; cpp[iter->done_idx]; cpp++) {
+                       for (p=section->first_child; p; p = p->next)
+                               if (!strcmp(p->name, *cpp) && !p->value)
+                                       break;
+                       if (!p) {
+                               section = 0;
+                               break;
+                       }
+                       section = p;
+                       if (p->final)
+                               iter->flags |= PROFILE_ITER_FINAL_SEEN;
+               }
+               if (!section) {
+                       iter->file = iter->file->next;
+                       skip_num = 0;
+                       continue;
+               }
+               iter->name = *cpp;
+               iter->node = section->first_child;
+       }
+       /*
+        * OK, now we know iter->node is set up correctly.  Let's do
+        * the search.
+        */
+       for (p = iter->node; p; p = p->next) {
+               if (iter->name && strcmp(p->name, iter->name))
+                       continue;
+               if ((iter->flags & PROFILE_ITER_SECTIONS_ONLY) &&
+                   p->value)
+                       continue;
+               if ((iter->flags & PROFILE_ITER_RELATIONS_ONLY) &&
+                   !p->value)
+                       continue;
+               if (skip_num > 0) {
+                       skip_num--;
+                       continue;
+               }
+               break;
+       }
+       iter->num++;
+       if (!p) {
+               iter->file = iter->file->next;
+               iter->node = 0;
+               skip_num = 0;
+               goto get_new_file;
+       }
+       if ((iter->node = p->next) == NULL)
+               iter->file = iter->file->next;
+       if (ret_node)
+               *ret_node = p;
+       if (ret_name)
+               *ret_name = p->name;
+       if (ret_value)
+               *ret_value = p->value;
+       return 0;
+};
+
+
diff --git a/src/util/profile/profile.5 b/src/util/profile/profile.5
new file mode 100644 (file)
index 0000000..7f3b36a
--- /dev/null
@@ -0,0 +1,71 @@
+
+A profile file is a generic way of storing program configuration
+information for applications.  An application may choose to consult
+multiple configuration files; for example, a Kerberos application
+might look first in ~/.krb5rc, and then in /etc/krb5.conf.  So
+/etc/krb5.conf would contain the side-wide default configuration for
+Kerberos, and ~/.krb5rc would contain the user's specific
+configuration overrides.
+
+Configuration information is stored in relations, which have a name
+and a value.  There may be multiple relations with the same name.
+Relations are always contained inside named sections.  Sections can
+contain relations and other named child sections.
+
+Top-level sections are defined by enclosing the section name in square
+braces.  Child sections are defined by enclosing the contents of the
+child section in curly braces.  Relations are defined by using the
+format "name = value".  
+
+An example profile file might look like this:
+
+[libdefaults]
+       default_realm = ATHENA.MIT.EDU
+
+[realms]
+       ATHENA.MIT.EDU = {
+               kdc = kerberos.mit.edu:88
+               kdc = kerberos-1.mit.edu:88
+               kdc = kerberos-2.mit.edu:88
+               admin_server = kerberos.mit.edu:88
+               default_domain = mit.edu
+       }
+       CYGNUS.COM = {
+               kdc = KERBEROS-1.CYGNUS.COM
+               kdc = KERBEROS.CYGNUS.COM
+               admin_server = KERBEROS.MIT.EDU
+       }
+
+In this example, the profile file has two top-level sections,
+"libdefaults" and "realms".  The libdefaults section has a single
+relation which is named "default_realm" and has the value
+"ATHENA.MIT.EDU".  The realms section has two child sections,
+"ATHENA.MIT.EDU" and "CYGNUS.MIT.EDU".  Each of these child has a
+number of relations, "kdc", "admin_server", and (in the case of
+"ATHENA.MIT.EDU"), "default_domain".  Note that there are multiple
+relations with the name "kdc" in both sections; if a
+profile_get_values() is called querying the "kdc" relation, both
+values will be returned.
+
+Sections may be marked as "final".  If they are marked as final, then
+the contents of that section override all subsequent profile files (if
+the application is searching multiple profile files for its
+configuration information).  Normally, all of the profiles are
+searched for a matching relation, and all of the values found in all
+of the various profile files will be returned.  
+
+Top-level sections are marked as final by adding an '*' character
+following the closing square brace.  Child sections are marked as
+final by adding a '*' character after the closing curly brace.  So for
+example, in this example both the "libdefaults" and "ATHENA.MIT.EDU"
+sections have been marked as final:
+
+[libdefaults]*
+       default_realm = ATHENA.MIT.EDU
+
+[realms]
+       ATHENA.MIT.EDU = {
+               kdc = kerberos.mit.edu:88
+               admin_server = kerberos.mit.edu:88
+       }*
+
diff --git a/src/util/profile/prtest.in b/src/util/profile/prtest.in
new file mode 100644 (file)
index 0000000..ef3efb3
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+SRCDIR=@srcdir@
+SCRIPT=$SRCDIR/prtest.script
+REPORT=prtest.report
+TESTPROG=./test_profile
+
+rm -f $REPORT
+
+if test -f $SCRIPT ; then 
+       : 
+else
+       echo "$SCRIPT not found!"
+       exit 1
+fi
+
+grep -v ^\# < $SCRIPT | while read filespec cmd args
+do
+       case $filespec in
+       krb5)
+               file=$SRCDIR/krb5.conf
+               ;;
+       test)
+               file=$SRCDIR/test.ini
+               ;;
+       *)
+               echo "Unknown file specifer $file!"
+               exit 1
+               ;;
+       esac
+       if test $cmd = parse ; then
+           echo \# test_parse $filespec   >> $REPORT 2>&1
+           $TESTPARSE $file >> $REPORT    >> $REPORT 2>&1
+       fi
+       echo \# $filespec $cmd $args  >> $REPORT 2>&1
+       $TESTPROG $file $cmd $args    >> $REPORT 2>&1 
+done 
diff --git a/src/util/profile/prtest.script b/src/util/profile/prtest.script
new file mode 100644 (file)
index 0000000..5d5a144
--- /dev/null
@@ -0,0 +1,11 @@
+krb5 query realms ATHENA.MIT.EDU kdc
+krb5 query1 realms ATHENA.MIT.EDU kdc
+krb5 query libdefaults ticket_lifetime
+krb5 query1 libdefaults ticket_lifetime
+krb5 query libdefaults not_present
+krb5 query1 libdefaults not_present
+krb5 list_sections libdefaults
+krb5 list_sections realms
+krb5 list_relations libdefaults
+krb5 list_relations realms
+krb5 list_relations realms ATHENA.MIT.EDU
index 19edbaf3f616916e40c931253b660ed339c489df..4b2df6d36de282cb4ccfb9e886d865ca2dda6712 100644 (file)
@@ -7,7 +7,7 @@
 #include <stdlib.h>
 #endif
 
-#include "profile.h"
+#include "prof_int.h"
 #ifndef _MSDOS
 #include "com_err.h"
 #else
@@ -29,8 +29,10 @@ int main(argc, argv)
     profile_t  profile;
     long       retval;
     char       **values, **cpp;
+    const char *value;
     const char **names;
     char       *cmd;
+    int                print_value = 0;
     
     if (argc < 3) {
            fprintf(stderr, "Usage: %s filename cmd argset\n", program_name);
@@ -48,6 +50,9 @@ int main(argc, argv)
     names = (const char **) argv+3;
     if (!strcmp(cmd, "query")) {
            retval = profile_get_values(profile, names, &values);
+    } else if (!strcmp(cmd, "query1")) {
+           retval = profile_get_value(profile, names, &value);
+           print_value++;
     } else if (!strcmp(cmd, "list_sections")) {
            retval = profile_get_subsection_names(profile, names, &values);
     } else if (!strcmp(cmd, "list_relations")) {
@@ -60,11 +65,13 @@ int main(argc, argv)
            com_err(argv[0], retval, "while getting values");
            exit(1);
     }
-    for (cpp = values; *cpp; cpp++) {
-       printf("%s\n", *cpp);
-       free(*cpp);
+    if (print_value) {
+           printf("%s\n", value);
+    } else {
+           for (cpp = values; *cpp; cpp++)
+                   printf("%s\n", *cpp);
+           profile_free_list(values);
     }
-    free(values);
     profile_release(profile);
 
     return 0;