Blame parse.c

Packit db3073
/* 
Packit db3073
 * Copyright (C) 2006-2011 Tollef Fog Heen <tfheen@err.no>
Packit db3073
 * Copyright (C) 2001, 2002, 2005-2006 Red Hat Inc.
Packit db3073
 * Copyright (C) 2010 Dan Nicholson <dbn.lists@gmail.com>
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
#ifdef HAVE_CONFIG_H
Packit db3073
#include "config.h"
Packit db3073
#endif
Packit db3073
Packit db3073
#include "parse.h"
Packit db3073
#include <stdio.h>
Packit db3073
#include <errno.h>
Packit db3073
#include <string.h>
Packit db3073
#include <stdlib.h>
Packit db3073
#include <ctype.h>
Packit db3073
#ifdef HAVE_SYS_WAIT_H
Packit db3073
#include <sys/wait.h>
Packit db3073
#endif
Packit db3073
#include <sys/types.h>
Packit db3073
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
gboolean dont_define_prefix = FALSE;
Packit db3073
char *prefix_variable = "prefix";
Packit db3073
gboolean msvc_syntax = FALSE;
Packit db3073
#endif
Packit db3073
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
#ifndef G_IS_DIR_SEPARATOR
Packit db3073
#define G_IS_DIR_SEPARATOR(c) ((c) == G_DIR_SEPARATOR || (c) == '/')
Packit db3073
#endif
Packit db3073
#endif
Packit db3073
Packit db3073
/**
Packit db3073
 * Read an entire line from a file into a buffer. Lines may
Packit db3073
 * be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter
Packit db3073
 * is not written into the buffer. Text after a '#' character is treated as
Packit db3073
 * a comment and skipped. '\' can be used to escape a # character.
Packit db3073
 * '\' proceding a line delimiter combines adjacent lines. A '\' proceding
Packit db3073
 * any other character is ignored and written into the output buffer
Packit db3073
 * unmodified.
Packit db3073
 * 
Packit db3073
 * Return value: %FALSE if the stream was already at an EOF character.
Packit db3073
 **/
