Blame pkg.c

Packit db3073
/* 
Packit db3073
 * Copyright (C) 2001, 2002 Red Hat Inc.
Packit db3073
 * 
Packit db3073
 * This program is free software; you can redistribute it and/or
Packit db3073
 * modify it under the terms of the GNU General Public License as
Packit db3073
 * published by the Free Software Foundation; either version 2 of the
Packit db3073
 * License, or (at your option) any later version.
Packit db3073
 *
Packit db3073
 * This program is distributed in the hope that it will be useful, but
Packit db3073
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit db3073
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit db3073
 * General Public License for more details.
Packit db3073
 * 
Packit db3073
 * You should have received a copy of the GNU General Public License
Packit db3073
 * along with this program; if not, write to the Free Software
Packit db3073
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
Packit db3073
 * 02111-1307, USA.
Packit db3073
 */
Packit db3073
Packit db3073
Packit db3073
#ifdef HAVE_CONFIG_H
Packit db3073
#include "config.h"
Packit db3073
#endif
Packit db3073
Packit db3073
#include "pkg.h"
Packit db3073
#include "parse.h"
Packit db3073
Packit db3073
#ifdef HAVE_MALLOC_H
Packit db3073
# include <malloc.h>
Packit db3073
#endif
Packit db3073
Packit db3073
#include <sys/types.h>
Packit db3073
#include <dirent.h>
Packit db3073
#include <string.h>
Packit db3073
#include <errno.h>
Packit db3073
#include <stdio.h>
Packit db3073
#ifdef HAVE_UNISTD_H
Packit db3073
#include <unistd.h>
Packit db3073
#endif
Packit db3073
#include <stdlib.h>
Packit db3073
#include <ctype.h>
Packit db3073
Packit db3073
static void verify_package (Package *pkg);
Packit db3073
Packit db3073
static GHashTable *packages = NULL;
Packit db3073
static GHashTable *locations = NULL;
Packit db3073
static GHashTable *path_positions = NULL;
Packit db3073
static GHashTable *globals = NULL;
Packit db3073
static GList *search_dirs = NULL;
Packit db3073
static int scanned_dir_count = 0;
Packit db3073
Packit db3073
gboolean disable_uninstalled = FALSE;
Packit db3073
gboolean ignore_requires = FALSE;
Packit db3073
gboolean ignore_requires_private = TRUE;
Packit db3073
gboolean ignore_private_libs = TRUE;
Packit db3073
Packit db3073
void
Packit db3073
add_search_dir (const char *path)
Packit db3073
{
Packit db3073
  search_dirs = g_list_append (search_dirs, g_strdup (path));
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
add_search_dirs (const char *path, const char *separator)
Packit db3073
{
Packit db3073
      char **search_dirs;
Packit db3073
      char **iter;
Packit db3073
Packit db3073
      search_dirs = g_strsplit (path, separator, -1);
Packit db3073
    
Packit db3073
      iter = search_dirs;
Packit db3073
      while (*iter)
Packit db3073
        {
Packit db3073
          debug_spew ("Adding directory '%s' from PKG_CONFIG_PATH\n",
Packit db3073
                      *iter);
Packit db3073
          add_search_dir (*iter);
Packit db3073
          
Packit db3073
          ++iter;
Packit db3073
        }
Packit db3073
      
Packit db3073
      g_strfreev (search_dirs);
Packit db3073
}
Packit db3073
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
/* Guard against .pc file being installed with UPPER CASE name */
Packit db3073
# define FOLD(x) tolower(x)
Packit db3073
# define FOLDCMP(a, b) g_ascii_strcasecmp (a, b)
Packit db3073
#else
Packit db3073
# define FOLD(x) (x)
Packit db3073
# define FOLDCMP(a, b) strcmp (a, b)
Packit db3073
#endif
Packit db3073
Packit db3073
#define EXT_LEN 3
Packit db3073
Packit db3073
static gboolean
Packit db3073
ends_in_dotpc (const char *str)
Packit db3073
{
Packit db3073
  int len = strlen (str);
Packit db3073
  
Packit db3073
  if (len > EXT_LEN &&
Packit db3073
      str[len - 3] == '.' &&
Packit db3073
      FOLD (str[len - 2]) == 'p' &&
Packit db3073
      FOLD (str[len - 1]) == 'c')
Packit db3073
    return TRUE;
Packit db3073
  else
Packit db3073
    return FALSE;
Packit db3073
}
Packit db3073
Packit db3073
/* strlen ("-uninstalled") */
Packit db3073
#define UNINSTALLED_LEN 12
Packit db3073
Packit db3073
gboolean
Packit db3073
name_ends_in_uninstalled (const char *str)
Packit db3073
{
Packit db3073
  int len = strlen (str);
Packit db3073
  
Packit db3073
  if (len > UNINSTALLED_LEN &&
Packit db3073
      FOLDCMP ((str + len - UNINSTALLED_LEN), "-uninstalled") == 0)
Packit db3073
    return TRUE;
Packit db3073
  else
Packit db3073
    return FALSE;
Packit db3073
}
Packit db3073
Packit db3073
Packit db3073
/* Look for .pc files in the given directory and add them into
Packit db3073
 * locations, ignoring duplicates
Packit db3073
 */
Packit db3073
static void
Packit db3073
scan_dir (const char *dirname)
Packit db3073
{
Packit db3073
  DIR *dir;
Packit db3073
  struct dirent *dent;
Packit db3073
  int dirnamelen = strlen (dirname);
Packit db3073
  /* Use a copy of dirname cause Win32 opendir doesn't like
Packit db3073
   * superfluous trailing (back)slashes in the directory name.
Packit db3073
   */
Packit db3073
  char *dirname_copy = g_strdup (dirname);
Packit db3073
Packit db3073
  if (dirnamelen > 1 && dirname[dirnamelen-1] == G_DIR_SEPARATOR)
Packit db3073
    {
Packit db3073
      dirnamelen--;
Packit db3073
      dirname_copy[dirnamelen] = '\0';
Packit db3073
    }
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
    {
Packit db3073
      gchar *p;
Packit db3073
      /* Turn backslashes into slashes or
Packit db3073
       * g_shell_parse_argv() will eat them when ${prefix}
Packit db3073
       * has been expanded in parse_libs().
Packit db3073
       */
Packit db3073
      p = dirname;
Packit db3073
      while (*p)
Packit db3073
        {
Packit db3073
          if (*p == '\\')
Packit db3073
            *p = '/';
Packit db3073
          p++;
Packit db3073
        }
Packit db3073
    }
Packit db3073
#endif
Packit db3073
  dir = opendir (dirname_copy);
Packit db3073
  g_free (dirname_copy);
Packit db3073
  if (!dir)
Packit db3073
    {
Packit db3073
      debug_spew ("Cannot open directory '%s' in package search path: %s\n",
Packit db3073
                  dirname, g_strerror (errno));
Packit db3073
      return;
Packit db3073
    }
Packit db3073
Packit db3073
  debug_spew ("Scanning directory '%s'\n", dirname);
Packit db3073
Packit db3073
  scanned_dir_count += 1;
Packit db3073
  
Packit db3073
  while ((dent = readdir (dir)))
Packit db3073
    {
Packit db3073
      int len = strlen (dent->d_name);
Packit db3073
Packit db3073
      if (ends_in_dotpc (dent->d_name))
Packit db3073
        {
Packit db3073
          char *pkgname = g_malloc (len - 2);
Packit db3073
Packit db3073
          debug_spew ("File '%s' appears to be a .pc file\n", dent->d_name);
Packit db3073
          
Packit db3073
	  strncpy (pkgname, dent->d_name, len - EXT_LEN);
Packit db3073
          pkgname[len-EXT_LEN] = '\0';
Packit db3073
Packit db3073
          if (g_hash_table_lookup (locations, pkgname))
Packit db3073
            {
Packit db3073
              debug_spew ("File '%s' ignored, we already know about package '%s'\n", dent->d_name, pkgname);
Packit db3073
              g_free (pkgname);
Packit db3073
            }
Packit db3073
          else
Packit db3073
            {
Packit db3073
              char *filename = g_malloc (dirnamelen + 1 + len + 1);
Packit db3073
              strncpy (filename, dirname, dirnamelen);
Packit db3073
              filename[dirnamelen] = G_DIR_SEPARATOR;
Packit db3073
              strcpy (filename + dirnamelen + 1, dent->d_name);
Packit db3073
              
Packit db3073
	      if (g_file_test(filename, G_FILE_TEST_IS_REGULAR) == TRUE) {
Packit db3073
		  g_hash_table_insert (locations, pkgname, filename);
Packit db3073
		  g_hash_table_insert (path_positions, pkgname,
Packit db3073
				       GINT_TO_POINTER (scanned_dir_count));
Packit db3073
		  debug_spew ("Will find package '%s' in file '%s'\n",
Packit db3073
			      pkgname, filename);
Packit db3073
	      } else {
Packit db3073
		  debug_spew ("Ignoring '%s' while looking for '%s'; not a "
Packit db3073
			      "regular file.\n", pkgname, filename);
Packit db3073
	      }
Packit db3073
	    }
Packit db3073
        }
Packit db3073
      else
Packit db3073
        {
Packit db3073
          debug_spew ("Ignoring file '%s' in search directory; not a .pc file\n",
Packit db3073
                      dent->d_name);
Packit db3073
        }
Packit db3073
    }
Packit db3073
  closedir(dir);
Packit db3073
}
Packit db3073
Packit db3073
static Package *
Packit db3073
add_virtual_pkgconfig_package (void)
Packit db3073
{
Packit db3073
  Package *pkg = NULL;
Packit db3073
Packit db3073
  pkg = g_new0 (Package, 1);
Packit db3073
Packit db3073
  pkg->key = g_strdup ("pkg-config");
Packit db3073
  pkg->version = g_strdup (VERSION);
Packit db3073
  pkg->name = g_strdup ("pkg-config");
Packit db3073
  pkg->description = g_strdup ("pkg-config is a system for managing "
Packit db3073
			       "compile/link flags for libraries");
Packit db3073
  pkg->url = g_strdup ("http://pkg-config.freedesktop.org/");
Packit db3073
Packit db3073
  if (pkg->vars == NULL)
Packit db3073
    pkg->vars = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
  g_hash_table_insert (pkg->vars, "pc_path", pkg_config_pc_path);
Packit db3073
Packit db3073
  debug_spew ("Adding virtual 'pkg-config' package to list of known packages\n");
Packit db3073
  g_hash_table_insert (packages, pkg->key, pkg);
Packit db3073
Packit db3073
  return pkg;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
package_init ()
Packit db3073
{
Packit db3073
  static gboolean initted = FALSE;
Packit db3073
Packit db3073
  if (!initted)
Packit db3073
    {
Packit db3073
      initted = TRUE;
Packit db3073
      
Packit db3073
      packages = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
      locations = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
      path_positions = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
      
Packit db3073
      add_virtual_pkgconfig_package ();
Packit db3073
Packit db3073
      g_list_foreach (search_dirs, (GFunc)scan_dir, NULL);
Packit db3073
    }
Packit db3073
}
Packit db3073
Packit db3073
static Package *
Packit db3073
internal_get_package (const char *name, gboolean warn)
Packit db3073
{
Packit db3073
  Package *pkg = NULL;
Packit db3073
  const char *location;
Packit db3073
  GList *iter;
Packit db3073
  
Packit db3073
  pkg = g_hash_table_lookup (packages, name);
Packit db3073
Packit db3073
  if (pkg)
Packit db3073
    return pkg;
Packit db3073
Packit db3073
  debug_spew ("Looking for package '%s'\n", name);
Packit db3073
  
Packit db3073
  /* treat "name" as a filename if it ends in .pc and exists */
Packit db3073
  if ( ends_in_dotpc (name) )
Packit db3073
    {
Packit db3073
      debug_spew ("Considering '%s' to be a filename rather than a package name\n", name);
Packit db3073
      location = name;
Packit db3073
    }
Packit db3073
  else
Packit db3073
    {
Packit db3073
      /* See if we should auto-prefer the uninstalled version */
Packit db3073
      if (!disable_uninstalled &&
Packit db3073
          !name_ends_in_uninstalled (name))
Packit db3073
        {
Packit db3073
          char *un;
Packit db3073
Packit db3073
          un = g_strconcat (name, "-uninstalled", NULL);
Packit db3073
Packit db3073
          pkg = internal_get_package (un, FALSE);
Packit db3073
Packit db3073
          g_free (un);
Packit db3073
          
Packit db3073
          if (pkg)
Packit db3073
            {
Packit db3073
              debug_spew ("Preferring uninstalled version of package '%s'\n", name);
Packit db3073
              return pkg;
Packit db3073
            }
Packit db3073
        }
Packit db3073
      
Packit db3073
      location = g_hash_table_lookup (locations, name);
Packit db3073
    }
Packit db3073
  
Packit db3073
  if (location == NULL)
Packit db3073
    {
Packit db3073
      if (warn)
Packit db3073
        verbose_error ("Package %s was not found in the pkg-config search path.\n"
Packit db3073
                       "Perhaps you should add the directory containing `%s.pc'\n"
Packit db3073
                       "to the PKG_CONFIG_PATH environment variable\n",
Packit db3073
                       name, name);
Packit db3073
Packit db3073
      return NULL;
Packit db3073
    }
Packit db3073
Packit db3073
  debug_spew ("Reading '%s' from file '%s'\n", name, location);
Packit db3073
  pkg = parse_package_file (location, ignore_requires, ignore_private_libs, 
Packit db3073
			    ignore_requires_private);
Packit db3073
  
Packit db3073
  if (pkg == NULL)
Packit db3073
    {
Packit db3073
      debug_spew ("Failed to parse '%s'\n", location);
Packit db3073
      return NULL;
Packit db3073
    }
Packit db3073
  
Packit db3073
  if (strstr (location, "uninstalled.pc"))
Packit db3073
    pkg->uninstalled = TRUE;
Packit db3073
  
Packit db3073
  if (location != name)
Packit db3073
    pkg->key = g_strdup (name);
Packit db3073
  else
Packit db3073
    {
Packit db3073
      /* need to strip package name out of the filename */
Packit db3073
      int len = strlen (name);
Packit db3073
      const char *end = name + (len - EXT_LEN);
Packit db3073
      const char *start = end;
Packit db3073
Packit db3073
      while (start != name && *start != G_DIR_SEPARATOR)
Packit db3073
        --start;
Packit db3073
Packit db3073
      g_assert (end >= start);
Packit db3073
      
Packit db3073
      pkg->key = g_strndup (start, end - start);
Packit db3073
    }
Packit db3073
Packit db3073
  pkg->path_position =
Packit db3073
    GPOINTER_TO_INT (g_hash_table_lookup (path_positions, pkg->key));
Packit db3073
Packit db3073
  debug_spew ("Path position of '%s' is %d\n",
Packit db3073
              pkg->key, pkg->path_position);
Packit db3073
  
Packit db3073
  debug_spew ("Adding '%s' to list of known packages\n", pkg->key);
Packit db3073
  g_hash_table_insert (packages, pkg->key, pkg);
Packit db3073
Packit db3073
  /* pull in Requires packages */
Packit db3073
  for (iter = pkg->requires_entries; iter != NULL; iter = g_list_next (iter))
Packit db3073
    {
Packit db3073
      Package *req;
Packit db3073
      RequiredVersion *ver = iter->data;
Packit db3073
Packit db3073
      debug_spew ("Searching for '%s' requirement '%s'\n",
Packit db3073
                  pkg->key, ver->name);
Packit db3073
      req = internal_get_package (ver->name, warn);
Packit db3073
      if (req == NULL)
Packit db3073
        {
Packit db3073
          verbose_error ("Package '%s', required by '%s', not found\n",
Packit db3073
                         ver->name, pkg->key);
Packit db3073
          exit (1);
Packit db3073
        }
Packit db3073
Packit db3073
      if (pkg->required_versions == NULL)
Packit db3073
        pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
Packit db3073
      g_hash_table_insert (pkg->required_versions, ver->name, ver);
Packit db3073
      pkg->requires = g_list_prepend (pkg->requires, req);
Packit db3073
    }
Packit db3073
Packit db3073
  /* pull in Requires.private packages */
Packit db3073
  for (iter = pkg->requires_private_entries; iter != NULL;
Packit db3073
       iter = g_list_next (iter))
Packit db3073
    {
Packit db3073
      Package *req;
Packit db3073
      RequiredVersion *ver = iter->data;
Packit db3073
Packit db3073
      debug_spew ("Searching for '%s' private requirement '%s'\n",
Packit db3073
                  pkg->key, ver->name);
Packit db3073
      req = internal_get_package (ver->name, warn);
Packit db3073
      if (req == NULL)
Packit db3073
        {
Packit db3073
          verbose_error ("Package '%s', required by '%s', not found\n",
Packit db3073
			 ver->name, pkg->key);
Packit db3073
          exit (1);
Packit db3073
        }
Packit db3073
Packit db3073
      if (pkg->required_versions == NULL)
Packit db3073
        pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
Packit db3073
      g_hash_table_insert (pkg->required_versions, ver->name, ver);
Packit db3073
      pkg->requires_private = g_list_prepend (pkg->requires_private, req);
Packit db3073
    }
Packit db3073
Packit db3073
  /* make requires_private include a copy of the public requires too */
Packit db3073
  pkg->requires_private = g_list_concat (g_list_copy (pkg->requires),
Packit db3073
                                         pkg->requires_private);
Packit db3073
Packit db3073
  pkg->requires = g_list_reverse (pkg->requires);
Packit db3073
  pkg->requires_private = g_list_reverse (pkg->requires_private);
Packit db3073
Packit db3073
  verify_package (pkg);
Packit db3073
Packit db3073
  return pkg;
Packit db3073
}
Packit db3073
Packit db3073
Package *
Packit db3073
get_package (const char *name)
Packit db3073
{
Packit db3073
  return internal_get_package (name, TRUE);
Packit db3073
}
Packit db3073
Packit db3073
Package *
Packit db3073
get_package_quiet (const char *name)
Packit db3073
{
Packit db3073
  return internal_get_package (name, FALSE);
Packit db3073
}
Packit db3073
Packit db3073
/* Strip consecutive duplicate arguments in the flag list. */
Packit db3073
static GList *
Packit db3073
flag_list_strip_duplicates (GList *list)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
Packit db3073
  /* Start at the 2nd element of the list so we don't have to check for an
Packit db3073
   * existing previous element. */
Packit db3073
  for (tmp = g_list_next (list); tmp != NULL; tmp = g_list_next (tmp))
Packit db3073
    {
Packit db3073
      Flag *cur = tmp->data;
Packit db3073
      Flag *prev = tmp->prev->data;
Packit db3073
Packit db3073
      if (cur->type == prev->type && g_strcmp0 (cur->arg, prev->arg) == 0)
Packit db3073
        {
Packit db3073
          /* Remove the duplicate flag from the list and move to the last
Packit db3073
           * element to prepare for the next iteration. */
Packit db3073
          GList *dup = tmp;
Packit db3073
Packit db3073
          debug_spew (" removing duplicate \"%s\"\n", cur->arg);
Packit db3073
          tmp = g_list_previous (tmp);
Packit db3073
          list = g_list_remove_link (list, dup);
Packit db3073
        }
Packit db3073
    }
Packit db3073
Packit db3073
  return list;
Packit db3073
}
Packit db3073
Packit db3073
static char *
Packit db3073
flag_list_to_string (GList *list)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
  GString *str = g_string_new ("");
Packit db3073
  char *retval;
Packit db3073
  
Packit db3073
  tmp = list;
Packit db3073
  while (tmp != NULL) {
Packit db3073
    Flag *flag = tmp->data;
Packit db3073
    char *tmpstr = flag->arg;
Packit db3073
Packit db3073
    if (pcsysrootdir != NULL && flag->type & (CFLAGS_I | LIBS_L)) {
Packit db3073
      g_string_append_c (str, '-');
Packit db3073
      g_string_append_c (str, tmpstr[1]);
Packit db3073
      g_string_append (str, pcsysrootdir);
Packit db3073
      g_string_append (str, tmpstr+2);
Packit db3073
    } else {
Packit db3073
      g_string_append (str, tmpstr);
Packit db3073
    }
Packit db3073
    g_string_append_c (str, ' ');
Packit db3073
    tmp = g_list_next (tmp);
Packit db3073
  }
Packit db3073
Packit db3073
  retval = str->str;
Packit db3073
  g_string_free (str, FALSE);
Packit db3073
Packit db3073
  return retval;
Packit db3073
}
Packit db3073
Packit db3073
static int
Packit db3073
pathposcmp (gconstpointer a, gconstpointer b)
Packit db3073
{
Packit db3073
  const Package *pa = a;
Packit db3073
  const Package *pb = b;
Packit db3073
  
Packit db3073
  if (pa->path_position < pb->path_position)
Packit db3073
    return -1;
Packit db3073
  else if (pa->path_position > pb->path_position)
Packit db3073
    return 1;
Packit db3073
  else
Packit db3073
    return 0;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
spew_package_list (const char *name,
Packit db3073
                   GList     *list)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
Packit db3073
  debug_spew (" %s:", name);
Packit db3073
Packit db3073
  tmp = list;
Packit db3073
  while (tmp != NULL)
Packit db3073
    {
Packit db3073
      Package *pkg = tmp->data;
Packit db3073
      debug_spew (" %s", pkg->key);
Packit db3073
      tmp = tmp->next;
Packit db3073
    }
Packit db3073
  debug_spew ("\n");
Packit db3073
}
Packit db3073
Packit db3073
Packit db3073
static GList *
Packit db3073
packages_sort_by_path_position (GList *list)
Packit db3073
{
Packit db3073
  return g_list_sort (list, pathposcmp);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
recursive_fill_list (Package *pkg, gboolean include_private, GList **listp)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
Packit db3073
  /*
Packit db3073
   * If the package is one of the parents, we can skip it. This allows
Packit db3073
   * circular requires loops to be broken.
Packit db3073
   */
Packit db3073
  if (pkg->in_requires_chain)
Packit db3073
    {
Packit db3073
      debug_spew ("Package %s already in requires chain, skipping\n",
Packit db3073
                  pkg->key);
Packit db3073
      return;
Packit db3073
    }
Packit db3073
Packit db3073
  /* record this package in the dependency chain */
Packit db3073
  pkg->in_requires_chain = TRUE;
Packit db3073
Packit db3073
  /* Start from the end of the required package list to maintain order since
Packit db3073
   * the recursive list is built by prepending. */
Packit db3073
  tmp = include_private ? pkg->requires_private : pkg->requires;
Packit db3073
  for (tmp = g_list_last (tmp); tmp != NULL; tmp = g_list_previous (tmp))
Packit db3073
    recursive_fill_list (tmp->data, include_private, listp);
Packit db3073
Packit db3073
  *listp = g_list_prepend (*listp, pkg);
Packit db3073
Packit db3073
  /* remove this package from the dependency chain now that we've unwound */
Packit db3073
  pkg->in_requires_chain = FALSE;
Packit db3073
}
Packit db3073
Packit db3073
/* merge the flags from the individual packages */
Packit db3073
static GList *
Packit db3073
merge_flag_lists (GList *packages, FlagType type)
Packit db3073
{
Packit db3073
  GList *last = NULL;
Packit db3073
  GList *merged = NULL;
Packit db3073
Packit db3073
  /* keep track of the last element to avoid traversing the whole list */
Packit db3073
  for (; packages != NULL; packages = g_list_next (packages))
Packit db3073
    {
Packit db3073
      Package *pkg = packages->data;
Packit db3073
      GList *flags = (type & LIBS_ANY) ? pkg->libs : pkg->cflags;
Packit db3073
Packit db3073
      /* manually copy the elements so we can keep track of the end */
Packit db3073
      for (; flags != NULL; flags = g_list_next (flags))
Packit db3073
        {
Packit db3073
          Flag *flag = flags->data;
Packit db3073
Packit db3073
          if (flag->type & type)
Packit db3073
            {
Packit db3073
              if (last == NULL)
Packit db3073
                {
Packit db3073
                  merged = g_list_prepend (NULL, flags->data);
Packit db3073
                  last = merged;
Packit db3073
                }
Packit db3073
              else
Packit db3073
                last = g_list_next (g_list_append (last, flags->data));
Packit db3073
            }
Packit db3073
        }
Packit db3073
    }
Packit db3073
Packit db3073
  return merged;
Packit db3073
}
Packit db3073
Packit db3073
/* Work backwards from the end of the package list to remove duplicate
Packit db3073
 * packages. This could happen because the package was specified multiple
Packit db3073
 * times on the command line, or because multiple packages require the same
Packit db3073
 * package. When we have duplicate dependencies, starting from the end of the
Packit db3073
 * list ensures that the dependency shows up later in the package list and
Packit db3073
 * Libs will come out correctly. */
