From 2fa9ba98337b07d6acfac52b5ee5dc8116dda866 Mon Sep 17 00:00:00 2001 From: Theodore Ts'o Date: Fri, 30 Dec 2005 23:57:32 -0500 Subject: [PATCH] Add the profile library code to e2fsck The profile library was originally written by Theodore Ts'o in 1995 for use in the MIT Kerberos v5 library. It has been modified/enhanced/bug-fixed over time by other members of the MIT Kerberos team. This version was originally taken from the Kerberos v5 distribution, version 1.4.2, and radically simplified for use in e2fsprogs. (Support for locking for multi-threaded operations, being able to modify and update the configuration file programmatically, and Mac/Windows portability have been removed. It has been folded into a single C source file to make it easier to fold into an application program.) Signed-off-by: "Theodore Ts'o" --- e2fsck/ChangeLog | 19 + e2fsck/Makefile.in | 87 +- e2fsck/argv_parse.c | 166 ++++ e2fsck/argv_parse.h | 43 + e2fsck/prof_err.et | 66 ++ e2fsck/profile.c | 2226 +++++++++++++++++++++++++++++++++++++++++++++++++++ e2fsck/profile.h | 106 +++ 7 files changed, 2683 insertions(+), 30 deletions(-) create mode 100644 e2fsck/argv_parse.c create mode 100644 e2fsck/argv_parse.h create mode 100644 e2fsck/prof_err.et create mode 100644 e2fsck/profile.c create mode 100644 e2fsck/profile.h diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog index 0fe4b08..3e7d76d 100644 --- a/e2fsck/ChangeLog +++ b/e2fsck/ChangeLog @@ -1,3 +1,22 @@ +2005-12-30 Theodore Ts'o + + * profile.c, profile.h, prof_err.et: Add the profile library code + to e2fsck. The profile library was originally written by + Theodore Ts'o in 1995 for use in the MIT Kerberos v5 + library. It has been modified/enhanced/bug-fixed over + time by other members of the MIT Kerberos team. This + version was originally taken from the Kerberos v5 + distribution, version 1.4.2, and radically simplified for + use in e2fsprogs. (Support for locking for multi-threaded + operations, being able to modify and update the + configuration file programmatically, and Mac/Windows + portability have been removed. It has been folded into a + single C source file to make it easier to fold into an + application program.) + + * argv_parse.c, argv_parse.h: Added auxiliary programs used only + for building the profile test program. + 2005-12-18 Theodore Ts'o * problem.c (PR_0_FUTURE_SB_LAST_MOUNT, diff --git a/e2fsck/Makefile.in b/e2fsck/Makefile.in index 8fbd01b..c173da7 100644 --- a/e2fsck/Makefile.in +++ b/e2fsck/Makefile.in @@ -14,7 +14,7 @@ LDFLAG_STATIC = @LDFLAG_STATIC@ PROGS= e2fsck MANPAGES= e2fsck.8 -XTRA_CFLAGS= -DRESOURCE_TRACK +XTRA_CFLAGS= -DRESOURCE_TRACK -I. LIBS= $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBBLKID) $(LIBUUID) $(LIBINTL) DEPLIBS= $(LIBEXT2FS) $(LIBCOM_ERR) $(LIBBLKID) $(DEPLIBUUID) @@ -29,6 +29,8 @@ PROFILED_LIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \ $(PROFILED_LIBBLKID) $(DEPPROFILED_LIBUUID) +COMPILE_ET=$(top_builddir)/lib/et/compile_et --build-tree + .c.o: @echo " CC $<" @$(CC) -c $(ALL_CFLAGS) $< -o $@ @@ -61,7 +63,7 @@ PROFILED_DEPLIBS= $(PROFILED_LIBEXT2FS) $(PROFILED_LIBCOM_ERR) \ OBJS= dict.o unix.o e2fsck.o super.o pass1.o pass1b.o pass2.o pass3.o pass4.o \ pass5.o journal.o swapfs.o badblocks.o util.o dirinfo.o dx_dirinfo.o \ ehandler.o problem.o message.o recovery.o region.o revoke.o \ - ea_refcount.o rehash.o $(MTRACE_OBJ) + ea_refcount.o rehash.o profile.o prof_err.o $(MTRACE_OBJ) PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \ profiled/super.o profiled/pass1.o profiled/pass1b.o \ @@ -70,7 +72,8 @@ PROFILED_OBJS= profiled/dict.o profiled/unix.o profiled/e2fsck.o \ profiled/dirinfo.o profiled/dx_dirinfo.o profiled/ehandler.o \ profiled/message.o profiled/problem.o profiled/swapfs.o \ profiled/recovery.o profiled/region.o profiled/revoke.o \ - profiled/ea_refcount.o profiled/rehash.o + profiled/ea_refcount.o profiled/rehash.o profiled/profile.o \ + profiled/prof_err.o SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/dict.c \ @@ -96,12 +99,18 @@ SRCS= $(srcdir)/e2fsck.c \ $(srcdir)/ea_refcount.c \ $(srcdir)/rehash.c \ $(srcdir)/region.c \ + $(srcdir)/profile.c \ + prof_err.c \ $(MTRACE_SRC) all:: profiled $(PROGS) e2fsck.static e2fsck.shared $(MANPAGES) @PROFILE_CMT@all:: e2fsck.profiled +prof_err.c prof_err.h: prof_err.et + @echo " COMPILE_ET prof_err.et" + @$(COMPILE_ET) $(srcdir)/prof_err.et + e2fsck: e2fsck.@E2FSCK_TYPE@ @echo " CP $@" @$(CP) e2fsck.@E2FSCK_TYPE@ e2fsck @@ -142,6 +151,12 @@ iscan: iscan.o util.o ehandler.o $(DEPLIBS) @echo " LD $@" @$(LD) $(ALL_LDFLAGS) -o iscan iscan.o util.o ehandler.o $(LIBS) +test_profile: $(srcdir)/profile.c argv_parse.o prof_err.o profile.h \ + $(STATIC_LIBCOM_ERR) + @echo " LD $@" + @$(CC) -o test_profile -DDEBUG_PROGRAM $(srcdir)/profile.c prof_err.o \ + argv_parse.o $(STATIC_LIBCOM_ERR) $(ALL_CFLAGS) + profiled: @PROFILE_CMT@ @echo " MKDIR $@" @PROFILE_CMT@ @mkdir profiled @@ -218,7 +233,7 @@ e2fsck.o: $(srcdir)/e2fsck.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h dict.o: $(srcdir)/dict.c $(srcdir)/dict.h super.o: $(srcdir)/super.c $(top_srcdir)/lib/uuid/uuid.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ @@ -226,147 +241,159 @@ super.o: $(srcdir)/super.c $(top_srcdir)/lib/uuid/uuid.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h pass1.o: $(srcdir)/pass1.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h \ + $(srcdir)/problem.h pass1b.o: $(srcdir)/pass1b.c $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h $(srcdir)/dict.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h $(srcdir)/dict.h pass2.o: $(srcdir)/pass2.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h $(srcdir)/dict.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h $(srcdir)/dict.h pass3.o: $(srcdir)/pass3.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h pass4.o: $(srcdir)/pass4.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h pass5.o: $(srcdir)/pass5.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h journal.o: $(srcdir)/journal.c $(srcdir)/jfs_user.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(top_srcdir)/lib/ext2fs/kernel-jbd.h $(top_srcdir)/lib/ext2fs/jfs_compat.h \ - $(top_srcdir)/lib/ext2fs/kernel-list.h $(srcdir)/problem.h \ - $(top_srcdir)/lib/uuid/uuid.h + $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/ext2fs/kernel-jbd.h \ + $(top_srcdir)/lib/ext2fs/jfs_compat.h $(top_srcdir)/lib/ext2fs/kernel-list.h \ + $(srcdir)/problem.h $(top_srcdir)/lib/uuid/uuid.h recovery.o: $(srcdir)/recovery.c $(srcdir)/jfs_user.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(top_srcdir)/lib/ext2fs/kernel-jbd.h $(top_srcdir)/lib/ext2fs/jfs_compat.h \ - $(top_srcdir)/lib/ext2fs/kernel-list.h + $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/ext2fs/kernel-jbd.h \ + $(top_srcdir)/lib/ext2fs/jfs_compat.h $(top_srcdir)/lib/ext2fs/kernel-list.h revoke.o: $(srcdir)/revoke.c $(srcdir)/jfs_user.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(top_srcdir)/lib/ext2fs/kernel-jbd.h $(top_srcdir)/lib/ext2fs/jfs_compat.h \ - $(top_srcdir)/lib/ext2fs/kernel-list.h + $(srcdir)/profile.h prof_err.h $(top_srcdir)/lib/ext2fs/kernel-jbd.h \ + $(top_srcdir)/lib/ext2fs/jfs_compat.h $(top_srcdir)/lib/ext2fs/kernel-list.h badblocks.o: $(srcdir)/badblocks.c $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h util.o: $(srcdir)/util.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h unix.o: $(srcdir)/unix.c $(top_srcdir)/lib/et/com_err.h $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h $(top_srcdir)/version.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h $(top_srcdir)/version.h dirinfo.o: $(srcdir)/dirinfo.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h dx_dirinfo.o: $(srcdir)/dx_dirinfo.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h ehandler.o: $(srcdir)/ehandler.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h problem.o: $(srcdir)/problem.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h $(srcdir)/problemP.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h $(srcdir)/problemP.h message.o: $(srcdir)/message.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h swapfs.o: $(srcdir)/swapfs.c $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/ext2fs/ext2fs.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h ea_refcount.o: $(srcdir)/ea_refcount.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h rehash.o: $(srcdir)/rehash.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ - $(srcdir)/problem.h + $(srcdir)/profile.h prof_err.h $(srcdir)/problem.h region.o: $(srcdir)/region.c $(srcdir)/e2fsck.h \ $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \ $(top_srcdir)/lib/ext2fs/ext2fs.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \ $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/ext2fs/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(top_srcdir)/lib/ext2fs/bitops.h \ - $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h + $(top_srcdir)/lib/blkid/blkid.h $(top_builddir)/lib/blkid/blkid_types.h \ + $(srcdir)/profile.h prof_err.h +profile.o: $(srcdir)/profile.c $(top_builddir)/lib/ext2fs/prof_err.h \ + $(top_srcdir)/lib/et/com_err.h $(srcdir)/profile.h +prof_err.o: prof_err.c diff --git a/e2fsck/argv_parse.c b/e2fsck/argv_parse.c new file mode 100644 index 0000000..404d960 --- /dev/null +++ b/e2fsck/argv_parse.c @@ -0,0 +1,166 @@ +/* + * argv_parse.c --- utility function for parsing a string into a + * argc, argv array. + * + * This file defines a function argv_parse() which parsing a + * passed-in string, handling double quotes and backslashes, and + * creates an allocated argv vector which can be freed using the + * argv_free() function. + * + * See argv_parse.h for the formal definition of the functions. + * + * Copyright 1999 by Theodore Ts'o. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose with or without fee is hereby granted, provided that + * the above copyright notice and this permission notice appear in all + * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE + * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't + * it sick that the U.S. culture of lawsuit-happy lawyers requires + * this kind of disclaimer?) + * + * Version 1.1, modified 2/27/1999 + */ + +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include "argv_parse.h" + +#define STATE_WHITESPACE 1 +#define STATE_TOKEN 2 +#define STATE_QUOTED 3 + +/* + * Returns 0 on success, -1 on failure. + */ +int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv) +{ + int argc = 0, max_argc = 0; + char **argv, **new_argv, *buf, ch; + char *cp = 0, *outcp = 0; + int state = STATE_WHITESPACE; + + buf = malloc(strlen(in_buf)+1); + if (!buf) + return -1; + + max_argc = 0; argc = 0; argv = 0; + outcp = buf; + for (cp = in_buf; (ch = *cp); cp++) { + if (state == STATE_WHITESPACE) { + if (isspace((int) ch)) + continue; + /* Not whitespace, so start a new token */ + state = STATE_TOKEN; + if (argc >= max_argc) { + max_argc += 3; + new_argv = realloc(argv, + (max_argc+1)*sizeof(char *)); + if (!new_argv) { + if (argv) free(argv); + free(buf); + return -1; + } + argv = new_argv; + } + argv[argc++] = outcp; + } + if (state == STATE_QUOTED) { + if (ch == '"') + state = STATE_TOKEN; + else + *outcp++ = ch; + continue; + } + /* Must be processing characters in a word */ + if (isspace((int) ch)) { + /* + * Terminate the current word and start + * looking for the beginning of the next word. + */ + *outcp++ = 0; + state = STATE_WHITESPACE; + continue; + } + if (ch == '"') { + state = STATE_QUOTED; + continue; + } + if (ch == '\\') { + ch = *++cp; + switch (ch) { + case '\0': + ch = '\\'; cp--; break; + case 'n': + ch = '\n'; break; + case 't': + ch = '\t'; break; + case 'b': + ch = '\b'; break; + } + } + *outcp++ = ch; + } + if (state != STATE_WHITESPACE) + *outcp++ = '\0'; + if (argv == 0) { + argv = malloc(sizeof(char *)); + free(buf); + } + argv[argc] = 0; + if (ret_argc) + *ret_argc = argc; + if (ret_argv) + *ret_argv = argv; + return 0; +} + +void argv_free(char **argv) +{ + if (*argv) + free(*argv); + free(argv); +} + +#ifdef DEBUG +/* + * For debugging + */ + +#include + +int main(int argc, char **argv) +{ + int ac, ret; + char **av, **cpp; + char buf[256]; + + while (!feof(stdin)) { + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + ret = argv_parse(buf, &ac, &av); + if (ret != 0) { + printf("Argv_parse returned %d!\n", ret); + continue; + } + printf("Argv_parse returned %d arguments...\n", ac); + for (cpp = av; *cpp; cpp++) { + if (cpp != av) + printf(", "); + printf("'%s'", *cpp); + } + printf("\n"); + argv_free(av); + } + exit(0); +} +#endif /* DEBUG */ diff --git a/e2fsck/argv_parse.h b/e2fsck/argv_parse.h new file mode 100644 index 0000000..84568e7 --- /dev/null +++ b/e2fsck/argv_parse.h @@ -0,0 +1,43 @@ +/* + * argv_parse.h --- header file for the argv parser. + * + * This file defines the interface for the functions argv_parse() and + * argv_free(). + * + *********************************************************************** + * int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv) + * + * This function takes as its first argument a string which it will + * parse into an argv argument vector, with each white-space separated + * word placed into its own slot in the argv. This function handles + * double quotes and backslashes so that the parsed words can contain + * special characters. The count of the number words found in the + * parsed string, as well as the argument vector, are returned into + * ret_argc and ret_argv, respectively. + *********************************************************************** + * extern void argv_free(char **argv); + * + * This function frees the argument vector created by argv_parse(). + *********************************************************************** + * + * Copyright 1999 by Theodore Ts'o. + * + * Permission to use, copy, modify, and distribute this software for + * any purpose with or without fee is hereby granted, provided that + * the above copyright notice and this permission notice appear in all + * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THEODORE TS'O (THE + * AUTHOR) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. (Isn't + * it sick that the U.S. culture of lawsuit-happy lawyers requires + * this kind of disclaimer?) + * + * Version 1.1, modified 2/27/1999 + */ + +extern int argv_parse(char *in_buf, int *ret_argc, char ***ret_argv); +extern void argv_free(char **argv); diff --git a/e2fsck/prof_err.et b/e2fsck/prof_err.et new file mode 100644 index 0000000..af7801e --- /dev/null +++ b/e2fsck/prof_err.et @@ -0,0 +1,66 @@ +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" +error_code PROF_MAGIC_ITERATOR, "Bad magic value in profile iterator" +error_code PROF_SET_SECTION_VALUE, "Can't set value on section node" +error_code PROF_EINVAL, "Invalid argument passed to profile library" +error_code PROF_READ_ONLY, "Attempt to modify read-only profile" + +# +# 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" +error_code PROF_NO_PROFILE, "No profile file open" + +# +# generated by prof_file.c +# +error_code PROF_MAGIC_FILE, "Bad magic value in profile_file_t" +error_code PROF_FAIL_OPEN, "Couldn't open profile file" + +# +# generated by prof_set.c +# +error_code PROF_EXISTS, "Section already exists" + +# +# generated by prof_get.c +# +error_code PROF_BAD_BOOLEAN, "Invalid boolean value" +error_code PROF_BAD_INTEGER, "Invalid integer value" + +error_code PROF_MAGIC_FILE_DATA, "Bad magic value in profile_file_data_t" + + +end diff --git a/e2fsck/profile.c b/e2fsck/profile.c new file mode 100644 index 0000000..f534886 --- /dev/null +++ b/e2fsck/profile.c @@ -0,0 +1,2226 @@ +/* + * profile.c -- A simple configuration file parsing "library in a file" + * + * The profile library was originally written by Theodore Ts'o in 1995 + * for use in the MIT Kerberos v5 library. It has been + * modified/enhanced/bug-fixed over time by other members of the MIT + * Kerberos team. This version was originally taken from the Kerberos + * v5 distribution, version 1.4.2, and radically simplified for use in + * e2fsprogs. (Support for locking for multi-threaded operations, + * being able to modify and update the configuration file + * programmatically, and Mac/Windows portability have been removed. + * It has been folded into a single C source file to make it easier to + * fold into an application program.) + * + * Copyright (C) 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original MIT software. + * M.I.T. makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#ifdef HAVE_STDLIB_H +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_PWD_H +#include +#endif + +#include "ext2fs/prof_err.h" + +#include "com_err.h" +#include "profile.h" + +#define STAT_ONCE_PER_SECOND + +/* Begin prof_int.h */ + +/* + * prof_int.h + */ + +typedef long prf_magic_t; + +/* + * This is the structure which stores the profile information for a + * particular configuration file. + */ +struct _prf_data_t { + prf_magic_t magic; + struct profile_node *root; +#ifdef STAT_ONCE_PER_SECOND + time_t last_stat; +#endif + time_t timestamp; /* time tree was last updated from file */ + int flags; /* r/w, dirty */ + int upd_serial; /* incremented when data changes */ + char *comment; + + size_t fslen; + + struct _prf_data_t *next; + /* Was: "profile_filespec_t filespec". Now: flexible char + array ... except, we need to work in C89, so an array + length must be specified. */ + const char filespec[sizeof("/etc/krb5.conf")]; +}; + +typedef struct _prf_data_t *prf_data_t; +prf_data_t profile_make_prf_data(const char *); + +struct _prf_file_t { + prf_magic_t magic; + struct _prf_data_t *data; + struct _prf_file_t *next; +}; + +typedef struct _prf_file_t *prf_file_t; + +/* + * The profile flags + */ +#define PROFILE_FILE_RW 0x0001 +#define PROFILE_FILE_DIRTY 0x0002 + +/* + * 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 { + prf_magic_t magic; + prf_file_t first_file; +}; + +/* + * Used by the profile iterator in prof_get.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 + +/* + * Check if a filespec is last in a list (NULL on UNIX, invalid FSSpec on MacOS + */ + +#define PROFILE_LAST_FILESPEC(x) (((x) == NULL) || ((x)[0] == '\0')) + +/* profile_parse.c */ + +static errcode_t profile_parse_file + (FILE *f, struct profile_node **root); + +#ifdef DEBUG_PROGRAM +static errcode_t profile_write_tree_file + (struct profile_node *root, FILE *dstfile); + +static errcode_t profile_write_tree_to_buffer + (struct profile_node *root, char **buf); +#endif + + +/* prof_tree.c */ + +static void profile_free_node + (struct profile_node *relation); + +static errcode_t profile_create_node + (const char *name, const char *value, + struct profile_node **ret_node); + +#ifdef DEBUG_PROGRAM +static errcode_t profile_verify_node + (struct profile_node *node); +#endif + +static errcode_t profile_add_node + (struct profile_node *section, + const char *name, const char *value, + struct profile_node **ret_node); + +static errcode_t profile_make_node_final + (struct profile_node *node); + +static int profile_is_node_final + (struct profile_node *node); + +#ifdef DEBUG_PROGRAM +static const char *profile_get_node_name + (struct profile_node *node); + +static const char *profile_get_node_value + (struct profile_node *node); +#endif + +static errcode_t profile_find_node + (struct profile_node *section, + const char *name, const char *value, + int section_flag, void **state, + struct profile_node **node); + +static errcode_t profile_find_node_relation + (struct profile_node *section, + const char *name, void **state, + char **ret_name, char **value); + +static errcode_t profile_find_node_subsection + (struct profile_node *section, + const char *name, void **state, + char **ret_name, struct profile_node **subsection); + +static errcode_t profile_get_node_parent + (struct profile_node *section, + struct profile_node **parent); + +static errcode_t profile_node_iterator_create + (profile_t profile, const char *const *names, + int flags, void **ret_iter); + +static void profile_node_iterator_free + (void **iter_p); + +static errcode_t profile_node_iterator + (void **iter_p, struct profile_node **ret_node, + char **ret_name, char **ret_value); + +/* prof_file.c */ + +static errcode_t profile_open_file + (const char * file, prf_file_t *ret_prof); + +#define profile_update_file(P) profile_update_file_data((P)->data) +static errcode_t profile_update_file_data + (prf_data_t profile); + +static void profile_free_file + (prf_file_t profile); + +/* prof_init.c -- included from profile.h */ + +/* prof_get.c */ + +static errcode_t profile_get_value + (profile_t profile, const char **names, + const char **ret_value); +/* Others included from profile.h */ + +/* prof_set.c -- included from profile.h */ + +/* End prof_int.h */ + +/* Begin prof_init.c */ +/* + * prof_init.c --- routines that manipulate the user-visible profile_t + * object. + */ + +errcode_t +profile_init(const char **files, profile_t *ret_profile) +{ + const char **fs; + profile_t profile; + prf_file_t new_file, last = 0; + errcode_t retval = 0; + + profile = malloc(sizeof(struct _profile_t)); + if (!profile) + return ENOMEM; + memset(profile, 0, sizeof(struct _profile_t)); + profile->magic = PROF_MAGIC_PROFILE; + + /* if the filenames list is not specified return an empty profile */ + if ( files ) { + for (fs = files; !PROFILE_LAST_FILESPEC(*fs); fs++) { + retval = profile_open_file(*fs, &new_file); + /* if this file is missing, skip to the next */ + if (retval == ENOENT || retval == EACCES) { + continue; + } + if (retval) { + profile_release(profile); + return retval; + } + if (last) + last->next = new_file; + else + profile->first_file = new_file; + last = new_file; + } + /* + * If last is still null after the loop, then all the files were + * missing, so return the appropriate error. + */ + if (!last) { + profile_release(profile); + return ENOENT; + } + } + + *ret_profile = profile; + return 0; +} + +errcode_t +profile_init_path(const char * filepath, + profile_t *ret_profile) +{ + int n_entries, i; + unsigned int ent_len; + const char *s, *t; + char **filenames; + errcode_t retval; + + /* count the distinct filename components */ + for(s = filepath, n_entries = 1; *s; s++) { + if (*s == ':') + n_entries++; + } + + /* the array is NULL terminated */ + filenames = (char **) malloc((n_entries+1) * sizeof(char*)); + if (filenames == 0) + return ENOMEM; + + /* measure, copy, and skip each one */ + for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) { + ent_len = t-s; + filenames[i] = (char*) malloc(ent_len + 1); + if (filenames[i] == 0) { + /* if malloc fails, free the ones that worked */ + while(--i >= 0) free(filenames[i]); + free(filenames); + return ENOMEM; + } + strncpy(filenames[i], s, ent_len); + filenames[i][ent_len] = 0; + if (*t == 0) { + i++; + break; + } + } + /* cap the array */ + filenames[i] = 0; + + retval = profile_init((const char **) filenames, + ret_profile); + + /* count back down and free the entries */ + while(--i >= 0) free(filenames[i]); + free(filenames); + + return retval; +} + +void +profile_release(profile_t profile) +{ + prf_file_t p, next; + + if (!profile || profile->magic != PROF_MAGIC_PROFILE) + return; + + for (p = profile->first_file; p; p = next) { + next = p->next; + profile_free_file(p); + } + profile->magic = 0; + free(profile); +} + + +/* End prof_init.c */ + +/* Begin prof_file.c */ +/* + * prof_file.c ---- routines that manipulate an individual profile file. + */ + +prf_data_t +profile_make_prf_data(const char *filename) +{ + prf_data_t d; + size_t len, flen, slen; + char *fcopy; + + flen = strlen(filename); + slen = offsetof(struct _prf_data_t, filespec); + len = slen + flen + 1; + if (len < sizeof(struct _prf_data_t)) + len = sizeof(struct _prf_data_t); + d = malloc(len); + if (d == NULL) + return NULL; + memset(d, 0, len); + fcopy = (char *) d + slen; + strcpy(fcopy, filename); + d->comment = NULL; + d->magic = PROF_MAGIC_FILE_DATA; + d->root = NULL; + d->next = NULL; + d->fslen = flen; + return d; +} + +errcode_t profile_open_file(const char * filespec, + prf_file_t *ret_prof) +{ + prf_file_t prf; + errcode_t retval; + char *home_env = 0; + unsigned int len; + prf_data_t data; + char *expanded_filename; + + prf = malloc(sizeof(struct _prf_file_t)); + if (!prf) + return ENOMEM; + memset(prf, 0, sizeof(struct _prf_file_t)); + prf->magic = PROF_MAGIC_FILE; + + len = strlen(filespec)+1; + if (filespec[0] == '~' && filespec[1] == '/') { + home_env = getenv("HOME"); +#ifdef HAVE_PWD_H + if (home_env == NULL) { + uid_t uid; + struct passwd *pw, pwx; + char pwbuf[BUFSIZ]; + + uid = getuid(); + if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) + && pw != NULL && pw->pw_dir[0] != 0) + home_env = pw->pw_dir; + } +#endif + if (home_env) + len += strlen(home_env); + } + expanded_filename = malloc(len); + if (expanded_filename == 0) + return errno; + if (home_env) { + strcpy(expanded_filename, home_env); + strcat(expanded_filename, filespec+1); + } else + memcpy(expanded_filename, filespec, len); + + data = profile_make_prf_data(expanded_filename); + if (data == NULL) { + free(prf); + free(expanded_filename); + return ENOMEM; + } + free(expanded_filename); + prf->data = data; + + retval = profile_update_file(prf); + if (retval) { + profile_free_file(prf); + return retval; + } + + *ret_prof = prf; + return 0; +} + +errcode_t profile_update_file_data(prf_data_t data) +{ + errcode_t retval; +#ifdef HAVE_STAT + struct stat st; +#ifdef STAT_ONCE_PER_SECOND + time_t now; +#endif +#endif + FILE *f; + +#ifdef HAVE_STAT +#ifdef STAT_ONCE_PER_SECOND + now = time(0); + if (now == data->last_stat && data->root != NULL) { + return 0; + } +#endif + if (stat(data->filespec, &st)) { + retval = errno; + return retval; + } +#ifdef STAT_ONCE_PER_SECOND + data->last_stat = now; +#endif + if (st.st_mtime == data->timestamp && data->root != NULL) { + return 0; + } + if (data->root) { + profile_free_node(data->root); + data->root = 0; + } + if (data->comment) { + free(data->comment); + data->comment = 0; + } +#else + /* + * If we don't have the stat() call, assume that our in-core + * memory image is correct. That is, we won't reread the + * profile file if it changes. + */ + if (data->root) { + return 0; + } +#endif + errno = 0; + f = fopen(data->filespec, "r"); + if (f == NULL) { + retval = errno; + if (retval == 0) + retval = ENOENT; + return retval; + } + data->upd_serial++; + retval = profile_parse_file(f, &data->root); + fclose(f); + if (retval) { + return retval; + } +#ifdef HAVE_STAT + data->timestamp = st.st_mtime; +#endif + return 0; +} + +void profile_free_file(prf_file_t prf) +{ + prf_data_t data = prf->data; + + if (data->root) + profile_free_node(data->root); + if (data->comment) + free(data->comment); + data->magic = 0; + free(data); + free(prf); +} + +/* End prof_file.c */ + +/* Begin prof_parse.c */ + +#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(char *cp) +{ + while (*cp && isspace((int) (*cp))) + cp++; + return cp; +} + +static void strip_line(char *line) +{ + char *p = line + strlen(line); + while (p > line && (p[-1] == '\n' || p[-1] == '\r')) + *p-- = 0; +} + +static void parse_quoted_string(char *str) +{ + char *to, *from; + + to = from = str; + + for (to = from = str; *from && *from != '"'; to++, from++) { + if (*from == '\\') { + from++; + switch (*from) { + case 'n': + *to = '\n'; + break; + case 't': + *to = '\t'; + break; + case 'b': + *to = '\b'; + break; + default: + *to = *from; + } + continue; + } + *to = *from; + } + *to = '\0'; +} + + +static errcode_t parse_init_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(char *line, struct parse_state *state) +{ + char *cp, ch, *tag, *value; + char *p; + errcode_t retval; + struct profile_node *node; + int do_subsection = 0; + void *iter = 0; + + if (*line == 0) + return 0; + if (line[0] == ';' || 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 == '*') { + profile_make_node_final(state->current_section); + cp++; + } + /* + * A space after ']' should not be fatal + */ + cp = skip_over_blanks(cp); + if (*cp) + return PROF_SECTION_SYNTAX; + return 0; + } + 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) + return retval; + state->group_level--; + return 0; + } + /* + * Parse the relations + */ + tag = cp; + cp = strchr(cp, '='); + if (!cp) + return PROF_RELATION_SYNTAX; + if (cp == tag) + return PROF_RELATION_SYNTAX; + *cp = '\0'; + p = tag; + /* Look for whitespace on left-hand side. */ + while (p < cp && !isspace((int)*p)) + p++; + if (p < cp) { + /* Found some sort of whitespace. */ + *p++ = 0; + /* If we have more non-whitespace, it's an error. */ + while (p < cp) { + if (!isspace((int)*p)) + return PROF_RELATION_SYNTAX; + p++; + } + } + cp = skip_over_blanks(cp+1); + value = cp; + if (value[0] == '"') { + value++; + parse_quoted_string(value); + } else if (value[0] == 0) { + do_subsection++; + state->state = STATE_GET_OBRACE; + } else if (value[0] == '{' && *(skip_over_blanks(value+1)) == 0) + do_subsection++; + else { + cp = value + strlen(value) - 1; + while ((cp > value) && isspace((int) (*cp))) + *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; + } + 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; +} + +static errcode_t parse_line(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(FILE *f, struct profile_node **root) +{ +#define BUF_SIZE 2048 + char *bptr; + errcode_t retval; + struct parse_state state; + + bptr = malloc (BUF_SIZE); + if (!bptr) + return ENOMEM; + + retval = parse_init_state(&state); + if (retval) { + free (bptr); + return retval; + } + while (!feof(f)) { + if (fgets(bptr, BUF_SIZE, f) == NULL) + break; +#ifndef PROFILE_SUPPORTS_FOREIGN_NEWLINES + retval = parse_line(bptr, &state); + if (retval) { + free (bptr); + return retval; + } +#else + { + char *p, *end; + + if (strlen(bptr) >= BUF_SIZE - 1) { + /* The string may have foreign newlines and + gotten chopped off on a non-newline + boundary. Seek backwards to the last known + newline. */ + long offset; + char *c = bptr + strlen (bptr); + for (offset = 0; offset > -BUF_SIZE; offset--) { + if (*c == '\r' || *c == '\n') { + *c = '\0'; + fseek (f, offset, SEEK_CUR); + break; + } + c--; + } + } + + /* First change all newlines to \n */ + for (p = bptr; *p != '\0'; p++) { + if (*p == '\r') + *p = '\n'; + } + /* Then parse all lines */ + p = bptr; + end = bptr + strlen (bptr); + while (p < end) { + char* newline; + char* newp; + + newline = strchr (p, '\n'); + if (newline != NULL) + *newline = '\0'; + + /* parse_line modifies contents of p */ + newp = p + strlen (p) + 1; + retval = parse_line (p, &state); + if (retval) { + free (bptr); + return retval; + } + + p = newp; + } + } +#endif + } + *root = state.root_section; + + free (bptr); + return 0; +} + +/* + * Return TRUE if the string begins or ends with whitespace + */ +static int need_double_quotes(char *str) +{ + if (!str || !*str) + return 0; + if (isspace((int) (*str)) ||isspace((int) (*(str + strlen(str) - 1)))) + return 1; + if (strchr(str, '\n') || strchr(str, '\t') || strchr(str, '\b')) + return 1; + return 0; +} + +/* + * Output a string with double quotes, doing appropriate backquoting + * of characters as necessary. + */ +static void output_quoted_string(char *str, void (*cb)(const char *,void *), + void *data) +{ + char ch; + char buf[2]; + + cb("\"", data); + if (!str) { + cb("\"", data); + return; + } + buf[1] = 0; + while ((ch = *str++)) { + switch (ch) { + case '\\': + cb("\\\\", data); + break; + case '\n': + cb("\\n", data); + break; + case '\t': + cb("\\t", data); + break; + case '\b': + cb("\\b", data); + break; + default: + /* This would be a lot faster if we scanned + forward for the next "interesting" + character. */ + buf[0] = ch; + cb(buf, data); + break; + } + } + cb("\"", data); +} + +#ifndef EOL +#define EOL "\n" +#endif + +/* Errors should be returned, not ignored! */ +static void dump_profile(struct profile_node *root, int level, + void (*cb)(const char *, void *), void *data) +{ + 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++) + cb("\t", data); + if (need_double_quotes(value)) { + cb(name, data); + cb(" = ", data); + output_quoted_string(value, cb, data); + cb(EOL, data); + } else { + cb(name, data); + cb(" = ", data); + cb(value, data); + cb(EOL, data); + } + } while (iter != 0); + + iter = 0; + do { + retval = profile_find_node_subsection(root, 0, &iter, + &name, &p); + if (retval) + break; + if (level == 0) { /* [xxx] */ + cb("[", data); + cb(name, data); + cb("]", data); + cb(profile_is_node_final(p) ? "*" : "", data); + cb(EOL, data); + dump_profile(p, level+1, cb, data); + cb(EOL, data); + } else { /* xxx = { ... } */ + for (i=0; i < level; i++) + cb("\t", data); + cb(name, data); + cb(" = {", data); + cb(EOL, data); + dump_profile(p, level+1, cb, data); + for (i=0; i < level; i++) + cb("\t", data); + cb("}", data); + cb(profile_is_node_final(p) ? "*" : "", data); + cb(EOL, data); + } + } while (iter != 0); +} + +#ifdef DEBUG_PROGRAM +static void dump_profile_to_file_cb(const char *str, void *data) +{ + fputs(str, data); +} + +errcode_t profile_write_tree_file(struct profile_node *root, FILE *dstfile) +{ + dump_profile(root, 0, dump_profile_to_file_cb, dstfile); + return 0; +} +#endif + +struct prof_buf { + char *base; + size_t cur, max; + int err; +}; + +#ifdef DEBUG_PROGRAM +static void add_data_to_buffer(struct prof_buf *b, const void *d, size_t len) +{ + if (b->err) + return; + if (b->max - b->cur < len) { + size_t newsize; + char *newptr; + + newsize = b->max + (b->max >> 1) + len + 1024; + newptr = realloc(b->base, newsize); + if (newptr == NULL) { + b->err = 1; + return; + } + b->base = newptr; + b->max = newsize; + } + memcpy(b->base + b->cur, d, len); + b->cur += len; /* ignore overflow */ +} + +static void dump_profile_to_buffer_cb(const char *str, void *data) +{ + add_data_to_buffer((struct prof_buf *)data, str, strlen(str)); +} + +errcode_t profile_write_tree_to_buffer(struct profile_node *root, + char **buf) +{ + struct prof_buf prof_buf = { 0, 0, 0, 0 }; + + dump_profile(root, 0, dump_profile_to_buffer_cb, &prof_buf); + if (prof_buf.err) { + *buf = NULL; + return ENOMEM; + } + add_data_to_buffer(&prof_buf, "", 1); /* append nul */ + if (prof_buf.max - prof_buf.cur > (prof_buf.max >> 3)) { + char *newptr = realloc(prof_buf.base, prof_buf.cur); + if (newptr) + prof_buf.base = newptr; + } + *buf = prof_buf.base; + return 0; +} +#endif + +/* End prof_parse.c */ + +/* Begin prof_tree.c */ + +/* + * 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. + * + */ + +struct profile_node { + errcode_t magic; + char *name; + char *value; + int group_level; + int final:1; /* Indicate don't search next file */ + int deleted:1; + 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(struct profile_node *node) +{ + struct profile_node *child, *next; + + 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 = next) { + next = child->next; + profile_free_node(child); + } + node->magic = 0; + + free(node); +} + +#ifndef HAVE_STRDUP +#undef strdup +#define strdup MYstrdup +static char *MYstrdup (const char *s) +{ + size_t sz = strlen(s) + 1; + char *p = malloc(sz); + if (p != 0) + memcpy(p, s, sz); + return p; +} +#endif + +/* + * Create a node + */ +errcode_t profile_create_node(const char *name, const char *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 = strdup(name); + if (new->name == 0) { + profile_free_node(new); + return ENOMEM; + } + if (value) { + new->value = strdup(value); + if (new->value == 0) { + profile_free_node(new); + return ENOMEM; + } + } + 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. + */ +#ifdef DEBUG_PROGRAM +errcode_t profile_verify_node(struct profile_node *node) +{ + struct profile_node *p, *last; + errcode_t retval; + + 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+1 != p->group_level) + return PROF_BAD_GROUP_LVL; + if (p->parent != node) + return PROF_BAD_PARENT_PTR; + retval = profile_verify_node(p); + if (retval) + return retval; + } + return 0; +} +#endif + +/* + * Add a node to a particular section + */ +errcode_t profile_add_node(struct profile_node *section, const char *name, + const char *value, struct profile_node **ret_node) +{ + errcode_t retval; + struct profile_node *p, *last, *new; + + CHECK_MAGIC(section); + + if (section->value) + return PROF_ADD_NOT_SECTION; + + /* + * Find the place to insert the new node. We look for the + * place *after* the last match of the node name, since + * order matters. + */ + for (p=section->first_child, last = 0; p; last = p, p = p->next) { + int cmp; + 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->deleted = 0; + new->parent = section; + new->prev = last; + new->next = p; + if (p) + p->prev = new; + if (last) + last->next = new; + else + section->first_child = new; + if (ret_node) + *ret_node = new; + return 0; +} + +/* + * Set the final flag on a particular node. + */ +errcode_t profile_make_node_final(struct profile_node *node) +{ + CHECK_MAGIC(node); + + node->final = 1; + return 0; +} + +/* + * Check the final flag on a node + */ +int profile_is_node_final(struct profile_node *node) +{ + return (node->final != 0); +} + +#ifdef DEBUG_PROGRAM +/* + * Return the name of a node. (Note: this is for internal functions + * only; if the name needs to be returned from an exported function, + * strdup it first!) + */ +const char *profile_get_node_name(struct profile_node *node) +{ + return node->name; +} + +/* + * Return the value of a node. (Note: this is for internal functions + * only; if the name needs to be returned from an exported function, + * strdup it first!) + */ +const char *profile_get_node_value(struct profile_node *node) +{ + return node->value; +} +#endif + +/* + * Iterate through the section, returning the nodes which match + * the given name. If name is NULL, then interate through all the + * nodes in the section. If section_flag is non-zero, only return the + * section which matches the name; don't return relations. If value + * is non-NULL, then only return relations which match the requested + * value. (The value argument is ignored if section_flag is non-zero.) + * + * The first time this routine is called, the state pointer must be + * null. When this profile_find_node_relation() returns, if the state + * pointer is non-NULL, then this routine should be called again. + * (This won't happen if section_flag is non-zero, obviously.) + * + */ +errcode_t profile_find_node(struct profile_node *section, const char *name, + const char *value, int section_flag, void **state, + struct profile_node **node) +{ + struct profile_node *p; + + CHECK_MAGIC(section); + p = *state; + if (p) { + CHECK_MAGIC(p); + } else + p = section->first_child; + + for (; p; p = p->next) { + if (name && (strcmp(p->name, name))) + continue; + if (section_flag) { + if (p->value) + continue; + } else { + if (!p->value) + continue; + if (value && (strcmp(p->value, value))) + continue; + } + if (p->deleted) + continue; + /* A match! */ + if (node) + *node = p; + break; + } + if (p == 0) { + *state = 0; + return section_flag ? PROF_NO_SECTION : 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. + */ + for (p = p->next; p; p = p->next) { + if (name && (strcmp(p->name, name))) + continue; + if (section_flag) { + if (p->value) + continue; + } else { + if (!p->value) + continue; + if (value && (strcmp(p->value, value))) + continue; + } + /* A match! */ + break; + } + *state = p; + 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_relation() + * 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_relation is not an + * exported interface), it should be strdup()'ed. + */ +errcode_t profile_find_node_relation(struct profile_node *section, + const char *name, void **state, + char **ret_name, char **value) +{ + struct profile_node *p; + errcode_t retval; + + retval = profile_find_node(section, name, 0, 0, state, &p); + if (retval) + return retval; + + if (p) { + if (value) + *value = p->value; + if (ret_name) + *ret_name = p->name; + } + 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. + * + * This is (plus accessor functions for the name and value given a + * profile node) makes this function mostly syntactic sugar for + * profile_find_node. + */ +errcode_t profile_find_node_subsection(struct profile_node *section, + const char *name, void **state, + char **ret_name, + struct profile_node **subsection) +{ + struct profile_node *p; + errcode_t retval; + + retval = profile_find_node(section, name, 0, 1, state, &p); + if (retval) + return retval; + + if (p) { + if (subsection) + *subsection = p; + if (ret_name) + *ret_name = p->name; + } + return 0; +} + +/* + * This function returns the parent of a particular node. + */ +errcode_t profile_get_node_parent(struct profile_node *section, + struct profile_node **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 *const *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_t profile, + const char *const *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(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; +} + +/* + * Note: the returned character strings in ret_name and ret_value + * points to the stored character string in the parse string. Before + * this string value is returned to a calling application + * (profile_node_iterator is not an exported interface), it should be + * strdup()'ed. + */ +errcode_t profile_node_iterator(void **iter_p, struct profile_node **ret_node, + char **ret_name, char **ret_value) +{ + struct profile_iterator *iter = *iter_p; + struct profile_node *section, *p; + const char *const *cpp; + errcode_t retval; + int skip_num = 0; + + if (!iter || iter->magic != PROF_MAGIC_ITERATOR) + return PROF_MAGIC_ITERATOR; + if (iter->file && iter->file->magic != PROF_MAGIC_FILE) + return PROF_MAGIC_FILE; + if (iter->file && iter->file->data->magic != PROF_MAGIC_FILE_DATA) + return PROF_MAGIC_FILE_DATA; + /* + * 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->data->upd_serial != iter->file_serial)) { + iter->flags &= ~PROFILE_ITER_FINAL_SEEN; + skip_num = iter->num; + iter->node = 0; + } + if (iter->node && iter->node->magic != PROF_MAGIC_NODE) { + return PROF_MAGIC_NODE; + } +get_new_file: + if (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))) { + if (retval == ENOENT || retval == EACCES) { + /* XXX memory leak? */ + iter->file = iter->file->next; + skip_num = 0; + retval = 0; + goto get_new_file; + } else { + profile_node_iterator_free(iter_p); + return retval; + } + } + iter->file_serial = iter->file->data->upd_serial; + /* + * Find the section to list if we are a LIST_SECTION, + * or find the containing section if not. + */ + section = iter->file->data->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; + goto get_new_file; + } + 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; + } + if (p->deleted) + continue; + break; + } + iter->num++; + if (!p) { + iter->file = iter->file->next; + if (iter->file) { + } + 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; +} + + + +/* End prof_tree.c */ + + +/* Begin prof_get.c */ +/* + * prof_get.c --- routines that expose the public interfaces for + * querying items from the profile. + * + */ + +/* + * These functions --- init_list(), end_list(), and add_to_list() are + * internal functions used to build up a null-terminated char ** list + * of strings to be returned by functions like profile_get_values. + * + * The profile_string_list structure is used for internal booking + * purposes to build up the list, which is returned in *ret_list by + * the end_list() function. + * + * The publicly exported interface for freeing char** list is + * profile_free_list(). + */ + +struct profile_string_list { + char **list; + int num; + int max; +}; + +/* + * Initialize the string list abstraction. + */ +static errcode_t init_list(struct profile_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; +} + +/* + * Free any memory left over in the string abstraction, returning the + * built up list in *ret_list if it is non-null. + */ +static void end_list(struct profile_string_list *list, char ***ret_list) +{ + char **cp; + + if (list == 0) + return; + + if (ret_list) { + *ret_list = list->list; + return; + } else { + for (cp = list->list; *cp; cp++) + free(*cp); + free(list->list); + } + list->num = list->max = 0; + list->list = 0; +} + +/* + * Add a string to the list. + */ +static errcode_t add_to_list(struct profile_string_list *list, const char *str) +{ + char *newstr, **newlist; + int newmax; + + if (list->num+1 >= list->max) { + newmax = list->max + 10; + newlist = realloc(list->list, newmax * sizeof(char *)); + if (newlist == 0) + return ENOMEM; + list->max = newmax; + list->list = newlist; + } + 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; +} + +/* + * Return TRUE if the string is already a member of the list. + */ +static int is_list_member(struct profile_string_list *list, const char *str) +{ + char **cpp; + + if (!list->list) + return 0; + + for (cpp = list->list; *cpp; cpp++) { + if (!strcmp(*cpp, str)) + return 1; + } + return 0; +} + +/* + * This function frees a null-terminated list as returned by + * profile_get_values. + */ +void profile_free_list(char **list) +{ + char **cp; + + if (list == 0) + return; + + for (cp = list; *cp; cp++) + free(*cp); + free(list); +} + +errcode_t +profile_get_values(profile_t profile, const char *const *names, + char ***ret_values) +{ + errcode_t retval; + void *state; + char *value; + struct profile_string_list values; + + if ((retval = profile_node_iterator_create(profile, names, + PROFILE_ITER_RELATIONS_ONLY, + &state))) + return retval; + + if ((retval = init_list(&values))) + return retval; + + do { + if ((retval = profile_node_iterator(&state, 0, 0, &value))) + goto cleanup; + if (value) + add_to_list(&values, value); + } while (state); + + if (values.num == 0) { + retval = PROF_NO_RELATION; + goto cleanup; + } + + end_list(&values, ret_values); + return 0; + +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. + */ +errcode_t profile_get_value(profile_t profile, const char **names, + const char **ret_value) +{ + errcode_t retval; + void *state; + char *value; + + if ((retval = profile_node_iterator_create(profile, names, + PROFILE_ITER_RELATIONS_ONLY, + &state))) + return retval; + + if ((retval = profile_node_iterator(&state, 0, 0, &value))) + goto cleanup; + + if (value) + *ret_value = value; + else + retval = PROF_NO_RELATION; + +cleanup: + profile_node_iterator_free(&state); + return retval; +} + +errcode_t +profile_get_string(profile_t profile, const char *name, const char *subname, + const char *subsubname, const char *def_val, + char **ret_string) +{ + const char *value; + errcode_t retval; + const char *names[4]; + + if (profile) { + names[0] = name; + names[1] = subname; + names[2] = subsubname; + names[3] = 0; + retval = profile_get_value(profile, names, &value); + if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) + value = def_val; + else if (retval) + return retval; + } else + value = def_val; + + if (value) { + *ret_string = malloc(strlen(value)+1); + if (*ret_string == 0) + return ENOMEM; + strcpy(*ret_string, value); + } else + *ret_string = 0; + return 0; +} + +errcode_t +profile_get_integer(profile_t profile, const char *name, const char *subname, + const char *subsubname, int def_val, int *ret_int) +{ + const char *value; + errcode_t retval; + const char *names[4]; + char *end_value; + long ret_long; + + *ret_int = def_val; + if (profile == 0) + return 0; + + names[0] = name; + names[1] = subname; + names[2] = subsubname; + names[3] = 0; + retval = profile_get_value(profile, names, &value); + if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { + *ret_int = def_val; + return 0; + } else if (retval) + return retval; + + if (value[0] == 0) + /* Empty string is no good. */ + return PROF_BAD_INTEGER; + errno = 0; + ret_long = strtol (value, &end_value, 10); + + /* Overflow or underflow. */ + if ((ret_long == LONG_MIN || ret_long == LONG_MAX) && errno != 0) + return PROF_BAD_INTEGER; + /* Value outside "int" range. */ + if ((long) (int) ret_long != ret_long) + return PROF_BAD_INTEGER; + /* Garbage in string. */ + if (end_value != value + strlen (value)) + return PROF_BAD_INTEGER; + + + *ret_int = ret_long; + return 0; +} + +static const char *const conf_yes[] = { + "y", "yes", "true", "t", "1", "on", + 0, +}; + +static const char *const conf_no[] = { + "n", "no", "false", "nil", "0", "off", + 0, +}; + +static errcode_t +profile_parse_boolean(const char *s, int *ret_boolean) +{ + const char *const *p; + + if (ret_boolean == NULL) + return PROF_EINVAL; + + for(p=conf_yes; *p; p++) { + if (!strcasecmp(*p,s)) { + *ret_boolean = 1; + return 0; + } + } + + for(p=conf_no; *p; p++) { + if (!strcasecmp(*p,s)) { + *ret_boolean = 0; + return 0; + } + } + + return PROF_BAD_BOOLEAN; +} + +errcode_t +profile_get_boolean(profile_t profile, const char *name, const char *subname, + const char *subsubname, int def_val, int *ret_boolean) +{ + const char *value; + errcode_t retval; + const char *names[4]; + + if (profile == 0) { + *ret_boolean = def_val; + return 0; + } + + names[0] = name; + names[1] = subname; + names[2] = subsubname; + names[3] = 0; + retval = profile_get_value(profile, names, &value); + if (retval == PROF_NO_SECTION || retval == PROF_NO_RELATION) { + *ret_boolean = def_val; + return 0; + } else if (retval) + return retval; + + return profile_parse_boolean (value, ret_boolean); +} + +/* + * This function will return the list of the names of subections in the + * under the specified section name. + */ +errcode_t +profile_get_subsection_names(profile_t profile, const char **names, + char ***ret_names) +{ + errcode_t retval; + void *state; + char *name; + struct profile_string_list values; + + if ((retval = profile_node_iterator_create(profile, names, + PROFILE_ITER_LIST_SECTION | PROFILE_ITER_SECTIONS_ONLY, + &state))) + return retval; + + 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); + + end_list(&values, ret_names); + return 0; + +cleanup: + end_list(&values, 0); + return retval; +} + +/* + * This function will return the list of the names of relations in the + * under the specified section name. + */ +errcode_t +profile_get_relation_names(profile_t profile, const char **names, + char ***ret_names) +{ + errcode_t retval; + void *state; + char *name; + struct profile_string_list values; + + if ((retval = profile_node_iterator_create(profile, names, + PROFILE_ITER_LIST_SECTION | PROFILE_ITER_RELATIONS_ONLY, + &state))) + return retval; + + if ((retval = init_list(&values))) + return retval; + + do { + if ((retval = profile_node_iterator(&state, 0, &name, 0))) + goto cleanup; + 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; +} + +errcode_t +profile_iterator_create(profile_t profile, const char *const *names, int flags, + void **ret_iter) +{ + return profile_node_iterator_create(profile, names, flags, ret_iter); +} + +void +profile_iterator_free(void **iter_p) +{ + profile_node_iterator_free(iter_p); +} + +errcode_t +profile_iterator(void **iter_p, char **ret_name, char **ret_value) +{ + char *name, *value; + errcode_t retval; + + retval = profile_node_iterator(iter_p, 0, &name, &value); + if (retval) + return retval; + + if (ret_name) { + if (name) { + *ret_name = malloc(strlen(name)+1); + if (!*ret_name) + return ENOMEM; + strcpy(*ret_name, name); + } else + *ret_name = 0; + } + if (ret_value) { + if (value) { + *ret_value = malloc(strlen(value)+1); + if (!*ret_value) { + if (ret_name) { + free(*ret_name); + *ret_name = 0; + } + return ENOMEM; + } + strcpy(*ret_value, value); + } else + *ret_value = 0; + } + return 0; +} + +void +profile_release_string(char *str) +{ + free(str); +} + +/* End prof_get.c */ + +#ifdef DEBUG_PROGRAM + +/* + * test_profile.c --- testing program for the profile routine + */ + +#include "argv_parse.h" + +const char *program_name = "test_profile"; + +#define PRINT_VALUE 1 +#define PRINT_VALUES 2 + +static void do_batchmode(profile) + profile_t profile; +{ + errcode_t retval; + int argc, ret; + char **argv, **values, **cpp; + char buf[256]; + const char **names, *value; + char *cmd; + int print_status; + + while (!feof(stdin)) { + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + printf(">%s", buf); + ret = argv_parse(buf, &argc, &argv); + if (ret != 0) { + printf("Argv_parse returned %d!\n", ret); + continue; + } + cmd = *(argv); + names = (const char **) argv + 1; + print_status = 0; + retval = 0; + if (cmd == 0) { + argv_free(argv); + continue; + } + if (!strcmp(cmd, "query")) { + retval = profile_get_values(profile, names, &values); + print_status = PRINT_VALUES; + } else if (!strcmp(cmd, "query1")) { + retval = profile_get_value(profile, names, &value); + print_status = PRINT_VALUE; + } else if (!strcmp(cmd, "list_sections")) { + retval = profile_get_subsection_names(profile, names, + &values); + print_status = PRINT_VALUES; + } else if (!strcmp(cmd, "list_relations")) { + retval = profile_get_relation_names(profile, names, + &values); + print_status = PRINT_VALUES; + } else if (!strcmp(cmd, "dump")) { + retval = profile_write_tree_file + (profile->first_file->data->root, stdout); +#if 0 + } else if (!strcmp(cmd, "clear")) { + retval = profile_clear_relation(profile, names); + } else if (!strcmp(cmd, "update")) { + retval = profile_update_relation(profile, names+2, + *names, *(names+1)); +#endif + } else if (!strcmp(cmd, "verify")) { + retval = profile_verify_node + (profile->first_file->data->root); +#if 0 + } else if (!strcmp(cmd, "rename_section")) { + retval = profile_rename_section(profile, names+1, + *names); + } else if (!strcmp(cmd, "add")) { + value = *names; + if (strcmp(value, "NULL") == 0) + value = NULL; + retval = profile_add_relation(profile, names+1, + value); + } else if (!strcmp(cmd, "flush")) { + retval = profile_flush(profile); +#endif + } else { + printf("Invalid command.\n"); + } + if (retval) { + com_err(cmd, retval, ""); + print_status = 0; + } + switch (print_status) { + case PRINT_VALUE: + printf("%s\n", value); + break; + case PRINT_VALUES: + for (cpp = values; *cpp; cpp++) + printf("%s\n", *cpp); + profile_free_list(values); + break; + } + printf("\n"); + argv_free(argv); + } + profile_release(profile); + exit(0); + +} + + +int main(argc, argv) + int argc; + char **argv; +{ + profile_t profile; + long retval; + char **values, **cpp; + const char *value; + const char **names; + char *cmd; + int print_value = 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s filename [cmd argset]\n", program_name); + exit(1); + } + + initialize_prof_error_table(); + + retval = profile_init_path(argv[1], &profile); + if (retval) { + com_err(program_name, retval, "while initializing profile"); + exit(1); + } + cmd = *(argv+2); + names = (const char **) argv+3; + if (!cmd || !strcmp(cmd, "batch")) + do_batchmode(profile); + 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")) { + retval = profile_get_relation_names(profile, names, &values); + } else { + fprintf(stderr, "Invalid command.\n"); + exit(1); + } + if (retval) { + com_err(argv[0], retval, "while getting values"); + profile_release(profile); + exit(1); + } + if (print_value) { + printf("%s\n", value); + } else { + for (cpp = values; *cpp; cpp++) + printf("%s\n", *cpp); + profile_free_list(values); + } + profile_release(profile); + + return 0; +} + +#endif diff --git a/e2fsck/profile.h b/e2fsck/profile.h new file mode 100644 index 0000000..8c909a1 --- /dev/null +++ b/e2fsck/profile.h @@ -0,0 +1,106 @@ +/* + * profile.h + * + * Copyright (C) 2005 by Theodore Ts'o. + * + * %Begin-Header% + * This file may be redistributed under the terms of the GNU Public + * License. + * %End-Header% + * + * Copyright (C) 1985-2005 by the Massachusetts Institute of Technology. + * + * All rights reserved. + * + * Export of this software from the United States of America may require + * a specific license from the United States Government. It is the + * responsibility of any person or organization contemplating export to + * obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original MIT software. + * M.I.T. makes no representations about the suitability of this software + * for any purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef _PROFILE_H +#define _PROFILE_H + +typedef struct _profile_t *profile_t; + +/* + * Used by the profile iterator in prof_get.c + */ +#define PROFILE_ITER_LIST_SECTION 0x0001 +#define PROFILE_ITER_SECTIONS_ONLY 0x0002 +#define PROFILE_ITER_RELATIONS_ONLY 0x0004 + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +long profile_init + (const char * *files, profile_t *ret_profile); + +long profile_init_path + (const char * filelist, profile_t *ret_profile); + +void profile_release + (profile_t profile); + +long profile_get_values + (profile_t profile, const char *const *names, char ***ret_values); + +void profile_free_list + (char **list); + +long profile_get_string + (profile_t profile, const char *name, const char *subname, + const char *subsubname, const char *def_val, + char **ret_string); +long profile_get_integer + (profile_t profile, const char *name, const char *subname, + const char *subsubname, int def_val, + int *ret_default); + +long profile_get_boolean + (profile_t profile, const char *name, const char *subname, + const char *subsubname, int def_val, + int *ret_default); + +long profile_get_relation_names + (profile_t profile, const char **names, char ***ret_names); + +long profile_get_subsection_names + (profile_t profile, const char **names, char ***ret_names); + +long profile_iterator_create + (profile_t profile, const char *const *names, + int flags, void **ret_iter); + +void profile_iterator_free + (void **iter_p); + +long profile_iterator + (void **iter_p, char **ret_name, char **ret_value); + +void profile_release_string (char *str); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _KRB5_H */ -- 1.8.3.1