--- /dev/null
+CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+LOCALINCLUDE=-I. -I$(srcdir)/../et
+
+OBJS = prof_tree.o \
+ prof_file.o \
+ prof_parse.o \
+ prof_err.o \
+ prof_init.o
+
+SRCS = $(srcdir)/prof_tree.c \
+ $(srcdir)/prof_file.c \
+ $(srcdir)/prof_parse.c \
+ prof_err.c \
+ $(srcdir)/prof_init.c
+
+LIBS = ../et/libcom_err.a
+
+all:: includes libprofile.a test_parse
+
+libprofile.a: $(OBJS)
+ $(ARCHIVE) $@ $(OBJS)
+ $(RANLIB) $@
+
+test_parse: test_parse.o $(OBJS) $(LIBS)
+ cc -o test_parse test_parse.o $(OBJS) $(LIBS)
+
+test_profile: test_profile.o $(OBJS) $(LIBS)
+ cc -o test_profile test_profile.o $(OBJS) $(LIBS)
+
+profile.h: prof_err.h profile.h.in
+ cat $(srcdir)/profile.h.in prof_err.h > $@
+
+prof_err.h: prof_err.et
+
+prof_err.c: prof_err.et
+
+clean::
+ rm -f $(PROGS) *.o *~ test_parse core libprofile.a prof_err.h \
+ prof_err.c
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+prof_tree.o : $(srcdir)/prof_tree.c $(srcdir)/prof_int.h ./prof_err.h
+prof_file.o : $(srcdir)/prof_file.c $(srcdir)/prof_int.h ./prof_err.h
+prof_parse.o : $(srcdir)/prof_parse.c $(srcdir)/prof_int.h ./prof_err.h
+prof_err.o : prof_err.c
+prof_init.o : $(srcdir)/prof_init.c $(srcdir)/prof_int.h ./prof_err.h
+
--- /dev/null
+AC_INIT(prof_parse.c)
+WITH_CCOPTS
+CONFIG_RULES
+AC_SET_BUILDTOP
+AC_PROG_ARCHIVE
+AC_PROG_RANLIB
+ET_RULES
+CopyHeader(profile.h,$(BUILDTOP)/include)
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+EGA.INI/VGA.INI
+**************** WARNING ********************
+This file may contain lines with more than 256
+characters. Some editors will truncate or split
+these lines. If you are not sure whether your
+editor can handle long lines, exit now without
+saving the file.
+
+Note: The editor which is invoked by the
+ MS-DOS 5.0 EDIT command can be used
+ to edit this file.
+**************** NOTE ***********************
+Everything up to the first left square bracket
+character is considered a comment.
+***********************************************
+[savestate]
+screenmode = text
+resolution = low
+startup = filemanager
+filemanagermode = shared
+sortkey = name
+pause = disabled
+explicitselection = disabled
+swapmouse = disabled
+tasklist = disabled
+switching = disabled
+[programstarter]
+currentcolor = Ocean
+filemanager = enabled
+command = enabled
+group =
+{
+ program =
+ {
+ command = COMMAND
+ title = Command Prompt
+ help = Starts the MS-DOS command prompt where you can type any MS-DOS command.^m^mTo return to MS-DOS Shell from the command line:^m^m1. Type exit^m2. Press ENTER.^m^mRelated Topic^m " More on Command Prompt "~$129~
+ pause = disabled
+ }
+ program =
+ {
+ command = EDIT %1
+ title = Editor
+ help = Starts MS-DOS Editor, a text editor you can use to create and modify text files. After you choose Editor, you can specify the file you want to work with in a dialog box.^m^mRelated Topic^m " More on Editor "~$130~
+ pause = disabled
+ dialog =
+ {
+ title = File to Edit
+ info = Enter the name of the file to edit. To start MS-DOS Editor without opening a file, press ENTER.
+ prompt = File to edit?
+ parameter = %1
+ }
+ }
+ program =
+ {
+ command = QBASIC %1
+ title = MS-DOS QBasic
+ help = Starts MS-DOS QBasic, a programming environment you can use to create, modify, run, and debug programs. After you choose MS-DOS QBasic, you can specify the file you want to work with in a dialog box.^m^mRelated Topic^m " More on MS-DOS QBasic "~$131~
+ pause = disabled
+ dialog =
+ {
+ title = MS-DOS QBasic File
+ info = Enter the name of a QBasic program. To start without opening a program, press ENTER.
+ prompt = QBasic File?
+ parameter = %1
+ }
+ }
+ group =
+ {
+ title = Disk Utilities
+ help = Displays program items you can choose to manage your disks. You can also choose to open the Main group or any group you may have added.
+ program =
+ {
+ command = diskcopy %1
+ title = Disk Copy
+ pause = enabled
+ dialog =
+ {
+ title = Disk Copy
+ info = Enter the source and destination drives.
+ prompt = Parameters . . .
+ default = a: b:
+ parameter = %1
+ }
+ help = Temporarily leaves MS-DOS Shell to copy the contents of a floppy disk to another floppy disk. After you choose Disk Copy, a dialog box suggests parameters and switches you can either accept or replace.^m^mRelated Topic^m " More on Disk Copy "~$132~
+ }
+ program =
+ {
+ command = backup %1
+ title = Backup Fixed Disk
+ pause = enabled
+ dialog =
+ {
+ title = Backup Fixed Disk
+ info = Enter the source and destination drives.
+ prompt = Parameters . . .
+ default = c:\*.* a: /s
+ parameter = %1
+ }
+ help = Temporarily leaves MS-DOS Shell to copy files from one disk to another. After you choose Backup Fixed Disk, a dialog box suggests parameters and switches you can either accept or replace.^m^mRelated Topic^m " More on Backup Fixed Disk "~$133~
+ }
+ program =
+ {
+ command = restore %1
+ title = Restore Fixed Disk
+ pause = enabled
+ dialog =
+ {
+ title = Restore Fixed Disk
+ info = Enter the source and destination drives.
+ prompt = Parameters . . .
+ parameter = %1
+ }
+ help = Temporarily leaves MS-DOS Shell to restore files that were backed up. After you choose Restore Fixed Disk, a dialog box suggests parameters and switches you can either accept or replace.^m^mRelated Topic^m " More on Restore Fixed Disk "~$134~
+ }
+ program =
+ {
+ command = format %1 /q
+ title = Quick Format
+ pause = enabled
+ dialog =
+ {
+ title = Quick Format
+ info = Enter the drive to quick format.
+ prompt = Parameters . . .
+ default = a:
+ parameter = %1
+ }
+ help = Temporarily leaves MS-DOS Shell to prepare a disk to accept MS-DOS files. After you choose Quick Format, a dialog box suggests parameters and switches you can either accept or replace.^m^mRelated Topic^m " More on Quick Format "~$136~
+ screenmode = text
+ alttab = enabled
+ altesc = enabled
+ ctrlesc = enabled
+ prevent = enabled
+ }
+ program =
+ {
+ command = format %1
+ title = Format
+ pause = enabled
+ dialog =
+ {
+ title = Format
+ info = Enter the drive to format.
+ prompt = Parameters . . .
+ default = a:
+ parameter = %1
+ }
+ help = Temporarily leaves MS-DOS Shell to prepare a disk to accept MS-DOS files. After you choose Format, a dialog box suggests parameters and switches you can either accept or replace.^m^mRelated Topic^m " More on Format "~$135~
+ }
+ program =
+ {
+ command = undelete %1
+ title = Undelete
+ help = Recovers deleted files.^m^mWARNING: If your disk is full or if you are using task swapping, using this program item may render some deleted files unrecoverable.^m^mRelated Procedure^m " Restoring Deleted Files "~I155~
+ pause = enabled
+ dialog =
+ {
+ title = Undelete
+ info = WARNING! This action may cause the permanent loss of some deleted files. Press F1 for more information.
+ prompt = Parameters . . .
+ default = /LIST
+ parameter = %1
+ }
+ screenmode = text
+ alttab = enabled
+ altesc = enabled
+ ctrlesc = enabled
+ prevent = enabled
+ }
+ }
+}
+color =
+{
+ selection =
+ {
+ title = Basic Blue
+ foreground =
+ {
+ base = black
+ highlight = brightwhite
+ selection = brightwhite
+ alert = brightred
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = cyan
+ dialog = black
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = blue
+ selection = black
+ alert = brightwhite
+ menubar = white
+ menu = brightwhite
+ disabled = brightwhite
+ accelerator = brightwhite
+ dialog = brightwhite
+ button = white
+ elevator = white
+ titlebar = white
+ scrollbar = black
+ borders = brightwhite
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = brightblack
+ }
+ }
+ selection =
+ {
+ title = Ocean
+ foreground =
+ {
+ base = black
+ highlight = brightwhite
+ selection = brightwhite
+ alert = brightwhite
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = brightwhite
+ dialog = black
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = blue
+ selection = black
+ alert = white
+ menubar = cyan
+ menu = cyan
+ disabled = cyan
+ accelerator = cyan
+ dialog = cyan
+ button = brightwhite
+ elevator = white
+ titlebar = white
+ scrollbar = black
+ borders = black
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = brightcyan
+ }
+ }
+ selection =
+ {
+ title = Monochrome-2 Colors
+ foreground =
+ {
+ base = black
+ highlight = white
+ selection = white
+ alert = black
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = white
+ dialog = black
+ button = white
+ elevator = black
+ titlebar = white
+ scrollbar = white
+ borders = black
+ drivebox = black
+ driveicon = black
+ }
+ background =
+ {
+ base = white
+ highlight = black
+ selection = black
+ alert = white
+ menubar = white
+ menu = white
+ disabled = white
+ accelerator = black
+ dialog = white
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = black
+ borders = black
+ drivebox = white
+ driveicon = white
+ }
+ }
+ selection =
+ {
+ title = Monochrome-4 Colors
+ foreground =
+ {
+ base = black
+ highlight = brightwhite
+ selection = brightwhite
+ alert = black
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = brightwhite
+ dialog = black
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = brightblack
+ selection = brightblack
+ alert = brightwhite
+ menubar = brightwhite
+ menu = white
+ disabled = white
+ accelerator = brightblack
+ dialog = brightwhite
+ button = white
+ elevator = white
+ titlebar = white
+ scrollbar = black
+ borders = black
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = black
+ }
+ }
+ selection =
+ {
+ title = Reverse
+ foreground =
+ {
+ base = white
+ highlight = black
+ selection = black
+ alert = white
+ menubar = white
+ menu = white
+ disabled = black
+ accelerator = black
+ dialog = white
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = black
+ borders = white
+ drivebox = white
+ driveicon = white
+ }
+ background =
+ {
+ base = black
+ highlight = white
+ selection = white
+ alert = black
+ menubar = black
+ menu = black
+ disabled = black
+ accelerator = white
+ dialog = black
+ button = white
+ elevator = black
+ titlebar = white
+ scrollbar = white
+ borders = black
+ drivebox = black
+ driveicon = black
+ }
+ }
+ selection =
+ {
+ title = Hot Pink
+ foreground =
+ {
+ base = black
+ highlight = brightwhite
+ selection = brightwhite
+ alert = brightmagenta
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = magenta
+ dialog = black
+ button = brightwhite
+ elevator = white
+ titlebar = brightwhite
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = brightmagenta
+ selection = magenta
+ alert = brightwhite
+ menubar = brightwhite
+ menu = brightwhite
+ disabled = brightwhite
+ accelerator = brightwhite
+ dialog = brightwhite
+ button = magenta
+ elevator = white
+ titlebar = magenta
+ scrollbar = black
+ borders = black
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = brightred
+ }
+ }
+ selection =
+ {
+ title = Emerald City
+ foreground =
+ {
+ base = black
+ highlight = black
+ selection = brightwhite
+ alert = green
+ menubar = black
+ menu = black
+ disabled = white
+ accelerator = green
+ dialog = black
+ button = brightwhite
+ elevator = white
+ titlebar = brightwhite
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = brightgreen
+ selection = green
+ alert = brightwhite
+ menubar = brightwhite
+ menu = brightwhite
+ disabled = brightwhite
+ accelerator = brightwhite
+ dialog = brightwhite
+ button = green
+ elevator = white
+ titlebar = green
+ scrollbar = black
+ borders = black
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = brightcyan
+ }
+ }
+ selection =
+ {
+ title = Turquoise
+ foreground =
+ {
+ base = black
+ highlight = brightwhite
+ selection = brightwhite
+ alert = brightred
+ menubar = brightwhite
+ menu = black
+ disabled = white
+ accelerator = white
+ dialog = black
+ button = black
+ elevator = white
+ titlebar = black
+ scrollbar = brightwhite
+ borders = black
+ drivebox = black
+ driveicon = black
+ cursor = black
+ }
+ background =
+ {
+ base = brightwhite
+ highlight = brightblue
+ selection = black
+ alert = brightwhite
+ menubar = brightcyan
+ menu = brightcyan
+ disabled = brightcyan
+ accelerator = brightcyan
+ dialog = brightcyan
+ button = brightwhite
+ elevator = white
+ titlebar = white
+ scrollbar = black
+ borders = black
+ drivebox = brightwhite
+ driveicon = brightwhite
+ cursor = cyan
+ }
+ }
+}
+
+associations =
+{
+ association =
+ {
+ program = EDIT
+ extension = TXT
+ }
+ association =
+ {
+ program = QBASIC /run
+ extension = BAS
+ }
+}
--- /dev/null
+[realms]
+ ATHENA.MIT.EDU = {
+ kdc = KERBEROS.MIT.EDU,88
+ kdc = KERBEROS-1.MIT.EDU,88
+ admin_server = KERBEROS.MIT.EDU
+ }
+
+[kdc]
+ port = 88
+
+[libdefaults]
+ ticket_lifetime = 10 hours
--- /dev/null
+error_table prof
+
+error_code PROF_VERSION, "Profile version 0.0"
+
+#
+# generated by prof_tree.c
+#
+error_code PROF_MAGIC_NODE, "Bad magic value in profile_node"
+error_code PROF_NO_SECTION, "Profile section not found"
+error_code PROF_NO_RELATION, "Profile relation not found"
+error_code PROF_ADD_NOT_SECTION,
+ "Attempt to add a relation to node which is not a section"
+error_code PROF_SECTION_WITH_VALUE,
+ "A profile section header has a non-zero value"
+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"
+
+#
+# generated by prof_parse.c
+#
+
+error_code PROF_SECTION_NOTOP, "Profile section header not at top level"
+error_code PROF_SECTION_SYNTAX, "Syntax error in profile section header"
+error_code PROF_RELATION_SYNTAX, "Syntax error in profile relation"
+error_code PROF_EXTRA_CBRACE, "Extra closing brace in profile"
+error_code PROF_MISSING_OBRACE, "Missing open brace in profile"
+
+#
+# generated by prof_init.c
+#
+error_code PROF_MAGIC_PROFILE, "Bad magic value in profile_t"
+error_code PROF_MAGIC_SECTION, "Bad magic value in profile_section_t"
+error_code PROF_TOPSECTION_ITER_NOSUPP,
+ "Iteration through all top level section not supported"
+error_code PROF_INVALID_SECTION, "Invalid profile_section object"
+error_code PROF_END_OF_SECTIONS, "No more sections"
+error_code PROF_BAD_NAMESET, "Bad nameset passed to query routine"
+
+end
--- /dev/null
+/*
+ * prof_file.c ---- routines that manipulate an individual profile file.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "prof_int.h"
+
+errcode_t profile_open_file(filename, ret_prof)
+ const char *filename;
+ prf_file_t *ret_prof;
+{
+ prf_file_t prf;
+ errcode_t retval;
+
+ prf = malloc(sizeof(struct _prf_file_t));
+ if (!prf)
+ return ENOMEM;
+ memset(prf, 0, sizeof(struct _prf_file_t));
+ prf->filename = malloc(strlen(filename)+1);
+ if (!prf->filename) {
+ free(prf);
+ return ENOMEM;
+ }
+ strcpy(prf->filename, filename);
+
+ retval = profile_update_file(prf);
+ if (retval) {
+ profile_close_file(prf);
+ return retval;
+ }
+
+ *ret_prof = prf;
+ return 0;
+}
+
+errcode_t profile_update_file(prf)
+ prf_file_t prf;
+{
+ errcode_t retval;
+ struct stat st;
+ FILE *f;
+
+ if (stat(prf->filename, &st))
+ return errno;
+ if (st.st_mtime == prf->timestamp)
+ return 0;
+ if (prf->root)
+ profile_free_node(prf->root);
+ f = fopen(prf->filename, "r");
+ if (f == NULL)
+ return errno;
+ retval = profile_parse_file(f, &prf->root);
+ fclose(f);
+ if (retval)
+ return retval;
+ prf->timestamp = st.st_mtime;
+ return 0;
+}
+
+errcode_t profile_close_file(prf)
+ prf_file_t prf;
+{
+ if (prf->filename)
+ free(prf->filename);
+ if (prf->root)
+ profile_free_node(prf->root);
+ free(prf);
+
+ return 0;
+}
+
--- /dev/null
+/*
+ * prof_init.c --- routines that manipulate the user-visible profile_t
+ * object.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "prof_int.h"
+
+errcode_t profile_init(filenames, ret_profile)
+ const char **filenames;
+ profile_t *ret_profile;
+{
+ const char **fn;
+ profile_t profile;
+ prf_file_t new_file, last = 0;
+ errcode_t retval;
+
+ profile = malloc(sizeof(struct _profile_t));
+ if (!profile)
+ return ENOMEM;
+ memset(profile, 0, sizeof(struct _profile_t));
+ profile->magic = PROF_MAGIC_PROFILE;
+
+ for (fn = filenames; *fn; fn++) {
+ retval = profile_open_file(*fn, &new_file);
+ if (retval) {
+ profile_release(profile);
+ return retval;
+ }
+ if (last)
+ last->next = new_file;
+ else
+ profile->first_file = new_file;
+ last = new_file;
+ }
+ *ret_profile = profile;
+ return 0;
+}
+
+void profile_release(profile)
+ profile_t profile;
+{
+ prf_file_t p, next;
+
+ for (p = profile->first_file; p; p = next) {
+ next = p->next;
+ profile_close_file(p);
+ }
+ profile->magic = 0;
+ free(profile);
+}
+
+struct string_list {
+ char **list;
+ int num;
+ int max;
+};
+
+static errcode_t init_list(list)
+ struct string_list *list;
+{
+ list->num = 0;
+ list->max = 10;
+ list->list = malloc(list->max * sizeof(char *));
+ if (list->list == 0)
+ return ENOMEM;
+ list->list[0] = 0;
+ return 0;
+}
+
+static void free_list(list)
+ struct string_list *list;
+{
+ char **cp;
+
+ for (cp = list->list; *cp; cp++)
+ free(*cp);
+ free(list->list);
+ list->num = list->max = 0;
+ list->list = 0;
+}
+
+static errcode_t add_to_list(list, str)
+ struct string_list *list;
+ const char *str;
+{
+ char *newstr;
+
+ if (list->num+1 >= list->max) {
+ list->max += 5;
+ list->list = realloc(list->list, list->max * sizeof(char *));
+ if (list->list == 0)
+ return ENOMEM;
+ }
+ newstr = malloc(strlen(str)+1);
+ if (newstr == 0)
+ return ENOMEM;
+ strcpy(newstr, str);
+
+ list->list[list->num++] = newstr;
+ list->list[list->num] = 0;
+ return 0;
+}
+
+/*
+ * XXX this version only works to get values from the first file.
+ * To do more than that means we have to implement some "interesting"
+ * code to do the section searching.
+ */
+errcode_t profile_get_values(profile, names, ret_values)
+ profile_t profile;
+ const char **names;
+ char ***ret_values;
+{
+ prf_file_t file;
+ errcode_t retval;
+ struct profile_node *parent, *section;
+ void *state;
+ char *value;
+ struct string_list values;
+ char **cpp;
+
+ if (names == 0 || names[0] == 0 || names[1] == 0)
+ return PROF_BAD_NAMESET;
+
+ init_list(&values);
+
+ file = profile->first_file;
+ section = file->root;
+
+ for (cpp = names; cpp[1]; cpp++) {
+ state = 0;
+ retval = profile_find_node_subsection(section, *cpp,
+ &state, 0, §ion);
+ if (retval)
+ goto cleanup;
+ }
+
+ state = 0;
+ do {
+ retval = profile_find_node_relation(section, *cpp, &state, 0, &value);
+ if (retval)
+ goto cleanup;
+ add_to_list(&values, value);
+ } while (state);
+
+ *ret_values = values.list;
+ return 0;
+cleanup:
+ free_list(&values);
+ return retval;
+}
+
--- /dev/null
+/*
+ * prof-int.h
+ */
+
+#include <time.h>
+#include "prof_err.h"
+
+#if defined(__STDC__) || defined(_WINDOWS)
+#define PROTOTYPE(x) x
+#else
+#define PROTOTYPE(x) ()
+#endif
+
+typedef long errcode_t;
+
+/*
+ * This is the structure which stores the profile information for a
+ * particular configuration file.
+ */
+struct _prf_file_t {
+ errcode_t magic;
+ char *filename;
+ struct profile_node *root;
+ time_t timestamp;
+ int flags;
+ struct _prf_file_t *next;
+};
+
+typedef struct _prf_file_t *prf_file_t;
+
+/*
+ * This structure defines the high-level, user visible profile_t
+ * object, which is used as a handle by users who need to query some
+ * configuration file(s)
+ */
+struct _profile_t {
+ errcode_t magic;
+ prf_file_t first_file;
+};
+
+typedef struct _profile_t *profile_t;
+
+/*
+ * This structure defines the profile_section_t object, which is
+ * returned to the user when a section is searched.
+ */
+struct _profile_section_t {
+ errcode_t magic;
+ int top_lvl:1, top_lvl_search:1;
+ char *name;
+ void *state;
+ struct profile_node *parent, *sect;
+ profile_t profile;
+ prf_file_t file_ptr;
+};
+
+typedef struct _profile_section_t *profile_section_t;
+
+extern errcode_t profile_get
+ PROTOTYPE((const char *filename, prf_file_t *ret_prof));
+
+extern errcode_t profile_update
+ PROTOTYPE((prf_file_t profile));
+
+extern errcode_t profile_parse_file
+ PROTOTYPE((FILE *f, struct profile_node **root));
+
+/* prof_tree.c */
+
+extern void profile_free_node
+ PROTOTYPE((struct profile_node *relation));
+
+extern errcode_t profile_create_node
+ PROTOTYPE((const char *name, const char *value,
+ struct profile_node **ret_node));
+
+extern errcode_t profile_add_node
+ PROTOTYPE ((struct profile_node *section,
+ const char *name, const char *value,
+ struct profile_node **ret_node));
+
+extern errcode_t profile_find_node_relation
+ PROTOTYPE ((struct profile_node *section,
+ const char *name, void **state,
+ char **ret_name, char **value));
+
+extern errcode_t profile_find_node_subsection
+ PROTOTYPE ((struct profile_node *section,
+ const char *name, void **state,
+ char **ret_name, struct profile_node **subsection));
+
+extern errcode_t profile_get_node_parent
+ PROTOTYPE ((struct profile_node *section,
+ struct profile_node **parent));
+
+extern errcode_t profile_delete_node_relation
+ PROTOTYPE ((struct profile_node *section, const char *name));
+
+/* prof_file.c */
+
+extern errcode_t profile_open_file
+ PROTOTYPE ((const char *filename, prf_file_t *ret_prof));
+
+extern errcode_t profile_update_file
+ PROTOTYPE ((prf_file_t profile));
+
+extern errcode_t profile_close_file
+ PROTOTYPE ((prf_file_t profile));
+
+/* prof_init.c */
+
+errcode_t profile_init
+ PROTOTYPE ((const char **filenames, profile_t *ret_profile));
+
+extern void profile_release
+ PROTOTYPE ((profile_t profile));
+
+
+
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "prof_int.h"
+
+#define SECTION_SEP_CHAR '/'
+
+#define STATE_INIT_COMMENT 1
+#define STATE_STD_LINE 2
+#define STATE_GET_OBRACE 3
+
+struct parse_state {
+ int state;
+ int group_level;
+ struct profile_node *root_section;
+ struct profile_node *current_section;
+};
+
+static char *skip_over_blanks(cp)
+ char *cp;
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+void strip_line(line)
+ char *line;
+{
+ char *p;
+
+ while (1) {
+ p = line + strlen(line) - 1;
+ if ((*p == '\n') || (*p == '\r'))
+ *p = 0;
+ else
+ break;
+ }
+}
+
+static errcode_t parse_init_state(state)
+ struct parse_state *state;
+{
+ state->state = STATE_INIT_COMMENT;
+ state->group_level = 0;
+
+ return profile_create_node("(root)", 0, &state->root_section);
+}
+
+static errcode_t parse_std_line(line, state)
+ char *line;
+ struct parse_state *state;
+{
+ char *cp, ch, *tag, *value;
+ char *p;
+ unsigned int retval;
+ int do_subsection = 0;
+ void *iter = 0;
+
+ if (*line == 0)
+ return 0;
+ if (line[0] == ';')
+ return 0;
+ strip_line(line);
+ cp = skip_over_blanks(line);
+ ch = *cp;
+ if (ch == 0)
+ return 0;
+ if (ch == '[') {
+ if (state->group_level > 0)
+ return PROF_SECTION_NOTOP;
+ cp++;
+ p = strchr(cp, ']');
+ if (p == NULL)
+ return PROF_SECTION_SYNTAX;
+ *p = '\0';
+ retval = profile_find_node_subsection(state->root_section,
+ cp, &iter, 0,
+ &state->current_section);
+ if (retval == PROF_NO_SECTION) {
+ retval = profile_add_node(state->root_section,
+ cp, 0,
+ &state->current_section);
+ if (retval)
+ return retval;
+ } else if (retval)
+ return retval;
+
+ /*
+ * Finish off the rest of the line.
+ */
+ cp = p+1;
+ if (*cp)
+ return PROF_SECTION_SYNTAX;
+ return 0;
+ }
+ if (ch == '}') {
+ if (state->group_level == 0)
+ return PROF_EXTRA_CBRACE;
+ retval = profile_get_node_parent(state->current_section,
+ &state->current_section);
+ if (retval)
+ return retval;
+ state->group_level--;
+ return 0;
+ }
+ /*
+ * Parse the relations
+ */
+ p = strchr(cp, ' ');
+ if (!p)
+ return PROF_RELATION_SYNTAX;
+ *p = '\0';
+ tag = cp;
+ cp = skip_over_blanks(p+1);
+ if (*cp != '=')
+ return PROF_RELATION_SYNTAX;
+ cp = skip_over_blanks(cp+1);
+ value = cp;
+ if (value[0] == 0) {
+ do_subsection++;
+ state->state = STATE_GET_OBRACE;
+ }
+ if (value[0] == '{' && value[1] == 0)
+ do_subsection++;
+ if (do_subsection) {
+ retval = profile_add_node(state->current_section,
+ tag, 0, &state->current_section);
+ if (retval)
+ return retval;
+
+ state->group_level++;
+ return 0;
+ }
+ profile_add_node(state->current_section, tag, value, 0);
+ return 0;
+}
+
+errcode_t parse_line(line, state)
+ char *line;
+ struct parse_state *state;
+{
+ char *cp;
+
+ switch (state->state) {
+ case STATE_INIT_COMMENT:
+ if (line[0] != '[')
+ return 0;
+ state->state = STATE_STD_LINE;
+ case STATE_STD_LINE:
+ return parse_std_line(line, state);
+ case STATE_GET_OBRACE:
+ cp = skip_over_blanks(line);
+ if (*cp != '{')
+ return PROF_MISSING_OBRACE;
+ state->state = STATE_STD_LINE;
+ }
+ return 0;
+}
+
+errcode_t profile_parse_file(f, root)
+ FILE *f;
+ struct profile_node **root;
+{
+ char buf[2048];
+ int retval;
+ struct parse_state state;
+
+ retval = parse_init_state(&state);
+ if (retval)
+ return retval;
+ while (!feof(f)) {
+ if (fgets(buf, sizeof(buf), f) == NULL)
+ break;
+ retval = parse_line(buf, &state);
+ if (retval)
+ return retval;
+ }
+ *root = state.root_section;
+ return 0;
+}
+
+void dump_profile(root, level)
+ struct profile_node *root;
+ int level;
+{
+ int i;
+ struct profile_node *p;
+ void *iter;
+ long retval;
+ char *name, *value;
+
+ iter = 0;
+ do {
+ retval = profile_find_node_relation(root, 0, &iter,
+ &name, &value);
+ if (retval)
+ break;
+ for (i=0; i < level; i++)
+ printf(" ");
+ printf("%s = '%s'\n", name, value);
+ } while (iter != 0);
+
+ iter = 0;
+ do {
+ retval = profile_find_node_subsection(root, 0, &iter,
+ &name, &p);
+ if (retval)
+ break;
+ for (i=0; i < level; i++)
+ printf(" ");
+ printf("[%s]\n", name);
+ dump_profile(p, level+1);
+ } while (iter != 0);
+}
+
--- /dev/null
+/*
+ * prof_section.c --- routines that manipulate the profile_section_t
+ * object
+ *
+ * XXX this file is still under construction.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "prof_int.h"
+
+/*
+ * This routine frees a profile_section
+ */
+void profile_free_section(sect)
+ profile_section_t sect;
+{
+ if (sect->name)
+ free(sect->name);
+ sect->magic = 0;
+ free(sect);
+}
+
+/*
+ * This routine creates a profile_section from its parent. If the
+ * parent is NULL, then a top-level profile section is created.
+ *
+ * Top-level profile sections are different from normal
+ * profile_sections in that top-level sections are agregated across
+ * multiple files, where as subsections are not.
+ */
+errcode_t profile_get_subsection(profile, parent, name, ret_name,
+ ret_section)
+ profile_t profile;
+ profile_section_t parent;
+ const char * name;
+ char ** ret_name;
+ profile_section_t *ret_section;
+{
+ profile_section_t section;
+ prf_file_t file;
+ errcode_t retval;
+
+ section = malloc(sizeof(struct _profile_section_t));
+ if (section == 0)
+ return ENOMEM;
+ memset(section, 0, sizeof(struct _profile_section_t));
+ section->magic = PROF_MAGIC_SECTION;
+ section->name = malloc(strlen(name)+1);
+ if (section->name == 0) {
+ free(section);
+ return ENOMEM;
+ }
+ strcpy(section->name, name);
+ section->file_ptr = file = profile->first_file;
+ section->profile = profile;
+
+ if (parent == 0) {
+ /*
+ * If parent is NULL, then we are creating a
+ * top-level section which hangs off the root.
+ *
+ * We make sure that the section exists in least one
+ * file.
+ */
+ section->top_lvl = 1;
+ if (name == 0)
+ return PROF_TOPSECTION_ITER_NOSUPP;
+ while (file) {
+ retval = profile_find_node_subsection(file->root,
+ name, §ion->state,
+ ret_name, §ion->sect);
+ file = file->next;
+ if (retval == 0)
+ break;
+ if (retval == PROF_NO_SECTION)
+ continue;
+ profile_free_section(section);
+ return retval;
+ }
+ if (section->sect == 0 && file == 0) {
+ profile_free_section(section);
+ return PROF_NO_SECTION;
+ }
+ *ret_section = section;
+ return 0;
+ }
+
+
+ section->top_lvl = 0;
+ if (parent->top_lvl) {
+ section->top_lvl_search = 1;
+
+ } else {
+ section->top_lvl_search = 0;
+ if (parent->sect == 0) {
+ profile_free_section(section);
+ return PROF_INVALID_SECTION;
+ }
+ section->parent = parent->sect;
+ retval = profile_find_node_subsection(parent->sect,
+ name, §ion->state, ret_name, §ion->sect);
+ if (retval) {
+ profile_free_section(section);
+ return retval;
+ }
+ }
+ *ret_section = section;
+ return 0;
+}
+
+errcode_t profile_next_section(section, ret_name)
+ profile_section_t section;
+ char **ret_name;
+{
+ prf_file_t file;
+ errcode_t retval;
+
+ if (section->top_lvl)
+ return PROF_END_OF_SECTIONS;
+ else {
+ if (section->sect == 0)
+ return PROF_INVALID_SECTION;
+ retval = profile_find_node_subsection(section->parent,
+ section->name, §ion->state, ret_name, §ion->sect);
+ if (retval == PROF_NO_SECTION)
+ retval = PROF_END_OF_SECTIONS;
+ return retval;
+ }
+}
+
+errcode_t profile_get_relation(section, name, ret_values)
+ profile_section_t section;
+ const char *name;
+ char ***ret_values;
+{
+ prf_file_t file;
+ char **values;
+ int num_values;
+ int max_values;
+ char *value;
+ errcode_t retval;
+
+
+ max_values = 10;
+ values = malloc(sizeof(char *) * max_values);
+
+ if (section->top_lvl) {
+ for (file = section->profile->first_file; file;
+ file = file->next) {
+ retval = profile_find_node_relation(file->root,
+ section->name, §ion->state, 0, &value);
+ if (retval)
+ continue;
+
+ }
+ } else {
+ if (section->sect == 0)
+ return PROF_INVALID_SECTION;
+ }
+ return 0;
+}
+
+
+
--- /dev/null
+/*
+ * prof_tree.c --- these routines maintain the parse tree of the
+ * config file.
+ *
+ * All of the details of how the tree is stored is abstracted away in
+ * this file; all of the other profile routines build, access, and
+ * modify the tree via the accessor functions found in this file.
+ *
+ * Each node may represent either a relation or a section header.
+ *
+ * A section header must have its value field set to 0, and may a one
+ * or more child nodes, pointed to by first_child.
+ *
+ * A relation has as its value a pointer to allocated memory
+ * containing a string. Its first_child pointer must be null.
+ *
+ */
+
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "prof_int.h"
+
+struct profile_node {
+ errcode_t magic;
+ char *name;
+ char *value;
+ int group_level;
+ struct profile_node *first_child;
+ struct profile_node *parent;
+ struct profile_node *next, *prev;
+};
+
+#define CHECK_MAGIC(node) \
+ if ((node)->magic != PROF_MAGIC_NODE) \
+ return PROF_MAGIC_NODE;
+
+/*
+ * Free a node, and any children
+ */
+void profile_free_node(node)
+ struct profile_node *node;
+{
+ struct profile_node *child;
+
+ if (node->magic != PROF_MAGIC_NODE)
+ return;
+
+ if (node->name)
+ free(node->name);
+ if (node->value)
+ free(node->value);
+ for (child=node->first_child; child; child = child->next)
+ profile_free_node(child);
+ node->magic = 0;
+
+ free(node);
+}
+
+/*
+ * Create a node
+ */
+errcode_t profile_create_node(name, value, ret_node)
+ const char *name, *value;
+ struct profile_node **ret_node;
+{
+ struct profile_node *new;
+
+ new = malloc(sizeof(struct profile_node));
+ if (!new)
+ return ENOMEM;
+ memset(new, 0, sizeof(struct profile_node));
+ new->name = malloc(strlen(name)+1);
+ if (new->name == 0) {
+ profile_free_node(new);
+ return ENOMEM;
+ }
+ strcpy(new->name, name);
+ if (value) {
+ new->value = malloc(strlen(value)+1);
+ if (new->value == 0) {
+ profile_free_node(new);
+ return ENOMEM;
+ }
+ strcpy(new->value, value);
+ }
+ new->magic = PROF_MAGIC_NODE;
+
+ *ret_node = new;
+ return 0;
+}
+
+/*
+ * This function verifies that all of the representation invarients of
+ * the profile are true. If not, we have a programming bug somewhere,
+ * probably in this file.
+ */
+errcode_t profile_verify_node(node)
+ struct profile_node *node;
+{
+ struct profile_node *p, *last;
+
+ CHECK_MAGIC(node);
+
+ if (node->value && node->first_child)
+ return PROF_SECTION_WITH_VALUE;
+
+ last = 0;
+ for (p = node->first_child; p; last = p, p = p->next) {
+ if (p->prev != last)
+ return PROF_BAD_LINK_LIST;
+ if (last && (last->next != p))
+ return PROF_BAD_LINK_LIST;
+ if (node->group_level != p->group_level+1)
+ return PROF_BAD_GROUP_LVL;
+ if (p->parent != node)
+ return PROF_BAD_PARENT_PTR;
+ profile_verify_node(p);
+ }
+ return 0;
+}
+
+/*
+ * Add a node to a particular section
+ */
+errcode_t profile_add_node(section, name, value, ret_node)
+ struct profile_node *section;
+ const char *name, *value;
+ struct profile_node **ret_node;
+{
+ errcode_t retval;
+ struct profile_node *p, *last, *new;
+ int cmp = -1;
+
+ CHECK_MAGIC(section);
+
+ if (section->value)
+ return PROF_ADD_NOT_SECTION;
+
+ for (p=section->first_child, last = 0; p; last = p, p = p->next) {
+ cmp = strcmp(p->name, name);
+ if (cmp >= 0)
+ break;
+ }
+ retval = profile_create_node(name, value, &new);
+ if (retval)
+ return retval;
+ new->group_level = section->group_level+1;
+ new->parent = section;
+ if (cmp == 0) {
+ do {
+ last = p;
+ p = p->next;
+ } while (p && strcmp(p->name, name) == 0);
+ }
+ new->prev = last;
+ if (last)
+ last->next = new;
+ else
+ section->first_child = new;
+ if (p)
+ new->next = p;
+ if (ret_node)
+ *ret_node = new;
+ return 0;
+}
+
+/*
+ * Iterate through the section, returning the relations which match
+ * the given name. If name is NULL, then interate through all the
+ * relations in the section. The first time this routine is called,
+ * the state pointer must be null. When this profile_find_node_relatioon()
+ * returns, if the state pointer is non-NULL, then this routine should
+ * be called again.
+ *
+ * The returned character string in value points to the stored
+ * character string in the parse string. Before this string value is
+ * returned to a calling application (profile_find_node_relatioon is not an
+ * exported interface), it should be strdup()'ed.
+ */
+errcode_t profile_find_node_relation(section, name, state, ret_name, value)
+ struct profile_node *section;
+ const char *name;
+ void **state;
+ char **ret_name, **value;
+{
+ struct profile_node *p;
+
+ CHECK_MAGIC(section);
+ p = *state;
+ if (p) {
+ CHECK_MAGIC(p);
+ } else
+ p = section->first_child;
+
+ while (p) {
+ if (((name == 0) || (strcmp(p->name, name) == 0)) &&
+ p->value) {
+ *value = p->value;
+ if (ret_name)
+ *ret_name = p->name;
+ break;
+ }
+ p = p->next;
+ }
+ if (p == 0) {
+ *state = 0;
+ return PROF_NO_RELATION;
+ }
+ /*
+ * OK, we've found one match; now let's try to find another
+ * one. This way, if we return a non-zero state pointer,
+ * there's guaranteed to be another match that's returned.
+ */
+ p = p->next;
+ while (p) {
+ if (((name == 0) || (strcmp(p->name, name) == 0)) &&
+ p->value)
+ break;
+ p = p->next;
+ }
+ *state = p;
+ return 0;
+}
+
+/*
+ * Iterate through the section, returning the subsections which match
+ * the given name. If name is NULL, then interate through all the
+ * subsections in the section. The first time this routine is called,
+ * the state pointer must be null. When this profile_find_node_subsection()
+ * returns, if the state pointer is non-NULL, then this routine should
+ * be called again.
+ */
+errcode_t profile_find_node_subsection(section, name, state, ret_name,
+ subsection)
+ struct profile_node *section;
+ const char *name;
+ void **state;
+ char **ret_name;
+ struct profile_node **subsection;
+{
+ struct profile_node *p;
+
+ CHECK_MAGIC(section);
+ p = *state;
+ if (p) {
+ CHECK_MAGIC(p);
+ } else
+ p = section->first_child;
+
+ while (p) {
+ if (((name == 0) || (strcmp(p->name, name) == 0)) &&
+ (p->value == 0)) {
+ *subsection = p;
+ if (ret_name)
+ *ret_name = p->name;
+ break;
+ }
+ p = p->next;
+ }
+ if (p == 0) {
+ *state = 0;
+ return PROF_NO_SECTION;
+ }
+ /*
+ * OK, we've found one match; now let's try to find another
+ * one. This way, if we return a non-zero state pointer,
+ * there's guaranteed to be another match that's returned.
+ */
+ p = p->next;
+ while (p) {
+ if (((name == 0) || (strcmp(p->name, name) == 0))
+ && (p->value == 0))
+ break;
+ p = p->next;
+ }
+ *state = p;
+ return 0;
+}
+
+/*
+ * This function deletes a relation from a section. Subsections are
+ * not deleted; if those need to be deleted, they must be done so manually.
+ */
+errcode_t profile_delete_node_relation(section, name)
+ struct profile_node *section;
+ const char *name;
+{
+ struct profile_node *p, *next;
+
+ for (p = section->first_child; p; p = p->next) {
+ if ((strcmp(p->name, name) == 0) && p->value)
+ break;
+ }
+ if (p == 0)
+ return PROF_NO_RELATION;
+ /*
+ * Now we start deleting the relations... if we find a
+ * subsection with the same name, delete it and keep going.
+ */
+ while (p && (strcmp(p->name, name) == 0)) {
+ if (p->value == 0) {
+ p = p->next;
+ continue;
+ }
+ if (p->prev)
+ p->prev->next = p->next;
+ else
+ section->first_child = p->next;
+ next = p->next;
+ if (p->next)
+ p->next->prev = p;
+ profile_free_node(p);
+ p = next;
+ }
+ return 0;
+}
+
+/*
+ * This function returns the parent of a particular node.
+ */
+errcode_t profile_get_node_parent(section, parent)
+ struct profile_node *section, **parent;
+{
+ *parent = section->parent;
+ return 0;
+}
+
+
--- /dev/null
+/*
+ * profile.h
+ */
+
+typedef struct _profile_t *profile_t;
+
+#if defined(__STDC__) || defined(_WINDOWS)
+#define PROTOTYPE(x) x
+#else
+#define PROTOTYPE(x) ()
+#endif
+
+extern long profile_init
+ PROTOTYPE ((const char **filenames, profile_t *ret_profile));
+
+extern void profile_release
+ PROTOTYPE ((profile_t profile));
+
+extern long profile_get_values
+ PROTOTYPE ((profile_t profile, const char **names, char ***ret_values));
--- /dev/null
+this is a comment. Everything up to the first square brace is ignored.
+
+[test section 2]
+ child_section2 = one
+ child_section2 = {
+ child = slick
+ child = harry
+ child = john
+ }
+ child_section2 = foo
+
+[realms]
+ATHENA.MIT.EDU = {
+ server = KERBEROS.MIT.EDU:88
+ server = KERBEROS1.MIT.EDU:750
+ server = KERBEROS2.MIT.EDU:750
+ admin = KERBEROS.MIT.EDU
+ etype = DES-MD5
+}
+
+
+
+[test section 1]
+ foo = "bar"
+
+[test section 2]
+ quux = "bar"
+ frep = bar
+ kappa = alpha
+ beta = epsilon
+
+[test section 1]
+ bar = foo
+ foo = bar2
+ quux = zap
+ foo = bar3
+ child_section = {
+ child = slick
+ child = harry
+ child = john
+ }
+ child_section = foo
+
+
+
+
--- /dev/null
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "profile.h"
+#include "com_err.h"
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct profile_relation *root;
+ unsigned long retval;
+ FILE *f;
+
+ initialize_prof_error_table();
+ if (argc != 2) {
+ fprintf(stderr, "%s: Usage <filename>\n", argv[0]);
+ exit(1);
+ }
+
+ f = fopen(argv[1], "r");
+ if (!f) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ retval = profile_parse_file(f, &root);
+ if (retval) {
+ printf("profile_parse_file error %s\n", error_message(retval));
+ return 0;
+ }
+ fclose(f);
+
+ printf("\n\nDebugging dump.\n");
+ dump_profile(root, 0);
+
+ profile_free_node(root);
+ return 0;
+}
--- /dev/null
+/*
+ * test_profile.c --- testing program for the profile routine
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "profile.h"
+#include "com_err.h"
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ profile_t profile;
+ long retval;
+ const char *filenames[2];
+ char **values, **cpp;
+
+ filenames[0] = argv[1];
+ filenames[1] = 0;
+
+ initialize_prof_error_table();
+
+ retval = profile_init(filenames, &profile);
+ if (retval) {
+ com_err(argv[0], retval, "while initializing profile");
+ exit(1);
+ }
+ retval = profile_get_values(profile, argv+2, &values);
+ if (retval) {
+ com_err(argv[0], retval, "while getting values");
+ exit(1);
+ }
+ for (cpp = values; *cpp; cpp++) {
+ printf("%s\n", *cpp);
+ free(*cpp);
+ }
+ free(values);
+ profile_release(profile);
+ exit(0);
+}
+
+