Packit db3073
static GList *
Packit db3073
package_list_strip_duplicates (GList *packages)
Packit db3073
{
Packit db3073
  GList *cur;
Packit db3073
  GHashTable *requires;
Packit db3073
Packit db3073
  requires = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
  for (cur = g_list_last (packages); cur != NULL; cur = g_list_previous (cur))
Packit db3073
    {
Packit db3073
      Package *pkg = cur->data;
Packit db3073
Packit db3073
      if (g_hash_table_lookup_extended (requires, pkg->key, NULL, NULL))
Packit db3073
        {
Packit db3073
          GList *dup = cur;
Packit db3073
Packit db3073
          /* Remove the duplicate package from the list */
Packit db3073
          debug_spew ("Removing duplicate package %s\n", pkg->key);
Packit db3073
          cur = cur->next;
Packit db3073
          packages = g_list_delete_link (packages, dup);
Packit db3073
        }
Packit db3073
      else
Packit db3073
        {
Packit db3073
          /* Unique package. Track it and move to the next. */
Packit db3073
          g_hash_table_replace (requires, pkg->key, pkg->key);
Packit db3073
        }
Packit db3073
    }
Packit db3073
  g_hash_table_destroy (requires);
Packit db3073
Packit db3073
  return packages;
Packit db3073
}
Packit db3073
Packit db3073
static GList *
Packit db3073
fill_list (GList *packages, FlagType type,
Packit db3073
           gboolean in_path_order, gboolean include_private)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
  GList *expanded = NULL;
