]> err.no Git - pkg-config/commitdiff
import
authorArch Librarian <arch@canonical.com>
Thu, 14 Jul 2005 13:04:01 +0000 (13:04 +0000)
committerArch Librarian <arch@canonical.com>
Thu, 14 Jul 2005 13:04:01 +0000 (13:04 +0000)
Author: hp
Date: 2001-06-05 16:39:09 GMT
import

28 files changed:
.cvsignore [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
acconfig.h [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.in [new file with mode: 0644]
findme.c [new file with mode: 0644]
findme.h [new file with mode: 0644]
glib-1.2.8.tar.gz [new file with mode: 0644]
main.c [new file with mode: 0644]
parse.c [new file with mode: 0644]
parse.h [new file with mode: 0644]
partial-glib.c [new file with mode: 0644]
partial-glib.h [new file with mode: 0644]
pkg-config.1 [new file with mode: 0644]
pkg-config.in [new file with mode: 0644]
pkg.c [new file with mode: 0644]
pkg.h [new file with mode: 0644]
pkg.m4 [new file with mode: 0644]
popt.c [new file with mode: 0644]
popt.h [new file with mode: 0644]
poptconfig.c [new file with mode: 0644]
popthelp.c [new file with mode: 0644]
poptint.h [new file with mode: 0644]
poptparse.c [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..752149f
--- /dev/null
@@ -0,0 +1,9 @@
+aclocal.m4
+config.h.in
+configure
+config.log
+config.cache
+config.status
+Makefile
+Makefile.in
+pkg-config
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..0a79d42
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,8 @@
+Original authors
+----------------
+James Henstridge  <james@daa.com.au>    original pkg-config
+Tim Janik  <timj@gtk.org>               the PKG_CHECK_VERSION macro
+
+Maintainer
+----------
+Martijn van Beers  <martijn@eekeek.org>
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
index 0000000..04804e5
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,310 @@
+2001-05-20  Havoc Pennington  <hp@pobox.com>
+
+       * configure.in: revert package name change, just screwing things up.
+
+2001-05-18  Havoc Pennington  <hp@redhat.com>
+
+       * main.c (main): Change default to print errors on --cflags,
+       --libs, etc., just not on the predicate-style args
+
+2001-05-18  Havoc Pennington  <hp@pobox.com>
+
+       * pkg.m4: always AC_SUBST the cflags/libs
+
+       * pkg-config.1: updates
+
+       * configure.in: call the package 'pkg-config' instead of
+       pkgconfig, for consistency      
+
+       * popt.c: conditionalize on HAVE_SETRESUID, HAVE_SETREUID 
+       maybe this will help with windows, and improves the #ifdef __hpux
+       test in any case.
+
+       * parse.c: use HAVE_FLOCKFILE to try for windows portability
+
+       * configure.in: check for flockfile
+
+2001-05-17  Havoc Pennington  <hp@pobox.com>
+
+       * pkg.m4: change to print errors only if no custom not-found
+       action is specified
+
+       * main.c (main): add PKG_CONFIG_DEBUG_SPEW environment variable
+       (main): implement --print-errors where errors are printed, and
+       otherwise don't print errors related to packages, just usage
+       errors; pointed out by Raja
+       (main): rename pcbuilddir to pc_top_builddir
+
+2001-05-17  Havoc Pennington  <hp@redhat.com>
+
+       Changes to support building against uninstalled packages.
+
+        * ${pcfiledir} variable used to locate builddir by locating
+       the .pc file
+
+       * ${pcbuilddir} variable set by the PKG_CONFIG_BUILD_DIR variable,
+       used for the name of the build directory where the cflags/libs
+       will be used, defaults to '$(top_builddir)'
+
+       * "uninstalled" feature looks for foo-uninstalled.pc before
+       foo.pc, unless PKG_CONFIG_DISABLE_UNINSTALLED is set
+
+       * --uninstalled option used to see if foo-uninstalled.pc is in use
+
+       * --define-variable option added, but turned out to be unused for
+       this
+       
+2001-05-09  Havoc Pennington  <hp@redhat.com>
+
+       * main.c, findme.c, parse.c, pkg.c, poptconfig.c, popthelp.c,
+       poptparse.c: portability fixes from Tomas Ogren
+
+2001-05-09  Havoc Pennington  <hp@redhat.com>
+
+       * Makefile.am (EXTRA_DIST): put the m4 files in the distribution
+
+2001-05-09  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.m4: switch to double quotes for module list, so you can use 
+       a variable there.
+
+2001-05-09  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.c (verify_package): fix error message on missing Name field, 
+       so that it doesn't try to use the name field to report which
+       package was broken
+
+       * parse.c (parse_package_file): change a debug spew to an actual
+       error message
+
+2001-04-13  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.m4: fixed this up
+
+       * main.c (main): remove --check-requires, instead allow version
+       predicates in the module list.
+
+2001-04-12  Havoc Pennington  <hp@pobox.com>
+
+       * main.c (main): Implement --check-requires='gtk+-2.0 = 1.3.4' 
+       option
+
+2001-01-24  Havoc Pennington  <hp@redhat.com>
+
+        Implement --debug spew option.
+       
+       * main.c: add debug_spew function and an option --debug 
+
+2001-01-06  Havoc Pennington  <hp@pobox.com>
+
+       * pkg.c (scan_dir): fail silently if we can't open a directory in
+       the PKG_CONFIG_PATH
+
+2001-01-02  Havoc Pennington  <hp@redhat.com>
+
+       * configure.in: bump version
+
+2001-01-02  Havoc Pennington  <hp@redhat.com>
+
+       * parse.c (parse_package_file): return NULL instead of exiting
+       if we can't open the file.
+
+       * main.c (main): Add options to check the version of pkg-config
+       itself, and to list all known packages
+
+       * parse.c (split_module_list): fix to work properly
+       (parse_module_list): pass variable-substituted string to
+       split_module_list(), silly typo
+
+       * pkg.c (get_package): Add ability to pass a filename instead of a
+       package name, if you want to use a specific pkg-config file (used
+       for configure.in in GTK+ for example, where you can build against
+       an uninstalled copy of GLib).
+
+2000-11-29  Havoc Pennington  <hp@redhat.com>
+
+       * parse.c (parse_module_list): Allow commas before/after the
+       module list, and allow spaces instead of commas to be used as
+       separators. This leniency makes it a lot easier to conditionally
+       build the module list according to configure.in checks.
+
+2000-11-29  Havoc Pennington  <hp@pobox.com>
+
+       * pkg.c (packages_get_other_libs): put a space after the
+       other_libs
+       (packages_get_other_cflags): put a space after the other_cflags
+
+2000-11-27  Havoc Pennington  <hp@redhat.com>
+
+       * main.c (main): don't print space after variable values
+
+       * pkg.c (packages_get_var): don't add space after last variable
+
+2000-11-22  Martijn van Beers  <martijn@earthling.net>
+
+       * main.c: added a --version option for martin
+       * parse.c: added jamesh's patch
+
+       release 0.4.1
+
+2000-10-17  Martijn van Beers  <martijn@earthling.net>
+
+       * configure.in:
+       * Makefile.am: Change to use C version only.
+
+       release 0.4.0
+
+2000-09-15  Havoc Pennington  <hp@redhat.com>
+
+       * configure.in: AM_PROG_LIBTOOL
+
+       * Makefile.am (experimental_pkg_config_LDADD): Link with .la, not
+       .a
+
+2000-08-10  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.c (verify_package): Bugfix from Anders
+
+2000-07-24  Havoc Pennington  <hp@redhat.com>
+
+       * parse.c (get_compat_package): Add support 
+       for imlib-config and orbit-config
+
+2000-07-22  Havoc Pennington  <hp@pobox.com>
+
+       * parse.c (get_compat_package): Make it work with any 
+       gnome-config package name.
+
+2000-07-22  Havoc Pennington  <hp@pobox.com>
+
+       * parse.c, pkg.c, pkg.h: Add Conflicts: keyword, and do 
+       version-checking for Requires: line. Untested.
+
+2000-07-21  Havoc Pennington  <hp@redhat.com>
+
+       * parse.c (get_compat_package): Add some compat stuff (execs
+       gnome-config, gtk-config, etc.). We don't yet support all 
+       the modules we might want to support.
+
+2000-07-20  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.c (get_package): fix error message formatting
+
+2000-07-20  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.c (recursive_fill_list): append rather than prepend the 
+       current libs to the required libs.
+
+       * parse.c (trim_and_sub): Make variables use ${} instead of %{} so
+       we can accept "shell variables" subbed by configure
+
+2000-07-20  Havoc Pennington  <hp@redhat.com>
+
+       * autogen.sh: Run perl on the Makefile.am in the glib tarball to
+       keep it from doing anything in 'make install'
+
+       * pkg.c (scan_dir): Revert to .pc extension
+
+2000-07-20  Havoc Pennington  <hp@redhat.com>
+
+       * glib-1.2.8.tar.gz: Decided it was easier to just stick 
+       in a copy of the tarball instead of hacking up glib;
+       this way we get bugfixes. If distribution size is a problem, 
+       we can hack on it later. Rerun autogen.sh to get the 
+       tarball unpacked and configured.
+       
+       * configure.in: AC_CONFIG_SUBDIRS(glib-1.2.8)
+
+       * main.c: Add version-comparison 
+
+       * Makefile.am: use new glib tarball
+       
+2000-07-18  Havoc Pennington  <hp@redhat.com>
+
+       * pkg.c: When removing -l duplicates, keep the last not the first
+       -l
+
+       * main.c (main): Added --variable and --module-exists options.
+
+       * Wrote an experimental version of pkg-config in C. For 
+       now, glib is required, until I get a cut-and-pasted subset
+       of glib up and running.
+
+       C version is not finished, don't release a tarball yet. ;-)
+       
+       * configure.in, Makefile.am: stuff to build the C version 
+       of pkg-config
+
+2000-07-10  Martijn van Beers  <martijn@earthling.net>
+
+       * pkg-config.in: remove -I/usr/include and -L/usr/lib from
+         the flags we output
+       * pkg.m4: add a PKG_ACLOCALFLAGS macro
+                 add a _DEPENDS output variable
+
+2000-07-01  Martijn van Beers  <martijn@earthling.net>
+
+       * data/gnomeconfig.pce: make output of --modversion be like
+         the output of .pc files
+
+2000-07-01  Martijn van Beers  <martijn@earthling.net>
+
+       * pkg.m4: clean up PKG_CHECK_CFLAG
+   
+2000-06-27  Martijn van Beers  <martijn@earthling.net>
+
+       * data/gnomeconfig.pce: check for existance with --cflags
+                               instead of --libs
+       * pkg.m4: remove stray debug echo command
+                 add PKG_CHECK_CFLAGS macro
+
+2000-06-23  Martijn van Beers  <martijn@earthling.net>
+
+       * data/gnomeconfig.pce,
+       * pkg-config.in:
+         - add checks to see if we're properly installed
+         - bug fixes for sh on Tru64
+
+Wed Jun 21 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * added support for extension modules that will be called
+         if a module doesn't have a .pc file
+       * added a gnomeconfig.pce extension module to allow for
+         old gnome-libs stuff to be used (at the request of hp)
+       * made CFLAGS be like the LIBS_* variables in that you
+         need to do the adding in the .pc file
+
+Thu Jun 15 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * add a --print-pc-dir that prints the default search dir
+       * only use the default search dir when nothing else is
+         specified
+
+Sat Jun 10 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * made the duplication removing code in a function
+       * fix the duplication code so that it checks $* correctly
+         while we have IFS=":$IFS"
+       * splitted up --libs into --libs-only-L, --libs-only-l-self
+         and --libs-only-l-system, as suggested by Tim Janik
+
+Thu Jun  8 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * made it use autoconf/automake
+       * pkg-config: removed in favour of a .in equivalent which generates
+                     pkg-config from configure
+       * pkg-config.in: new file, mostly a copy from pkg-config
+       * pkg-config.in: get the prefix for the default pc_path from configure
+
+Wed Jun  7 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * pkg.m4: new file, contains a macro that checks for packages and
+                 whether they're the right version
+       * pkg-config: added a --modversion flag to get the version of the
+                     module (needs a VERSION var in the .pc files)
+
+Tue Jun  6 2000  Martijn van Beers  <martijn@earthling.net>
+
+       * pkg-config: removed the pc_name_pkg functionality
+       * pkg-config: show help and error out when there are no arguments
+       * pkg-config: get the version from configure
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..68e0118
--- /dev/null
@@ -0,0 +1,30 @@
+SUBDIRS = glib-1.2.8
+
+m4dir = $(datadir)/aclocal
+m4_DATA = pkg.m4
+
+man_MANS = pkg-config.1
+EXTRA_DIST = $(m4_DATA) $(man_MANS)
+
+bin_PROGRAMS = pkg-config
+
+INCLUDES=-DPKGLIBDIR="\"$(pkglibdir)\"" -I./glib-1.2.8
+
+pkg_config_SOURCES= \
+       pkg.h \
+       pkg.c \
+       partial-glib.h \
+       partial-glib.c \
+       parse.h \
+       parse.c \
+       main.c \
+       findme.c \
+       findme.h \
+       popt.c \
+       popt.h \
+       poptconfig.c \
+       popthelp.c \
+       poptint.h \
+       poptparse.c
+
+pkg_config_LDADD=glib-1.2.8/libglib.la
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..4d3cea3
--- /dev/null
+++ b/README
@@ -0,0 +1,17 @@
+pkg-config is a script to make putting together all the build
+flags when compiling/linking a lot easier.
+
+to use it, do something like the following in your configure.in
+
+    PKG_CHECK_MODULES(GNOME, gtk:1.2.8, gnomeui)
+    AC_SUBST(GNOME_CFLAGS)
+    AC_SUBST(GNOME_LIBS)
+
+This puts the neccesary include flags to compile/link something against
+libgnomeui and all its dependencies in $(GNOME_CFLAGS), and the -L/-l flags
+for linking in $(GNOME_LIBS)
+
+The gtk:1.2.8 part is only neccesary if you want to specifically check
+if libgtk is version 1.2.8 or higher. otherwise, the flags for gtk
+will be included automatically, since libgnomeui depends on gtk.
+
diff --git a/acconfig.h b/acconfig.h
new file mode 100644 (file)
index 0000000..46d3386
--- /dev/null
@@ -0,0 +1,2 @@
+#undef SIZEOF_LONG
+#undef SIZEOF_VOID_P
diff --git a/autogen.sh b/autogen.sh
new file mode 100755 (executable)
index 0000000..10ae1d9
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+ORIGDIR=`pwd`
+cd $srcdir
+
+PROJECT=pkg-config
+TEST_TYPE=-f
+FILE=pkg.m4
+
+DIE=0
+
+(autoconf --version) < /dev/null > /dev/null 2>&1 || {
+       echo
+       echo "You must have autoconf installed to compile $PROJECT."
+       echo "Download the appropriate package for your distribution,"
+       echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/"
+       DIE=1
+}
+
+(automake --version) < /dev/null > /dev/null 2>&1 || {
+       echo
+       echo "You must have automake installed to compile $PROJECT."
+       echo "Get ftp://ftp.cygnus.com/pub/home/tromey/automake-1.2d.tar.gz"
+       echo "(or a newer version if it is available)"
+       DIE=1
+}
+
+if test "$DIE" -eq 1; then
+       exit 1
+fi
+
+test $TEST_TYPE $FILE || {
+       echo "You must run this script in the top-level $PROJECT directory"
+       exit 1
+}
+
+gunzip --stdout glib-1.2.8.tar.gz | tar xf - || { 
+    echo "glib tarball not unpacked"
+    exit 1
+}
+
+perl -pi -e "s/lib_LTLIBRARIES/noinst_LTLIBRARIES/g" `find glib-1.2.8 -name Makefile.am`
+perl -pi -e "s/bin_SCRIPTS/noinst_SCRIPTS/g" `find glib-1.2.8 -name Makefile.am`
+perl -pi -e "s/include_HEADERS/noinst_HEADERS/g" `find glib-1.2.8 -name Makefile.am`
+perl -pi -e "s/[a-zA-Z0-9]+_DATA/noinst_DATA/g" `find glib-1.2.8 -name Makefile.am`
+perl -pi -e "s/info_TEXINFOS/noinst_TEXINFOS/g" `find glib-1.2.8 -name Makefile.am`
+perl -pi -e "s/man_MANS/noinst_MANS/g" `find glib-1.2.8 -name Makefile.am`
+
+(cd glib-1.2.8 && automake)
+
+if test -z "$*"; then
+       echo "I am going to run ./configure with no arguments - if you wish "
+        echo "to pass any to it, please specify them on the $0 command line."
+fi
+
+echo aclocal $ACLOCAL_FLAGS
+aclocal $ACLOCAL_FLAGS
+
+# optionally feature autoheader
+(autoheader --version)  < /dev/null > /dev/null 2>&1 && autoheader
+
+automake -a $am_opt
+autoconf
+
+cd $ORIGDIR
+
+$srcdir/configure --enable-maintainer-mode --disable-shared --disable-threads "$@"
+
+echo 
+echo "Now type 'make' to compile $PROJECT."
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..6ba746d
--- /dev/null
@@ -0,0 +1,21 @@
+
+AC_INIT(pkg-config.1)
+
+AC_CONFIG_SUBDIRS(glib-1.2.8)
+
+AM_INIT_AUTOMAKE(pkgconfig, 0.6.0)
+
+AM_MAINTAINER_MODE
+
+AM_CONFIG_HEADER(config.h)
+
+AM_PROG_LIBTOOL
+
+AC_PROG_CC
+
+AC_FUNC_ALLOCA
+
+AC_CHECK_FUNCS(flockfile)
+AC_CHECK_FUNCS(setresuid setreuid,break)
+
+AC_OUTPUT([Makefile])
diff --git a/findme.c b/findme.c
new file mode 100644 (file)
index 0000000..79f2b54
--- /dev/null
+++ b/findme.c
@@ -0,0 +1,64 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#ifdef __NeXT
+/* access macros are not declared in non posix mode in unistd.h -
+ don't try to use posix on NeXTstep 3.3 ! */ 
+#include <libc.h>
+#endif
+
+#include "findme.h"
+
+char * findProgramPath(char * argv0) {
+    char * path = getenv("PATH");
+    char * pathbuf;
+    char * start, * chptr;
+    char * buf;
+
+    /* If there is a / in the argv[0], it has to be an absolute
+       path */
+    if (strchr(argv0, '/'))
+       return strdup(argv0);
+
+    if (!path) return NULL;
+
+    start = pathbuf = alloca(strlen(path) + 1);
+    buf = malloc(strlen(path) + strlen(argv0) + 2);
+    strcpy(pathbuf, path);
+
+    chptr = NULL;
+    do {
+       if ((chptr = strchr(start, ':')))
+           *chptr = '\0';
+       sprintf(buf, "%s/%s", start, argv0);
+
+       if (!access(buf, X_OK))
+           return buf;
+
+       if (chptr) 
+           start = chptr + 1;
+       else
+           start = NULL;
+    } while (start && *start);
+
+    free(buf);
+
+    return NULL;
+}
diff --git a/findme.h b/findme.h
new file mode 100644 (file)
index 0000000..fdd01d5
--- /dev/null
+++ b/findme.h
@@ -0,0 +1,10 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_FINDME
+#define H_FINDME
+
+char * findProgramPath(char * argv0);
+
+#endif
diff --git a/glib-1.2.8.tar.gz b/glib-1.2.8.tar.gz
new file mode 100644 (file)
index 0000000..9f70ce6
Binary files /dev/null and b/glib-1.2.8.tar.gz differ
diff --git a/main.c b/main.c
new file mode 100644 (file)
index 0000000..140e531
--- /dev/null
+++ b/main.c
@@ -0,0 +1,489 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pkg.h"
+#include "parse.h"
+
+#include "popt.h"
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+static int want_debug_spew = 0;
+static int want_verbose_errors = 0;
+
+void
+debug_spew (const char *format, ...)
+{
+  va_list args;
+  gchar *str;
+
+  g_return_if_fail (format != NULL);
+  
+  if (!want_debug_spew)
+    return;
+  
+  va_start (args, format);
+  str = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  fputs (str, stderr);
+  fflush (stdout);
+  
+  g_free (str);
+}
+
+void
+verbose_error (const char *format, ...)
+{
+  va_list args;
+  gchar *str;
+
+  g_return_if_fail (format != NULL);
+  
+  if (!want_verbose_errors)
+    return;
+  
+  va_start (args, format);
+  str = g_strdup_vprintf (format, args);
+  va_end (args);
+
+  fputs (str, stderr);
+  fflush (stdout);
+  
+  g_free (str);
+}
+
+#define DEFINE_VARIABLE 1
+
+static void
+popt_callback (poptContext con, 
+               enum poptCallbackReason reason,
+               const struct poptOption * opt,
+               const char * arg, void * data)
+{
+  if (opt->val == DEFINE_VARIABLE)
+    {
+      char *varname;
+      char *varval;
+      char *tmp;
+
+      tmp = g_strdup (arg);
+
+      varname = tmp;
+      while (*varname && isspace (*varname))
+        ++varname;
+
+      varval = varname;
+      while (*varval && *varval != '=' && *varval != ' ')
+        ++varval;
+
+      while (*varval && (*varval == '=' || *varval == ' '))
+        {
+          *varval = '\0';
+          ++varval;
+        }
+
+      if (*varval == '\0')
+        {
+          fprintf (stderr, "--define-variable argument does not have a value for the variable\n");
+
+          exit (1);
+        }
+
+      define_global_variable (varname, varval);
+
+      g_free (tmp);
+    }
+}
+
+static gboolean
+pkg_uninstalled (Package *pkg)
+{
+  /* See if > 0 pkgs were uninstalled */
+  GSList *tmp;
+
+  if (pkg->uninstalled)
+    return TRUE;
+  
+  tmp = pkg->requires;
+  while (tmp != NULL)
+    {
+      Package *pkg = tmp->data;
+      
+      if (pkg_uninstalled (pkg))
+        return TRUE;
+      
+      tmp = g_slist_next (tmp);
+    }
+
+  return FALSE;
+}
+
+int 
+main (int argc, char **argv)
+{
+  static int want_my_version = 0;
+  static int want_version = 0;
+  static int want_libs = 0;
+  static int want_cflags = 0;
+  static int want_l_libs = 0;
+  static int want_L_libs = 0;
+  static int want_I_cflags = 0;
+  static int want_list = 0;
+  static int result;
+  static int want_uninstalled = 0;
+  static char *variable_name = NULL;
+  static int want_exists = 0;
+  static char *required_atleast_version = NULL;
+  static char *required_exact_version = NULL;
+  static char *required_max_version = NULL;
+  static char *required_pkgconfig_version = NULL;
+  static int want_silence_errors = 0;
+  GString *str;
+  GSList *packages = NULL;
+  char *search_path;
+  char *pcbuilddir;
+  gboolean need_newline;
+  
+  const char *pkgname;
+  Package *pkg;
+
+  poptContext opt_context;
+  
+  struct poptOption options_table[] = {
+    { NULL, 0, POPT_ARG_CALLBACK, popt_callback, 0, NULL, NULL },
+    { "version", 0, POPT_ARG_NONE, &want_my_version, 0,
+      "output version of pkg-config" },
+    { "modversion", 0, POPT_ARG_NONE, &want_version, 0,
+      "output version for package" },
+    { "atleast-pkgconfig-version", 0, POPT_ARG_STRING, &required_pkgconfig_version, 0,
+      "require given version of pkg-config", "VERSION" },
+    { "libs", 0, POPT_ARG_NONE, &want_libs, 0,
+      "output all linker flags" },
+    { "libs-only-l", 0, POPT_ARG_NONE, &want_l_libs, 0,
+      "output -l flags" },
+    { "libs-only-L", 0, POPT_ARG_NONE, &want_L_libs, 0,
+      "output -L flags" },
+    { "cflags", 0, POPT_ARG_NONE, &want_cflags, 0,
+      "output all pre-processor and compiler flags" },
+    { "cflags-only-I", 0, POPT_ARG_NONE, &want_I_cflags, 0,
+      "output -I flags" },
+    { "variable", 0, POPT_ARG_STRING, &variable_name, 0,
+      "get the value of a variable", "VARIABLENAME" },
+    { "define-variable", 0, POPT_ARG_STRING, NULL, DEFINE_VARIABLE,
+      "set the value of a variable", "VARIABLENAME=VARIABLEVALUE" },
+    { "exists", 0, POPT_ARG_NONE, &want_exists, 0,
+      "return 0 if the module(s) exist" },
+    { "uninstalled", 0, POPT_ARG_NONE, &want_uninstalled, 0,
+      "return 0 if the uninstalled version of one or more module(s) or their dependencies will be used" },
+    { "atleast-version", 0, POPT_ARG_STRING, &required_atleast_version, 0,
+      "return 0 if the module is at least version VERSION", "VERSION" },
+    { "exact-version", 0, POPT_ARG_STRING, &required_exact_version, 0,
+      "return 0 if the module is at exactly version VERSION", "VERSION" },
+    { "max-version", 0, POPT_ARG_STRING, &required_max_version, 0,
+      "return 0 if the module is at no newer than version VERSION", "VERSION" },
+    { "list-all", 0, POPT_ARG_NONE, &want_list, 0,
+      "list all known packages" },
+    { "debug", 0, POPT_ARG_NONE, &want_debug_spew, 0,
+      "show verbose debug information" },
+    { "print-errors", 0, POPT_ARG_NONE, &want_verbose_errors, 0,
+      "show verbose information about missing or conflicting packages" },
+    { "silence-errors", 0, POPT_ARG_NONE, &want_silence_errors, 0,
+      "show verbose information about missing or conflicting packages" },
+    POPT_AUTOHELP
+    { NULL, 0, 0, NULL, 0 }
+  };
+
+  /* This is here so that we get debug spew from the start,
+   * during arg parsing
+   */
+  if (getenv ("PKG_CONFIG_DEBUG_SPEW"))
+    {
+      want_debug_spew = TRUE;
+      want_verbose_errors = TRUE;
+      want_silence_errors = FALSE;
+    }
+  
+  search_path = getenv ("PKG_CONFIG_PATH");
+  if (search_path)
+    {
+      char **search_dirs;
+      char **iter;
+
+      search_dirs = g_strsplit (search_path, ":", -1);
+
+      iter = search_dirs;
+      while (*iter)
+        {
+          debug_spew ("Adding directory '%s' from PKG_CONFIG_PATH\n",
+                      *iter);
+          add_search_dir (*iter);
+
+          ++iter;
+        }
+
+      g_strfreev (search_dirs);
+    }
+
+  pcbuilddir = getenv ("PKG_CONFIG_TOP_BUILD_DIR");
+  if (pcbuilddir)
+    {
+      define_global_variable ("pc_top_builddir", pcbuilddir);
+    }
+  else
+    {
+      /* Default appropriate for automake */
+      define_global_variable ("pc_top_builddir", "$(top_builddir)");
+    }
+
+  if (getenv ("PKG_CONFIG_DISABLE_UNINSTALLED"))
+    {
+      debug_spew ("disabling auto-preference for uninstalled packages\n");
+      disable_uninstalled = TRUE;
+    }
+  
+  opt_context = poptGetContext (NULL, argc, argv,
+                                options_table, 0);
+
+  result = poptGetNextOpt (opt_context);
+  if (result != -1)
+    {
+      fprintf(stderr, "%s: %s\n",
+             poptBadOption(opt_context, POPT_BADOPTION_NOALIAS),
+             poptStrerror(result));
+      return 1;
+    }
+
+
+  /* Error printing is determined as follows:
+   *     - for --cflags, --libs, etc. it's on by default
+   *       and --silence-errors can turn it off
+   *     - for --exists, --max-version, etc. and no options
+   *       at all, it's off by default and --print-errors
+   *       will turn it on
+   */
+
+  if (want_my_version ||
+      want_version ||
+      want_libs ||
+      want_cflags ||
+      want_l_libs ||
+      want_L_libs ||
+      want_I_cflags ||
+      want_list)
+    {
+      if (want_silence_errors && getenv ("PKG_CONFIG_DEBUG_SPEW") == NULL)
+        want_verbose_errors = FALSE;
+      else
+        want_verbose_errors = TRUE;
+    }
+  else
+    {
+      /* Leave want_verbose_errors unchanged, reflecting --print-errors */
+    }  
+  
+  if (want_my_version)
+    {
+      printf ("%s\n", VERSION);
+      return 0;
+    }
+
+  if (required_pkgconfig_version)
+    {
+      if (compare_versions (VERSION, required_pkgconfig_version) >= 0)
+        return 0;
+      else
+        return 1;
+    }
+  
+  package_init ();
+
+  if (want_list)
+    {
+      print_package_list ();
+      return 0;
+    }
+  
+  str = g_string_new ("");
+  while (1)
+    {
+      pkgname = poptGetArg (opt_context);
+      if (pkgname == NULL)
+       break;
+
+      g_string_append (str, pkgname);
+      g_string_append (str, " ");
+    }
+
+  g_strstrip (str->str);  
+  
+  {
+    GSList *reqs;
+    GSList *iter;
+    
+    reqs = parse_module_list (NULL, str->str,
+                              "(command line arguments)");      
+    
+    iter = reqs;
+    
+    while (iter != NULL)
+      {
+        Package *req;
+        RequiredVersion *ver = iter->data;
+
+        req = get_package (ver->name);
+
+        if (req == NULL)
+          {
+            verbose_error ("No package '%s' found\n", ver->name);
+            return 1;
+          }
+
+        if (!version_test (ver->comparison, req->version, ver->version))
+          {
+            verbose_error ("Requested '%s %s %s' but version of %s is %s\n",
+                           ver->name,
+                           comparison_to_str (ver->comparison),
+                           ver->version,
+                           req->name,
+                           req->version);
+              
+            return 1;
+          }
+
+        packages = g_slist_prepend (packages, req);
+        
+        iter = g_slist_next (iter);
+      }
+  }
+
+  g_string_free (str, TRUE);
+  
+  packages = g_slist_reverse (packages);
+
+  if (packages == NULL)
+    {      
+      fprintf (stderr, "Must specify package names on the command line\n");
+      
+      exit (1);
+    }
+  
+  if (want_exists)
+    return 0; /* if we got here, all the packages existed. */
+
+  if (want_uninstalled)
+    {
+      /* See if > 0 pkgs (including dependencies recursively) were uninstalled */
+      GSList *tmp;
+      tmp = packages;
+      while (tmp != NULL)
+        {
+          Package *pkg = tmp->data;
+
+          if (pkg_uninstalled (pkg))
+            return 0;
+
+          tmp = g_slist_next (tmp);
+        }
+
+      return 1;
+    }
+  
+  if (want_version)
+    {
+      GSList *tmp;
+      tmp = packages;
+      while (tmp != NULL)
+        {
+          Package *pkg = tmp->data;
+
+          printf ("%s\n", pkg->version);
+
+          tmp = g_slist_next (tmp);
+        }
+    }
+
+  if (required_exact_version)
+    {
+      Package *pkg = packages->data;
+      
+      if (compare_versions (pkg->version, required_exact_version) == 0)
+        return 0;
+      else
+        return 1;
+    }
+  else if (required_atleast_version)
+    {
+      Package *pkg = packages->data;
+      
+      if (compare_versions (pkg->version, required_atleast_version) >= 0)
+        return 0;
+      else
+        return 1;
+    }
+  else if (required_max_version)
+    {
+      Package *pkg = packages->data;
+      
+      if (compare_versions (pkg->version, required_max_version) <= 0)
+        return 0;
+      else
+        return 1;
+    }
+  
+  /* Print all flags; then print a newline at the end. */
+  need_newline = FALSE;
+
+  if (variable_name)
+    {
+      char *str = packages_get_var (packages, variable_name);
+      printf ("%s", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+  
+  if (want_I_cflags)
+    {
+      char *str = packages_get_I_cflags (packages);
+      printf ("%s ", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+  else if (want_cflags)
+    {
+      char *str = packages_get_all_cflags (packages);
+      printf ("%s ", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+  
+  if (want_l_libs)
+    {
+      char *str = packages_get_l_libs (packages);
+      printf ("%s ", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+  else if (want_L_libs)
+    {
+      char *str = packages_get_L_libs (packages);
+      printf ("%s ", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+  else if (want_libs)
+    {
+      char *str = packages_get_all_libs (packages);
+      printf ("%s ", str);
+      g_free (str);
+      need_newline = TRUE;
+    }
+
+  if (need_newline)
+    printf ("\n");
+  
+  return 0;
+}
diff --git a/parse.c b/parse.c
new file mode 100644 (file)
index 0000000..1495932
--- /dev/null
+++ b/parse.c
@@ -0,0 +1,1236 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "parse.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include "popt.h"
+#include <sys/wait.h>
+#include <sys/types.h>
+
+#ifndef HAVE_FLOCKFILE
+#  define flockfile(f) (void)1
+#  define funlockfile(f) (void)1
+#  define getc_unlocked(f) getc(f)
+#endif /* !HAVE_FLOCKFILE */
+
+#ifdef NATIVE_WIN32
+
+#define STRICT
+#include <windows.h>
+
+#endif
+
+/**
+ * Read an entire line from a file into a buffer. Lines may
+ * be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter
+ * is not written into the buffer. Text after a '#' character is treated as
+ * a comment and skipped. '\' can be used to escape a # character.
+ * '\' proceding a line delimiter combines adjacent lines. A '\' proceding
+ * any other character is ignored and written into the output buffer
+ * unmodified.
+ * 
+ * Return value: %FALSE if the stream was already at an EOF character.
+ **/
+static gboolean
+read_one_line (FILE *stream, GString *str)
+{
+  gboolean quoted = FALSE;
+  gboolean comment = FALSE;
+  int n_read = 0;
+  
+  flockfile (stream);
+
+  g_string_truncate (str, 0);
+  
+  while (1)
+    {
+      int c;
+      
+      c = getc_unlocked (stream);
+
+      if (c == EOF)
+       {
+         if (quoted)
+           g_string_append_c (str, '\\');
+         
+         goto done;
+       }
+      else
+       n_read++;
+
+      if (quoted)
+       {
+         quoted = FALSE;
+         
+         switch (c)
+           {
+           case '#':
+             g_string_append_c (str, '#');
+             break;
+           case '\r':
+           case '\n':
+             {
+               int next_c = getc_unlocked (stream);
+
+               if (!(c == EOF ||
+                     (c == '\r' && next_c == '\n') ||
+                     (c == '\n' && next_c == '\r')))
+                 ungetc (next_c, stream);
+               
+               break;
+             }
+           default:
+             g_string_append_c (str, '\\');          
+             g_string_append_c (str, c);
+           }
+       }
+      else
+       {
+         switch (c)
+           {
+           case '#':
+             comment = TRUE;
+             break;
+           case '\\':
+             if (!comment)
+               quoted = TRUE;
+             break;
+           case '\n':
+             {
+               int next_c = getc_unlocked (stream);
+
+               if (!(c == EOF ||
+                     (c == '\r' && next_c == '\n') ||
+                     (c == '\n' && next_c == '\r')))
+                 ungetc (next_c, stream);
+
+               goto done;
+             }
+           default:
+             if (!comment)
+               g_string_append_c (str, c);
+           }
+       }
+    }
+
+ done:
+
+  funlockfile (stream);
+
+  return n_read > 0;
+}
+
+static char *
+trim_string (const char *str)
+{
+  int len;
+
+  g_return_val_if_fail (str != NULL, NULL);
+  
+  while (*str && isspace (*str))
+    str++;
+
+  len = strlen (str);
+  while (len > 0 && isspace (str[len-1]))
+    len--;
+
+  return g_strndup (str, len);
+}
+
+static char *
+trim_and_sub (Package *pkg, const char *str, const char *path)
+{
+  char *trimmed;
+  GString *subst;
+  char *p;
+  
+  trimmed = trim_string (str);
+
+  subst = g_string_new ("");
+
+  p = trimmed;
+  while (*p)
+    {
+      if (p[0] == '$' &&
+          p[1] == '$')
+        {
+          /* escaped % */
+          g_string_append_c (subst, '%');
+          p += 2;
+        }
+      else if (p[0] == '$' &&
+               p[1] == '{')
+        {
+          /* variable */
+          char *var_start;
+          char *varname;
+          char *varval;
+          
+          var_start = &p[2];
+
+          /* Get up to close brace. */
+          while (*p && *p != '}')
+            ++p;
+
+          varname = g_strndup (var_start, p - var_start);
+
+          ++p; /* past brace */
+          
+          varval = package_get_var (pkg, varname);
+          
+          if (varval == NULL)
+            {
+              verbose_error ("Variable '%s' not defined in '%s'\n",
+                             varname, path);
+              
+              exit (1);
+            }
+
+          g_free (varname);
+
+          g_string_append (subst, varval);
+        }
+      else
+        {
+          g_string_append_c (subst, *p);
+
+          ++p;          
+        }
+    }
+
+  g_free (trimmed);
+  p = subst->str;
+  g_string_free (subst, FALSE);
+
+  return p;
+}
+
+static void
+parse_name (Package *pkg, const char *str, const char *path)
+{
+  if (pkg->name)
+    {
+      verbose_error ("Name field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+  
+  pkg->name = trim_and_sub (pkg, str, path);
+}
+
+static void
+parse_version (Package *pkg, const char *str, const char *path)
+{
+  if (pkg->version)
+    {
+      verbose_error ("Version field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+  
+  pkg->version = trim_and_sub (pkg, str, path);
+}
+
+static void
+parse_description (Package *pkg, const char *str, const char *path)
+{
+  if (pkg->description)
+    {
+      verbose_error ("Description field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+  
+  pkg->description = trim_and_sub (pkg, str, path);
+}
+
+
+#define MODULE_SEPARATOR(c) ((c) == ',' || isspace ((c)))
+#define OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=')
+
+/* A module list is a list of modules with optional version specification,
+ * separated by commas and/or spaces. Commas are treated just like whitespace,
+ * in order to allow stuff like: Requires: @FRIBIDI_PC@, glib, gmodule
+ * where @FRIBIDI_PC@ gets substituted to nothing or to 'fribidi'
+ */
+
+typedef enum
+{
+  /* put numbers to help interpret lame debug spew ;-) */
+  OUTSIDE_MODULE = 0,
+  IN_MODULE_NAME = 1,
+  BEFORE_OPERATOR = 2,
+  IN_OPERATOR = 3,
+  AFTER_OPERATOR = 4,
+  IN_MODULE_VERSION = 5  
+} ModuleSplitState;
+
+#define PARSE_SPEW 0
+
+static GSList*
+split_module_list (const char *str, const char *path)
+{
+  GSList *retval = NULL;
+  const char *p;
+  const char *start;
+  ModuleSplitState state = OUTSIDE_MODULE;
+  ModuleSplitState last_state = OUTSIDE_MODULE;
+
+  /*   fprintf (stderr, "Parsing: '%s'\n", str); */
+  
+  start = str;
+  p = str;
+
+  while (*p)
+    {
+#if PARSE_SPEW
+      fprintf (stderr, "p: %c state: %d last_state: %d\n", *p, state, last_state);
+#endif
+      
+      switch (state)
+        {
+        case OUTSIDE_MODULE:
+          if (!MODULE_SEPARATOR (*p))
+            state = IN_MODULE_NAME;          
+          break;
+
+        case IN_MODULE_NAME:
+          if (isspace (*p))
+            {
+              /* Need to look ahead to determine next state */
+              const char *s = p;
+              while (*s && isspace (*s))
+                ++s;
+
+              if (*s == '\0')
+                state = OUTSIDE_MODULE;
+              else if (MODULE_SEPARATOR (*s))
+                state = OUTSIDE_MODULE;
+              else if (OPERATOR_CHAR (*s))
+                state = BEFORE_OPERATOR;
+              else
+                state = OUTSIDE_MODULE;
+            }
+          else if (MODULE_SEPARATOR (*p))
+            state = OUTSIDE_MODULE; /* comma precludes any operators */
+          break;
+
+        case BEFORE_OPERATOR:
+          /* We know an operator is coming up here due to lookahead from
+           * IN_MODULE_NAME
+           */
+          if (isspace (*p))
+            ; /* no change */
+          else if (OPERATOR_CHAR (*p))
+            state = IN_OPERATOR;
+          else
+            g_assert_not_reached ();
+          break;
+
+        case IN_OPERATOR:
+          if (!OPERATOR_CHAR (*p))
+            state = AFTER_OPERATOR;
+          break;
+
+        case AFTER_OPERATOR:
+          if (!isspace (*p))
+            state = IN_MODULE_VERSION;
+          break;
+
+        case IN_MODULE_VERSION:
+          if (MODULE_SEPARATOR (*p))
+            state = OUTSIDE_MODULE;
+          break;
+          
+        default:
+          g_assert_not_reached ();
+        }
+
+      if (state == OUTSIDE_MODULE &&
+          last_state != OUTSIDE_MODULE)
+        {
+          /* We left a module */
+          char *module = g_strndup (start, p - start);
+          retval = g_slist_prepend (retval, module);
+
+#if PARSE_SPEW
+          fprintf (stderr, "found module: '%s'\n", module);
+#endif
+          
+          /* reset start */
+          start = p;
+        }
+      
+      last_state = state;
+      ++p;
+    }
+
+  if (p != start)
+    {
+      /* get the last module */
+      char *module = g_strndup (start, p - start);
+      retval = g_slist_prepend (retval, module);
+
+#if PARSE_SPEW
+      fprintf (stderr, "found module: '%s'\n", module);
+#endif
+      
+    }
+  
+  retval = g_slist_reverse (retval);
+
+  return retval;
+}
+
+GSList*
+parse_module_list (Package *pkg, const char *str, const char *path)
+{
+  GSList *split;
+  GSList *iter;
+  GSList *retval = NULL;
+
+  split = split_module_list (str, path);
+  
+  iter = split;
+  while (iter != NULL)
+    {
+      RequiredVersion *ver;
+      char *p;
+      char *start;
+      
+      p = iter->data;
+
+      ver = g_new0 (RequiredVersion, 1);
+      ver->comparison = ALWAYS_MATCH;
+      ver->owner = pkg;
+      retval = g_slist_prepend (retval, ver);
+      
+      while (*p && MODULE_SEPARATOR (*p))
+        ++p;
+      
+      start = p;
+
+      while (*p && !isspace (*p))
+        ++p;
+
+      while (*p && MODULE_SEPARATOR (*p))
+        {
+          *p = '\0';
+          ++p;
+        }
+
+      if (*start == '\0')
+        {
+          verbose_error ("Empty package name in Requires or Conflicts in file '%s'\n", path);
+          
+          exit (1);
+        }
+      
+      ver->name = g_strdup (start);
+
+      start = p;
+
+      while (*p && !isspace (*p))
+        ++p;
+
+      while (*p && isspace (*p))
+        {
+          *p = '\0';
+          ++p;
+        }
+      
+      if (*start != '\0')
+        {
+          if (strcmp (start, "=") == 0)
+            ver->comparison = EQUAL;
+          else if (strcmp (start, ">=") == 0)
+            ver->comparison = GREATER_THAN_EQUAL;
+          else if (strcmp (start, "<=") == 0)
+            ver->comparison = LESS_THAN_EQUAL;
+          else if (strcmp (start, ">") == 0)
+            ver->comparison = GREATER_THAN;
+          else if (strcmp (start, "<") == 0)
+            ver->comparison = LESS_THAN;
+          else if (strcmp (start, "!=") == 0)
+            ver->comparison = NOT_EQUAL;
+          else
+            {
+              verbose_error ("Unknown version comparison operator '%s' after package name '%s' in file '%s'\n", start, ver->name, path);
+              
+              exit (1);
+            }
+        }
+
+      start = p;
+      
+      while (*p && !MODULE_SEPARATOR (*p))
+        ++p;
+
+      while (*p && MODULE_SEPARATOR (*p))
+        {
+          *p = '\0';
+          ++p;
+        }
+      
+      if (ver->comparison != ALWAYS_MATCH && *start == '\0')
+        {
+          verbose_error ("Comparison operator but no version after package name '%s' in file '%s'\n", ver->name, path);
+          
+          exit (1);
+        }
+
+      if (*start != '\0')
+        {
+          ver->version = g_strdup (start);
+        }
+
+      g_assert (ver->name);
+      
+      iter = g_slist_next (iter);
+    }
+
+  g_slist_foreach (split, (GFunc) g_free, NULL);
+  g_slist_free (split);
+
+  retval = g_slist_reverse (retval);
+
+  return retval;
+}
+
+static void
+parse_requires (Package *pkg, const char *str, const char *path)
+{
+  GSList *parsed;
+  GSList *iter;
+  char *trimmed;
+  
+  if (pkg->requires)
+    {
+      verbose_error ("Requires field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+
+  trimmed = trim_and_sub (pkg, str, path);
+  parsed = parse_module_list (pkg, trimmed, path);
+  g_free (trimmed);
+  
+  iter = parsed;
+  while (iter != NULL)
+    {
+      Package *req;
+      RequiredVersion *ver = iter->data;
+      
+      req = get_package (ver->name);
+
+      if (req == NULL)
+        {
+          verbose_error ("Package '%s', required by '%s', not found\n",
+                         ver->name, pkg->name ? pkg->name : path);
+          
+          exit (1);
+        }
+
+      if (pkg->required_versions == NULL)
+        pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal);
+      
+      g_hash_table_insert (pkg->required_versions, ver->name, ver);
+      
+      pkg->requires = g_slist_prepend (pkg->requires, req);
+
+      iter = g_slist_next (iter);
+    }
+
+  g_slist_free (parsed);
+  
+  pkg->requires = g_slist_reverse (pkg->requires);
+}
+
+static void
+parse_conflicts (Package *pkg, const char *str, const char *path)
+{
+  GSList *parsed;
+  GSList *iter;
+  char *trimmed;
+  
+  if (pkg->conflicts)
+    {
+      verbose_error ("Conflicts field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+
+  trimmed = trim_and_sub (pkg, str, path);
+  pkg->conflicts = parse_module_list (pkg, trimmed, path);
+  g_free (trimmed);
+}
+
+static void
+parse_libs (Package *pkg, const char *str, const char *path)
+{
+  /* Strip out -l and -L flags, put them in a separate list. */
+  
+  char *trimmed;
+  GString *other;
+  char **argv = NULL;
+  int argc;
+  int result;
+  int i;
+  
+  if (pkg->l_libs || pkg->L_libs || pkg->other_libs)
+    {
+      verbose_error ("Libs field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+  
+  trimmed = trim_and_sub (pkg, str, path);
+
+  result = poptParseArgvString (trimmed, &argc, &argv);
+
+  if (result < 0)
+    {
+      verbose_error ("Couldn't parse Libs field into an argument vector: %s\n",
+               poptStrerror (result));
+
+      exit (1);
+    }
+  
+  other = g_string_new ("");
+
+  i = 0;
+  while (i < argc)
+    {
+      char *arg = trim_string (argv[i]);
+      char *p;
+      char *start;
+
+      start = arg;
+      p = start;      
+
+      if (p[0] == '-' &&
+          p[1] == 'l')
+        {
+          char *libname;          
+              
+          p += 2;
+          while (*p && isspace (*p))
+            ++p;
+              
+          start = p;
+          while (*p && !isspace (*p))
+            ++p;
+
+          libname = g_strndup (start, p - start);
+          
+          pkg->l_libs = g_slist_prepend (pkg->l_libs,
+                                         g_strconcat ("-l", libname, NULL));
+
+          g_free (libname);
+        }
+      else if (p[0] == '-' &&
+               p[1] == 'L')
+        {
+          char *libname;          
+          
+          p += 2;
+          while (*p && isspace (*p))
+            ++p;
+              
+          start = p;
+          while (*p && !isspace (*p))
+            ++p;
+
+          libname = g_strndup (start, p - start);
+          
+          pkg->L_libs = g_slist_prepend (pkg->L_libs,
+                                         g_strconcat ("-L", libname, NULL));
+
+          g_free (libname);
+        }
+      else
+        {
+          g_string_append_c (other, ' ');
+          g_string_append (other, arg);
+          g_string_append_c (other, ' ');
+        }
+
+      g_free (arg);
+      
+      ++i;
+    }
+
+  g_free (argv);
+  g_free (trimmed);
+
+  pkg->other_libs = other->str;
+  
+  g_string_free (other, FALSE);
+
+  pkg->l_libs = g_slist_reverse (pkg->l_libs);
+  pkg->L_libs = g_slist_reverse (pkg->L_libs);
+}
+     
+static void
+parse_cflags (Package *pkg, const char *str, const char *path)
+{
+  /* Strip out -I flags, put them in a separate list. */
+  
+  char *trimmed;
+  GString *other;
+  char **argv = NULL;
+  int argc;
+  int result;
+  int i;
+  
+  if (pkg->I_cflags || pkg->other_cflags)
+    {
+      verbose_error ("Cflags field occurs twice in '%s'\n", path);
+
+      exit (1);
+    }
+  
+  trimmed = trim_and_sub (pkg, str, path);
+
+  result = poptParseArgvString (trimmed, &argc, &argv);
+
+  if (result < 0)
+    {
+      verbose_error ("Couldn't parse Cflags field into an argument vector: %s\n",
+                     poptStrerror (result));
+
+      exit (1);
+    }
+  
+  other = g_string_new ("");
+
+  i = 0;
+  while (i < argc)
+    {
+      char *arg = trim_string (argv[i]);
+      char *p;
+      char *start;
+
+      start = arg;
+      p = start;      
+
+      if (p[0] == '-' &&
+          p[1] == 'I')
+        {
+          char *libname;          
+              
+          p += 2;
+          while (*p && isspace (*p))
+            ++p;
+              
+          start = p;
+          while (*p && !isspace (*p))
+            ++p;
+
+          libname = g_strndup (start, p - start);
+          
+          pkg->I_cflags = g_slist_prepend (pkg->I_cflags,
+                                           g_strconcat ("-I", libname, NULL));
+
+          g_free (libname);
+        }
+      else
+        {
+          g_string_append (other, arg);
+        }
+
+      g_free (arg);
+      
+      ++i;
+    }
+
+  g_free (argv);
+  g_free (trimmed);
+
+  pkg->other_cflags = other->str;
+  
+  g_string_free (other, FALSE);
+
+  pkg->I_cflags = g_slist_reverse (pkg->I_cflags);
+}
+     
+static void
+parse_line (Package *pkg, const char *untrimmed, const char *path)
+{
+  char *str;
+  char *p;
+  char *tag;
+
+  debug_spew ("  line>%s\n", untrimmed);
+  
+  str = trim_string (untrimmed);
+  
+  if (*str == '\0')
+    return; /* empty line */
+  
+  p = str;
+
+  /* Get first word */
+  while ((*p >= 'A' && *p <= 'Z') ||
+        (*p >= 'a' && *p <= 'z') ||
+        (*p >= '0' && *p <= '9') ||
+        *p == '_')
+    p++;
+
+  tag = g_strndup (str, p - str);
+  
+  while (*p && isspace (*p))
+    ++p;
+
+  if (*p == ':')
+    {
+      /* keyword */
+      ++p;
+      while (*p && isspace (*p))
+        ++p;
+
+      if (strcmp (tag, "Name") == 0)
+        parse_name (pkg, p, path);
+      else if (strcmp (tag, "Description") == 0)
+        parse_description (pkg, p, path);
+      else if (strcmp (tag, "Version") == 0)
+        parse_version (pkg, p, path);
+      else if (strcmp (tag, "Requires") == 0)
+        parse_requires (pkg, p, path);
+      else if (strcmp (tag, "Libs") == 0)
+        parse_libs (pkg, p, path);
+      else if (strcmp (tag, "Cflags") == 0)
+        parse_cflags (pkg, p, path);
+      else if (strcmp (tag, "Conflicts") == 0)
+        parse_conflicts (pkg, p, path);
+      else
+        {
+          verbose_error ("Unknown keyword '%s' in '%s'\n",
+                         tag, path);
+
+          exit (1);
+        }
+    }
+  else if (*p == '=')
+    {
+      /* variable */
+      char *varname;
+      char *varval;
+      
+      ++p;
+      while (*p && isspace (*p))
+        ++p;
+      
+      if (pkg->vars == NULL)
+        pkg->vars = g_hash_table_new (g_str_hash, g_str_equal);
+
+      if (g_hash_table_lookup (pkg->vars, tag))
+        {
+          verbose_error ("Duplicate definition of variable '%s' in '%s'\n",
+                         tag, path);
+
+          exit (1);
+        }
+
+      varname = g_strdup (tag);
+      varval = trim_and_sub (pkg, p, path);     
+
+      debug_spew (" Variable declaration, '%s' has value '%s'\n",
+                  varname, varval);
+      g_hash_table_insert (pkg->vars, varname, varval);
+  
+    }
+  
+  g_free (str);
+  g_free (tag);
+}
+
+Package*
+parse_package_file (const char *path)
+{
+  FILE *f;
+  Package *pkg;
+  GString *str;
+  gboolean one_line = FALSE;
+  
+  f = fopen (path, "r");
+
+  if (f == NULL)
+    {
+      verbose_error ("Failed to open '%s': %s\n",
+                     path, strerror (errno));
+      
+      return NULL;
+    }
+
+  debug_spew ("Parsing package file '%s'\n", path);
+  
+  pkg = g_new0 (Package, 1);
+
+  if (path)
+    {
+      pkg->pcfiledir = g_dirname (path);
+    }
+  else
+    {
+      debug_spew ("No pcfiledir determined for package\n");
+      pkg->pcfiledir = g_strdup ("???????");
+    }
+  
+  str = g_string_new ("");
+
+  while (read_one_line (f, str))
+    {
+      one_line = TRUE;
+      
+      parse_line (pkg, str->str, path);
+
+      g_string_truncate (str, 0);
+    }
+
+  if (!one_line)
+    verbose_error ("Package file '%s' appears to be empty\n",
+                   path);
+  
+  return pkg;
+}
+
+static char *
+backticks (const char *command)
+{
+  FILE *f;
+  char buf[4096];
+  size_t len;
+  int status;
+  
+  f = popen (command, "r");
+
+  if (f == NULL)
+    return NULL;
+  
+  len = fread (buf, 1, 4090, f);
+
+  if (ferror (f))
+    {
+      pclose (f);
+      return NULL;
+    }
+  
+  buf[len] = '\0';
+
+  status = pclose (f);
+
+  return g_strdup (buf);
+}
+
+static gboolean
+try_command (const char *command)
+{
+  int status;
+  char *munged;
+
+  munged = g_strdup_printf ("%s > /dev/null 2>&1", command);
+  
+  status = system (munged);
+
+  g_free (munged);
+  
+  return WIFEXITED(status) && (WEXITSTATUS(status) == 0);
+}
+
+Package *
+get_compat_package (const char *name)
+{
+  Package *pkg;
+
+  debug_spew ("Looking for '%s' using old-style -config scripts\n", name);
+  
+  pkg = g_new0 (Package, 1);
+  
+  if (strcmp (name, "glib") == 0)
+    {
+      char *output;
+
+      debug_spew ("Calling glib-config\n");
+      
+      pkg->version = backticks ("glib-config --version");
+      if (pkg->version == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+      
+      pkg->name = g_strdup ("GLib");
+      pkg->key = g_strdup ("glib");
+      pkg->description = g_strdup ("C Utility Library");
+
+      output = backticks ("glib-config --libs");
+      parse_libs (pkg, output, "glib-config");
+      g_free (output);
+
+      output = backticks ("glib-config --cflags");
+      parse_cflags (pkg, output, "glib-config");
+      g_free (output);
+
+      return pkg;
+    }
+  else if (strcmp (name, "gtk+") == 0)
+    {
+      char *output;
+
+      debug_spew ("Calling gtk-config\n");
+      
+      pkg->version = backticks ("gtk-config --version");
+      if (pkg->version == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+      
+      pkg->name = g_strdup ("GTK+");
+      pkg->key = g_strdup ("gtk+");
+      pkg->description = g_strdup ("GIMP Tool Kit");
+
+      output = backticks ("gtk-config --libs");
+      parse_libs (pkg, output, "gtk-config");
+      g_free (output);
+
+      output = backticks ("gtk-config --cflags");
+      parse_cflags (pkg, output, "gtk-config");
+      g_free (output);
+
+      return pkg;
+    }
+  else if (strcmp (name, "imlib") == 0)
+    {
+      char *output;
+
+      debug_spew ("Calling imlib-config\n");
+      
+      pkg->version = backticks ("imlib-config --version");
+      if (pkg->version == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+      
+      pkg->name = g_strdup ("Imlib");
+      pkg->key = g_strdup ("imlib");
+      pkg->description = g_strdup ("Imlib image loading library");
+
+      output = backticks ("imlib-config --libs-gdk");
+      parse_libs (pkg, output, "imlib-config");
+      g_free (output);
+
+      output = backticks ("imlib-config --cflags-gdk");
+      parse_cflags (pkg, output, "imlib-config");
+      g_free (output);
+
+      return pkg;
+    }
+  else if (strcmp (name, "orbit-client") == 0)
+    {
+      char *output;
+      char *p;
+
+      debug_spew ("Calling orbit-config\n");
+      
+      output = backticks ("orbit-config --version");
+      
+      if (output == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+
+      p = output;
+
+      while (*p && isspace (*p))
+        ++p;
+
+      if (*p == '\0')
+        {
+          /* empty output */
+          g_free (output);
+          g_free (pkg);
+          return NULL;
+        }
+
+      /* only heuristic; find a number or . */
+      while (*p && ! (isdigit (*p) || *p == '.'))
+        ++p;      
+
+      pkg->version = g_strdup (p);
+
+      g_free (output);
+      
+      pkg->name = g_strdup ("ORBit Client");
+      pkg->key = g_strdup ("orbit-client");
+      pkg->description = g_strdup ("ORBit Client Libraries");
+
+      output = backticks ("orbit-config --libs client");
+      parse_libs (pkg, output, "orbit-config");
+      g_free (output);
+
+      output = backticks ("orbit-config --cflags client");
+      parse_cflags (pkg, output, "orbit-config");
+      g_free (output);
+
+      return pkg;
+    }
+  else if (strcmp (name, "orbit-server") == 0)
+    {
+      char *output;
+      char *p;
+
+      debug_spew ("Calling orbit-config\n");
+      
+      output = backticks ("orbit-config --version");
+      
+      if (output == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+
+      p = output;
+
+      while (*p && isspace (*p))
+        ++p;
+
+      if (*p == '\0')
+        {
+          /* empty output */
+          g_free (output);
+          g_free (pkg);
+          return NULL;
+        }
+
+      /* only heuristic; find a number or . */
+      while (*p && ! (isdigit (*p) || *p == '.'))
+        ++p;      
+
+      pkg->version = g_strdup (p);
+
+      g_free (output);
+      
+      pkg->name = g_strdup ("ORBit Server");
+      pkg->key = g_strdup ("orbit-server");
+      pkg->description = g_strdup ("ORBit Server Libraries");
+
+      output = backticks ("orbit-config --libs server");
+      parse_libs (pkg, output, "orbit-config");
+      g_free (output);
+
+      output = backticks ("orbit-config --cflags server");
+      parse_cflags (pkg, output, "orbit-config");
+      g_free (output);
+
+      return pkg;
+    }
+  else
+    {
+      /* Check for the module in gnome-config */
+      char *output;
+      char *p;
+      char *command;
+
+      debug_spew ("Calling gnome-config\n");
+      
+      /* Annoyingly, --modversion doesn't return a failure
+       * code if the lib is unknown, so we have to use --libs
+       * for that.
+       */
+      
+      command = g_strdup_printf ("gnome-config --libs %s",
+                                 name);
+      
+      if (!try_command (command))
+        {
+          g_free (command);
+          g_free (pkg);
+          return NULL;
+        }
+      else
+        g_free (command);
+      
+      command = g_strdup_printf ("gnome-config --modversion %s",
+                                 name);
+      
+      output = backticks (command);
+      g_free (command);
+      if (output == NULL)
+        {
+          g_free (pkg);
+          return NULL;
+        }
+      
+      /* Unknown modules give "Unknown library `foo'" from gnome-config
+       * (but on stderr so this is useless, nevermind)
+       */
+      if (strstr (output, "Unknown") || *output == '\0')
+        {
+          g_free (output);
+          g_free (pkg);
+          return NULL;
+        }
+
+      /* gnome-config --modversion gnomeui outputs e.g. "gnome-libs-1.2.4"
+       * or libglade-0.12
+       */
+      p = output;
+
+      while (*p && isspace (*p))
+        ++p;
+
+      if (*p == '\0')
+        {
+          /* empty output */
+          g_free (output);
+          g_free (pkg);
+          return NULL;
+        }
+
+      /* only heuristic; find a number or . */
+      while (*p && ! (isdigit (*p) || *p == '.'))
+        ++p;      
+
+      pkg->version = g_strdup (p);
+
+      g_free (output);
+      
+      /* Strip newline */
+      p = pkg->version;
+      while (*p)
+        {
+          if (*p == '\n')
+            *p = '\0';
+
+          ++p;
+        }
+      
+      pkg->name = g_strdup (name);
+      pkg->key = g_strdup (name);
+      pkg->description = g_strdup ("No description");
+
+      command = g_strdup_printf ("gnome-config --libs %s", name);
+      output = backticks (command);
+      g_free (command);
+      parse_libs (pkg, output, "gnome-config");
+      g_free (output);
+
+      command = g_strdup_printf ("gnome-config --cflags %s", name);
+      output = backticks (command);
+      g_free (command);
+      parse_cflags (pkg, output, "gnome-config");
+      g_free (output);
+
+      return pkg;
+    }
+}
+
diff --git a/parse.h b/parse.h
new file mode 100644 (file)
index 0000000..77187dc
--- /dev/null
+++ b/parse.h
@@ -0,0 +1,15 @@
+#ifndef PKG_CONFIG_PARSE_H
+#define PKG_CONFIG_PARSE_H
+
+#include "pkg.h"
+
+Package *parse_package_file (const char *path);
+
+Package *get_compat_package (const char *name);
+
+GSList  *parse_module_list (Package *pkg, const char *str, const char *path);
+
+#endif
+
+
+
diff --git a/partial-glib.c b/partial-glib.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/partial-glib.h b/partial-glib.h
new file mode 100644 (file)
index 0000000..0f1fd81
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef PKG_CONFIG_PARTIAL_GLIB_H
+#define PKG_CONFIG_PARTIAL_GLIB_H
+
+#include "glib-1.2.8/glib.h"
+
+#endif
diff --git a/pkg-config.1 b/pkg-config.1
new file mode 100644 (file)
index 0000000..6eb4937
--- /dev/null
@@ -0,0 +1,313 @@
+.\" 
+.\" pkg-config manual page.
+.\" (C) Red Hat, Inc. based on gnome-config man page (C) Miguel de Icaza (miguel@gnu.org)
+.\"
+.TH pkg-config 1
+.SH NAME
+pkg-config \- Return metainformation about installed libraries
+.SH SYNOPSIS
+.PP
+.B pkg-config
+[\-\-modversion] [\-\-help] [\-\-print-errors] [\-\-silence-errors] 
+[\-\-cflags] [\-\-libs] [\-\-libs-only-L]
+[\-\-libs-only-l] [\-\-cflags-only-I]
+[\-\-variable=VARIABLENAME]
+[\-\-define-variable=VARIABLENAME=VARIABLEVALUE]
+[\-\-uninstalled]
+[\-\-exists] [\-\-atleast-version=VERSION] [\-\-exact-version=VERSION]
+[\-\-max-version=VERSION] [LIBRARIES...]
+.SH DESCRIPTION
+
+The \fIpkg-config\fP program is used to retrieve information about
+installed libraries in the system.  It is typically used to compile
+and link against one or more libraries.  Here is a typical usage
+scenario in a Makefile:
+.PP
+.nf
+program: program.c
+       cc program.c `pkg-config --cflags --libs gnomeui`
+.fi
+.PP
+
+.PP
+\fIpkg-config\fP retrieves information about packages from 
+special metadata files. These files are named after the package, 
+with the extension \fI.pc\fP. By default, pkg-config looks in 
+the directory \fIprefix\fP/lib/pkgconfig for these files; it will also
+look in the colon-separated list of directories specified by the 
+PKG_CONFIG_PATH environment variable. 
+
+.PP
+The package name specified on the \fIpkg-config\fP command line is
+defined to be the name of the metadata file, minus the \fI.pc\fP
+extension. If a library can install multiple versions simultaneously,
+it must give each version its own name (for example, GTK 1.2 might
+have the package name "gtk+" while GTK 2.0 has "gtk+-2.0").
+
+.SH OPTIONS
+The following options are supported:
+.TP
+.I "--modversion"
+Requests that the version information of the libraries specified on
+the command line be displayed.  If \fIpkg-config\fP can find all the
+libraries on the command line, each library's version string is
+printed to stdout, one version per line. In this case \fIpkg-config\fP
+exits successfully. If one or more libraries is unknown,
+\fIpkg-config\fP exits with a nonzero code, and the contents of stdout
+are undefined.
+.TP
+.I "--help"
+Displays a help message and terminates.
+
+.TP
+.I "--print-errors"
+If one or more of the modules on the command line, or their
+dependencies, are not found, or if an error occurs in parsing a
+\fI.pc\fP file, then this option will cause errors explaining the
+problem to be printed. With "predicate" options such as "--exists"
+\fIpkg-config\fP runs silently by default, because it's usually used
+in scripts that want to control what's output. This option can be used
+alone (to just print errors encountered locating modules on the 
+command line) or with other options. The PKG_CONFIG_DEBUG_SPEW
+environment variable overrides this option.
+
+.TP
+.I "--silence-errors"
+If one or more of the modules on the command line, or their
+dependencies, are not found, or if an error occurs in parsing a
+\fI.pc\fP file, then this option will keep errors explaining the
+problem from being printed. With "predicate" options such as
+"--exists" \fIpkg-config\fP runs silently by default, because it's
+usually used in scripts that want to control what's output. So this
+option is only useful with options such as "--cflags" or
+"--modversion" that print errors by default. The PKG_CONFIG_DEBUG_SPEW
+environment variable overrides this option.
+
+.PP
+The following options are used to compile and link programs:
+.TP
+.I "--cflags"
+This prints pre-processor and compile flags required to compile the
+packages on the command line, including flags for all their
+dependencies. Flags are "compressed" so that each identical flag
+appears only once. \fIpkg-config\fP exits with a nonzero code if it
+can't find metadata for one or more of the packages on the command
+line.
+.TP 
+.I "--libs"
+This option is identical to "--cflags", only it prints the link
+flags. As with "--cflags", duplicate flags are merged (maintaining
+proper ordering), and flags for dependencies are included in the
+output.
+.TP
+.I "--libs-only-L"
+This prints the -L/-R part of "--libs". That is, it defines the 
+library search path but doesn't specify which libraries to link with.
+.TP
+.I "--libs-only-l"
+This prints the -l part of "--libs" for the libraries specified on
+the command line. Note that the union of "--libs-only-l" and
+"--libs-only-L" may be smaller than "--libs", due to flags such as
+-rdynamic.
+
+.TP
+.I "--variable=VARIABLENAME"
+This returns the value of a variable defined in a package's \fI.pc\fP
+file. Most packages define the variable "prefix", for example, so you 
+can say:
+.nf
+  $ pkg-config --variable=prefix glib-2.0
+  /usr/
+.fi
+.TP
+.I "--define-variable=VARIABLENAME=VARIABLEVALUE"
+This sets a global value for a variable, overriding the value in any
+\fI.pc\fP files. Most packages define the variable "prefix", for
+example, so you can say:
+.nf
+  $ pkg-config --print-errors --define-variable=prefix=/foo --variable=prefix glib-2.0
+  /foo
+.fi
+
+.TP
+.I "--uninstalled"
+Normally if you request the package "foo" and the package
+"foo-uninstalled" exists, \fIpkg-config\fP will prefer the 
+"-uninstalled" variant. This allows compilation/linking against
+uninstalled packages. If you specify the "--uninstalled" option,
+\fIpkg-config\fP will return successfully if any "-uninstalled"
+packages are being used, and return failure (false) otherwise.
+(The "PKG_CONFIG_DISABLE_UNINSTALLED" environment variable keeps 
+\fIpkg-config\fP from implicitly choosing "-uninstalled" packages, so
+if that variable is set, they will only have been used if you pass 
+a name like "foo-uninstalled" on the command line explicitly.)
+
+.TP
+.I "--exists"
+.TP
+.I "--atleast-version=VERSION"
+.TP
+.I "--exact-version=VERSION"
+.TP
+.I "--max-version=VERSION"
+These options test whether the package or list of packages on the
+command line are known to \fIpkg-config\fP, and optionally 
+whether the version number of a package meets certain contraints.
+If all packages exist and meet the specified version constraints,
+\fIpkg-config\fP exits successfully. Otherwise it exits unsuccessfully.
+
+Rather than using the version-test options, you can simply give a version
+constraint after each package name, for example:
+.nf
+  $ pkg-config --exists 'glib-2.0 >= 1.3.4 libxml = 1.8.3'
+.fi
+Remember to use \-\-print-errors if you want error messages.
+
+.SH ENVIRONMENT VARIABLES
+
+.TP
+.I "PKG_CONFIG_PATH"
+A colon-separated list of directories to search for .pc files.
+The default directory will always be searched after searching the
+path; the default is \fIlibdir\fP/pkgconfig where \fIlibdir\fP
+is the libdir where \fIpkg-config\fP was installed.
+
+.TP
+.I "PKG_CONFIG_DEBUG_SPEW"
+If set, causes \fIpkg-config\fP to print all kinds of
+debugging information and report all errors.
+
+.TP
+.I "PKG_CONFIG_TOP_BUILD_DIR"
+A value to set for the magic variable \fIpc_top_builddir\fP
+which may appear in \fI.pc\fP files. If the environment variable is
+not set, the default value '$(top_builddir)' will be used. This
+variable should refer to the top builddir of the Makefile where the 
+compile/link flags reported by \fIpkg-config\fP will be used.
+This only matters when compiling/linking against a package that hasn't
+yet been installed.
+
+.TP
+.I "PKG_CONFIG_DISABLE_UNINSTALLED"
+Normally if you request the package "foo" and the package
+"foo-uninstalled" exists, \fIpkg-config\fP will prefer the 
+"-uninstalled" variant. This allows compilation/linking against
+uninstalled packages.  If this environment variable is set, it
+disables said behavior.
+
+.SH AUTOCONF MACROS
+
+.TP
+.I "PKG_CHECK_MODULES(VARIABLEBASE,MODULELIST[,ACTION-IF-FOUND,[ACTION-IF-NOT-FOUND]])"
+
+The macro PKG_CHECK_MODULES can be used in \fIconfigure.in\fP to 
+check whether modules exist. A typical usage would be:
+.nf
+ PKG_CHECK_MODULES(MYSTUFF, gtk+-2.0 >= 1.3.5 libxml = 1.8.4)
+.fi
+
+This would result in MYSTUFF_LIBS and MYSTUFF_CFLAGS substitution
+variables, set to the libs and cflags for the given module list. 
+If a module is missing or has the wrong version, by default configure
+will abort with a message. To replace the default action, 
+specify an ACTION-IF-NOT-FOUND. PKG_CHECK_MODULES will not print any
+error messages if you specify your own ACTION-IF-NOT-FOUND.
+
+.SH METADATA FILE SYNTAX
+To add a library to the set of packages \fIpkg-config\fP knows about,
+simply install a \fI.pc\fP file. You should install this file to 
+\fIlibdir\fP/pkgconfig.
+
+.PP
+Here is an example file:
+.nf
+# This is a comment
+prefix=/home/hp/unst   # this defines a variable
+exec_prefix=${prefix}  # defining another variable in terms of the first
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include
+
+Name: GObject                            # human-readable name
+Description: Object/type system for GLib # human-readable description
+Version: 1.3.1                           
+Requires: glib-2.0 = 1.3.1
+Conflicts: foobar <= 4.5
+Libs: -L${libdir} -lgobject-1.3
+Cflags: -I${includedir}/glib-2.0 -I${libdir}/glib/include 
+.fi
+
+.PP
+You would normally generate the file using configure, of course, so
+that the prefix, etc. are set to the proper values.
+
+.PP
+Files have two kinds of line: keyword lines start with a keyword plus
+a colon, and variable definitions start with an alphanumeric string
+plus an equals sign. Keywords are defined in advance and have special
+meaning to \fIpkg-config\fP; variables do not, you can have any
+variables that you wish (however, users may expect to retrieve the
+usual directory name variables).
+
+.PP
+Note that variable references are written "${foo}"; you can escape
+literal "${" as "$${".
+
+.TP
+.I "Name:"
+This field should be a human-readable name for the package. Note that
+it is not the name passed as an argument to \fIpkg-config\fP.
+.TP
+.I "Description:"
+This should be a brief description of the package
+.TP
+.I "Version:"
+This should be the most-specific-possible package version string.
+.TP
+.I "Requires:"
+This is a comma-separated list of packages that are required by your
+package. Flags from dependent packages will be merged in to the flags
+reported for your package. Optionally, you can specify the version 
+of the required package (using the operators =, <, >, >=, <=);
+specifying a version allows \fIpkg-config\fP to perform extra sanity
+checks. You may only mention the same package one time on the 
+.I "Requires:"
+line. If the version of a package is unspecified, any version will
+be used with no checking.
+.TP
+.I "Conflicts:"
+This optional line allows \fIpkg-config\fP to perform additional
+sanity checks, primarily to detect broken user installations.  The
+syntax is the same as
+.I "Requires:"
+except that
+you can list the same package more than once here, for example 
+"foobar = 1.2.3, foobar = 1.2.5, foobar >= 1.3", if you have reason to
+do so. If a version isn't specified, then your package conflicts with
+all versions of the mentioned package. 
+If a user tries to use your package and a conflicting package at the
+same time, then \fIpkg-config\fP will complain.
+.TP
+.I "Libs:"
+This line should give the link flags specific to your package. 
+Don't add any flags for required packages; \fIpkg-config\fP will 
+add those automatically.
+
+.TP
+.I "Cflags:"
+This line should list the compile flags specific to your package. 
+Don't add any flags for required packages; \fIpkg-config\fP will 
+add those automatically.
+
+.SH AUTHOR
+
+\fIpkg-config\fP was written by James Henstridge, rewritten by Martijn
+van Beers, and rewritten again by Havoc Pennington. Tim Janik, Owen
+Taylor, and Raja Harinath submitted suggestions and some code.
+\fIgnome-config\fP was written by Miguel de Icaza, Raja Harinath and
+various hackers in the GNOME team.  It was inspired by Owen Taylor's
+\fIgtk-config\fP program.
+
+.SH BUGS
+Hah!
+
+
diff --git a/pkg-config.in b/pkg-config.in
new file mode 100644 (file)
index 0000000..2d47c8e
--- /dev/null
@@ -0,0 +1,306 @@
+#!/bin/sh
+
+# All variables are prefixed with pc_ so that there is low likelyhood of
+# conflicts with the .pc files.
+
+IFS=":$IFS"
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+pc_default_path=@libexecdir@/pkgconfig
+
+if [ ! -d ${pc_default_path} ]; then
+       echo "${pc_default_path} doesn't exist. Make sure you've installed pkgconfig correctly."
+       exit 1;
+fi
+if [ ! -f "${pc_default_path}/gnomeconfig.pce" ]; then
+       echo "Can't find gnomeconfig.pce. Make sure you've installed pkgconfig correctly."
+       exit 1;
+fi
+
+cleanup() {
+    pc_prepend=$1
+    shift
+    pc_keep=$1
+    shift
+    pc_cleanup_var=$1
+    shift
+    pc_temp=""
+    while [ $# -ne 0 ]; do
+       pc_var=$1   # iterate over $1 ...
+       shift
+       if test "x$pc_keep" = "xkeepfirst"; then
+           case " $pc_temp " in
+               *\ $pc_var\ *) ;;
+               *)  if test "x$pc_prepend" = "xprepend"; then
+                       pc_temp="$pc_var $pc_temp"
+                   else
+                       pc_temp="$pc_temp $pc_var"
+                   fi
+               ;;
+           esac
+       else
+           # use : as seperator because that's what we prepended to IFS
+           case ":$*:" in
+               *\:$pc_var\:*) ;;
+               *)  if test "x$pc_prepend" = "xprepend"; then
+                       pc_temp="$pc_var $pc_temp"
+                   else
+                       pc_temp="$pc_temp $pc_var"
+                   fi
+               ;;
+           esac
+       fi
+    done
+    eval $pc_cleanup_var='$pc_temp'
+}
+
+find_configfile() {
+    for pc_dir in $pc_path XXX; do
+       if [ -f "${pc_dir}/${pc_pkg}.pc" ]; then
+           . ${pc_dir}/${pc_pkg}.pc
+           break
+       fi
+    done
+    if [ "$pc_dir" = XXX ]; then
+       pc_extns=`ls ${pc_default_path}/*.pce`
+       for pc_ext in $pc_extns XXX; do
+           if [ -f "${pc_ext}" ]; then
+               . ${pc_ext} $pc_pkg
+               # sh on Tru64 (OSF1??) doesn't like return in sourced
+               # files, so we set $result
+               if [ "$result" = "0" ]; then
+                   break;
+               fi
+           fi
+       done
+       if [ "$pc_ext" = XXX ]; then
+           echo "couldn't find information for package $pc_pkg." 1>&2
+           exit 1
+       fi
+       
+    fi
+}
+
+# make sure we give a non-zero return value if we get interrupted
+# signals HUP, INT and TERM
+trap 'echo; echo "Interrupted."; exit 1' 1 2 15
+
+pc_help_msg="Usage: $0 [-I DIR] [GENERIC_OPTION | FLAGSOPTION...] [LIBRARY...]
+
+  -I DIR       Add a directory to the .pc file search path
+
+Generic options:
+  --version           Output the pkg-config version and exit
+  --modversion        Output the version of the first library and exit
+  --help              Show this help and exit
+  --expand            Show expanded list of packages to link with
+  --print-pc-dir      Print the default .pc search dir and exit
+  --extra-flags       Print the extra flags the selected modules support
+  --get-flag FLAGNAME Print the content of the extra flag specified
+
+Output link information for a combination of libraries
+Options to control which compile/link flags to output
+
+  --cflags             Show cflags to compile with
+  --libs-only-L        Output only -L/-R flags needed for the libraries listed
+  --libs-onlu-l-self   Output the -l flags for the libraries listed
+  --libs-only-l-system Output the -l flags for system libraries the
+                       listed libraries depend on.
+  --libs               Output a combination of all the --libs-only-* flags above
+  LIBRARIES            Packages that your program needs to compile
+  
+  "
+
+pc_want_ldflags=no
+pc_want_libs_self=no
+pc_want_libs_system=no
+pc_want_cflags=no
+pc_want_libs=no
+pc_want_expand=no
+pc_want_dir=no
+pc_want_varlist=no
+pc_want_extra_var=no
+pc_prev=
+pc_extra_var=
+
+# show help and exit with an errorstatus when no arguments
+if test $# -eq 0; then
+    echo "$pc_help_msg"
+    exit 1
+fi
+
+# add extra path to searched dirs
+pc_path=
+if [ -n "$PC_INCLUDEPATH" ]; then
+    pc_path="${PC_INCLUDEPATH}:${pc_path}"
+fi
+for pc_extra in `echo $GNOME_PATH | sed 's/:/ /g'`; do
+    pc_path="${extra}/libexec/pkgconfig:${pc_path}"
+done
+# only use default path if no other paths are given, to
+# avoid ACLOCAL_CONFIG type problems
+if [ -z "${GNOME_PATH}${PC_INCLUDEPATH}" ]; then
+    pc_path="${pc_default_path}"
+fi
+
+
+while [ $# -ne 0 ]; do
+    pc_arg="$1"
+    shift
+    case "$pc_arg" in
+       -*=*) pc_optarg=`echo $pc_arg | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+       *)    pc_optarg= ;;
+    esac
+    if [ -n "$pc_prev" ]; then
+       if [ "$pc_prev" = pcpath ]; then
+           pc_path="$pc_arg:$pc_path"
+       elif [ "$pc_prev" = "extravar" ]; then
+           pc_extra_var="$pc_arg"
+       else
+           eval "$pc_prev=\$pc_arg"
+       fi
+       pc_prev=
+       continue
+    fi
+    case "$pc_arg" in
+       --help)
+           echo "$pc_help_msg"
+           exit 0 ;;
+       --version)
+           echo "pkgconfig @VERSION@"
+           exit 0 ;;
+       -I*)                  pc_path="`echo $pc_arg | sed 's/^-I//'`:$pc_path" ;;
+        -I)                   pc_prev=pcpath ;;
+       --cflags)             pc_want_cflags=yes ;;
+       --libs-only-L)        pc_want_ldflags=yes ;;
+       --libs-only-l-self)   pc_want_libs_self=yes ;;
+       --libs-only-l-system) pc_want_libs_system=yes ;;
+       --libs)
+           pc_want_ldflags=yes
+           pc_want_libs_self=yes
+           pc_want_libs_system=yes ;;
+       --expand)             pc_want_expand=yes ;;
+       --modversion)         pc_want_modversion=yes ;;
+       --print-pc-dir)       pc_want_dir=yes ;;
+       --extra-flags)        pc_want_varlist=yes ;;
+       --get-flag)           pc_prev="extravar" pc_want_var=yes ;;
+       -*)
+           echo "Unknown option: $pc_arg"
+           echo "$pc_help_msg"
+           exit 1 ;;
+       *) pc_caps="$pc_caps $pc_arg" ;;
+    esac
+done
+
+# print the default pc_path only
+if [ "$pc_want_dir" = yes ]; then
+    echo $pc_default_path
+    exit 0
+fi
+
+# expand the packages list, so to satisfy dependencies ...
+set - $pc_caps
+pc_pkgs=""
+while [ $# -ne 0 ]; do
+    pc_pkg="$1"
+    shift
+
+    if [ -z "$pc_pkg" ]; then
+       # ash seems to count the space at the start of pc_caps as an extra
+       # argument.  This catches that case.
+       continue
+    fi
+    pc_pkgs="$pc_pkgs $pc_pkg"
+    REQUIRES=""
+
+    find_configfile $pc_pkg
+
+    if [ "x$REQUIRES" != x ]; then
+       for pc_dep in $REQUIRES; do
+           case ":$*:" in
+               *\:$pc_dep\:*) ;;
+               *) set - $* $pc_dep;;
+           esac
+       done
+    fi
+done
+
+# now remove duplicate packages from the list (all but last duplicate) ...
+cleanup prepend keepfirst pc_pkgs $pc_pkgs
+
+
+#
+# from this point on, the list of packages should all exist, and be ordered
+#
+
+# give the module version
+if [ "$pc_want_modversion" = yes ]; then
+    echo $VERSION
+    exit 0
+fi
+
+# list the expanded list of packages if asked for ...
+if [ "$pc_want_expand" = yes ]; then
+    echo $pc_pkgs
+    exit 0;
+fi
+
+# list the list of extra vars
+if [ "$pc_want_varlist" = yes ]; then
+    cleanup prepend keepfirst pc_varlist $EXTRA_VARS
+    echo $pc_varlist
+    exit 0;
+fi
+
+if [ "$pc_want_var" = yes ]; then
+    eval echo \$`eval echo $pc_extra_var`
+    exit 0;
+fi
+
+#ignore what the previous runs of the scripts have set
+CFLAGS=
+LIBS_PATH=
+LIBS_LINK_SELF=
+LIBS_LINK_SYSTEM=
+
+for pc_pkg in $pc_pkgs; do
+    find_configfile $pc_pkg
+done
+
+# clean up duplicate flags.  Discard all but last occurence of -l* flags,
+# and all but first occurence of other flags
+if [ "$pc_want_cflags" = "yes" ]; then
+    pc_includepath=
+    for pc_flag in $CFLAGS; do
+       if [ "$pc_flag" != "-I/usr/include" ]; then
+           pc_includepath="$pc_includepath $pc_flag"
+       fi
+    done
+    cleanup append keepfirst CFLAGS $pc_includepath
+    echo -n $CFLAGS
+fi
+if [ "$pc_want_ldflags" = "yes" ]; then
+    pc_libpath=
+    for pc_flag in $LIBS_PATH; do
+       if [ "$pc_flag" != "-L/usr/lib" ]; then
+           pc_libpath="$pc_libpath $pc_flag"
+       fi
+    done
+    cleanup append keepfirst LIBS_PATH $pc_libpath
+    echo -n "$LIBS_PATH"
+fi
+if [ "$pc_want_libs_self" = "yes" ]; then
+    cleanup append keeplast LIBS_LINK_SELF $LIBS_LINK_SELF
+    echo -n "$LIBS_LINK_SELF"
+fi
+if [ "$pc_want_libs_system" = "yes" ];then
+    cleanup append keeplast LIBS_LINK_SYSTEM $LIBS_LINK_SYSTEM
+    echo -n "$LIBS_LINK_SYSTEM"
+fi
+
+#end the line
+echo
+#everything seems to have gone well
+exit 0
+
diff --git a/pkg.c b/pkg.c
new file mode 100644 (file)
index 0000000..877207f
--- /dev/null
+++ b/pkg.c
@@ -0,0 +1,1099 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pkg.h"
+#include "parse.h"
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+static void verify_package (Package *pkg);
+
+static GHashTable *packages = NULL;
+static GHashTable *locations = NULL;
+static GHashTable *globals = NULL;
+static GSList *search_dirs = NULL;
+
+gboolean disable_uninstalled = FALSE;
+
+void
+add_search_dir (const char *path)
+{
+  search_dirs = g_slist_prepend (search_dirs, g_strdup (path));
+}
+
+#define EXT_LEN 3
+
+static gboolean
+ends_in_dotpc (const char *str)
+{
+  int len = strlen (str);
+  
+  if (len > EXT_LEN &&
+      str[len - 3] == '.' &&
+      str[len - 2] == 'p' &&
+      str[len - 1] == 'c')
+    return TRUE;
+  else
+    return FALSE;
+}
+
+/* strlen ("uninstalled") */
+#define UNINSTALLED_LEN 11 
+
+static gboolean
+ends_in_uninstalled (const char *str)
+{
+  int len = strlen (str);
+  
+  if (len > UNINSTALLED_LEN &&
+      strcmp ((str + len - UNINSTALLED_LEN), "uninstalled") == 0)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+
+/* Look for .pc files in the given directory and add them into
+ * locations, ignoring duplicates
+ */
+static void
+scan_dir (const char *dirname)
+{
+  DIR *dir = opendir (dirname);
+  struct dirent *dent;
+  int dirnamelen = strlen (dirname);
+
+  if (dirnamelen > 0 && dirname[dirnamelen-1] == '/')
+    dirnamelen--;
+
+  if (!dir)
+    {
+      debug_spew ("Cannot open directory '%s' in package search path: %s\n",
+                  dirname, g_strerror (errno));
+      return;
+    }
+
+  debug_spew ("Scanning directory '%s'\n", dirname);
+  
+  while ((dent = readdir (dir)))
+    {
+      int len = strlen (dent->d_name);
+
+      if (ends_in_dotpc (dent->d_name))
+        {
+          char *pkgname = malloc (len - 2);
+
+          debug_spew ("File '%s' appears to be a .pc file\n", dent->d_name);
+          
+         strncpy (pkgname, dent->d_name, len - EXT_LEN);
+          pkgname[len-EXT_LEN] = '\0';
+
+          if (g_hash_table_lookup (locations, pkgname))
+            {
+              debug_spew ("File '%s' ignored, we already know about package '%s'\n", dent->d_name, pkgname);
+              g_free (pkgname);
+            }
+          else
+            {
+              char *filename = g_malloc (dirnamelen + 1 + len + 1);
+              strncpy (filename, dirname, dirnamelen);
+              filename[dirnamelen] = '/';
+              strcpy (filename + dirnamelen + 1, dent->d_name);
+              
+              g_hash_table_insert (locations, pkgname, filename);
+
+              debug_spew ("Will find package '%s' in file '%s'\n",
+                          pkgname, filename);
+            }
+        }
+      else
+        {
+          debug_spew ("Ignoring file '%s' in search directory; not a .pc file\n",
+                      dent->d_name);
+        }
+    }
+}
+
+void
+package_init ()
+{
+  static gboolean initted = FALSE;
+
+  if (!initted)
+    {
+      initted = TRUE;
+      
+      packages = g_hash_table_new (g_str_hash, g_str_equal);
+      locations = g_hash_table_new (g_str_hash, g_str_equal);
+      
+      g_slist_foreach (search_dirs, (GFunc)scan_dir, NULL);
+      scan_dir (PKGLIBDIR);
+    }
+}
+
+static gboolean
+file_readable (const char *path)
+{
+  FILE *f = fopen (path, "r");
+
+  if (f != NULL)
+    {
+      fclose (f);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+
+static Package *
+internal_get_package (const char *name, gboolean warn)
+{
+  Package *pkg = NULL;
+  const char *location;
+  
+  pkg = g_hash_table_lookup (packages, name);
+
+  if (pkg)
+    return pkg;
+
+  debug_spew ("Looking for package '%s'\n", name);
+  
+  /* treat "name" as a filename if it ends in .pc and exists */
+  if ( ends_in_dotpc (name) )
+    {
+      debug_spew ("Considering '%s' to be a filename rather than a package name\n", name);
+      location = name;
+    }
+  else
+    {
+      /* See if we should auto-prefer the uninstalled version */
+      if (!disable_uninstalled &&
+          !ends_in_uninstalled (name))
+        {
+          char *un;
+
+          un = g_strconcat (name, "-uninstalled", NULL);
+
+          pkg = internal_get_package (un, FALSE);
+
+          g_free (un);
+          
+          if (pkg)
+            {
+              debug_spew ("Preferring uninstalled version of package '%s'\n", name);
+              return pkg;
+            }
+        }
+      
+      location = g_hash_table_lookup (locations, name);
+      debug_spew ("Reading '%s' from file '%s'\n", name, location);
+    }
+  
+  if (location == NULL)
+    {
+      pkg = get_compat_package (name);
+      
+      if (pkg == NULL)
+        {
+          if (warn)
+            verbose_error ("Package %s was not found in the pkg-config search path.\n"
+                           "Perhaps you should add the directory containing `%s.pc'\n"
+                           "to the PKG_CONFIG_PATH environment variable\n",
+                           name, name);
+        }
+
+      debug_spew ("Returning values for '%s' from a legacy -config script\n",
+                  name);
+      
+      return pkg;
+    }
+
+  pkg = parse_package_file (location);
+  
+  if (pkg == NULL)
+    {
+      debug_spew ("Failed to parse '%s'\n", location);
+      return NULL;
+    }
+
+  if (strstr (location, "uninstalled.pc"))
+    pkg->uninstalled = TRUE;
+  
+  if (location != name)
+    pkg->key = g_strdup (name);
+  else
+    {
+      /* need to strip package name out of the filename */
+      int len = strlen (name);
+      const char *end = name + (len - EXT_LEN);
+      const char *start = end;
+
+      while (start != name && *start != G_DIR_SEPARATOR)
+        --start;
+
+      g_assert (end >= start);
+      
+      pkg->key = g_strndup (start, end - start);
+    }
+
+  verify_package (pkg);
+
+  debug_spew ("Adding '%s' to list of known packages, returning as package '%s'\n",
+              pkg->key, name);
+  
+  g_hash_table_insert (packages, pkg->key, pkg);
+
+  return pkg;
+}
+
+Package *
+get_package (const char *name)
+{
+  return internal_get_package (name, TRUE);
+}
+
+static GSList*
+string_list_strip_duplicates (GSList *list)
+{
+  GHashTable *table;
+  GSList *tmp;
+  GSList *nodups = NULL;
+  
+  table = g_hash_table_new (g_str_hash, g_str_equal);
+
+  tmp = list;
+  while (tmp != NULL)
+    {
+      if (g_hash_table_lookup (table, tmp->data) == NULL)
+        {
+          nodups = g_slist_prepend (nodups, tmp->data);
+          g_hash_table_insert (table, tmp->data, tmp->data);
+        }
+
+      tmp = g_slist_next (tmp);
+    }
+
+  nodups = g_slist_reverse (nodups);
+  
+  g_hash_table_destroy (table);
+  
+  return nodups;
+}
+
+static GSList*
+string_list_strip_duplicates_from_back (GSList *list)
+{
+  GHashTable *table;
+  GSList *tmp;
+  GSList *nodups = NULL;
+  GSList *reversed;
+  
+  table = g_hash_table_new (g_str_hash, g_str_equal);
+
+  reversed = g_slist_reverse (g_slist_copy (list));
+  
+  tmp = reversed;
+  while (tmp != NULL)
+    {
+      if (g_hash_table_lookup (table, tmp->data) == NULL)
+        {
+          /* This unreverses the reversed list */
+          nodups = g_slist_prepend (nodups, tmp->data);
+          g_hash_table_insert (table, tmp->data, tmp->data);
+        }
+
+      tmp = g_slist_next (tmp);
+    }
+
+  g_slist_free (reversed);
+  
+  g_hash_table_destroy (table);
+  
+  return nodups;
+}
+
+static char *
+string_list_to_string (GSList *list)
+{
+  GSList *tmp;
+  GString *str = g_string_new ("");
+  char *retval;
+  
+  tmp = list;
+  while (tmp != NULL)
+    {
+      g_string_append (str, tmp->data);
+      g_string_append_c (str, ' ');
+      
+      tmp = g_slist_next (tmp);
+    }
+
+  retval = str->str;
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+typedef GSList *(* GetListFunc) (Package *pkg);
+
+static GSList *
+get_l_libs (Package *pkg)
+{
+  return pkg->l_libs;
+}
+
+static GSList *
+get_L_libs (Package *pkg)
+{
+  return pkg->L_libs;
+}
+
+static GSList *
+get_I_cflags (Package *pkg)
+{
+  return pkg->I_cflags;
+}
+
+static GSList *
+get_conflicts (Package *pkg)
+{
+  return pkg->conflicts;
+}
+
+static GSList *
+get_requires (Package *pkg)
+{
+  return pkg->requires;
+}
+
+static void
+recursive_fill_list (Package *pkg, GetListFunc func, GSList **listp)
+{
+  GSList *tmp;
+  GSList *copy;
+
+  copy = g_slist_copy ((*func)(pkg));
+
+  *listp = g_slist_concat (*listp, copy);
+  
+  tmp = pkg->requires;
+
+  while (tmp != NULL)
+    {
+      recursive_fill_list (tmp->data, func, listp);
+
+      tmp = g_slist_next (tmp);
+    }
+}
+
+static gint
+compare_req_version_names (gconstpointer a, gconstpointer b)
+{
+  const RequiredVersion *ver_a = a;
+  const RequiredVersion *ver_b = b;
+
+  return strcmp (ver_a->name, ver_b->name);
+}
+
+static gint
+compare_package_keys (gconstpointer a, gconstpointer b)
+{
+  const Package *pkg_a = a;
+  const Package *pkg_b = b;
+
+  return strcmp (pkg_a->key, pkg_b->key);
+}
+
+static void
+verify_package (Package *pkg)
+{
+  GSList *requires = NULL;
+  GSList *conflicts = NULL;
+  GSList *iter;
+  GSList *requires_iter;
+  GSList *conflicts_iter;  
+
+  /* Be sure we have the required fields */
+
+  if (pkg->key == NULL)
+    {
+      fprintf (stderr,
+               "Internal pkg-config error, package with no key, please file a bug report\n");
+      exit (1);
+    }
+  
+  if (pkg->name == NULL)
+    {
+      verbose_error ("Package '%s' has no Name: field\n",
+                     pkg->key);
+      exit (1);
+    }
+
+  if (pkg->version == NULL)
+    {
+      verbose_error ("Package '%s' has no Version: field\n",
+                     pkg->name);
+      exit (1);
+    }
+
+  if (pkg->description == NULL)
+    {
+      verbose_error ("Package '%s' has no Description: field\n",
+                     pkg->description);
+      exit (1);
+    }
+  
+  /* Make sure we have the right version for all requirements */
+
+  iter = pkg->requires;
+
+  while (iter != NULL)
+    {
+      Package *req = iter->data;
+      RequiredVersion *ver = NULL;
+
+      if (pkg->required_versions)
+        ver = g_hash_table_lookup (pkg->required_versions,
+                                   req->key);
+
+      if (ver)
+        {
+          if (!version_test (ver->comparison, req->version, ver->version))
+            {
+              verbose_error ("Package '%s' requires '%s %s %s' but version of %s is %s\n",
+                             pkg->name, req->key,
+                             comparison_to_str (ver->comparison),
+                             ver->version,
+                             req->name,
+                             req->version);
+
+              exit (1);
+            }
+        }
+                                   
+      iter = g_slist_next (iter);
+    }
+
+  /* Make sure we didn't drag in any conflicts via Requires
+   * (inefficient algorithm, who cares)
+   */
+  
+  recursive_fill_list (pkg, get_requires, &requires);
+  recursive_fill_list (pkg, get_conflicts, &conflicts);
+
+  requires_iter = requires;
+  while (requires_iter != NULL)
+    {
+      Package *req = requires_iter->data;
+      
+      conflicts_iter = conflicts;
+
+      while (conflicts_iter != NULL)
+        {
+          RequiredVersion *ver = conflicts_iter->data;
+
+          if (version_test (ver->comparison,
+                            req->version,
+                            ver->version))
+            {
+              verbose_error ("Version %s of %s creates a conflict.\n"
+                             "(%s %s %s conflicts with %s %s)\n",
+                             req->version, req->name,
+                             ver->name,
+                             comparison_to_str (ver->comparison),
+                             ver->version,
+                             ver->owner->name,
+                             ver->owner->version);
+
+              exit (1);
+            }
+
+          conflicts_iter = g_slist_next (conflicts_iter);
+        }
+      
+      requires_iter = g_slist_next (requires_iter);
+    }
+  
+  g_slist_free (requires);
+  g_slist_free (conflicts);
+}
+
+static char*
+get_merged (Package *pkg, GetListFunc func)
+{
+  GSList *list;
+  GSList *dups_list = NULL;
+  char *retval;
+  
+  recursive_fill_list (pkg, func, &dups_list);
+  
+  list = string_list_strip_duplicates (dups_list);
+
+  g_slist_free (dups_list);
+  
+  retval = string_list_to_string (list);
+
+  g_slist_free (list);
+  
+  return retval;
+}
+
+static char*
+get_merged_from_back (Package *pkg, GetListFunc func)
+{
+  GSList *list;
+  GSList *dups_list = NULL;
+  char *retval;
+  
+  recursive_fill_list (pkg, func, &dups_list);
+  
+  list = string_list_strip_duplicates_from_back (dups_list);
+
+  g_slist_free (dups_list);
+  
+  retval = string_list_to_string (list);
+
+  g_slist_free (list);
+  
+  return retval;
+}
+
+static char*
+get_multi_merged (GSList *pkgs, GetListFunc func)
+{
+  GSList *tmp;
+  GSList *dups_list = NULL;
+  GSList *list;
+  char *retval;
+
+  tmp = pkgs;
+  while (tmp != NULL)
+    {
+      recursive_fill_list (tmp->data, func, &dups_list);  
+      
+      tmp = g_slist_next (tmp);
+    }
+  
+  list = string_list_strip_duplicates (dups_list);
+
+  g_slist_free (dups_list);
+  
+  retval = string_list_to_string (list);
+
+  g_slist_free (list);
+  
+  return retval;
+}
+
+static char*
+get_multi_merged_from_back (GSList *pkgs, GetListFunc func)
+{
+  GSList *tmp;
+  GSList *dups_list = NULL;
+  GSList *list;
+  char *retval;
+
+  tmp = pkgs;
+  while (tmp != NULL)
+    {
+      recursive_fill_list (tmp->data, func, &dups_list);  
+      
+      tmp = g_slist_next (tmp);
+    }
+  
+  list = string_list_strip_duplicates_from_back (dups_list);
+
+  g_slist_free (dups_list);
+  
+  retval = string_list_to_string (list);
+
+  g_slist_free (list);
+  
+  return retval;
+}
+
+char *
+package_get_l_libs (Package *pkg)
+{
+  if (pkg->l_libs_merged == NULL)
+    pkg->l_libs_merged = get_merged_from_back (pkg, get_l_libs);
+
+  return pkg->l_libs_merged;
+}
+
+char *
+packages_get_l_libs (GSList     *pkgs)
+{
+  return get_multi_merged_from_back (pkgs, get_l_libs);
+}
+
+char *
+package_get_L_libs (Package *pkg)
+{
+  if (pkg->L_libs_merged == NULL)
+    pkg->L_libs_merged = get_merged (pkg, get_L_libs);
+
+  return pkg->L_libs_merged;
+
+}
+
+char *
+packages_get_L_libs (GSList     *pkgs)
+{
+  return get_multi_merged (pkgs, get_L_libs);
+}
+
+char *
+package_get_other_libs (Package *pkg)
+{
+  return g_strdup (pkg->other_libs);
+}
+
+char *
+packages_get_other_libs (GSList   *pkgs)
+{
+  GSList *tmp;
+  GString *str;
+  char *retval;
+  
+  str = g_string_new ("");
+  
+  tmp = pkgs;
+  while (tmp != NULL)
+    {
+      Package *pkg = tmp->data;
+
+      if (pkg->other_libs)
+        {
+          g_string_append (str, pkg->other_libs);
+          g_string_append (str, " ");
+        }
+
+      tmp = g_slist_next (tmp);
+    }
+
+  retval = str->str;
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+char *
+packages_get_all_libs (GSList *pkgs)
+{
+  char *l_libs;
+  char *L_libs;
+  char *other_libs;
+  GString *str;
+  char *retval;
+  
+  str = g_string_new ("");  
+
+  other_libs = packages_get_other_libs (pkgs);
+  L_libs = packages_get_L_libs (pkgs);
+  l_libs = packages_get_l_libs (pkgs);
+
+  if (other_libs)
+    g_string_append (str, other_libs);
+  
+ if (L_libs)
+    g_string_append (str, L_libs);
+  
+  if (l_libs)
+    g_string_append (str, l_libs); 
+
+  g_free (l_libs);
+  g_free (L_libs);
+  g_free (other_libs);
+
+  retval = str->str;
+
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+char *
+package_get_I_cflags (Package *pkg)
+{
+  if (pkg->I_cflags_merged == NULL)
+    pkg->I_cflags_merged = get_merged (pkg, get_I_cflags);
+
+  return pkg->I_cflags_merged;
+}
+
+char *
+packages_get_I_cflags (GSList     *pkgs)
+{
+  return get_multi_merged (pkgs, get_I_cflags);
+}
+
+char *
+package_get_other_cflags (Package *pkg)
+{
+  return g_strdup (pkg->other_cflags);
+}
+
+char *
+packages_get_other_cflags (GSList *pkgs)
+{
+  GSList *tmp;
+  GString *str;
+  char *retval;
+  
+  str = g_string_new ("");
+  
+  tmp = pkgs;
+  while (tmp != NULL)
+    {
+      Package *pkg = tmp->data;
+
+      if (pkg->other_cflags)
+        {
+          g_string_append (str, pkg->other_cflags);
+          g_string_append (str, " ");
+        }
+
+      tmp = g_slist_next (tmp);
+    }
+
+  retval = str->str;
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+char *
+package_get_cflags (Package *pkg)
+{
+
+  g_assert_not_reached ();
+  return NULL;
+}
+
+char *
+packages_get_all_cflags (GSList     *pkgs)
+{
+  char *I_cflags;
+  char *other_cflags;
+  GString *str;
+  char *retval;
+  
+  str = g_string_new ("");  
+
+  other_cflags = packages_get_other_cflags (pkgs);
+  I_cflags = packages_get_I_cflags (pkgs);
+
+  if (other_cflags)
+    g_string_append (str, other_cflags);
+  
+ if (I_cflags)
+    g_string_append (str, I_cflags);
+
+  g_free (I_cflags);
+  g_free (other_cflags);
+
+  retval = str->str;
+
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+
+void
+define_global_variable (const char *varname,
+                        const char *varval)
+{
+  if (globals == NULL)
+    globals = g_hash_table_new (g_str_hash, g_str_equal);
+
+  if (g_hash_table_lookup (globals, varname))
+    {
+      verbose_error ("Variable '%s' defined twice globally\n", varname);
+      exit (1);
+    }
+  
+  g_hash_table_insert (globals, g_strdup (varname), g_strdup (varval));
+      
+  debug_spew ("Global variable definition '%s' = '%s'\n",
+              varname, varval);
+}
+
+char *
+package_get_var (Package *pkg,
+                 const char *var)
+{
+  char *varval = NULL;
+
+  if (globals)
+    varval = g_hash_table_lookup (globals, var);
+  
+  if (varval == NULL && pkg->vars)
+    varval = g_strdup (g_hash_table_lookup (pkg->vars, var));
+
+  /* Magic "pcfiledir" variable */
+  if (varval == NULL && pkg->pcfiledir && strcmp (var, "pcfiledir") == 0)
+    varval = g_strdup (pkg->pcfiledir);
+
+  return varval;
+}
+
+char *
+packages_get_var (GSList     *pkgs,
+                  const char *varname)
+{
+  GSList *tmp;
+  GString *str;
+  char *retval;
+  
+  str = g_string_new ("");
+  
+  tmp = pkgs;
+  while (tmp != NULL)
+    {
+      Package *pkg = tmp->data;
+      char *var;
+
+      var = package_get_var (pkg, varname);
+      
+      if (var)
+        {
+          g_string_append (str, var);
+          g_string_append_c (str, ' ');                
+          g_free (var);
+        }
+
+      tmp = g_slist_next (tmp);
+    }
+
+  /* chop last space */
+  str->str[str->len - 1] = '\0';
+  retval = str->str;
+  g_string_free (str, FALSE);
+
+  return retval;
+}
+
+
+
+/* Stolen verbatim from rpm/lib/misc.c 
+   RPM is Copyright (c) 1998 by Red Hat Software, Inc.,
+   and may be distributed under the terms of the GPL and LGPL.
+*/
+/* compare alpha and numeric segments of two versions */
+/* return 1: a is newer than b */
+/*        0: a and b are the same version */
+/*       -1: b is newer than a */
+static int rpmvercmp(const char * a, const char * b) {
+    char oldch1, oldch2;
+    char * str1, * str2;
+    char * one, * two;
+    int rc;
+    int isnum;
+    
+    /* easy comparison to see if versions are identical */
+    if (!strcmp(a, b)) return 0;
+
+    str1 = alloca(strlen(a) + 1);
+    str2 = alloca(strlen(b) + 1);
+
+    strcpy(str1, a);
+    strcpy(str2, b);
+
+    one = str1;
+    two = str2;
+
+    /* loop through each version segment of str1 and str2 and compare them */
+    while (*one && *two) {
+       while (*one && !isalnum(*one)) one++;
+       while (*two && !isalnum(*two)) two++;
+
+       str1 = one;
+       str2 = two;
+
+       /* grab first completely alpha or completely numeric segment */
+       /* leave one and two pointing to the start of the alpha or numeric */
+       /* segment and walk str1 and str2 to end of segment */
+       if (isdigit(*str1)) {
+           while (*str1 && isdigit(*str1)) str1++;
+           while (*str2 && isdigit(*str2)) str2++;
+           isnum = 1;
+       } else {
+           while (*str1 && isalpha(*str1)) str1++;
+           while (*str2 && isalpha(*str2)) str2++;
+           isnum = 0;
+       }
+               
+       /* save character at the end of the alpha or numeric segment */
+       /* so that they can be restored after the comparison */
+       oldch1 = *str1;
+       *str1 = '\0';
+       oldch2 = *str2;
+       *str2 = '\0';
+
+       /* take care of the case where the two version segments are */
+       /* different types: one numeric and one alpha */
+       if (one == str1) return -1;     /* arbitrary */
+       if (two == str2) return -1;
+
+       if (isnum) {
+           /* this used to be done by converting the digit segments */
+           /* to ints using atoi() - it's changed because long  */
+           /* digit segments can overflow an int - this should fix that. */
+         
+           /* throw away any leading zeros - it's a number, right? */
+           while (*one == '0') one++;
+           while (*two == '0') two++;
+
+           /* whichever number has more digits wins */
+           if (strlen(one) > strlen(two)) return 1;
+           if (strlen(two) > strlen(one)) return -1;
+       }
+
+       /* strcmp will return which one is greater - even if the two */
+       /* segments are alpha or if they are numeric.  don't return  */
+       /* if they are equal because there might be more segments to */
+       /* compare */
+       rc = strcmp(one, two);
+       if (rc) return rc;
+       
+       /* restore character that was replaced by null above */
+       *str1 = oldch1;
+       one = str1;
+       *str2 = oldch2;
+       two = str2;
+    }
+
+    /* this catches the case where all numeric and alpha segments have */
+    /* compared identically but the segment sepparating characters were */
+    /* different */
+    if ((!*one) && (!*two)) return 0;
+
+    /* whichever version still has characters left over wins */
+    if (!*one) return -1; else return 1;
+}
+
+int
+compare_versions (const char * a, const char *b)
+{
+  return rpmvercmp (a, b);
+}
+
+gboolean
+version_test (ComparisonType comparison,
+              const char *a,
+              const char *b)
+{
+  switch (comparison)
+    {
+    case LESS_THAN:
+      return compare_versions (a, b) < 0;
+      break;
+
+    case GREATER_THAN:
+      return compare_versions (a, b) > 0;
+      break;
+
+    case LESS_THAN_EQUAL:
+      return compare_versions (a, b) <= 0;
+      break;
+
+    case GREATER_THAN_EQUAL:
+      return compare_versions (a, b) >= 0;
+      break;
+
+    case EQUAL:
+      return compare_versions (a, b) == 0;
+      break;
+
+    case NOT_EQUAL:
+      return compare_versions (a, b) != 0;
+      break;
+
+    case ALWAYS_MATCH:
+      return TRUE;
+      break;
+      
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return FALSE;
+}
+
+const char *
+comparison_to_str (ComparisonType comparison)
+{
+  switch (comparison)
+    {
+    case LESS_THAN:
+      return "<";
+      break;
+
+    case GREATER_THAN:
+      return ">";
+      break;
+
+    case LESS_THAN_EQUAL:
+      return "<=";
+      break;
+
+    case GREATER_THAN_EQUAL:
+      return ">=";
+      break;
+
+    case EQUAL:
+      return "=";
+      break;
+
+    case NOT_EQUAL:
+      return "!=";
+      break;
+
+    case ALWAYS_MATCH:
+      return "(any)";
+      break;
+      
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return "???";
+}
+
+static void
+packages_foreach (gpointer key, gpointer value, gpointer data)
+{
+  Package *pkg = get_package (key);
+
+  if (pkg != NULL)
+    {
+      printf ("%s \t\t%s - %s\n",
+              pkg->key, pkg->name, pkg->description);
+    }
+}
+
+void
+print_package_list (void)
+{
+  g_hash_table_foreach (locations, packages_foreach, NULL);
+}
+
diff --git a/pkg.h b/pkg.h
new file mode 100644 (file)
index 0000000..59e711b
--- /dev/null
+++ b/pkg.h
@@ -0,0 +1,91 @@
+#ifndef PKG_CONFIG_PKG_H
+#define PKG_CONFIG_PKG_H
+
+#include "partial-glib.h"
+
+typedef enum
+{
+  LESS_THAN,
+  GREATER_THAN,
+  LESS_THAN_EQUAL,
+  GREATER_THAN_EQUAL,
+  EQUAL,
+  NOT_EQUAL,
+  ALWAYS_MATCH
+} ComparisonType;
+
+typedef struct _Package Package;
+typedef struct _RequiredVersion RequiredVersion;
+
+struct _RequiredVersion
+{
+  char *name;
+  ComparisonType comparison;
+  char *version;
+  Package *owner;
+};
+
+struct _Package
+{
+  char *key;  /* filename name */
+  char *name; /* human-readable name */
+  char *version;
+  char *description;
+  char *pcfiledir; /* directory it was loaded from */
+  GSList *requires;
+  GSList *l_libs;
+  char   *l_libs_merged;
+  GSList *L_libs;
+  char   *L_libs_merged;
+  char   *other_libs;
+  char   *other_libs_merged;
+  GSList *I_cflags;
+  char   *I_cflags_merged;
+  char *other_cflags;
+  char *other_cflags_merged;
+  GHashTable *vars;
+  GHashTable *required_versions; /* hash from name to RequiredVersion */
+  GSList *conflicts; /* list of RequiredVersion */
+  gboolean uninstalled; /* used the -uninstalled file */
+};
+
+Package *get_package              (const char *name);
+char *   package_get_l_libs       (Package    *pkg);
+char *   packages_get_l_libs      (GSList     *pkgs);
+char *   package_get_L_libs       (Package    *pkg);
+char *   packages_get_L_libs      (GSList     *pkgs);
+char *   package_get_other_libs   (Package    *pkg);
+char *   packages_get_other_libs  (GSList     *pkgs);
+char *   packages_get_all_libs    (GSList     *pkgs);
+char *   package_get_I_cflags     (Package    *pkg);
+char *   packages_get_I_cflags    (GSList     *pkgs);
+char *   package_get_other_cflags (Package    *pkg);
+char *   packages_get_all_cflags  (GSList     *pkgs);
+char *   package_get_var          (Package    *pkg,
+                                   const char *var);
+char *   packages_get_var          (GSList     *pkgs,
+                                    const char *var);
+
+
+void add_search_dir (const char *path);
+void package_init (void);
+int compare_versions (const char * a, const char *b);
+gboolean version_test (ComparisonType comparison,
+                       const char *a,
+                       const char *b);
+
+const char *comparison_to_str (ComparisonType comparison);
+
+void print_package_list (void);
+
+void define_global_variable (const char *varname,
+                             const char *varval);
+
+void debug_spew (const char *format, ...);
+void verbose_error (const char *format, ...);
+
+/* If TRUE, do not automatically prefer uninstalled versions */
+extern gboolean disable_uninstalled;
+
+#endif
+
diff --git a/pkg.m4 b/pkg.m4
new file mode 100644 (file)
index 0000000..8c78225
--- /dev/null
+++ b/pkg.m4
@@ -0,0 +1,83 @@
+
+dnl PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not)
+dnl defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page
+AC_DEFUN(PKG_CHECK_MODULES,
+[
+  succeeded=no
+
+  if test -z "$PKG_CONFIG"; then
+    AC_PATH_PROG(PKG_CONFIG, pkg-config, no)
+  fi
+
+  if test "$PKG_CONFIG" = "no" ; then
+     echo "*** The pkg-config script could not be found. Make sure it is"
+     echo "*** in your path, or set the PKG_CONFIG environment variable"
+     echo "*** to the full path to pkg-config."
+  else
+     if ! $PKG_CONFIG --atleast-pkgconfig-version 0.6.0; then
+        echo "*** Your version of pkg-config is too old. You need version 0.6.0 or newer."
+     else
+        AC_MSG_CHECKING(for $2)
+
+        if $PKG_CONFIG --exists "$2" ; then
+            AC_MSG_RESULT(yes)
+            succeeded=yes
+
+            AC_MSG_CHECKING($1_CFLAGS)
+            $1_CFLAGS=`$PKG_CONFIG --cflags "$2"`
+            AC_MSG_RESULT($$1_CFLAGS)
+
+            AC_MSG_CHECKING($1_LIBS)
+            $1_LIBS=`$PKG_CONFIG --libs "$2"
+            AC_MSG_RESULT($$1_LIBS)
+        else
+            $1_CFLAGS=""
+            $1_LIBS=""
+            ## If we have a custom action on failure, don't print errors
+            ifelse([$4], , $PKG_CONFIG --print-errors "$2",)
+        fi
+
+        AC_SUBST($1_CFLAGS)
+        AC_SUBST($1_LIBS)
+     fi
+  fi
+
+  if test $succeeded = yes; then
+     ifelse([$3], , :, [$3])
+  else
+     ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4])
+  fi
+])
+
+
+dnl Check if the C compiler accepts a certain C flag, and if so adds it to
+dnl CFLAGS
+AC_DEFUN(PKG_CHECK_CFLAG, [
+  AC_MSG_CHECKING(if C compiler accepts $1)
+  save_CFLAGS="$CFLAGS"
+
+  dnl make sure we add it only once
+  dnl this one doesn't seem to work: *[\ \     ]$1[\ \ ]*) ;;
+  case " $CFLAGS " in
+  *\ $1\ *) echo $ac_n "(already in CFLAGS) ... " ;;
+  *\ $1\       *) echo $ac_n "(already in CFLAGS) ... " ;;
+  *\   $1\ *) echo $ac_n "(already in CFLAGS) ... " ;;
+  *\   $1\     *) echo $ac_n "(already in CFLAGS) ... " ;;
+  *) CFLAGS="$CFLAGS $1" ;;
+  esac
+
+  AC_TRY_COMPILE([#include <stdio.h>], [printf("hello");],
+                [ AC_MSG_RESULT(yes)],dnl
+                [ CFLAGS="$save_CFLAGS" AC_MSG_RESULT(no) ])
+])
+
+dnl add $ACLOCAL_FLAGS (and optionally more dirs) to the aclocal
+dnl commandline, so make can work even if it needs to rerun aclocal
+AC_DEFUN(PKG_ACLOCALFLAGS,
+[
+  test -n "$ACLOCAL_FLAGS" && ACLOCAL="$ACLOCAL $ACLOCAL_FLAGS"
+
+  for i in "$1"; do
+    ACLOCAL="$ACLOCAL -I $i"
+  done
+])
diff --git a/popt.c b/popt.c
new file mode 100644 (file)
index 0000000..517f54c
--- /dev/null
+++ b/popt.c
@@ -0,0 +1,568 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "findme.h"
+#include "popt.h"
+#include "poptint.h"
+
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute) {
+    if (con->execPath) free(con->execPath);
+    con->execPath = strdup(path);
+    con->execAbsolute = allowAbsolute;
+}
+
+static void invokeCallbacks(poptContext con, const struct poptOption * table,
+                           int post) {
+    const struct poptOption * opt = table;
+    poptCallbackType cb;
+    
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           invokeCallbacks(con, opt->arg, post);
+       } else if (((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) &&
+                  ((!post && (opt->argInfo & POPT_CBFLAG_PRE)) ||
+                   ( post && (opt->argInfo & POPT_CBFLAG_POST)))) {
+           cb = opt->arg;
+           cb(con, post ? POPT_CALLBACK_REASON_POST : POPT_CALLBACK_REASON_PRE,
+              NULL, NULL, opt->descrip);
+       }
+       opt++;
+    }
+}
+
+poptContext poptGetContext(char * name, int argc, char ** argv, 
+                          const struct poptOption * options, int flags) {
+    poptContext con = malloc(sizeof(*con));
+
+    memset(con, 0, sizeof(*con));
+
+    con->os = con->optionStack;
+    con->os->argc = argc;
+    con->os->argv = argv;
+
+    if (!(flags & POPT_CONTEXT_KEEP_FIRST))
+       con->os->next = 1;                      /* skip argv[0] */
+
+    con->leftovers = malloc(sizeof(char *) * (argc + 1));
+    con->options = options;
+    con->finalArgv = malloc(sizeof(*con->finalArgv) * (argc * 2));
+    con->finalArgvAlloced = argc * 2;
+    con->flags = flags;
+    con->execAbsolute = 1;
+
+    if (getenv("POSIXLY_CORRECT") || getenv("POSIX_ME_HARDER"))
+       con->flags |= POPT_CONTEXT_POSIXMEHARDER;
+    
+    if (name)
+       con->appName = strcpy(malloc(strlen(name) + 1), name);
+
+    invokeCallbacks(con, con->options, 0);
+
+    return con;
+}
+
+void poptResetContext(poptContext con) {
+    con->os = con->optionStack;
+    con->os->currAlias = NULL;
+    con->os->nextCharArg = NULL;
+    con->os->nextArg = NULL;
+    con->os->next = 1;                 /* skip argv[0] */
+
+    con->numLeftovers = 0;
+    con->nextLeftover = 0;
+    con->restLeftover = 0;
+    con->doExec = NULL;
+    con->finalArgvCount = 0;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleExec(poptContext con, char * longName, char shortName) {
+    int i;
+
+    i = con->numExecs - 1;
+    if (longName) {
+       while (i >= 0 && (!con->execs[i].longName ||
+           strcmp(con->execs[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->execs[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if (con->flags & POPT_CONTEXT_NO_EXEC)
+       return 1;
+
+    if (!con->doExec) {
+       con->doExec = con->execs + i;
+       return 1;
+    }
+
+    /* We already have an exec to do; remember this option for next
+       time 'round */
+    if ((con->finalArgvCount + 1) >= (con->finalArgvAlloced)) {
+       con->finalArgvAlloced += 10;
+       con->finalArgv = realloc(con->finalArgv,
+                       sizeof(*con->finalArgv) * con->finalArgvAlloced);
+    }
+
+    i = con->finalArgvCount++;
+    con->finalArgv[i] = malloc((longName ? strlen(longName) : 0) + 3);
+    if (longName)
+       sprintf(con->finalArgv[i], "--%s", longName);
+    else 
+       sprintf(con->finalArgv[i], "-%c", shortName);
+
+    return 1;
+}
+
+/* Only one of longName, shortName may be set at a time */
+static int handleAlias(poptContext con, char * longName, char shortName,
+                      char * nextCharArg) {
+    int i;
+
+    if (con->os->currAlias && con->os->currAlias->longName && longName &&
+       !strcmp(con->os->currAlias->longName, longName)) 
+       return 0;
+    if (con->os->currAlias && shortName == con->os->currAlias->shortName)
+       return 0;
+
+    i = con->numAliases - 1;
+    if (longName) {
+       while (i >= 0 && (!con->aliases[i].longName ||
+           strcmp(con->aliases[i].longName, longName))) i--;
+    } else {
+       while (i >= 0 &&
+           con->aliases[i].shortName != shortName) i--;
+    }
+
+    if (i < 0) return 0;
+
+    if ((con->os - con->optionStack + 1) 
+           == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    if (nextCharArg && *nextCharArg)
+       con->os->nextCharArg = nextCharArg;
+
+    con->os++;
+    con->os->next = 0;
+    con->os->stuffed = 0;
+    con->os->nextArg = con->os->nextCharArg = NULL;
+    con->os->currAlias = con->aliases + i;
+    con->os->argc = con->os->currAlias->argc;
+    con->os->argv = con->os->currAlias->argv;
+
+    return 1;
+}
+
+static void execCommand(poptContext con) {
+    char ** argv;
+    int pos = 0;
+    char * script = con->doExec->script;
+
+    argv = malloc(sizeof(*argv) * 
+                       (6 + con->numLeftovers + con->finalArgvCount));
+
+    if (!con->execAbsolute && strchr(script, '/')) return;
+
+    if (!strchr(script, '/') && con->execPath) {
+       argv[pos] = alloca(strlen(con->execPath) + strlen(script) + 2);
+       sprintf(argv[pos], "%s/%s", con->execPath, script);
+    } else {
+       argv[pos] = script;
+    }
+    pos++;
+
+    argv[pos] = findProgramPath(con->os->argv[0]);
+    if (argv[pos]) pos++;
+    argv[pos++] = ";";
+
+    memcpy(argv + pos, con->finalArgv, sizeof(*argv) * con->finalArgvCount);
+    pos += con->finalArgvCount;
+
+    if (con->numLeftovers) {
+       argv[pos++] = "--";
+       memcpy(argv + pos, con->leftovers, sizeof(*argv) * con->numLeftovers);
+       pos += con->numLeftovers;
+    }
+
+    argv[pos++] = NULL;
+
+#ifdef HAVE_SETRESUID
+    setresuid(getuid(), getuid(),-1);
+#endif
+#ifdef HAVE_SETREUID
+    setreuid(getuid(), getuid()); /*hlauer: not portable to hpux9.01 */
+#endif
+    
+    execvp(argv[0], argv);
+}
+
+static const struct poptOption * findOption(const struct poptOption * table,
+                                           const char * longName,
+                                           const char shortName,
+                                           poptCallbackType * callback,
+                                           void ** callbackData,
+                                           int singleDash) {
+    const struct poptOption * opt = table;
+    const struct poptOption * opt2;
+    const struct poptOption * cb = NULL;
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           opt2 = findOption(opt->arg, longName, shortName, callback, 
+                             callbackData, singleDash);
+           if (opt2) {
+               if (*callback && !*callbackData)
+                   *callbackData = opt->descrip;
+               return opt2;
+           }
+       } else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_CALLBACK) {
+           cb = opt;
+       } else if (longName && opt->longName && 
+                  (!singleDash || (opt->argInfo & POPT_ARGFLAG_ONEDASH)) &&
+                  !strcmp(longName, opt->longName)) {
+           break;
+       } else if (shortName && shortName == opt->shortName) {
+           break;
+       }
+       opt++;
+    }
+
+    if (!opt->longName && !opt->shortName) return NULL;
+    *callbackData = NULL;
+    *callback = NULL;
+    if (cb) {
+       *callback = cb->arg;
+       if (!(cb->argInfo & POPT_CBFLAG_INC_DATA))
+           *callbackData = cb->descrip;
+    }
+
+    return opt;
+}
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con) {
+    char * optString, * chptr, * localOptString;
+    char * longArg = NULL;
+    char * origOptString;
+    long aLong;
+    char * end;
+    const struct poptOption * opt = NULL;
+    int done = 0;
+    int i;
+    poptCallbackType cb;
+    void * cbData;
+    int singleDash;
+
+    while (!done) {
+       while (!con->os->nextCharArg && con->os->next == con->os->argc 
+               && con->os > con->optionStack)
+           con->os--;
+       if (!con->os->nextCharArg && con->os->next == con->os->argc) {
+           invokeCallbacks(con, con->options, 1);
+           if (con->doExec) execCommand(con);
+           return -1;
+       }
+
+       if (!con->os->nextCharArg) {
+               
+           origOptString = con->os->argv[con->os->next++];
+
+           if (con->restLeftover || *origOptString != '-') {
+               con->leftovers[con->numLeftovers++] = origOptString;
+               if (con->flags & POPT_CONTEXT_POSIXMEHARDER)
+                   con->restLeftover = 1;
+               continue;
+           }
+
+           /* Make a copy we can hack at */
+           localOptString = optString = 
+                       strcpy(alloca(strlen(origOptString) + 1), 
+                       origOptString);
+
+           if (!optString[0])
+               return POPT_ERROR_BADOPT;
+
+           if (optString[1] == '-' && !optString[2]) {
+               con->restLeftover = 1;
+               continue;
+           } else {
+               optString++;
+               if (*optString == '-')
+                   singleDash = 0, optString++;
+               else
+                   singleDash = 1;
+
+               if (handleAlias(con, optString, '\0', NULL))
+                   continue;
+               if (handleExec(con, optString, '\0'))
+                   continue;
+
+               chptr = optString;
+               while (*chptr && *chptr != '=') chptr++;
+               if (*chptr == '=') {
+                   longArg = origOptString + (chptr - localOptString) + 1;
+                   *chptr = '\0';
+               }
+
+               opt = findOption(con->options, optString, '\0', &cb, &cbData,
+                                singleDash);
+               if (!opt && !singleDash) return POPT_ERROR_BADOPT;
+           }
+
+           if (!opt)
+               con->os->nextCharArg = origOptString + 1;
+       }
+
+       if (con->os->nextCharArg) {
+           origOptString = con->os->nextCharArg;
+
+           con->os->nextCharArg = NULL;
+
+           if (handleAlias(con, NULL, *origOptString,
+                           origOptString + 1)) {
+               origOptString++;
+               continue;
+           }
+           if (handleExec(con, NULL, *origOptString))
+               continue;
+
+           opt = findOption(con->options, NULL, *origOptString, &cb, 
+                            &cbData, 0);
+           if (!opt) return POPT_ERROR_BADOPT;
+
+           origOptString++;
+           if (*origOptString)
+               con->os->nextCharArg = origOptString;
+       }
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) == POPT_ARG_NONE) 
+           *((int *)opt->arg) = 1;
+       else if ((opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) {
+           if (longArg) {
+               con->os->nextArg = longArg;
+           } else if (con->os->nextCharArg) {
+               con->os->nextArg = con->os->nextCharArg;
+               con->os->nextCharArg = NULL;
+           } else { 
+               while (con->os->next == con->os->argc && 
+                      con->os > con->optionStack)
+                   con->os--;
+               if (con->os->next == con->os->argc)
+                   return POPT_ERROR_NOARG;
+
+               con->os->nextArg = con->os->argv[con->os->next++];
+           }
+
+           if (opt->arg) {
+               switch (opt->argInfo & POPT_ARG_MASK) {
+                 case POPT_ARG_STRING:
+                   *((char **) opt->arg) = con->os->nextArg;
+                   break;
+
+                 case POPT_ARG_INT:
+                 case POPT_ARG_LONG:
+                   aLong = strtol(con->os->nextArg, &end, 0);
+                   if (*end) 
+                       return POPT_ERROR_BADNUMBER;
+
+                   if (aLong == LONG_MIN || aLong == LONG_MAX)
+                       return POPT_ERROR_OVERFLOW;
+                   if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
+                       *((long *) opt->arg) = aLong;
+                   } else {
+                       if (aLong > INT_MAX || aLong < INT_MIN)
+                           return POPT_ERROR_OVERFLOW;
+                       *((int *) opt->arg) =aLong;
+                   }
+                   break;
+
+                 default:
+                   fprintf(stdout, POPT_("option type (%d) not implemented in popt\n"),
+                     opt->argInfo & POPT_ARG_MASK);
+                   exit(1);
+               }
+           }
+       }
+
+       if (cb)
+           cb(con, POPT_CALLBACK_REASON_OPTION, opt, con->os->nextArg, cbData);
+       else if (opt->val) 
+           done = 1;
+
+       if ((con->finalArgvCount + 2) >= (con->finalArgvAlloced)) {
+           con->finalArgvAlloced += 10;
+           con->finalArgv = realloc(con->finalArgv,
+                           sizeof(*con->finalArgv) * con->finalArgvAlloced);
+       }
+
+       i = con->finalArgvCount++;
+       con->finalArgv[i] = 
+               malloc((opt->longName ? strlen(opt->longName) : 0) + 3);
+       if (opt->longName)
+           sprintf(con->finalArgv[i], "--%s", opt->longName);
+       else 
+           sprintf(con->finalArgv[i], "-%c", opt->shortName);
+
+       if (opt->arg && (opt->argInfo & POPT_ARG_MASK) != POPT_ARG_NONE) 
+           con->finalArgv[con->finalArgvCount++] = strdup(con->os->nextArg);
+    }
+
+    return opt->val;
+}
+
+char * poptGetOptArg(poptContext con) {
+    char * ret = con->os->nextArg;
+    con->os->nextArg = NULL;
+    return ret;
+}
+
+char * poptGetArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return (con->leftovers[con->nextLeftover++]);
+}
+
+char * poptPeekArg(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+    return (con->leftovers[con->nextLeftover]);
+}
+
+char ** poptGetArgs(poptContext con) {
+    if (con->numLeftovers == con->nextLeftover) return NULL;
+
+    /* some apps like [like RPM ;-) ] need this NULL terminated */
+    con->leftovers[con->numLeftovers] = NULL;
+
+    return (con->leftovers + con->nextLeftover);
+}
+
+void poptFreeContext(poptContext con) {
+    int i;
+
+    for (i = 0; i < con->numAliases; i++) {
+       if (con->aliases[i].longName) free(con->aliases[i].longName);
+       free(con->aliases[i].argv);
+    }
+
+    for (i = 0; i < con->numExecs; i++) {
+       if (con->execs[i].longName) free(con->execs[i].longName);
+       free(con->execs[i].script);
+    }
+
+    for (i = 0; i < con->finalArgvCount; i++)
+       free(con->finalArgv[i]);
+
+    free(con->leftovers);
+    free(con->finalArgv);
+    if (con->appName) free(con->appName);
+    if (con->aliases) free(con->aliases);
+    if (con->otherHelp) free(con->otherHelp);
+    free(con);
+}
+
+int poptAddAlias(poptContext con, struct poptAlias newAlias, int flags) {
+    int aliasNum = con->numAliases++;
+    struct poptAlias * alias;
+
+    /* SunOS won't realloc(NULL, ...) */
+    if (!con->aliases)
+       con->aliases = malloc(sizeof(newAlias) * con->numAliases);
+    else
+       con->aliases = realloc(con->aliases, 
+                              sizeof(newAlias) * con->numAliases);
+    alias = con->aliases + aliasNum;
+    
+    *alias = newAlias;
+    if (alias->longName)
+       alias->longName = strcpy(malloc(strlen(alias->longName) + 1), 
+                                   alias->longName);
+    else
+       alias->longName = NULL;
+
+    return 0;
+}
+
+char * poptBadOption(poptContext con, int flags) {
+    struct optionStackEntry * os;
+
+    if (flags & POPT_BADOPTION_NOALIAS)
+       os = con->optionStack;
+    else
+       os = con->os;
+
+    return os->argv[os->next - 1];
+}
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+
+const char * poptStrerror(const int error) {
+    switch (error) {
+      case POPT_ERROR_NOARG:
+       return POPT_("missing argument");
+      case POPT_ERROR_BADOPT:
+       return POPT_("unknown option");
+      case POPT_ERROR_OPTSTOODEEP:
+       return POPT_("aliases nested too deeply");
+      case POPT_ERROR_BADQUOTE:
+       return POPT_("error in paramter quoting");
+      case POPT_ERROR_BADNUMBER:
+       return POPT_("invalid numeric value");
+      case POPT_ERROR_OVERFLOW:
+       return POPT_("number too large or too small");
+      case POPT_ERROR_ERRNO:
+       return strerror(errno);
+      default:
+       return POPT_("unknown error");
+    }
+}
+
+int poptStuffArgs(poptContext con, char ** argv) {
+    int i;
+
+    if ((con->os - con->optionStack) == POPT_OPTION_DEPTH)
+       return POPT_ERROR_OPTSTOODEEP;
+
+    for (i = 0; argv[i]; i++);
+
+    con->os++;
+    con->os->next = 0;
+    con->os->nextArg = con->os->nextCharArg = NULL;
+    con->os->currAlias = NULL;
+    con->os->argc = i;
+    con->os->argv = argv;
+    con->os->stuffed = 1;
+
+    return 0;
+}
+
+const char * poptGetInvocationName(poptContext con) {
+    return con->os->argv[0];
+}
diff --git a/popt.h b/popt.h
new file mode 100644 (file)
index 0000000..9bac797
--- /dev/null
+++ b/popt.h
@@ -0,0 +1,110 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPT
+#define H_POPT
+
+#include <stdio.h>                     /* for FILE * */
+
+#define POPT_OPTION_DEPTH      10
+
+#define POPT_ARG_NONE          0
+#define POPT_ARG_STRING                1
+#define POPT_ARG_INT           2
+#define POPT_ARG_LONG          3
+#define POPT_ARG_INCLUDE_TABLE 4       /* arg points to table */
+#define POPT_ARG_CALLBACK      5       /* table-wide callback... must be
+                                          set first in table; arg points 
+                                          to callback, descrip points to 
+                                          callback data to pass */
+#define POPT_ARG_MASK          0x0000FFFF
+#define POPT_ARGFLAG_ONEDASH   0x80000000  /* allow -longoption */
+#define POPT_ARGFLAG_DOC_HIDDEN 0x40000000  /* don't show in help/usage */
+#define POPT_CBFLAG_PRE                0x80000000  /* call the callback before parse */
+#define POPT_CBFLAG_POST       0x40000000  /* call the callback after parse */
+#define POPT_CBFLAG_INC_DATA   0x20000000  /* use data from the include line,
+                                              not the subtable */
+
+#define POPT_ERROR_NOARG       -10
+#define POPT_ERROR_BADOPT      -11
+#define POPT_ERROR_OPTSTOODEEP -13
+#define POPT_ERROR_BADQUOTE    -15     /* only from poptParseArgString() */
+#define POPT_ERROR_ERRNO       -16     /* only from poptParseArgString() */
+#define POPT_ERROR_BADNUMBER   -17
+#define POPT_ERROR_OVERFLOW    -18
+
+/* poptBadOption() flags */
+#define POPT_BADOPTION_NOALIAS  (1 << 0)  /* don't go into an alias */
+
+/* poptGetContext() flags */
+#define POPT_CONTEXT_NO_EXEC   (1 << 0)  /* ignore exec expansions */
+#define POPT_CONTEXT_KEEP_FIRST        (1 << 1)  /* pay attention to argv[0] */
+#define POPT_CONTEXT_POSIXMEHARDER (1 << 2) /* options can't follow args */
+
+struct poptOption {
+    const char * longName;     /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argInfo;
+    void * arg;                        /* depends on argInfo */
+    int val;                   /* 0 means don't return, just update flag */
+    char * descrip;            /* description for autohelp -- may be NULL */
+    char * argDescrip;         /* argument description for autohelp */
+};
+
+struct poptAlias {
+    char * longName;           /* may be NULL */
+    char shortName;            /* may be '\0' */
+    int argc;
+    char ** argv;              /* must be free()able */
+};
+
+extern struct poptOption poptHelpOptions[];
+#define POPT_AUTOHELP { NULL, '\0', POPT_ARG_INCLUDE_TABLE, poptHelpOptions, \
+                       0, "Help options", NULL },
+
+typedef struct poptContext_s * poptContext;
+#ifndef __cplusplus
+typedef struct poptOption * poptOption;
+#endif
+
+enum poptCallbackReason { POPT_CALLBACK_REASON_PRE, 
+                         POPT_CALLBACK_REASON_POST,
+                         POPT_CALLBACK_REASON_OPTION };
+typedef void (*poptCallbackType)(poptContext con, 
+                                enum poptCallbackReason reason,
+                                const struct poptOption * opt,
+                                const char * arg, void * data);
+
+poptContext poptGetContext(char * name, int argc, char ** argv, 
+                          const struct poptOption * options, int flags);
+void poptResetContext(poptContext con);
+
+/* returns 'val' element, -1 on last item, POPT_ERROR_* on error */
+int poptGetNextOpt(poptContext con);
+/* returns NULL if no argument is available */
+char * poptGetOptArg(poptContext con);
+/* returns NULL if no more options are available */
+char * poptGetArg(poptContext con);
+char * poptPeekArg(poptContext con);
+char ** poptGetArgs(poptContext con);
+/* returns the option which caused the most recent error */
+char * poptBadOption(poptContext con, int flags);
+void poptFreeContext(poptContext con);
+int poptStuffArgs(poptContext con, char ** argv);
+int poptAddAlias(poptContext con, struct poptAlias alias, int flags);
+int poptReadConfigFile(poptContext con, char * fn);
+/* like above, but reads /etc/popt and $HOME/.popt along with environment 
+   vars */
+int poptReadDefaultConfig(poptContext con, int useEnv);
+/* argv should be freed -- this allows ', ", and \ quoting, but ' is treated
+   the same as " and both may include \ quotes */
+int poptParseArgvString(char * s, int * argcPtr, char *** argvPtr);
+const char * poptStrerror(const int error);
+void poptSetExecPath(poptContext con, const char * path, int allowAbsolute);
+void poptPrintHelp(poptContext con, FILE * f, int flags);
+void poptPrintUsage(poptContext con, FILE * f, int flags);
+void poptSetOtherOptionHelp(poptContext con, const char * text);
+const char * poptGetInvocationName(poptContext con);
+
+#endif
diff --git a/poptconfig.c b/poptconfig.c
new file mode 100644 (file)
index 0000000..a09a249
--- /dev/null
@@ -0,0 +1,155 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "popt.h"
+#include "poptint.h"
+
+static void configLine(poptContext con, char * line) {
+    int nameLength = strlen(con->appName);
+    char * opt;
+    struct poptAlias alias;
+    char * entryType;
+    char * longName = NULL;
+    char shortName = '\0';
+    
+    if (strncmp(line, con->appName, nameLength)) return;
+    line += nameLength;
+    if (!*line || !isspace(*line)) return;
+    while (*line && isspace(*line)) line++;
+    entryType = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+    opt = line;
+
+    while (!*line || !isspace(*line)) line++;
+    *line++ = '\0';
+    while (*line && isspace(*line)) line++;
+    if (!*line) return;
+
+    if (opt[0] == '-' && opt[1] == '-')
+       longName = opt + 2;
+    else if (opt[0] == '-' && !opt[2])
+       shortName = opt[1];
+
+    if (!strcmp(entryType, "alias")) {
+       if (poptParseArgvString(line, &alias.argc, &alias.argv)) return;
+       alias.longName = longName, alias.shortName = shortName;
+       poptAddAlias(con, alias, 0);
+    } else if (!strcmp(entryType, "exec")) {
+       con->execs = realloc(con->execs, 
+                               sizeof(*con->execs) * (con->numExecs + 1));
+       if (longName)
+           con->execs[con->numExecs].longName = strdup(longName);
+       else
+           con->execs[con->numExecs].longName = NULL;
+
+       con->execs[con->numExecs].shortName = shortName;
+       con->execs[con->numExecs].script = strdup(line);
+       
+       con->numExecs++;
+    }
+}
+
+int poptReadConfigFile(poptContext con, char * fn) {
+    char * file, * chptr, * end;
+    char * buf, * dst;
+    int fd, rc;
+    int fileLength;
+
+    fd = open(fn, O_RDONLY);
+    if (fd < 0) {
+       if (errno == ENOENT)
+           return 0;
+       else 
+           return POPT_ERROR_ERRNO;
+    }
+
+    fileLength = lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, 0);
+
+    file = alloca(fileLength + 1);
+    if ((fd = read(fd, file, fileLength)) != fileLength) {
+       rc = errno;
+       close(fd);
+       errno = rc;
+       return POPT_ERROR_ERRNO;
+    }
+    close(fd);
+
+    dst = buf = alloca(fileLength + 1);
+
+    chptr = file;
+    end = (file + fileLength);
+    while (chptr < end) {
+       switch (*chptr) {
+         case '\n':
+           *dst = '\0';
+           dst = buf;
+           while (*dst && isspace(*dst)) dst++;
+           if (*dst && *dst != '#') {
+               configLine(con, dst);
+           }
+           chptr++;
+           break;
+         case '\\':
+           *dst++ = *chptr++;
+           if (chptr < end) {
+               if (*chptr == '\n') 
+                   dst--, chptr++;     
+                   /* \ at the end of a line does not insert a \n */
+               else
+                   *dst++ = *chptr++;
+           }
+           break;
+         default:
+           *dst++ = *chptr++;
+       }
+    }
+
+    return 0;
+}
+
+int poptReadDefaultConfig(poptContext con, int useEnv) {
+    char * fn, * home;
+    int rc;
+
+    if (!con->appName) return 0;
+
+    rc = poptReadConfigFile(con, "/etc/popt");
+    if (rc) return rc;
+    if (getuid() != geteuid()) return 0;
+
+    if ((home = getenv("HOME"))) {
+       fn = alloca(strlen(home) + 20);
+       strcpy(fn, home);
+       strcat(fn, "/.popt");
+       rc = poptReadConfigFile(con, fn);
+       if (rc) return rc;
+    }
+
+    return 0;
+}
+
diff --git a/popthelp.c b/popthelp.c
new file mode 100644 (file)
index 0000000..1878a56
--- /dev/null
@@ -0,0 +1,276 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "popt.h"
+#include "poptint.h"
+
+static void displayArgs(poptContext con, enum poptCallbackReason foo, 
+                       struct poptOption * key, 
+                       const char * arg, void * data) {
+    if (key->shortName== '?')
+       poptPrintHelp(con, stderr, 0);
+    else
+       poptPrintUsage(con, stderr, 0);
+    exit(0);
+}
+
+struct poptOption poptHelpOptions[] = {
+    { NULL, '\0', POPT_ARG_CALLBACK, &displayArgs, '\0', NULL },
+    { "help", '?', 0, NULL, '?', N_("Show this help message") },
+    { "usage", '\0', 0, NULL, 'u', N_("Display brief usage message") },
+    { NULL, '\0', 0, NULL, 0 }
+} ;
+
+static const char * getArgDescrip(const struct poptOption * opt) {
+    if (!(opt->argInfo & POPT_ARG_MASK)) return NULL;
+
+    if (opt == (poptHelpOptions + 1) || opt == (poptHelpOptions + 2))
+       if (opt->argDescrip) return POPT_(opt->argDescrip);
+
+    if (opt->argDescrip) return _(opt->argDescrip);
+    return POPT_("ARG");
+}
+
+static void singleOptionHelp(FILE * f, int maxLeftCol, 
+                            const struct poptOption * opt) {
+    int indentLength = maxLeftCol + 5;
+    int lineLength = 79 - indentLength;
+    const char * help = _(opt->descrip);
+    int helpLength;
+    const char * ch;
+    char format[10];
+    char * left = alloca(maxLeftCol + 1);
+    const char * argDescrip = getArgDescrip(opt);
+
+    *left = '\0';
+    if (opt->longName && opt->shortName)
+       sprintf(left, "-%c, --%s", opt->shortName, opt->longName);
+    else if (opt->shortName) 
+       sprintf(left, "-%c", opt->shortName);
+    else if (opt->longName)
+       sprintf(left, "--%s", opt->longName);
+    if (!*left) return ;
+    if (argDescrip) {
+       strcat(left, "=");
+       strcat(left, argDescrip);
+    }
+
+    if (help)
+       fprintf(f,"  %-*s   ", maxLeftCol, left);
+    else {
+       fprintf(f,"  %s\n", left); 
+       return;
+    }
+
+    helpLength = strlen(help);
+    while (helpLength > lineLength) {
+       ch = help + lineLength - 1;
+       while (ch > help && !isspace(*ch)) ch--;
+       if (ch == help) break;          /* give up */
+       while (ch > (help + 1) && isspace(*ch)) ch--;
+       ch++;
+
+       sprintf(format, "%%.%ds\n%%%ds", (int) (ch - help), indentLength);
+       fprintf(f, format, help, " ");
+       help = ch;
+       while (isspace(*help) && *help) help++;
+       helpLength = strlen(help);
+    }
+
+    if (helpLength) fprintf(f, "%s\n", help);
+}
+
+static int maxArgWidth(const struct poptOption * opt) {
+    int max = 0;
+    int this;
+    const char * s;
+    
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           this = maxArgWidth(opt->arg);
+           if (this > max) max = this;
+       } else if (!(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN)) {
+           this = opt->shortName ? 2 : 0;
+           if (opt->longName) {
+               if (this) this += 2;
+               this += strlen(opt->longName) + 2;
+           }
+
+           s = getArgDescrip(opt);
+           if (s)
+               this += strlen(s) + 1;
+           if (this > max) max = this;
+       }
+
+       opt++;
+    }
+    
+    return max;
+}
+
+static void singleTableHelp(FILE * f, const struct poptOption * table, 
+                           int left) {
+    const struct poptOption * opt;
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->longName || opt->shortName) && 
+           !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+           singleOptionHelp(f, left, opt);
+       opt++;
+    }
+
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) {
+           if (opt->descrip)
+               fprintf(f, "\n%s\n", _(opt->descrip));
+           singleTableHelp(f, opt->arg, left);
+       }
+       opt++;
+    }
+}
+
+static int showHelpIntro(poptContext con, FILE * f) {
+    int len = 6;
+    char * fn;
+
+    fprintf(f, POPT_("Usage:"));
+    if (!(con->flags & POPT_CONTEXT_KEEP_FIRST)) {
+       fn = con->optionStack->argv[0];
+       if (strchr(fn, '/')) fn = strchr(fn, '/') + 1;
+       fprintf(f, " %s", fn);
+       len += strlen(fn) + 1;
+    }
+
+    return len;
+}
+
+void poptPrintHelp(poptContext con, FILE * f, int flags) {
+    int leftColWidth;
+
+    showHelpIntro(con, f);
+    if (con->otherHelp)
+       fprintf(f, " %s\n", con->otherHelp);
+    else
+       fprintf(f, " %s\n", POPT_("[OPTION...]"));
+
+    leftColWidth = maxArgWidth(con->options);
+    singleTableHelp(f, con->options, leftColWidth);
+}
+
+static int singleOptionUsage(FILE * f, int cursor, 
+                             const struct poptOption * opt) {
+    int len = 3;
+    char shortStr[2];
+    const char * item = shortStr;
+    const char * argDescrip = getArgDescrip(opt);
+
+    if (opt->shortName) {
+       if (!(opt->argInfo & POPT_ARG_MASK)) 
+           return cursor;      /* we did these already */
+       len++;
+       *shortStr = opt->shortName;
+       shortStr[1] = '\0';
+    } else if (opt->longName) {
+       len += 1 + strlen(opt->longName);
+       item = opt->longName;
+    }
+
+    if (len == 3) return cursor;
+
+    if (argDescrip) 
+       len += strlen(argDescrip) + 1;
+
+    if ((cursor + len) > 79) {
+       fprintf(f, "\n       ");
+       cursor = 7;
+    } 
+
+    fprintf(f, " [-%s%s%s%s]", opt->shortName ? "" : "-", item,
+           argDescrip ? (opt->shortName ? " " : "=") : "",
+           argDescrip ? argDescrip : "");
+
+    return cursor + len + 1;
+}
+
+int singleTableUsage(FILE * f, int cursor, const struct poptOption * table) {
+    const struct poptOption * opt;
+    
+    opt = table;
+    while (opt->longName || opt->shortName || opt->arg) {
+       if ((opt->longName || opt->shortName) && 
+           !(opt->argInfo & POPT_ARGFLAG_DOC_HIDDEN))
+           cursor = singleOptionUsage(f, cursor, opt);
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE) 
+           cursor = singleTableUsage(f, cursor, opt->arg);
+       opt++;
+    }
+
+    return cursor;
+}
+
+static int showShortOptions(const struct poptOption * opt, FILE * f, 
+                           char * str) {
+    char s[300];               /* this is larger then the ascii set, so
+                                  it should do just fine */
+
+    if (!str) {
+       str = s;
+       memset(str, 0, sizeof(str));
+    }
+
+    while (opt->longName || opt->shortName || opt->arg) {
+       if (opt->shortName && !(opt->argInfo & POPT_ARG_MASK))
+           str[strlen(str)] = opt->shortName;
+       else if ((opt->argInfo & POPT_ARG_MASK) == POPT_ARG_INCLUDE_TABLE)
+           showShortOptions(opt->arg, f, str);
+
+       opt++;
+    } 
+
+    if (s != str || !*s)
+       return 0;
+
+    fprintf(f, " [-%s]", s);
+    return strlen(s) + 4;
+}
+
+void poptPrintUsage(poptContext con, FILE * f, int flags) {
+    int cursor;
+
+    cursor = showHelpIntro(con, f);
+    cursor += showShortOptions(con->options, f, NULL);
+    singleTableUsage(f, cursor, con->options);
+
+    if (con->otherHelp) {
+       cursor += strlen(con->otherHelp) + 1;
+       if (cursor > 79) fprintf(f, "\n       ");
+       fprintf(f, " %s", con->otherHelp);
+    }
+
+    fprintf(f, "\n");
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char * text) {
+    if (con->otherHelp) free(con->otherHelp);
+    con->otherHelp = strdup(text);
+}
diff --git a/poptint.h b/poptint.h
new file mode 100644 (file)
index 0000000..5d99be5
--- /dev/null
+++ b/poptint.h
@@ -0,0 +1,64 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifndef H_POPTINT
+#define H_POPTINT
+
+struct optionStackEntry {
+    int argc;
+    char ** argv;
+    int next;
+    char * nextArg;
+    char * nextCharArg;
+    struct poptAlias * currAlias;
+    int stuffed;
+};
+
+struct execEntry {
+    char * longName;
+    char shortName;
+    char * script;
+};
+
+struct poptContext_s {
+    struct optionStackEntry optionStack[POPT_OPTION_DEPTH], * os;
+    char ** leftovers;
+    int numLeftovers;
+    int nextLeftover;
+    const struct poptOption * options;
+    int restLeftover;
+    char * appName;
+    struct poptAlias * aliases;
+    int numAliases;
+    int flags;
+    struct execEntry * execs;
+    int numExecs;
+    char ** finalArgv;
+    int finalArgvCount;
+    int finalArgvAlloced;
+    struct execEntry * doExec;
+    char * execPath;
+    int execAbsolute;
+    char * otherHelp;
+};
+
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#ifdef HAVE_GETTEXT
+#define _(foo) gettext(foo)
+#else
+#define _(foo) (foo)
+#endif
+
+#ifdef HAVE_DGETTEXT
+#define POPT_(foo) dgettext("popt", foo)
+#else
+#define POPT_(foo) (foo)
+#endif
+
+#define N_(foo) (foo)
+
+#endif
diff --git a/poptparse.c b/poptparse.c
new file mode 100644 (file)
index 0000000..a0f95c1
--- /dev/null
@@ -0,0 +1,101 @@
+/* (C) 1998 Red Hat Software, Inc. -- Licensing details are in the COPYING
+   file accompanying popt source distributions, available from 
+   ftp://ftp.redhat.com/pub/code/popt */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#else
+# ifdef _AIX
+#  pragma alloca
+# endif
+#endif
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "popt.h"
+
+int poptParseArgvString(char * s, int * argcPtr, char *** argvPtr) {
+    char * buf = strcpy(alloca(strlen(s) + 1), s);
+    char * bufStart = buf;
+    char * src, * dst;
+    char quote = '\0';
+    int argvAlloced = 5;
+    char ** argv = malloc(sizeof(*argv) * argvAlloced);
+    char ** argv2;
+    int argc = 0;
+    int i;
+
+    src = s;
+    dst = buf;
+    argv[argc] = buf;
+
+    memset(buf, '\0', strlen(s) + 1);
+
+    while (*src) {
+       if (quote == *src) {
+           quote = '\0';
+       } else if (quote) {
+           if (*src == '\\') {
+               src++;
+               if (!*src) {
+                   free(argv);
+                   return POPT_ERROR_BADQUOTE;
+               }
+               if (*src != quote) *buf++ = '\\';
+           }
+           *buf++ = *src;
+       } else if (isspace(*src)) {
+           if (*argv[argc]) {
+               buf++, argc++;
+               if (argc == argvAlloced) {
+                   argvAlloced += 5;
+                   argv = realloc(argv, sizeof(*argv) * argvAlloced);
+               }
+               argv[argc] = buf;
+           }
+       } else switch (*src) {
+         case '"':
+         case '\'':
+           quote = *src;
+           break;
+         case '\\':
+           src++;
+           if (!*src) {
+               free(argv);
+               return POPT_ERROR_BADQUOTE;
+           }
+           /* fallthrough */
+         default:
+           *buf++ = *src;
+       }
+
+       src++;
+    }
+
+    if (strlen(argv[argc])) {
+       argc++, buf++;
+    }
+
+    dst = malloc(argc * sizeof(*argv) + (buf - bufStart));
+    argv2 = (void *) dst;
+    dst += argc * sizeof(*argv);
+    memcpy(argv2, argv, argc * sizeof(*argv));
+    memcpy(dst, bufStart, buf - bufStart);
+
+    for (i = 0; i < argc; i++) {
+       argv2[i] = dst + (argv[i] - bufStart);
+    }
+
+    free(argv);
+
+    *argvPtr = argv2;
+    *argcPtr = argc;
+
+    return 0;
+}