Packit db3073
static gboolean
Packit db3073
read_one_line (FILE *stream, GString *str)
Packit db3073
{
Packit db3073
  gboolean quoted = FALSE;
Packit db3073
  gboolean comment = FALSE;
Packit db3073
  int n_read = 0;
Packit db3073
Packit db3073
  g_string_truncate (str, 0);
Packit db3073
  
Packit db3073
  while (1)
Packit db3073
    {
Packit db3073
      int c;
Packit db3073
      
Packit db3073
      c = getc (stream);
Packit db3073
Packit db3073
      if (c == EOF)
Packit db3073
	{
Packit db3073
	  if (quoted)
Packit db3073
	    g_string_append_c (str, '\\');
Packit db3073
	  
Packit db3073
	  goto done;
Packit db3073
	}
Packit db3073
      else
Packit db3073
	n_read++;
Packit db3073
Packit db3073
      if (quoted)
Packit db3073
	{
Packit db3073
	  quoted = FALSE;
Packit db3073
	  
Packit db3073
	  switch (c)
Packit db3073
	    {
Packit db3073
	    case '#':
Packit db3073
	      g_string_append_c (str, '#');
Packit db3073
	      break;
Packit db3073
	    case '\r':
Packit db3073
	    case '\n':
Packit db3073
	      {
Packit db3073
		int next_c = getc (stream);
Packit db3073
Packit db3073
		if (!(c == EOF ||
Packit db3073
		      (c == '\r' && next_c == '\n') ||
Packit db3073
		      (c == '\n' && next_c == '\r')))
Packit db3073
		  ungetc (next_c, stream);
Packit db3073
		
Packit db3073
		break;
Packit db3073
	      }
Packit db3073
	    default:
Packit db3073
	      g_string_append_c (str, '\\');	      
Packit db3073
	      g_string_append_c (str, c);
Packit db3073
	    }
Packit db3073
	}
Packit db3073
      else
Packit db3073
	{
Packit db3073
	  switch (c)
Packit db3073
	    {
Packit db3073
	    case '#':
Packit db3073
	      comment = TRUE;
Packit db3073
	      break;
Packit db3073
	    case '\\':
Packit db3073
	      if (!comment)
Packit db3073
		quoted = TRUE;
Packit db3073
	      break;
Packit db3073
	    case '\n':
Packit db3073
	      {
Packit db3073
		int next_c = getc (stream);
Packit db3073
Packit db3073
		if (!(c == EOF ||
Packit db3073
		      (c == '\r' && next_c == '\n') ||
Packit db3073
		      (c == '\n' && next_c == '\r')))
Packit db3073
		  ungetc (next_c, stream);
Packit db3073
Packit db3073
		goto done;
Packit db3073
	      }
Packit db3073
	    default:
Packit db3073
	      if (!comment)
Packit db3073
		g_string_append_c (str, c);
Packit db3073
	    }
Packit db3073
	}
Packit db3073
    }
Packit db3073
Packit db3073
 done:
Packit db3073
Packit db3073
  return n_read > 0;
Packit db3073
}
Packit db3073
Packit db3073
static char *
Packit db3073
trim_string (const char *str)
Packit db3073
{
Packit db3073
  int len;
Packit db3073
Packit db3073
  g_return_val_if_fail (str != NULL, NULL);
Packit db3073
  
Packit db3073
  while (*str && isspace ((guchar)*str))
Packit db3073
    str++;
Packit db3073
Packit db3073
  len = strlen (str);
Packit db3073
  while (len > 0 && isspace ((guchar)str[len-1]))
Packit db3073
    len--;
Packit db3073
Packit db3073
  return g_strndup (str, len);
Packit db3073
}
Packit db3073
Packit db3073
static char *
Packit db3073
trim_and_sub (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  char *trimmed;
Packit db3073
  GString *subst;
Packit db3073
  char *p;
Packit db3073
  
Packit db3073
  trimmed = trim_string (str);
Packit db3073
Packit db3073
  subst = g_string_new ("");
Packit db3073
Packit db3073
  p = trimmed;
Packit db3073
  while (*p)
Packit db3073
    {
Packit db3073
      if (p[0] == '$' &&
Packit db3073
          p[1] == '$')
Packit db3073
        {
Packit db3073
          /* escaped $ */
Packit db3073
          g_string_append_c (subst, '$');
Packit db3073
          p += 2;
Packit db3073
        }
Packit db3073
      else if (p[0] == '$' &&
Packit db3073
               p[1] == '{')
Packit db3073
        {
Packit db3073
          /* variable */
Packit db3073
          char *var_start;
Packit db3073
          char *varname;
Packit db3073
          char *varval;
Packit db3073
          
Packit db3073
          var_start = &p[2];
Packit db3073
Packit db3073
          /* Get up to close brace. */
Packit db3073
          while (*p && *p != '}')
Packit db3073
            ++p;
Packit db3073
Packit db3073
          varname = g_strndup (var_start, p - var_start);
Packit db3073
Packit db3073
          ++p; /* past brace */
Packit db3073
          
Packit db3073
          varval = package_get_var (pkg, varname);
Packit db3073
          
Packit db3073
          if (varval == NULL)
Packit db3073
            {
Packit db3073
              verbose_error ("Variable '%s' not defined in '%s'\n",
Packit db3073
                             varname, path);
Packit db3073
              
Packit db3073
              exit (1);
Packit db3073
            }
Packit db3073
Packit db3073
          g_free (varname);
Packit db3073
Packit db3073
          g_string_append (subst, varval);
Packit db3073
          g_free (varval);
Packit db3073
        }
Packit db3073
      else
Packit db3073
        {
Packit db3073
          g_string_append_c (subst, *p);
Packit db3073
Packit db3073
          ++p;          
Packit db3073
        }
Packit db3073
    }
Packit db3073
Packit db3073
  g_free (trimmed);
Packit db3073
  p = subst->str;
Packit db3073
  g_string_free (subst, FALSE);
Packit db3073
Packit db3073
  return p;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_name (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  if (pkg->name)
Packit db3073
    {
Packit db3073
      verbose_error ("Name field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  pkg->name = trim_and_sub (pkg, str, path);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_version (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  if (pkg->version)
Packit db3073
    {
Packit db3073
      verbose_error ("Version field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  pkg->version = trim_and_sub (pkg, str, path);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_description (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  if (pkg->description)
Packit db3073
    {
Packit db3073
      verbose_error ("Description field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  pkg->description = trim_and_sub (pkg, str, path);
Packit db3073
}
Packit db3073
Packit db3073
Packit db3073
#define MODULE_SEPARATOR(c) ((c) == ',' || isspace ((guchar)(c)))
Packit db3073
#define OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=')
Packit db3073
Packit db3073
/* A module list is a list of modules with optional version specification,
Packit db3073
 * separated by commas and/or spaces. Commas are treated just like whitespace,
Packit db3073
 * in order to allow stuff like: Requires: @FRIBIDI_PC@, glib, gmodule
Packit db3073
 * where @FRIBIDI_PC@ gets substituted to nothing or to 'fribidi'
Packit db3073
 */
Packit db3073
Packit db3073
typedef enum
Packit db3073
{
Packit db3073
  /* put numbers to help interpret lame debug spew ;-) */
Packit db3073
  OUTSIDE_MODULE = 0,
Packit db3073
  IN_MODULE_NAME = 1,
Packit db3073
  BEFORE_OPERATOR = 2,
Packit db3073
  IN_OPERATOR = 3,
Packit db3073
  AFTER_OPERATOR = 4,
Packit db3073
  IN_MODULE_VERSION = 5  
Packit db3073
} ModuleSplitState;
Packit db3073
Packit db3073
#define PARSE_SPEW 0
Packit db3073
Packit db3073
static GList *
Packit db3073
split_module_list (const char *str, const char *path)
Packit db3073
{
Packit db3073
  GList *retval = NULL;
Packit db3073
  const char *p;
Packit db3073
  const char *start;
Packit db3073
  ModuleSplitState state = OUTSIDE_MODULE;
Packit db3073
  ModuleSplitState last_state = OUTSIDE_MODULE;
Packit db3073
Packit db3073
  /*   fprintf (stderr, "Parsing: '%s'\n", str); */
Packit db3073
  
Packit db3073
  start = str;
Packit db3073
  p = str;
Packit db3073
Packit db3073
  while (*p)
Packit db3073
    {
Packit db3073
#if PARSE_SPEW
Packit db3073
      fprintf (stderr, "p: %c state: %d last_state: %d\n", *p, state, last_state);
Packit db3073
#endif
Packit db3073
      
Packit db3073
      switch (state)
Packit db3073
        {
Packit db3073
        case OUTSIDE_MODULE:
Packit db3073
          if (!MODULE_SEPARATOR (*p))
Packit db3073
            state = IN_MODULE_NAME;          
Packit db3073
          break;
Packit db3073
Packit db3073
        case IN_MODULE_NAME:
Packit db3073
          if (isspace ((guchar)*p))
Packit db3073
            {
Packit db3073
              /* Need to look ahead to determine next state */
Packit db3073
              const char *s = p;
Packit db3073
              while (*s && isspace ((guchar)*s))
Packit db3073
                ++s;
Packit db3073
Packit db3073
              if (*s == '\0')
Packit db3073
                state = OUTSIDE_MODULE;
Packit db3073
              else if (MODULE_SEPARATOR (*s))
Packit db3073
                state = OUTSIDE_MODULE;
Packit db3073
              else if (OPERATOR_CHAR (*s))
Packit db3073
                state = BEFORE_OPERATOR;
Packit db3073
              else
Packit db3073
                state = OUTSIDE_MODULE;
Packit db3073
            }
Packit db3073
          else if (MODULE_SEPARATOR (*p))
Packit db3073
            state = OUTSIDE_MODULE; /* comma precludes any operators */
Packit db3073
          break;
Packit db3073
Packit db3073
        case BEFORE_OPERATOR:
Packit db3073
          /* We know an operator is coming up here due to lookahead from
Packit db3073
           * IN_MODULE_NAME
Packit db3073
           */
Packit db3073
          if (isspace ((guchar)*p))
Packit db3073
            ; /* no change */
Packit db3073
          else if (OPERATOR_CHAR (*p))
Packit db3073
            state = IN_OPERATOR;
Packit db3073
          else
Packit db3073
            g_assert_not_reached ();
Packit db3073
          break;
Packit db3073
Packit db3073
        case IN_OPERATOR:
Packit db3073
          if (!OPERATOR_CHAR (*p))
Packit db3073
            state = AFTER_OPERATOR;
Packit db3073
          break;
Packit db3073
Packit db3073
        case AFTER_OPERATOR:
Packit db3073
          if (!isspace ((guchar)*p))
Packit db3073
            state = IN_MODULE_VERSION;
Packit db3073
          break;
Packit db3073
Packit db3073
        case IN_MODULE_VERSION:
Packit db3073
          if (MODULE_SEPARATOR (*p))
Packit db3073
            state = OUTSIDE_MODULE;
Packit db3073
          break;
Packit db3073
          
Packit db3073
        default:
Packit db3073
          g_assert_not_reached ();
Packit db3073
        }
Packit db3073
Packit db3073
      if (state == OUTSIDE_MODULE &&
Packit db3073
          last_state != OUTSIDE_MODULE)
Packit db3073
        {
Packit db3073
          /* We left a module */
Packit db3073
          char *module = g_strndup (start, p - start);
Packit db3073
          retval = g_list_prepend (retval, module);
Packit db3073
Packit db3073
#if PARSE_SPEW
Packit db3073
          fprintf (stderr, "found module: '%s'\n", module);
Packit db3073
#endif
Packit db3073
          
Packit db3073
          /* reset start */
Packit db3073
          start = p;
Packit db3073
        }
Packit db3073
      
Packit db3073
      last_state = state;
Packit db3073
      ++p;
Packit db3073
    }
Packit db3073
Packit db3073
  if (p != start)
Packit db3073
    {
Packit db3073
      /* get the last module */
Packit db3073
      char *module = g_strndup (start, p - start);
Packit db3073
      retval = g_list_prepend (retval, module);
Packit db3073
Packit db3073
#if PARSE_SPEW
Packit db3073
      fprintf (stderr, "found module: '%s'\n", module);
Packit db3073
#endif
Packit db3073
      
Packit db3073
    }
Packit db3073
  
Packit db3073
  retval = g_list_reverse (retval);
Packit db3073
Packit db3073
  return retval;
Packit db3073
}
Packit db3073
Packit db3073
GList *
Packit db3073
parse_module_list (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  GList *split;
Packit db3073
  GList *iter;
Packit db3073
  GList *retval = NULL;
Packit db3073
Packit db3073
  split = split_module_list (str, path);
Packit db3073
  
Packit db3073
  iter = split;
Packit db3073
  while (iter != NULL)
Packit db3073
    {
Packit db3073
      RequiredVersion *ver;
Packit db3073
      char *p;
Packit db3073
      char *start;
Packit db3073
      
Packit db3073
      p = iter->data;
Packit db3073
Packit db3073
      ver = g_new0 (RequiredVersion, 1);
Packit db3073
      ver->comparison = ALWAYS_MATCH;
Packit db3073
      ver->owner = pkg;
Packit db3073
      retval = g_list_prepend (retval, ver);
Packit db3073
      
Packit db3073
      while (*p && MODULE_SEPARATOR (*p))
Packit db3073
        ++p;
Packit db3073
      
Packit db3073
      start = p;
Packit db3073
Packit db3073
      while (*p && !isspace ((guchar)*p))
Packit db3073
        ++p;
Packit db3073
Packit db3073
      while (*p && MODULE_SEPARATOR (*p))
Packit db3073
        {
Packit db3073
          *p = '\0';
Packit db3073
          ++p;
Packit db3073
        }
Packit db3073
Packit db3073
      if (*start == '\0')
Packit db3073
        {
Packit db3073
          verbose_error ("Empty package name in Requires or Conflicts in file '%s'\n", path);
Packit db3073
          
Packit db3073
          exit (1);
Packit db3073
        }
Packit db3073
      
Packit db3073
      ver->name = g_strdup (start);
Packit db3073
Packit db3073
      start = p;
Packit db3073
Packit db3073
      while (*p && !isspace ((guchar)*p))
Packit db3073
        ++p;
Packit db3073
Packit db3073
      while (*p && isspace ((guchar)*p))
Packit db3073
        {
Packit db3073
          *p = '\0';
Packit db3073
          ++p;
Packit db3073
        }
Packit db3073
      
Packit db3073
      if (*start != '\0')
Packit db3073
        {
Packit db3073
          if (strcmp (start, "=") == 0)
Packit db3073
            ver->comparison = EQUAL;
Packit db3073
          else if (strcmp (start, ">=") == 0)
Packit db3073
            ver->comparison = GREATER_THAN_EQUAL;
Packit db3073
          else if (strcmp (start, "<=") == 0)
Packit db3073
            ver->comparison = LESS_THAN_EQUAL;
Packit db3073
          else if (strcmp (start, ">") == 0)
Packit db3073
            ver->comparison = GREATER_THAN;
Packit db3073
          else if (strcmp (start, "<") == 0)
Packit db3073
            ver->comparison = LESS_THAN;
Packit db3073
          else if (strcmp (start, "!=") == 0)
Packit db3073
            ver->comparison = NOT_EQUAL;
Packit db3073
          else
Packit db3073
            {
Packit db3073
              verbose_error ("Unknown version comparison operator '%s' after package name '%s' in file '%s'\n", start, ver->name, path);
Packit db3073
              
Packit db3073
              exit (1);
Packit db3073
            }
Packit db3073
        }
Packit db3073
Packit db3073
      start = p;
Packit db3073
      
Packit db3073
      while (*p && !MODULE_SEPARATOR (*p))
Packit db3073
        ++p;
Packit db3073
Packit db3073
      while (*p && MODULE_SEPARATOR (*p))
Packit db3073
        {
Packit db3073
          *p = '\0';
Packit db3073
          ++p;
Packit db3073
        }
Packit db3073
      
Packit db3073
      if (ver->comparison != ALWAYS_MATCH && *start == '\0')
Packit db3073
        {
Packit db3073
          verbose_error ("Comparison operator but no version after package name '%s' in file '%s'\n", ver->name, path);
Packit db3073
          
Packit db3073
          exit (1);
Packit db3073
        }
Packit db3073
Packit db3073
      if (*start != '\0')
Packit db3073
        {
Packit db3073
          ver->version = g_strdup (start);
Packit db3073
        }
Packit db3073
Packit db3073
      g_assert (ver->name);
Packit db3073
      
Packit db3073
      iter = g_list_next (iter);
Packit db3073
    }
Packit db3073
Packit db3073
  g_list_foreach (split, (GFunc) g_free, NULL);
Packit db3073
  g_list_free (split);
Packit db3073
Packit db3073
  retval = g_list_reverse (retval);
Packit db3073
Packit db3073
  return retval;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_requires (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  char *trimmed;
Packit db3073
Packit db3073
  if (pkg->requires)
Packit db3073
    {
Packit db3073
      verbose_error ("Requires field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
  pkg->requires_entries = parse_module_list (pkg, trimmed, path);
Packit db3073
  g_free (trimmed);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_requires_private (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  char *trimmed;
Packit db3073
Packit db3073
  if (pkg->requires_private)
Packit db3073
    {
Packit db3073
      verbose_error ("Requires.private field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
  pkg->requires_private_entries = parse_module_list (pkg, trimmed, path);
Packit db3073
  g_free (trimmed);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_conflicts (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  char *trimmed;
Packit db3073
  
Packit db3073
  if (pkg->conflicts)
Packit db3073
    {
Packit db3073
      verbose_error ("Conflicts field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
  pkg->conflicts = parse_module_list (pkg, trimmed, path);
Packit db3073
  g_free (trimmed);
Packit db3073
}
Packit db3073
Packit db3073
static char *strdup_escape_shell(const char *s)
Packit db3073
{
Packit db3073
	size_t r_s = strlen(s)+10, c = 0;
Packit db3073
	char *r = g_malloc(r_s);
Packit db3073
	while (s[0]) {
Packit db3073
		if ((s[0] < '$') ||
Packit db3073
		    (s[0] > '$' && s[0] < '(') ||
Packit db3073
		    (s[0] > ')' && s[0] < '+') ||
Packit db3073
		    (s[0] > ':' && s[0] < '=') ||
Packit db3073
		    (s[0] > '=' && s[0] < '@') ||
Packit db3073
		    (s[0] > 'Z' && s[0] < '^') ||
Packit db3073
		    (s[0] == '`') ||
Packit db3073
		    (s[0] > 'z' && s[0] < '~') ||
Packit db3073
		    (s[0] > '~')) {
Packit db3073
			r[c] = '\\';
Packit db3073
			c++;
Packit db3073
		}
Packit db3073
		r[c] = *s;
Packit db3073
		c++;
Packit db3073
		if (c+2 >= r_s) {
Packit db3073
			r_s *= 2;
Packit db3073
			r = g_realloc(r, r_s);
Packit db3073
		}
Packit db3073
		s++;
Packit db3073
	}
Packit db3073
	r[c] = 0;
Packit db3073
	return r;
Packit db3073
}
Packit db3073
Packit db3073
static void _do_parse_libs (Package *pkg, int argc, char **argv)
Packit db3073
{
Packit db3073
  int i;
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
  char *L_flag = (msvc_syntax ? "/libpath:" : "-L");
Packit db3073
  char *l_flag = (msvc_syntax ? "" : "-l");
Packit db3073
  char *lib_suffix = (msvc_syntax ? ".lib" : "");
Packit db3073
#else
Packit db3073
  char *L_flag = "-L";
Packit db3073
  char *l_flag = "-l";
Packit db3073
  char *lib_suffix = "";
Packit db3073
#endif
Packit db3073
Packit db3073
  i = 0;
Packit db3073
  while (i < argc)
Packit db3073
    {
Packit db3073
      Flag *flag = g_new (Flag, 1);
Packit db3073
      char *tmp = trim_string (argv[i]);
Packit db3073
      char *arg = strdup_escape_shell(tmp);
Packit db3073
      char *p;
Packit db3073
      p = arg;
Packit db3073
      g_free(tmp);
Packit db3073
Packit db3073
      if (p[0] == '-' &&
Packit db3073
          p[1] == 'l' &&
Packit db3073
	  /* -lib: is used by the C# compiler for libs; it's not an -l
Packit db3073
              flag. */
Packit db3073
	  (strncmp(p, "-lib:", 5) != 0))
Packit db3073
        {
Packit db3073
          p += 2;
Packit db3073
          while (*p && isspace ((guchar)*p))
Packit db3073
            ++p;
Packit db3073
Packit db3073
          flag->type = LIBS_l;
Packit db3073
          flag->arg = g_strconcat (l_flag, p, lib_suffix, NULL);
Packit db3073
          pkg->libs = g_list_prepend (pkg->libs, flag);
Packit db3073
        }
Packit db3073
      else if (p[0] == '-' &&
Packit db3073
               p[1] == 'L')
Packit db3073
        {
Packit db3073
          p += 2;
Packit db3073
          while (*p && isspace ((guchar)*p))
Packit db3073
            ++p;
Packit db3073
Packit db3073
          flag->type = LIBS_L;
Packit db3073
          flag->arg = g_strconcat (L_flag, p, lib_suffix, NULL);
Packit db3073
          pkg->libs = g_list_prepend (pkg->libs, flag);
Packit db3073
	}
Packit db3073
      else if (strcmp("-framework",p) == 0 && i+1 < argc)
Packit db3073
        {
Packit db3073
          /* Mac OS X has a -framework Foo which is really one option,
Packit db3073
           * so we join those to avoid having -framework Foo
Packit db3073
           * -framework Bar being changed into -framework Foo Bar
Packit db3073
           * later
Packit db3073
          */
Packit db3073
          gchar *framework, *tmp = trim_string (argv[i+1]);
Packit db3073
Packit db3073
          framework = strdup_escape_shell(tmp);
Packit db3073
          flag->type = LIBS_OTHER;
Packit db3073
          flag->arg = g_strconcat (arg, " ", framework, NULL);
Packit db3073
          pkg->libs = g_list_prepend (pkg->libs, flag);
Packit db3073
          i++;
Packit db3073
          g_free (framework);
Packit db3073
          g_free (tmp);
Packit db3073
        }
Packit db3073
      else if (*arg != '\0')
Packit db3073
        {
Packit db3073
          flag->type = LIBS_OTHER;
Packit db3073
          flag->arg = g_strdup (arg);
Packit db3073
          pkg->libs = g_list_prepend (pkg->libs, flag);
Packit db3073
        }
Packit db3073
      else
Packit db3073
        /* flag wasn't used */
Packit db3073
        g_free (flag);
Packit db3073
Packit db3073
      g_free (arg);
Packit db3073
Packit db3073
      ++i;
Packit db3073
    }
Packit db3073
Packit db3073
}
Packit db3073
Packit db3073
Packit db3073
static void
Packit db3073
parse_libs (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  /* Strip out -l and -L flags, put them in a separate list. */
Packit db3073
  
Packit db3073
  char *trimmed;
Packit db3073
  char **argv = NULL;
Packit db3073
  int argc = 0;
Packit db3073
  GError *error = NULL;
Packit db3073
  
Packit db3073
  if (pkg->libs_num > 0)
Packit db3073
    {
Packit db3073
      verbose_error ("Libs field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
Packit db3073
  if (trimmed && *trimmed &&
Packit db3073
      !g_shell_parse_argv (trimmed, &argc, &argv, &error))
Packit db3073
    {
Packit db3073
      verbose_error ("Couldn't parse Libs field into an argument vector: %s\n",
Packit db3073
                     error ? error->message : "unknown");
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  _do_parse_libs(pkg, argc, argv);
Packit db3073
Packit db3073
  g_free (trimmed);
Packit db3073
  g_strfreev (argv);
Packit db3073
  pkg->libs_num++;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_libs_private (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  /*
Packit db3073
    List of private libraries.  Private libraries are libraries which
Packit db3073
    are needed in the case of static linking or on platforms not
Packit db3073
    supporting inter-library dependencies.  They are not supposed to
Packit db3073
    be used for libraries which are exposed through the library in
Packit db3073
    question.  An example of an exposed library is GTK+ exposing Glib.
Packit db3073
    A common example of a private library is libm.
Packit db3073
    
Packit db3073
    Generally, if include another library's headers in your own, it's
Packit db3073
    a public dependency and not a private one.
Packit db3073
  */
Packit db3073
  
Packit db3073
  char *trimmed;
Packit db3073
  char **argv = NULL;
Packit db3073
  int argc = 0;
Packit db3073
  GError *error = NULL;
Packit db3073
  
Packit db3073
  if (pkg->libs_private_num > 0)
Packit db3073
    {
Packit db3073
      verbose_error ("Libs.private field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
Packit db3073
  if (trimmed && *trimmed &&
Packit db3073
      !g_shell_parse_argv (trimmed, &argc, &argv, &error))
Packit db3073
    {
Packit db3073
      verbose_error ("Couldn't parse Libs.private field into an argument vector: %s\n",
Packit db3073
                     error ? error->message : "unknown");
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  _do_parse_libs(pkg, argc, argv);
Packit db3073
Packit db3073
  g_strfreev (argv);
Packit db3073
  g_free (trimmed);
Packit db3073
Packit db3073
  pkg->libs_private_num++;
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_cflags (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  /* Strip out -I flags, put them in a separate list. */
Packit db3073
  
Packit db3073
  char *trimmed;
Packit db3073
  char **argv = NULL;
Packit db3073
  int argc = 0;
Packit db3073
  GError *error = NULL;
Packit db3073
  int i;
Packit db3073
  
Packit db3073
  if (pkg->cflags)
Packit db3073
    {
Packit db3073
      verbose_error ("Cflags field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
  
Packit db3073
  trimmed = trim_and_sub (pkg, str, path);
Packit db3073
Packit db3073
  if (trimmed && *trimmed &&
Packit db3073
      !g_shell_parse_argv (trimmed, &argc, &argv, &error))
Packit db3073
    {
Packit db3073
      verbose_error ("Couldn't parse Cflags field into an argument vector: %s\n",
Packit db3073
                     error ? error->message : "unknown");
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  i = 0;
Packit db3073
  while (i < argc)
Packit db3073
    {
Packit db3073
      Flag *flag = g_new (Flag, 1);
Packit db3073
      char *tmp = trim_string (argv[i]);
Packit db3073
      char *arg = strdup_escape_shell(tmp);
Packit db3073
      char *p = arg;
Packit db3073
      g_free(tmp);
Packit db3073
Packit db3073
      if (p[0] == '-' &&
Packit db3073
          p[1] == 'I')
Packit db3073
        {
Packit db3073
          p += 2;
Packit db3073
          while (*p && isspace ((guchar)*p))
Packit db3073
            ++p;
Packit db3073
Packit db3073
          flag->type = CFLAGS_I;
Packit db3073
          flag->arg = g_strconcat ("-I", p, NULL);
Packit db3073
          pkg->cflags = g_list_prepend (pkg->cflags, flag);
Packit db3073
        }
Packit db3073
      else if (strcmp("-idirafter", arg) == 0 && i+1 < argc)
Packit db3073
        {
Packit db3073
          char *dirafter, *tmp;
Packit db3073
Packit db3073
          tmp = trim_string (argv[i+1]);
Packit db3073
          dirafter = strdup_escape_shell (tmp);
Packit db3073
          flag->type = CFLAGS_OTHER;
Packit db3073
          flag->arg = g_strconcat (arg, " ", dirafter, NULL);
Packit db3073
          pkg->cflags = g_list_prepend (pkg->cflags, flag);
Packit db3073
          i++;
Packit db3073
          g_free (dirafter);
Packit db3073
          g_free (tmp);
Packit db3073
        }
Packit db3073
      else if (*arg != '\0')
Packit db3073
        {
Packit db3073
          flag->type = CFLAGS_OTHER;
Packit db3073
          flag->arg = g_strdup (arg);
Packit db3073
          pkg->cflags = g_list_prepend (pkg->cflags, flag);
Packit db3073
        }
Packit db3073
      else
Packit db3073
        /* flag wasn't used */
Packit db3073
        g_free (flag);
Packit db3073
Packit db3073
      g_free (arg);
Packit db3073
      
Packit db3073
      ++i;
Packit db3073
    }
Packit db3073
Packit db3073
  g_strfreev (argv);
Packit db3073
  g_free (trimmed);
Packit db3073
}
Packit db3073
Packit db3073
static void
Packit db3073
parse_url (Package *pkg, const char *str, const char *path)
Packit db3073
{
Packit db3073
  if (pkg->url != NULL)
Packit db3073
    {
Packit db3073
      verbose_error ("URL field occurs twice in '%s'\n", path);
Packit db3073
Packit db3073
      exit (1);
Packit db3073
    }
Packit db3073
Packit db3073
  pkg->url = trim_and_sub (pkg, str, path);
Packit db3073
}
Packit db3073
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
static char *orig_prefix = NULL;
Packit db3073
Packit db3073
static int
Packit db3073
pathnamecmp (const char *a,
Packit db3073
	     const char *b)
Packit db3073
{
Packit db3073
  while (*a && *b &&
Packit db3073
	 ((G_IS_DIR_SEPARATOR (*a) && G_IS_DIR_SEPARATOR (*b)) ||
Packit db3073
	  g_ascii_toupper (*a) == g_ascii_toupper (*b)))
Packit db3073
    {
Packit db3073
      a++;
Packit db3073
      b++;
Packit db3073
    }
Packit db3073
  return g_ascii_toupper (*a) - g_ascii_toupper (*b);
Packit db3073
}
Packit db3073
#endif
Packit db3073
Packit db3073
static void
Packit db3073
parse_line (Package *pkg, const char *untrimmed, const char *path,
Packit db3073
	    gboolean ignore_requires, gboolean ignore_private_libs,
Packit db3073
	    gboolean ignore_requires_private)
Packit db3073
{
Packit db3073
  char *str;
Packit db3073
  char *p;
Packit db3073
  char *tag;
Packit db3073
Packit db3073
  debug_spew ("  line>%s\n", untrimmed);
Packit db3073
  
Packit db3073
  str = trim_string (untrimmed);
Packit db3073
  
Packit db3073
  if (*str == '\0') /* empty line */
Packit db3073
    {
Packit db3073
      g_free(str);
Packit db3073
      return;
Packit db3073
    }
Packit db3073
  
Packit db3073
  p = str;
Packit db3073
Packit db3073
  /* Get first word */
Packit db3073
  while ((*p >= 'A' && *p <= 'Z') ||
Packit db3073
	 (*p >= 'a' && *p <= 'z') ||
Packit db3073
	 (*p >= '0' && *p <= '9') ||
Packit db3073
	 *p == '_' || *p == '.')
Packit db3073
    p++;
Packit db3073
Packit db3073
  tag = g_strndup (str, p - str);
Packit db3073
  
Packit db3073
  while (*p && isspace ((guchar)*p))
Packit db3073
    ++p;
Packit db3073
Packit db3073
  if (*p == ':')
Packit db3073
    {
Packit db3073
      /* keyword */
Packit db3073
      ++p;
Packit db3073
      while (*p && isspace ((guchar)*p))
Packit db3073
        ++p;
Packit db3073
Packit db3073
      if (strcmp (tag, "Name") == 0)
Packit db3073
        parse_name (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Description") == 0)
Packit db3073
        parse_description (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Version") == 0)
Packit db3073
        parse_version (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Requires.private") == 0)
Packit db3073
	{
Packit db3073
	  if (!ignore_requires_private)
Packit db3073
	    parse_requires_private (pkg, p, path);
Packit db3073
	}
Packit db3073
      else if (strcmp (tag, "Requires") == 0)
Packit db3073
	{
Packit db3073
          if (ignore_requires == FALSE)
Packit db3073
	    parse_requires (pkg, p, path);
Packit db3073
          else
Packit db3073
	    goto cleanup;
Packit db3073
        }
Packit db3073
      else if ((strcmp (tag, "Libs.private") == 0) && 
Packit db3073
               ignore_private_libs == FALSE)
Packit db3073
        parse_libs_private (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Libs") == 0)
Packit db3073
        parse_libs (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Cflags") == 0 ||
Packit db3073
               strcmp (tag, "CFlags") == 0)
Packit db3073
        parse_cflags (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "Conflicts") == 0)
Packit db3073
        parse_conflicts (pkg, p, path);
Packit db3073
      else if (strcmp (tag, "URL") == 0)
Packit db3073
        parse_url (pkg, p, path);
Packit db3073
      else
Packit db3073
        {
Packit db3073
	  /* we don't error out on unknown keywords because they may
Packit db3073
	   * represent additions to the .pc file format from future
Packit db3073
	   * versions of pkg-config.  We do make a note of them in the
Packit db3073
	   * debug spew though, in order to help catch mistakes in .pc
Packit db3073
	   * files. */
Packit db3073
          debug_spew ("Unknown keyword '%s' in '%s'\n",
Packit db3073
		      tag, path);
Packit db3073
        }
Packit db3073
    }
Packit db3073
  else if (*p == '=')
Packit db3073
    {
Packit db3073
      /* variable */
Packit db3073
      char *varname;
Packit db3073
      char *varval;
Packit db3073
      
Packit db3073
      ++p;
Packit db3073
      while (*p && isspace ((guchar)*p))
Packit db3073
        ++p;
Packit db3073
      
Packit db3073
      if (pkg->vars == NULL)
Packit db3073
        pkg->vars = g_hash_table_new (g_str_hash, g_str_equal);
Packit db3073
Packit db3073
#ifdef G_OS_WIN32
Packit db3073
      if (!dont_define_prefix && strcmp (tag, prefix_variable) == 0)
Packit db3073
	{
Packit db3073
	  /* This is the prefix variable. Try to guesstimate a value for it
Packit db3073
	   * for this package from the location of the .pc file.
Packit db3073
	   */
Packit db3073
Packit db3073
	  gchar *prefix = pkg->pcfiledir;
Packit db3073
	  const int prefix_len = strlen (prefix);
Packit db3073
	  const char *const lib_pkgconfig = "\\lib\\pkgconfig";
Packit db3073
	  const char *const share_pkgconfig = "\\share\\pkgconfig";
Packit db3073
	  const int lib_pkgconfig_len = strlen (lib_pkgconfig);
Packit db3073
	  const int share_pkgconfig_len = strlen (share_pkgconfig);
Packit db3073
Packit db3073
	  if ((strlen (prefix) > lib_pkgconfig_len &&
Packit db3073
	       pathnamecmp (prefix + prefix_len - lib_pkgconfig_len, lib_pkgconfig) == 0) ||
Packit db3073
	      (strlen (prefix) > share_pkgconfig_len &&
Packit db3073
	       pathnamecmp (prefix + prefix_len - share_pkgconfig_len, share_pkgconfig) == 0))
Packit db3073
	    {
Packit db3073
	      /* It ends in lib\pkgconfig or share\pkgconfig. Good. */
Packit db3073
	      
Packit db3073
	      gchar *q;
Packit db3073
	      
Packit db3073
	      orig_prefix = g_strdup (p);
Packit db3073
Packit db3073
	      prefix = g_strdup (prefix);
Packit db3073
	      if (strlen (prefix) > lib_pkgconfig_len &&
Packit db3073
		  pathnamecmp (prefix + prefix_len - lib_pkgconfig_len, lib_pkgconfig) == 0)
Packit db3073
		prefix[prefix_len - lib_pkgconfig_len] = '\0';
Packit db3073
	      else
Packit db3073
		prefix[prefix_len - share_pkgconfig_len] = '\0';
Packit db3073
	      
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
	      q = prefix;
Packit db3073
	      while (*q)
Packit db3073
		{
Packit db3073
		  if (*q == '\\')
Packit db3073
		    *q = '/';
Packit db3073
		  q++;
Packit db3073
		}
Packit db3073
Packit db3073
	      /* Now escape the special characters so that there's no danger
Packit db3073
	       * of arguments that include the prefix getting split.
Packit db3073
	       */
Packit db3073
	      q = prefix;
Packit db3073
	      prefix = strdup_escape_shell (prefix);
Packit db3073
	      g_free (q);
Packit db3073
Packit db3073
	      varname = g_strdup (tag);
Packit db3073
	      debug_spew (" Variable declaration, '%s' overridden with '%s'\n",
Packit db3073
			  tag, prefix);
Packit db3073
	      g_hash_table_insert (pkg->vars, varname, prefix);
Packit db3073
	      goto cleanup;
Packit db3073
	    }
Packit db3073
	}
Packit db3073
      else if (!dont_define_prefix &&
Packit db3073
	       orig_prefix != NULL &&
Packit db3073
	       strncmp (p, orig_prefix, strlen (orig_prefix)) == 0 &&
Packit db3073
	       G_IS_DIR_SEPARATOR (p[strlen (orig_prefix)]))
Packit db3073
	{
Packit db3073
	  char *oldstr = str;
Packit db3073
Packit db3073
	  p = str = g_strconcat (g_hash_table_lookup (pkg->vars, prefix_variable), p + strlen (orig_prefix), NULL);
Packit db3073
	  g_free (oldstr);
Packit db3073
	}
Packit db3073
#endif
Packit db3073
Packit db3073
      if (g_hash_table_lookup (pkg->vars, tag))
Packit db3073
        {
Packit db3073
          verbose_error ("Duplicate definition of variable '%s' in '%s'\n",
Packit db3073
                         tag, path);
Packit db3073
Packit db3073
          exit (1);
Packit db3073
        }
Packit db3073
Packit db3073
      varname = g_strdup (tag);
Packit db3073
      varval = trim_and_sub (pkg, p, path);     
Packit db3073
Packit db3073
      debug_spew (" Variable declaration, '%s' has value '%s'\n",
Packit db3073
                  varname, varval);
Packit db3073
      g_hash_table_insert (pkg->vars, varname, varval);
Packit db3073
  
Packit db3073
    }
Packit db3073
Packit db3073
 cleanup:  
Packit db3073
  g_free (str);
Packit db3073
  g_free (tag);
Packit db3073
}
Packit db3073
Packit db3073
Package*
Packit db3073
parse_package_file (const char *path, gboolean ignore_requires,
Packit db3073
		    gboolean ignore_private_libs,
Packit db3073
		    gboolean ignore_requires_private)
Packit db3073
{
Packit db3073
  FILE *f;
Packit db3073
  Package *pkg;
Packit db3073
  GString *str;
Packit db3073
  gboolean one_line = FALSE;
Packit db3073
  
Packit db3073
  f = fopen (path, "r");
Packit db3073
Packit db3073
  if (f == NULL)
Packit db3073
    {
Packit db3073
      verbose_error ("Failed to open '%s': %s\n",
Packit db3073
                     path, strerror (errno));
Packit db3073
      
Packit db3073
      return NULL;
Packit db3073
    }
Packit db3073
Packit db3073
  debug_spew ("Parsing package file '%s'\n", path);
Packit db3073
  
Packit db3073
  pkg = g_new0 (Package, 1);
Packit db3073
Packit db3073
  if (path)
Packit db3073
    {
Packit db3073
      pkg->pcfiledir = g_dirname (path);
Packit db3073
    }
Packit db3073
  else
Packit db3073
    {
Packit db3073
      debug_spew ("No pcfiledir determined for package\n");
Packit db3073
      pkg->pcfiledir = g_strdup ("???????");
Packit db3073
    }
Packit db3073
  
Packit db3073
  str = g_string_new ("");
Packit db3073
Packit db3073
  while (read_one_line (f, str))
Packit db3073
    {
Packit db3073
      one_line = TRUE;
Packit db3073
      
Packit db3073
      parse_line (pkg, str->str, path, ignore_requires, ignore_private_libs,
Packit db3073
		  ignore_requires_private);
Packit db3073
Packit db3073
      g_string_truncate (str, 0);
Packit db3073
    }
Packit db3073
Packit db3073
  if (!one_line)
Packit db3073
    verbose_error ("Package file '%s' appears to be empty\n",
Packit db3073
                   path);
Packit db3073
  g_string_free (str, TRUE);
Packit db3073
  fclose(f);
Packit db3073
Packit db3073
  pkg->cflags = g_list_reverse (pkg->cflags);
Packit db3073
  pkg->libs = g_list_reverse (pkg->libs);
Packit db3073
  
Packit db3073
  return pkg;
Packit db3073
}