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