Packit db3073
  GList *flags;
Packit db3073
Packit db3073
  /* Start from the end of the requested package list to maintain order since
Packit db3073
   * the recursive list is built by prepending. */
Packit db3073
  for (tmp = g_list_last (packages); tmp != NULL; tmp = g_list_previous (tmp))
Packit db3073
    recursive_fill_list (tmp->data, include_private, &expanded);
Packit db3073
Packit db3073
  /* Remove duplicate packages from the recursive list. This should provide a
Packit db3073
   * serialized package list where all interdependencies are resolved
Packit db3073
   * consistently. */
Packit db3073
  spew_package_list (" pre-remove", expanded);
Packit db3073
  expanded = package_list_strip_duplicates (expanded);
Packit db3073
  spew_package_list ("post-remove", expanded);
Packit db3073
Packit db3073
  if (in_path_order)
Packit db3073
    {
Packit db3073
      spew_package_list ("original", expanded);
Packit db3073
      expanded = packages_sort_by_path_position (expanded);
Packit db3073
      spew_package_list ("  sorted", expanded);
Packit db3073
    }
Packit db3073
Packit db3073
  flags = merge_flag_lists (expanded, type);
Packit db3073
  g_list_free (expanded);
Packit db3073
Packit db3073
  return flags;
Packit db3073
}
Packit db3073
Packit db3073
static GList *
Packit db3073
add_env_variable_to_list (GList *list, const gchar *env)
Packit db3073
{
Packit db3073
  gchar **values;
Packit db3073
  gint i;
Packit db3073
Packit db3073
  values = g_strsplit (env, G_SEARCHPATH_SEPARATOR_S, 0);
Packit db3073
  for (i = 0; values[i] != NULL; i++)
Packit db3073
    {
Packit db3073
      list = g_list_append (list, g_strdup (values[i]));
Packit db3073
    }
Packit db3073
  g_strfreev (values);
Packit db3073
Packit db3073
  return list;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
verify_package (Package *pkg)
Packit db3073
{
Packit db3073
  GList *requires = NULL;
Packit db3073
  GList *conflicts = NULL;
Packit db3073
  GList *system_directories = NULL;
Packit db3073
  GList *iter;
Packit db3073
  GList *requires_iter;
Packit db3073
  GList *conflicts_iter;
Packit db3073
  GList *system_dir_iter = NULL;
Packit db3073
  int count;
Packit db3073
  const gchar *search_path;
Packit db3073
Packit db3073
  /* Be sure we have the required fields */
Packit db3073
Packit db3073
  if (pkg->key == NULL)
Packit db3073
    {
Packit db3073
      fprintf (stderr,
Packit db3073
               "Internal pkg-config error, package with no key, please file a bug report\n");
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  if (pkg->name == NULL)
Packit db3073
    {
Packit db3073
      verbose_error ("Package '%s' has no Name: field\n",
Packit db3073
                     pkg->key);
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  if (pkg->version == NULL)
Packit db3073
    {
Packit db3073
      verbose_error ("Package '%s' has no Version: field\n",
Packit db3073
                     pkg->key);
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  if (pkg->description == NULL)
Packit db3073
    {
Packit db3073
      verbose_error ("Package '%s' has no Description: field\n",
Packit db3073
                     pkg->key);
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  /* Make sure we have the right version for all requirements */
Packit db3073
Packit db3073
  iter = pkg->requires_private;
Packit db3073
Packit db3073
  while (iter != NULL)
Packit db3073
    {
Packit db3073
      Package *req = iter->data;
Packit db3073
      RequiredVersion *ver = NULL;
Packit db3073
Packit db3073
      if (pkg->required_versions)
Packit db3073
        ver = g_hash_table_lookup (pkg->required_versions,
Packit db3073
                                   req->key);
Packit db3073
Packit db3073
      if (ver)
Packit db3073
        {
Packit db3073
          if (!version_test (ver->comparison, req->version, ver->version))
Packit db3073
            {
Packit db3073
              verbose_error ("Package '%s' requires '%s %s %s' but version of %s is %s\n",
Packit db3073
                             pkg->key, req->key,
Packit db3073
                             comparison_to_str (ver->comparison),
Packit db3073
                             ver->version,
Packit db3073
                             req->key,
Packit db3073
                             req->version);
Packit db3073
              if (req->url)
Packit db3073
                verbose_error ("You may find new versions of %s at %s\n",
Packit db3073
                               req->name, req->url);
Packit db3073
Packit db3073
              exit (1);
Packit db3073
            }
Packit db3073
        }
Packit db3073
                                   
Packit db3073
      iter = g_list_next (iter);
Packit db3073
    }
Packit db3073
Packit db3073
  /* Make sure we didn't drag in any conflicts via Requires
Packit db3073
   * (inefficient algorithm, who cares)
Packit db3073
   */
Packit db3073
  recursive_fill_list (pkg, TRUE, &requires);
Packit db3073
  conflicts = pkg->conflicts;
Packit db3073
Packit db3073
  requires_iter = requires;
Packit db3073
  while (requires_iter != NULL)
Packit db3073
    {
Packit db3073
      Package *req = requires_iter->data;
Packit db3073
      
Packit db3073
      conflicts_iter = conflicts;
Packit db3073
Packit db3073
      while (conflicts_iter != NULL)
Packit db3073
        {
Packit db3073
          RequiredVersion *ver = conflicts_iter->data;
Packit db3073
Packit db3073
	  if (strcmp (ver->name, req->key) == 0 &&
Packit db3073
	      version_test (ver->comparison,
Packit db3073
			    req->version,
Packit db3073
			    ver->version))
Packit db3073
            {
Packit db3073
              verbose_error ("Version %s of %s creates a conflict.\n"
Packit db3073
                             "(%s %s %s conflicts with %s %s)\n",
Packit db3073
                             req->version, req->key,
Packit db3073
                             ver->name,
Packit db3073
                             comparison_to_str (ver->comparison),
Packit db3073
                             ver->version ? ver->version : "(any)",
Packit db3073
                             ver->owner->key,
Packit db3073
                             ver->owner->version);
Packit db3073
Packit db3073
              exit (1);
Packit db3073
            }
Packit db3073
Packit db3073
          conflicts_iter = g_list_next (conflicts_iter);
Packit db3073
        }
Packit db3073
      
Packit db3073
      requires_iter = g_list_next (requires_iter);
Packit db3073
    }
Packit db3073
  
Packit db3073
  g_list_free (requires);
Packit db3073
Packit db3073
  /* We make a list of system directories that gcc expects so we can remove
Packit db3073
   * them.
Packit db3073
   */
Packit db3073
Packit db3073
  search_path = g_getenv ("PKG_CONFIG_SYSTEM_INCLUDE_PATH");
Packit db3073
Packit db3073
  if (search_path == NULL)
Packit db3073
    {
Packit db3073
      search_path = PKG_CONFIG_SYSTEM_INCLUDE_PATH;
Packit db3073
    }
Packit db3073
Packit db3073
  system_directories = add_env_variable_to_list (system_directories, search_path);
Packit db3073
Packit db3073
  search_path = g_getenv ("C_INCLUDE_PATH");
Packit db3073
  if (search_path != NULL)
Packit db3073
    {
Packit db3073
      system_directories = add_env_variable_to_list (system_directories, search_path);
Packit db3073
    }
Packit db3073
Packit db3073
  search_path = g_getenv ("CPLUS_INCLUDE_PATH");
Packit db3073
  if (search_path != NULL)
Packit db3073
    {
Packit db3073
      system_directories = add_env_variable_to_list (system_directories, search_path);
Packit db3073
    }
Packit db3073
Packit db3073
  count = 0;
Packit db3073
  for (iter = pkg->cflags; iter != NULL; iter = g_list_next (iter))
Packit db3073
    {
Packit db3073
      gint offset = 0;
Packit db3073
      Flag *flag = iter->data;
Packit db3073
Packit db3073
      if (!(flag->type & CFLAGS_I))
Packit db3073
        continue;
Packit db3073
Packit db3073
      /* we put things in canonical -I/usr/include (vs. -I /usr/include) format,
Packit db3073
       * but if someone changes it later we may as well be robust
Packit db3073
       */
Packit db3073
      if (((strncmp (flag->arg, "-I", 2) == 0) && (offset = 2))||
Packit db3073
          ((strncmp (flag->arg, "-I ", 3) == 0) && (offset = 3)))
Packit db3073
        {
Packit db3073
	  if (offset == 0)
Packit db3073
	    {
Packit db3073
	      iter = iter->next;
Packit db3073
	      continue;
Packit db3073
	    }
Packit db3073
Packit db3073
	  system_dir_iter = system_directories;
Packit db3073
	  while (system_dir_iter != NULL)
Packit db3073
	    {
Packit db3073
	      if (strcmp (system_dir_iter->data,
Packit db3073
                          ((char*)flag->arg) + offset) == 0)
Packit db3073
		{
Packit db3073
                  debug_spew ("Package %s has %s in Cflags\n",
Packit db3073
			      pkg->key, (gchar *)flag->arg);
Packit db3073
		  if (g_getenv ("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS") == NULL)
Packit db3073
		    {
Packit db3073
                      debug_spew ("Removing %s from cflags for %s\n",
Packit db3073
                                  flag->arg, pkg->key);
Packit db3073
		      ++count;
Packit db3073
		      iter->data = NULL;
Packit db3073
Packit db3073
		      break;
Packit db3073
		    }
Packit db3073
		}
Packit db3073
	      system_dir_iter = system_dir_iter->next;
Packit db3073
	    }
Packit db3073
        }
Packit db3073
    }
Packit db3073
Packit db3073
  while (count)
Packit db3073
    {
Packit db3073
      pkg->cflags = g_list_remove (pkg->cflags, NULL);
Packit db3073
      --count;
Packit db3073
    }
Packit db3073
Packit db3073
  g_list_foreach (system_directories, (GFunc) g_free, NULL);
Packit db3073
  g_list_free (system_directories);
Packit db3073
Packit db3073
  system_directories = NULL;
Packit db3073
Packit db3073
  search_path = g_getenv ("PKG_CONFIG_SYSTEM_LIBRARY_PATH");
Packit db3073
Packit db3073
  if (search_path == NULL)
Packit db3073
    {
Packit db3073
      search_path = PKG_CONFIG_SYSTEM_LIBRARY_PATH;
Packit db3073
    }
Packit db3073
Packit db3073
  system_directories = add_env_variable_to_list (system_directories, search_path);
Packit db3073
Packit db3073
  count = 0;
Packit db3073
  for (iter = pkg->libs; iter != NULL; iter = g_list_next (iter))
Packit db3073
    {
Packit db3073
      GList *system_dir_iter = system_directories;
Packit db3073
      Flag *flag = iter->data;
Packit db3073
Packit db3073
      if (!(flag->type & LIBS_L))
Packit db3073
        continue;
Packit db3073
Packit db3073
      while (system_dir_iter != NULL)
Packit db3073
        {
Packit db3073
          gboolean is_system = FALSE;
Packit db3073
          const char *linker_arg = flag->arg;
Packit db3073
          const char *system_libpath = system_dir_iter->data;
Packit db3073
Packit db3073
          if (strncmp (linker_arg, "-L ", 3) == 0 &&
Packit db3073
              strcmp (linker_arg + 3, system_libpath) == 0)
Packit db3073
            is_system = TRUE;
Packit db3073
          else if (strncmp (linker_arg, "-L", 2) == 0 &&
Packit db3073
              strcmp (linker_arg + 2, system_libpath) == 0)
Packit db3073
            is_system = TRUE;
Packit db3073
          if (is_system)
Packit db3073
            {
Packit db3073
              debug_spew ("Package %s has -L %s in Libs\n",
Packit db3073
                          pkg->key, system_libpath);
Packit db3073
              if (g_getenv ("PKG_CONFIG_ALLOW_SYSTEM_LIBS") == NULL)
Packit db3073
                {
Packit db3073
                  iter->data = NULL;
Packit db3073
                  ++count;
Packit db3073
                  debug_spew ("Removing -L %s from libs for %s\n",
Packit db3073
                              system_libpath, pkg->key);
Packit db3073
                  break;
Packit db3073
                }
Packit db3073
            }
Packit db3073
          system_dir_iter = system_dir_iter->next;
Packit db3073
        }
Packit db3073
      iter = iter->next;
Packit db3073
    }
Packit db3073
  g_list_free (system_directories);
Packit db3073
Packit db3073
  while (count)
Packit db3073
    {
Packit db3073
      pkg->libs = g_list_remove (pkg->libs, NULL);
Packit db3073
      --count;
Packit db3073
    }
Packit db3073
}
Packit db3073
Packit db3073
/* Create a merged list of required packages and retrieve the flags from them.
Packit db3073
 * Strip the duplicates from the flags list. The sorting and stripping can be
Packit db3073
 * done in one of two ways: packages sorted by position in the pkg-config path
Packit db3073
 * and stripping done from the beginning of the list, or packages sorted from
Packit db3073
 * most dependent to least dependent and stripping from the end of the list.
Packit db3073
 * The former is done for -I/-L flags, and the latter for all others.
Packit db3073
 */
Packit db3073
static char *
Packit db3073
get_multi_merged (GList *pkgs, FlagType type, gboolean in_path_order,
Packit db3073
                  gboolean include_private)
Packit db3073
{
Packit db3073
  GList *list;
Packit db3073
  char *retval;
Packit db3073
Packit db3073
  list = fill_list (pkgs, type, in_path_order, include_private);
Packit db3073
  list = flag_list_strip_duplicates (list);
Packit db3073
  retval = flag_list_to_string (list);
Packit db3073
  g_list_free (list);
Packit db3073
Packit db3073
  return retval;
Packit db3073
}
Packit db3073
Packit db3073
char *
Packit db3073
packages_get_flags (GList *pkgs, FlagType flags)
Packit db3073
{
Packit db3073
  GString *str;
Packit db3073
  char *cur;
Packit db3073
Packit db3073
  str = g_string_new (NULL);
Packit db3073
Packit db3073
  /* sort packages in path order for -L/-I, dependency order otherwise */
Packit db3073
  if (flags & CFLAGS_OTHER)
Packit db3073
    {
Packit db3073
      cur = get_multi_merged (pkgs, CFLAGS_OTHER, FALSE, TRUE);
Packit db3073
      debug_spew ("adding CFLAGS_OTHER string \"%s\"\n", cur);
Packit db3073
      g_string_append (str, cur);
Packit db3073
      g_free (cur);
Packit db3073
    }
Packit db3073
  if (flags & CFLAGS_I)
Packit db3073
    {
Packit db3073
      cur = get_multi_merged (pkgs, CFLAGS_I, TRUE, TRUE);
Packit db3073
      debug_spew ("adding CFLAGS_I string \"%s\"\n", cur);
Packit db3073
      g_string_append (str, cur);
Packit db3073
      g_free (cur);
Packit db3073
    }
Packit db3073
  if (flags & LIBS_L)
Packit db3073
    {
Packit db3073
      cur = get_multi_merged (pkgs, LIBS_L, TRUE, !ignore_private_libs);
Packit db3073
      debug_spew ("adding LIBS_L string \"%s\"\n", cur);
Packit db3073
      g_string_append (str, cur);
Packit db3073
      g_free (cur);
Packit db3073
    }
Packit db3073
  if (flags & (LIBS_OTHER | LIBS_l))
Packit db3073
    {
Packit db3073
      cur = get_multi_merged (pkgs, flags & (LIBS_OTHER | LIBS_l), FALSE,
Packit db3073
                              !ignore_private_libs);
Packit db3073
      debug_spew ("adding LIBS_OTHER | LIBS_l string \"%s\"\n", cur);
Packit db3073
      g_string_append (str, cur);
Packit db3073
      g_free (cur);
Packit db3073
    }
Packit db3073
Packit db3073
  debug_spew ("returning flags string \"%s\"\n", str->str);
Packit db3073
  return g_string_free (str, FALSE);
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
define_global_variable (const char *varname,
Packit db3073
                        const char *varval)
Packit db3073
{
Packit db3073
  if (globals == NULL)
Packit db3073
    globals = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
Packit db3073
  if (g_hash_table_lookup (globals, varname))
Packit db3073
    {
Packit db3073
      verbose_error ("Variable '%s' defined twice globally\n", varname);
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  g_hash_table_insert (globals, g_strdup (varname), g_strdup (varval));
Packit db3073
      
Packit db3073
  debug_spew ("Global variable definition '%s' = '%s'\n",
Packit db3073
              varname, varval);
Packit db3073
}
Packit db3073
Packit db3073
char *
Packit db3073
package_get_var (Package *pkg,
Packit db3073
                 const char *var)
Packit db3073
{
Packit db3073
  char *varval = NULL;
Packit db3073
Packit db3073
  if (globals)
Packit db3073
    varval = g_strdup (g_hash_table_lookup (globals, var));
Packit db3073
  
Packit db3073
  if (varval == NULL && pkg->vars)
Packit db3073
    varval = g_strdup (g_hash_table_lookup (pkg->vars, var));
Packit db3073
Packit db3073
  /* Magic "pcfiledir" variable */
Packit db3073
  if (varval == NULL && pkg->pcfiledir && strcmp (var, "pcfiledir") == 0)
Packit db3073
    varval = g_strdup (pkg->pcfiledir);
Packit db3073
Packit db3073
  return varval;
Packit db3073
}
Packit db3073
Packit db3073
char *
Packit db3073
packages_get_var (GList     *pkgs,
Packit db3073
                  const char *varname)
Packit db3073
{
Packit db3073
  GList *tmp;
Packit db3073
  GString *str;
Packit db3073
  char *retval;
Packit db3073
  
Packit db3073
  str = g_string_new ("");
Packit db3073
  
Packit db3073
  tmp = pkgs;
Packit db3073
  while (tmp != NULL)
Packit db3073
    {
Packit db3073
      Package *pkg = tmp->data;
Packit db3073
      char *var;
Packit db3073
Packit db3073
      var = package_get_var (pkg, varname);
Packit db3073
      
Packit db3073
      if (var)
Packit db3073
        {
Packit db3073
          g_string_append (str, var);
Packit db3073
          g_string_append_c (str, ' ');                
Packit db3073
          g_free (var);
Packit db3073
        }
Packit db3073
Packit db3073
      tmp = g_list_next (tmp);
Packit db3073
    }
Packit db3073
Packit db3073
  /* chop last space */
Packit db3073
  if (str->len > 0)
Packit db3073
      str->str[str->len - 1] = '\0';
Packit db3073
  retval = str->str;
Packit db3073
  g_string_free (str, FALSE);
Packit db3073
Packit db3073
  return retval;
Packit db3073
}
Packit db3073
Packit db3073
Packit db3073
Packit db3073
/* Stolen verbatim from rpm/lib/misc.c 
Packit db3073
   RPM is Copyright (c) 1998 by Red Hat Software, Inc.,
Packit db3073
   and may be distributed under the terms of the GPL and LGPL.
Packit db3073
*/
Packit db3073
/* compare alpha and numeric segments of two versions */
Packit db3073
/* return 1: a is newer than b */
Packit db3073
/*        0: a and b are the same version */
Packit db3073
/*       -1: b is newer than a */
Packit db3073
static int rpmvercmp(const char * a, const char * b) {
Packit db3073
    char oldch1, oldch2;
Packit db3073
    char * str1, * str2;
Packit db3073
    char * one, * two;
Packit db3073
    int rc;
Packit db3073
    int isnum;
Packit db3073
    
Packit db3073
    /* easy comparison to see if versions are identical */
Packit db3073
    if (!strcmp(a, b)) return 0;
Packit db3073
Packit db3073
    str1 = g_alloca(strlen(a) + 1);
Packit db3073
    str2 = g_alloca(strlen(b) + 1);
Packit db3073
Packit db3073
    strcpy(str1, a);
Packit db3073
    strcpy(str2, b);
Packit db3073
Packit db3073
    one = str1;
Packit db3073
    two = str2;
Packit db3073
Packit db3073
    /* loop through each version segment of str1 and str2 and compare them */
Packit db3073
    while (*one && *two) {
Packit db3073
	while (*one && !isalnum((guchar)*one)) one++;
Packit db3073
	while (*two && !isalnum((guchar)*two)) two++;
Packit db3073
Packit db3073
	/* If we ran to the end of either, we are finished with the loop */
Packit db3073
	if (!(*one && *two)) break;
Packit db3073
Packit db3073
	str1 = one;
Packit db3073
	str2 = two;
Packit db3073
Packit db3073
	/* grab first completely alpha or completely numeric segment */
Packit db3073
	/* leave one and two pointing to the start of the alpha or numeric */
Packit db3073
	/* segment and walk str1 and str2 to end of segment */
Packit db3073
	if (isdigit((guchar)*str1)) {
Packit db3073
	    while (*str1 && isdigit((guchar)*str1)) str1++;
Packit db3073
	    while (*str2 && isdigit((guchar)*str2)) str2++;
Packit db3073
	    isnum = 1;
Packit db3073
	} else {
Packit db3073
	    while (*str1 && isalpha((guchar)*str1)) str1++;
Packit db3073
	    while (*str2 && isalpha((guchar)*str2)) str2++;
Packit db3073
	    isnum = 0;
Packit db3073
	}
Packit db3073
		
Packit db3073
	/* save character at the end of the alpha or numeric segment */
Packit db3073
	/* so that they can be restored after the comparison */
Packit db3073
	oldch1 = *str1;
Packit db3073
	*str1 = '\0';
Packit db3073
	oldch2 = *str2;
Packit db3073
	*str2 = '\0';
Packit db3073
Packit db3073
	/* take care of the case where the two version segments are */
Packit db3073
	/* different types: one numeric and one alpha */
Packit db3073
	if (one == str1) return -1;	/* arbitrary */
Packit db3073
	/* XXX See patch #60884 (and details) from bugzilla #50977. */
Packit db3073
	if (two == str2) return (isnum ? 1 : -1);
Packit db3073
Packit db3073
	if (isnum) {
Packit db3073
	    /* this used to be done by converting the digit segments */
Packit db3073
	    /* to ints using atoi() - it's changed because long  */
Packit db3073
	    /* digit segments can overflow an int - this should fix that. */
Packit db3073
	  
Packit db3073
	    /* throw away any leading zeros - it's a number, right? */
Packit db3073
	    while (*one == '0') one++;
Packit db3073
	    while (*two == '0') two++;
Packit db3073
Packit db3073
	    /* whichever number has more digits wins */
Packit db3073
	    if (strlen(one) > strlen(two)) return 1;
Packit db3073
	    if (strlen(two) > strlen(one)) return -1;
Packit db3073
	}
Packit db3073
Packit db3073
	/* strcmp will return which one is greater - even if the two */
Packit db3073
	/* segments are alpha or if they are numeric.  don't return  */
Packit db3073
	/* if they are equal because there might be more segments to */
Packit db3073
	/* compare */
Packit db3073
	rc = strcmp(one, two);
Packit db3073
	if (rc) return rc;
Packit db3073
	
Packit db3073
	/* restore character that was replaced by null above */
Packit db3073
	*str1 = oldch1;
Packit db3073
	one = str1;
Packit db3073
	*str2 = oldch2;
Packit db3073
	two = str2;
Packit db3073
    }
Packit db3073
Packit db3073
    /* this catches the case where all numeric and alpha segments have */
Packit db3073
    /* compared identically but the segment sepparating characters were */
Packit db3073
    /* different */
Packit db3073
    if ((!*one) && (!*two)) return 0;
Packit db3073
Packit db3073
    /* whichever version still has characters left over wins */
Packit db3073
    if (!*one) return -1; else return 1;
Packit db3073
}
Packit db3073
Packit db3073
int
Packit db3073
compare_versions (const char * a, const char *b)
Packit db3073
{
Packit db3073
  return rpmvercmp (a, b);
Packit db3073
}
Packit db3073
Packit db3073
gboolean
Packit db3073
version_test (ComparisonType comparison,
Packit db3073
              const char *a,
Packit db3073
              const char *b)
Packit db3073
{
Packit db3073
  switch (comparison)
Packit db3073
    {
Packit db3073
    case LESS_THAN:
Packit db3073
      return compare_versions (a, b) < 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case GREATER_THAN:
Packit db3073
      return compare_versions (a, b) > 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case LESS_THAN_EQUAL:
Packit db3073
      return compare_versions (a, b) <= 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case GREATER_THAN_EQUAL:
Packit db3073
      return compare_versions (a, b) >= 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case EQUAL:
Packit db3073
      return compare_versions (a, b) == 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case NOT_EQUAL:
Packit db3073
      return compare_versions (a, b) != 0;
Packit db3073
      break;
Packit db3073
Packit db3073
    case ALWAYS_MATCH:
Packit db3073
      return TRUE;
Packit db3073
      break;
Packit db3073
      
Packit db3073
    default:
Packit db3073
      g_assert_not_reached ();
Packit db3073
      break;
Packit db3073
    }
Packit db3073
Packit db3073
  return FALSE;
Packit db3073
}
Packit db3073
Packit db3073
const char *
Packit db3073
comparison_to_str (ComparisonType comparison)
Packit db3073
{
Packit db3073
  switch (comparison)
Packit db3073
    {
Packit db3073
    case LESS_THAN:
Packit db3073
      return "<";
Packit db3073
      break;
Packit db3073
Packit db3073
    case GREATER_THAN:
Packit db3073
      return ">";
Packit db3073
      break;
Packit db3073
Packit db3073
    case LESS_THAN_EQUAL:
Packit db3073
      return "<=";
Packit db3073
      break;
Packit db3073
Packit db3073
    case GREATER_THAN_EQUAL:
Packit db3073
      return ">=";
Packit db3073
      break;
Packit db3073
Packit db3073
    case EQUAL:
Packit db3073
      return "=";
Packit db3073
      break;
Packit db3073
Packit db3073
    case NOT_EQUAL:
Packit db3073
      return "!=";
Packit db3073
      break;
Packit db3073
Packit db3073
    case ALWAYS_MATCH:
Packit db3073
      return "(any)";
Packit db3073
      break;
Packit db3073
      
Packit db3073
    default:
Packit db3073
      g_assert_not_reached ();
Packit db3073
      break;
Packit db3073
    }
Packit db3073
Packit db3073
  return "???";
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
max_len_foreach (gpointer key, gpointer value, gpointer data)
Packit db3073
{
Packit db3073
  int *mlen = data;
Packit db3073
Packit db3073
  *mlen = MAX (*mlen, strlen (key));
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
packages_foreach (gpointer key, gpointer value, gpointer data)
Packit db3073
{
Packit db3073
  Package *pkg = get_package (key);
Packit db3073
Packit db3073
  if (pkg != NULL)
Packit db3073
    {
Packit db3073
      char *pad;
Packit db3073
Packit db3073
      pad = g_strnfill (GPOINTER_TO_INT (data) - strlen (pkg->key), ' ');
Packit db3073
      
Packit db3073
      printf ("%s%s%s - %s\n",
Packit db3073
              pkg->key, pad, pkg->name, pkg->description);
Packit db3073
Packit db3073
      g_free (pad);
Packit db3073
    }
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
print_package_list (void)
Packit db3073
{
Packit db3073
  int mlen = 0;
Packit db3073
Packit db3073
  ignore_requires = TRUE;
Packit db3073
  ignore_requires_private = TRUE;
Packit db3073
Packit db3073
  g_hash_table_foreach (locations, max_len_foreach, &mlen);
Packit db3073
  g_hash_table_foreach (locations, packages_foreach, GINT_TO_POINTER (mlen + 1));
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
enable_private_libs(void)
Packit db3073
{
Packit db3073
  ignore_private_libs = FALSE;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
disable_private_libs(void)
Packit db3073
{
Packit db3073
  ignore_private_libs = TRUE;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
enable_requires(void)
Packit db3073
{
Packit db3073
  ignore_requires = FALSE;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
disable_requires(void)
Packit db3073
{
Packit db3073
  ignore_requires = TRUE;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
enable_requires_private(void)
Packit db3073
{
Packit db3073
  ignore_requires_private = FALSE;
Packit db3073
}
Packit db3073
Packit db3073
void
Packit db3073
disable_requires_private(void)
Packit db3073
{
Packit db3073
  ignore_requires_private = TRUE;
Packit db3073
}