source: trunk/third/pkgconfig/parse.c @ 18224

Revision 18224, 29.6 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18223, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (C) 2001, 2002 Red Hat Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
18 */
19
20#ifdef HAVE_CONFIG_H
21#include "config.h"
22#endif
23
24#include "parse.h"
25#include <stdio.h>
26#include <errno.h>
27#include <string.h>
28#include <stdlib.h>
29#include <ctype.h>
30#include "popt.h"
31#ifdef HAVE_SYS_WAIT_H
32#include <sys/wait.h>
33#endif
34#include <sys/types.h>
35
36#ifdef G_OS_WIN32
37int dont_define_prefix = FALSE;
38char *prefix_variable = "prefix";
39int msvc_syntax = FALSE;
40#endif
41
42
43/**
44 * Read an entire line from a file into a buffer. Lines may
45 * be delimited with '\n', '\r', '\n\r', or '\r\n'. The delimiter
46 * is not written into the buffer. Text after a '#' character is treated as
47 * a comment and skipped. '\' can be used to escape a # character.
48 * '\' proceding a line delimiter combines adjacent lines. A '\' proceding
49 * any other character is ignored and written into the output buffer
50 * unmodified.
51 *
52 * Return value: %FALSE if the stream was already at an EOF character.
53 **/
54static gboolean
55read_one_line (FILE *stream, GString *str)
56{
57  gboolean quoted = FALSE;
58  gboolean comment = FALSE;
59  int n_read = 0;
60
61  g_string_truncate (str, 0);
62 
63  while (1)
64    {
65      int c;
66     
67      c = getc (stream);
68
69      if (c == EOF)
70        {
71          if (quoted)
72            g_string_append_c (str, '\\');
73         
74          goto done;
75        }
76      else
77        n_read++;
78
79      if (quoted)
80        {
81          quoted = FALSE;
82         
83          switch (c)
84            {
85            case '#':
86              g_string_append_c (str, '#');
87              break;
88            case '\r':
89            case '\n':
90              {
91                int next_c = getc (stream);
92
93                if (!(c == EOF ||
94                      (c == '\r' && next_c == '\n') ||
95                      (c == '\n' && next_c == '\r')))
96                  ungetc (next_c, stream);
97               
98                break;
99              }
100            default:
101              g_string_append_c (str, '\\');         
102              g_string_append_c (str, c);
103            }
104        }
105      else
106        {
107          switch (c)
108            {
109            case '#':
110              comment = TRUE;
111              break;
112            case '\\':
113              if (!comment)
114                quoted = TRUE;
115              break;
116            case '\n':
117              {
118                int next_c = getc (stream);
119
120                if (!(c == EOF ||
121                      (c == '\r' && next_c == '\n') ||
122                      (c == '\n' && next_c == '\r')))
123                  ungetc (next_c, stream);
124
125                goto done;
126              }
127            default:
128              if (!comment)
129                g_string_append_c (str, c);
130            }
131        }
132    }
133
134 done:
135
136  return n_read > 0;
137}
138
139static char *
140trim_string (const char *str)
141{
142  int len;
143
144  g_return_val_if_fail (str != NULL, NULL);
145 
146  while (*str && isspace ((guchar)*str))
147    str++;
148
149  len = strlen (str);
150  while (len > 0 && isspace ((guchar)str[len-1]))
151    len--;
152
153  return g_strndup (str, len);
154}
155
156static char *
157trim_and_sub (Package *pkg, const char *str, const char *path)
158{
159  char *trimmed;
160  GString *subst;
161  char *p;
162 
163  trimmed = trim_string (str);
164
165  subst = g_string_new ("");
166
167  p = trimmed;
168  while (*p)
169    {
170      if (p[0] == '$' &&
171          p[1] == '$')
172        {
173          /* escaped % */
174          g_string_append_c (subst, '%');
175          p += 2;
176        }
177      else if (p[0] == '$' &&
178               p[1] == '{')
179        {
180          /* variable */
181          char *var_start;
182          char *varname;
183          char *varval;
184         
185          var_start = &p[2];
186
187          /* Get up to close brace. */
188          while (*p && *p != '}')
189            ++p;
190
191          varname = g_strndup (var_start, p - var_start);
192
193          ++p; /* past brace */
194         
195          varval = package_get_var (pkg, varname);
196         
197          if (varval == NULL)
198            {
199              verbose_error ("Variable '%s' not defined in '%s'\n",
200                             varname, path);
201             
202              exit (1);
203            }
204
205          g_free (varname);
206
207          g_string_append (subst, varval);
208        }
209      else
210        {
211          g_string_append_c (subst, *p);
212
213          ++p;         
214        }
215    }
216
217  g_free (trimmed);
218  p = subst->str;
219  g_string_free (subst, FALSE);
220
221  return p;
222}
223
224static void
225parse_name (Package *pkg, const char *str, const char *path)
226{
227  if (pkg->name)
228    {
229      verbose_error ("Name field occurs twice in '%s'\n", path);
230
231      exit (1);
232    }
233 
234  pkg->name = trim_and_sub (pkg, str, path);
235}
236
237static void
238parse_version (Package *pkg, const char *str, const char *path)
239{
240  if (pkg->version)
241    {
242      verbose_error ("Version field occurs twice in '%s'\n", path);
243
244      exit (1);
245    }
246 
247  pkg->version = trim_and_sub (pkg, str, path);
248}
249
250static void
251parse_description (Package *pkg, const char *str, const char *path)
252{
253  if (pkg->description)
254    {
255      verbose_error ("Description field occurs twice in '%s'\n", path);
256
257      exit (1);
258    }
259 
260  pkg->description = trim_and_sub (pkg, str, path);
261}
262
263
264#define MODULE_SEPARATOR(c) ((c) == ',' || isspace ((guchar)(c)))
265#define OPERATOR_CHAR(c) ((c) == '<' || (c) == '>' || (c) == '!' || (c) == '=')
266
267/* A module list is a list of modules with optional version specification,
268 * separated by commas and/or spaces. Commas are treated just like whitespace,
269 * in order to allow stuff like: Requires: @FRIBIDI_PC@, glib, gmodule
270 * where @FRIBIDI_PC@ gets substituted to nothing or to 'fribidi'
271 */
272
273typedef enum
274{
275  /* put numbers to help interpret lame debug spew ;-) */
276  OUTSIDE_MODULE = 0,
277  IN_MODULE_NAME = 1,
278  BEFORE_OPERATOR = 2,
279  IN_OPERATOR = 3,
280  AFTER_OPERATOR = 4,
281  IN_MODULE_VERSION = 5 
282} ModuleSplitState;
283
284#define PARSE_SPEW 0
285
286static GSList*
287split_module_list (const char *str, const char *path)
288{
289  GSList *retval = NULL;
290  const char *p;
291  const char *start;
292  ModuleSplitState state = OUTSIDE_MODULE;
293  ModuleSplitState last_state = OUTSIDE_MODULE;
294
295  /*   fprintf (stderr, "Parsing: '%s'\n", str); */
296 
297  start = str;
298  p = str;
299
300  while (*p)
301    {
302#if PARSE_SPEW
303      fprintf (stderr, "p: %c state: %d last_state: %d\n", *p, state, last_state);
304#endif
305     
306      switch (state)
307        {
308        case OUTSIDE_MODULE:
309          if (!MODULE_SEPARATOR (*p))
310            state = IN_MODULE_NAME;         
311          break;
312
313        case IN_MODULE_NAME:
314          if (isspace ((guchar)*p))
315            {
316              /* Need to look ahead to determine next state */
317              const char *s = p;
318              while (*s && isspace ((guchar)*s))
319                ++s;
320
321              if (*s == '\0')
322                state = OUTSIDE_MODULE;
323              else if (MODULE_SEPARATOR (*s))
324                state = OUTSIDE_MODULE;
325              else if (OPERATOR_CHAR (*s))
326                state = BEFORE_OPERATOR;
327              else
328                state = OUTSIDE_MODULE;
329            }
330          else if (MODULE_SEPARATOR (*p))
331            state = OUTSIDE_MODULE; /* comma precludes any operators */
332          break;
333
334        case BEFORE_OPERATOR:
335          /* We know an operator is coming up here due to lookahead from
336           * IN_MODULE_NAME
337           */
338          if (isspace ((guchar)*p))
339            ; /* no change */
340          else if (OPERATOR_CHAR (*p))
341            state = IN_OPERATOR;
342          else
343            g_assert_not_reached ();
344          break;
345
346        case IN_OPERATOR:
347          if (!OPERATOR_CHAR (*p))
348            state = AFTER_OPERATOR;
349          break;
350
351        case AFTER_OPERATOR:
352          if (!isspace ((guchar)*p))
353            state = IN_MODULE_VERSION;
354          break;
355
356        case IN_MODULE_VERSION:
357          if (MODULE_SEPARATOR (*p))
358            state = OUTSIDE_MODULE;
359          break;
360         
361        default:
362          g_assert_not_reached ();
363        }
364
365      if (state == OUTSIDE_MODULE &&
366          last_state != OUTSIDE_MODULE)
367        {
368          /* We left a module */
369          char *module = g_strndup (start, p - start);
370          retval = g_slist_prepend (retval, module);
371
372#if PARSE_SPEW
373          fprintf (stderr, "found module: '%s'\n", module);
374#endif
375         
376          /* reset start */
377          start = p;
378        }
379     
380      last_state = state;
381      ++p;
382    }
383
384  if (p != start)
385    {
386      /* get the last module */
387      char *module = g_strndup (start, p - start);
388      retval = g_slist_prepend (retval, module);
389
390#if PARSE_SPEW
391      fprintf (stderr, "found module: '%s'\n", module);
392#endif
393     
394    }
395 
396  retval = g_slist_reverse (retval);
397
398  return retval;
399}
400
401GSList*
402parse_module_list (Package *pkg, const char *str, const char *path)
403{
404  GSList *split;
405  GSList *iter;
406  GSList *retval = NULL;
407
408  split = split_module_list (str, path);
409 
410  iter = split;
411  while (iter != NULL)
412    {
413      RequiredVersion *ver;
414      char *p;
415      char *start;
416     
417      p = iter->data;
418
419      ver = g_new0 (RequiredVersion, 1);
420      ver->comparison = ALWAYS_MATCH;
421      ver->owner = pkg;
422      retval = g_slist_prepend (retval, ver);
423     
424      while (*p && MODULE_SEPARATOR (*p))
425        ++p;
426     
427      start = p;
428
429      while (*p && !isspace ((guchar)*p))
430        ++p;
431
432      while (*p && MODULE_SEPARATOR (*p))
433        {
434          *p = '\0';
435          ++p;
436        }
437
438      if (*start == '\0')
439        {
440          verbose_error ("Empty package name in Requires or Conflicts in file '%s'\n", path);
441         
442          exit (1);
443        }
444     
445      ver->name = g_strdup (start);
446
447      start = p;
448
449      while (*p && !isspace ((guchar)*p))
450        ++p;
451
452      while (*p && isspace ((guchar)*p))
453        {
454          *p = '\0';
455          ++p;
456        }
457     
458      if (*start != '\0')
459        {
460          if (strcmp (start, "=") == 0)
461            ver->comparison = EQUAL;
462          else if (strcmp (start, ">=") == 0)
463            ver->comparison = GREATER_THAN_EQUAL;
464          else if (strcmp (start, "<=") == 0)
465            ver->comparison = LESS_THAN_EQUAL;
466          else if (strcmp (start, ">") == 0)
467            ver->comparison = GREATER_THAN;
468          else if (strcmp (start, "<") == 0)
469            ver->comparison = LESS_THAN;
470          else if (strcmp (start, "!=") == 0)
471            ver->comparison = NOT_EQUAL;
472          else
473            {
474              verbose_error ("Unknown version comparison operator '%s' after package name '%s' in file '%s'\n", start, ver->name, path);
475             
476              exit (1);
477            }
478        }
479
480      start = p;
481     
482      while (*p && !MODULE_SEPARATOR (*p))
483        ++p;
484
485      while (*p && MODULE_SEPARATOR (*p))
486        {
487          *p = '\0';
488          ++p;
489        }
490     
491      if (ver->comparison != ALWAYS_MATCH && *start == '\0')
492        {
493          verbose_error ("Comparison operator but no version after package name '%s' in file '%s'\n", ver->name, path);
494         
495          exit (1);
496        }
497
498      if (*start != '\0')
499        {
500          ver->version = g_strdup (start);
501        }
502
503      g_assert (ver->name);
504     
505      iter = g_slist_next (iter);
506    }
507
508  g_slist_foreach (split, (GFunc) g_free, NULL);
509  g_slist_free (split);
510
511  retval = g_slist_reverse (retval);
512
513  return retval;
514}
515
516static void
517parse_requires (Package *pkg, const char *str, const char *path)
518{
519  GSList *parsed;
520  GSList *iter;
521  char *trimmed;
522 
523  if (pkg->requires)
524    {
525      verbose_error ("Requires field occurs twice in '%s'\n", path);
526
527      exit (1);
528    }
529
530  trimmed = trim_and_sub (pkg, str, path);
531  parsed = parse_module_list (pkg, trimmed, path);
532  g_free (trimmed);
533 
534  iter = parsed;
535  while (iter != NULL)
536    {
537      Package *req;
538      RequiredVersion *ver = iter->data;
539     
540      req = get_package (ver->name);
541
542      if (req == NULL)
543        {
544          verbose_error ("Package '%s', required by '%s', not found\n",
545                         ver->name, pkg->name ? pkg->name : path);
546         
547          exit (1);
548        }
549
550      if (pkg->required_versions == NULL)
551        pkg->required_versions = g_hash_table_new (g_str_hash, g_str_equal);
552     
553      g_hash_table_insert (pkg->required_versions, ver->name, ver);
554     
555      pkg->requires = g_slist_prepend (pkg->requires, req);
556
557      iter = g_slist_next (iter);
558    }
559
560  g_slist_free (parsed);
561 
562  pkg->requires = g_slist_reverse (pkg->requires);
563}
564
565static void
566parse_conflicts (Package *pkg, const char *str, const char *path)
567{
568  GSList *parsed;
569  GSList *iter;
570  char *trimmed;
571 
572  if (pkg->conflicts)
573    {
574      verbose_error ("Conflicts field occurs twice in '%s'\n", path);
575
576      exit (1);
577    }
578
579  trimmed = trim_and_sub (pkg, str, path);
580  pkg->conflicts = parse_module_list (pkg, trimmed, path);
581  g_free (trimmed);
582}
583
584static void
585parse_libs (Package *pkg, const char *str, const char *path)
586{
587  /* Strip out -l and -L flags, put them in a separate list. */
588 
589  char *trimmed;
590  char **argv = NULL;
591  int argc;
592  int result;
593  int i;
594#ifdef G_OS_WIN32
595  char *L_flag = (msvc_syntax ? "/libpath:" : "-L");
596  char *l_flag = (msvc_syntax ? "" : "-l");
597  char *lib_suffix = (msvc_syntax ? ".lib" : "");
598#else
599  char *L_flag = "-L";
600  char *l_flag = "-l";
601  char *lib_suffix = "";
602#endif
603 
604  if (pkg->l_libs || pkg->L_libs || pkg->other_libs)
605    {
606      verbose_error ("Libs field occurs twice in '%s'\n", path);
607
608      exit (1);
609    }
610 
611  trimmed = trim_and_sub (pkg, str, path);
612
613  result = poptParseArgvString (trimmed, &argc, &argv);
614
615  if (result < 0)
616    {
617      verbose_error ("Couldn't parse Libs field into an argument vector: %s\n",
618               poptStrerror (result));
619
620      exit (1);
621    }
622
623  i = 0;
624  while (i < argc)
625    {
626      char *arg = trim_string (argv[i]);
627      char *p;
628      char *start;
629
630      start = arg;
631      p = start;     
632
633      if (p[0] == '-' &&
634          p[1] == 'l')
635        {
636          char *libname;         
637             
638          p += 2;
639          while (*p && isspace ((guchar)*p))
640            ++p;
641             
642          start = p;
643          while (*p && !isspace ((guchar)*p))
644            ++p;
645
646          libname = g_strndup (start, p - start);
647         
648          pkg->l_libs = g_slist_prepend (pkg->l_libs,
649                                         g_strconcat (l_flag, libname, lib_suffix, NULL));
650
651          g_free (libname);
652        }
653      else if (p[0] == '-' &&
654               p[1] == 'L')
655        {
656          char *libname;         
657         
658          p += 2;
659          while (*p && isspace ((guchar)*p))
660            ++p;
661             
662          start = p;
663          while (*p && !isspace ((guchar)*p))
664            ++p;
665
666          libname = g_strndup (start, p - start);
667         
668          pkg->L_libs = g_slist_prepend (pkg->L_libs,
669                                         g_strconcat (L_flag, libname, NULL));
670
671          g_free (libname);
672        }
673      else
674        {
675          if (*arg != '\0')
676            pkg->other_libs = g_slist_prepend (pkg->other_libs,
677                                               g_strdup (arg));
678        }
679
680      g_free (arg);
681     
682      ++i;
683    }
684
685  g_free (argv);
686  g_free (trimmed);
687
688  pkg->l_libs = g_slist_reverse (pkg->l_libs);
689  pkg->L_libs = g_slist_reverse (pkg->L_libs);
690  pkg->other_libs = g_slist_reverse (pkg->other_libs);
691}
692     
693static void
694parse_cflags (Package *pkg, const char *str, const char *path)
695{
696  /* Strip out -I flags, put them in a separate list. */
697 
698  char *trimmed;
699  char **argv = NULL;
700  int argc;
701  int result;
702  int i;
703 
704  if (pkg->I_cflags || pkg->other_cflags)
705    {
706      verbose_error ("Cflags field occurs twice in '%s'\n", path);
707
708      exit (1);
709    }
710 
711  trimmed = trim_and_sub (pkg, str, path);
712
713  result = poptParseArgvString (trimmed, &argc, &argv);
714
715  if (result < 0)
716    {
717      verbose_error ("Couldn't parse Cflags field into an argument vector: %s\n",
718                     poptStrerror (result));
719
720      exit (1);
721    }
722
723  i = 0;
724  while (i < argc)
725    {
726      char *arg = trim_string (argv[i]);
727      char *p;
728      char *start;
729
730      start = arg;
731      p = start;     
732
733      if (p[0] == '-' &&
734          p[1] == 'I')
735        {
736          char *libname;         
737             
738          p += 2;
739          while (*p && isspace ((guchar)*p))
740            ++p;
741             
742          start = p;
743          while (*p && !isspace ((guchar)*p))
744            ++p;
745
746          libname = g_strndup (start, p - start);
747         
748          pkg->I_cflags = g_slist_prepend (pkg->I_cflags,
749                                           g_strconcat ("-I", libname, NULL));
750
751          g_free (libname);
752        }
753      else
754        {
755          if (*arg != '\0')
756            pkg->other_cflags = g_slist_prepend (pkg->other_cflags,
757                                                 g_strdup (arg));
758        }
759
760      g_free (arg);
761     
762      ++i;
763    }
764
765  g_free (argv);
766  g_free (trimmed);
767
768  pkg->I_cflags = g_slist_reverse (pkg->I_cflags);
769  pkg->other_cflags = g_slist_reverse (pkg->other_cflags);
770}
771     
772static void
773parse_line (Package *pkg, const char *untrimmed, const char *path)
774{
775  char *str;
776  char *p;
777  char *tag;
778
779  debug_spew ("  line>%s\n", untrimmed);
780 
781  str = trim_string (untrimmed);
782 
783  if (*str == '\0')
784    return; /* empty line */
785 
786  p = str;
787
788  /* Get first word */
789  while ((*p >= 'A' && *p <= 'Z') ||
790         (*p >= 'a' && *p <= 'z') ||
791         (*p >= '0' && *p <= '9') ||
792         *p == '_')
793    p++;
794
795  tag = g_strndup (str, p - str);
796 
797  while (*p && isspace ((guchar)*p))
798    ++p;
799
800  if (*p == ':')
801    {
802      /* keyword */
803      ++p;
804      while (*p && isspace ((guchar)*p))
805        ++p;
806
807      if (strcmp (tag, "Name") == 0)
808        parse_name (pkg, p, path);
809      else if (strcmp (tag, "Description") == 0)
810        parse_description (pkg, p, path);
811      else if (strcmp (tag, "Version") == 0)
812        parse_version (pkg, p, path);
813      else if (strcmp (tag, "Requires") == 0)
814        parse_requires (pkg, p, path);
815      else if (strcmp (tag, "Libs") == 0)
816        parse_libs (pkg, p, path);
817      else if (strcmp (tag, "Cflags") == 0 ||
818               strcmp (tag, "CFlags") == 0)
819        parse_cflags (pkg, p, path);
820      else if (strcmp (tag, "Conflicts") == 0)
821        parse_conflicts (pkg, p, path);
822      else
823        {
824          verbose_error ("Unknown keyword '%s' in '%s'\n",
825                         tag, path);
826
827          exit (1);
828        }
829    }
830  else if (*p == '=')
831    {
832      /* variable */
833      char *varname;
834      char *varval;
835     
836      ++p;
837      while (*p && isspace ((guchar)*p))
838        ++p;
839     
840      if (pkg->vars == NULL)
841        pkg->vars = g_hash_table_new (g_str_hash, g_str_equal);
842
843#ifdef G_OS_WIN32
844      if (!dont_define_prefix && strcmp (tag, prefix_variable) == 0)
845        {
846          /* This is the prefix variable. Try to guesstimate a value for it
847           * for this package from the location of the .pc file.
848           */
849
850          gchar *prefix = pkg->pcfiledir;
851          const int prefix_len = strlen (prefix);
852          const char *const lib_pkgconfig = "\\lib\\pkgconfig";
853          const int lib_pkgconfig_len = strlen (lib_pkgconfig);
854
855          if (strlen (prefix) > lib_pkgconfig_len &&
856              g_ascii_strcasecmp (prefix + prefix_len - lib_pkgconfig_len,
857                                  lib_pkgconfig) == 0)
858            {
859              /* It ends in lib\pkgconfig. Good. */
860             
861              gchar *p;
862             
863              prefix = g_strdup (prefix);
864              prefix[prefix_len - lib_pkgconfig_len] = '\0';
865             
866              /* Turn backslashes into slashes or
867               * poptParseArgvString() will eat them when ${prefix}
868               * has been expanded in parse_libs().
869               */
870              p = prefix;
871              while (*p)
872                {
873                  if (*p == '\\')
874                    *p = '/';
875                  p++;
876                }
877              varname = g_strdup (tag);
878              debug_spew (" Variable declaration, '%s' overridden with '%s'\n",
879                          tag, prefix);
880              g_hash_table_insert (pkg->vars, varname, prefix);
881              g_free (str);
882              g_free (tag);
883              return;
884            }
885        }
886#endif
887
888      if (g_hash_table_lookup (pkg->vars, tag))
889        {
890          verbose_error ("Duplicate definition of variable '%s' in '%s'\n",
891                         tag, path);
892
893          exit (1);
894        }
895
896      varname = g_strdup (tag);
897      varval = trim_and_sub (pkg, p, path);     
898
899      debug_spew (" Variable declaration, '%s' has value '%s'\n",
900                  varname, varval);
901      g_hash_table_insert (pkg->vars, varname, varval);
902 
903    }
904 
905  g_free (str);
906  g_free (tag);
907}
908
909Package*
910parse_package_file (const char *path)
911{
912  FILE *f;
913  Package *pkg;
914  GString *str;
915  gboolean one_line = FALSE;
916 
917  f = fopen (path, "r");
918
919  if (f == NULL)
920    {
921      verbose_error ("Failed to open '%s': %s\n",
922                     path, strerror (errno));
923     
924      return NULL;
925    }
926
927  debug_spew ("Parsing package file '%s'\n", path);
928 
929  pkg = g_new0 (Package, 1);
930
931  if (path)
932    {
933      pkg->pcfiledir = g_dirname (path);
934    }
935  else
936    {
937      debug_spew ("No pcfiledir determined for package\n");
938      pkg->pcfiledir = g_strdup ("???????");
939    }
940 
941  str = g_string_new ("");
942
943  while (read_one_line (f, str))
944    {
945      one_line = TRUE;
946     
947      parse_line (pkg, str->str, path);
948
949      g_string_truncate (str, 0);
950    }
951
952  if (!one_line)
953    verbose_error ("Package file '%s' appears to be empty\n",
954                   path);
955 
956  return pkg;
957}
958
959static char *
960backticks (const char *command)
961{
962  FILE *f;
963  char buf[4096];
964  size_t len;
965  int status;
966 
967  f = popen (command, "r");
968
969  if (f == NULL)
970    return NULL;
971 
972  len = fread (buf, 1, 4090, f);
973
974  if (ferror (f))
975    {
976      pclose (f);
977      return NULL;
978    }
979 
980  buf[len] = '\0';
981
982  status = pclose (f);
983
984  return g_strdup (buf);
985}
986
987static gboolean
988try_command (const char *command)
989{
990  int status;
991  char *munged;
992
993#ifdef G_OS_WIN32
994  munged = g_strdup_printf ("%s > NUL", command);
995#else
996  munged = g_strdup_printf ("%s > /dev/null 2>&1", command);
997#endif
998 
999  status = system (munged);
1000
1001  g_free (munged);
1002 
1003#ifdef G_OS_WIN32
1004  return status == 0;
1005#else
1006  return WIFEXITED(status) && (WEXITSTATUS(status) == 0);
1007#endif
1008}
1009
1010Package *
1011get_compat_package (const char *name)
1012{
1013#ifdef G_OS_WIN32
1014  /* There has never been any of these legacy *-config scripts on
1015   * Windows as far as I know. No use trying to execute them, will
1016   * only confuse users to see the "blabla is not recognized as an
1017   * internal or external command, operable program or batch file"
1018   * messages.
1019   */
1020  return NULL;
1021#else
1022
1023  Package *pkg;
1024
1025  if (name_ends_in_uninstalled (name))
1026    debug_spew ("Suspiciously looking for compat package for -uninstalled: %s\n", name);
1027 
1028  debug_spew ("Looking for '%s' using legacy -config scripts\n", name);
1029 
1030  pkg = g_new0 (Package, 1);
1031
1032  pkg->path_position = G_MAXINT;
1033 
1034  if (strcmp (name, "glib") == 0)
1035    {
1036      char *output;
1037
1038      debug_spew ("Calling glib-config\n");
1039     
1040      pkg->version = backticks ("glib-config --version");
1041      if (pkg->version == NULL)
1042        {
1043          g_free (pkg);
1044          return NULL;
1045        }
1046     
1047      pkg->name = g_strdup ("GLib");
1048      pkg->key = g_strdup ("glib");
1049      pkg->description = g_strdup ("C Utility Library");
1050
1051      output = backticks ("glib-config --libs");
1052      parse_libs (pkg, output, "glib-config");
1053      g_free (output);
1054
1055      output = backticks ("glib-config --cflags");
1056      parse_cflags (pkg, output, "glib-config");
1057      g_free (output);
1058
1059      return pkg;
1060    }
1061  else if (strcmp (name, "gtk+") == 0)
1062    {
1063      char *output;
1064
1065      debug_spew ("Calling gtk-config\n");
1066     
1067      pkg->version = backticks ("gtk-config --version");
1068      if (pkg->version == NULL)
1069        {
1070          g_free (pkg);
1071          return NULL;
1072        }
1073     
1074      pkg->name = g_strdup ("GTK+");
1075      pkg->key = g_strdup ("gtk+");
1076      pkg->description = g_strdup ("GIMP Tool Kit");
1077
1078      output = backticks ("gtk-config --libs");
1079      parse_libs (pkg, output, "gtk-config");
1080      g_free (output);
1081
1082      output = backticks ("gtk-config --cflags");
1083      parse_cflags (pkg, output, "gtk-config");
1084      g_free (output);
1085
1086      return pkg;
1087    }
1088  else if (strcmp (name, "libgnomevfs") == 0)
1089    {
1090      char *output;
1091
1092      debug_spew ("Calling gnome-vfs-config\n");
1093     
1094      pkg->version = backticks ("gnome-vfs-config --version");
1095      if (pkg->version == NULL)
1096        {
1097          g_free (pkg);
1098          return NULL;
1099        }
1100     
1101      pkg->name = g_strdup ("GNOME VFS");
1102      pkg->key = g_strdup ("libgnomevfs");
1103      pkg->description = g_strdup ("GNOME Virtual File System");
1104
1105      output = backticks ("gnome-vfs-config --libs");
1106      parse_libs (pkg, output, "gnome-vfs-config");
1107      g_free (output);
1108
1109      output = backticks ("gnome-vfs-config --cflags");
1110      parse_cflags (pkg, output, "gnome-vfs-config");
1111      g_free (output);
1112
1113      return pkg;
1114    }
1115  else if (strcmp (name, "imlib") == 0)
1116    {
1117      char *output;
1118
1119      debug_spew ("Calling imlib-config\n");
1120     
1121      pkg->version = backticks ("imlib-config --version");
1122      if (pkg->version == NULL)
1123        {
1124          g_free (pkg);
1125          return NULL;
1126        }
1127     
1128      pkg->name = g_strdup ("Imlib");
1129      pkg->key = g_strdup ("imlib");
1130      pkg->description = g_strdup ("Imlib image loading library");
1131
1132      output = backticks ("imlib-config --libs-gdk");
1133      parse_libs (pkg, output, "imlib-config");
1134      g_free (output);
1135
1136      output = backticks ("imlib-config --cflags-gdk");
1137      parse_cflags (pkg, output, "imlib-config");
1138      g_free (output);
1139
1140      return pkg;
1141    }
1142  else if (strcmp (name, "orbit-client") == 0)
1143    {
1144      char *output;
1145      char *p;
1146
1147      debug_spew ("Calling orbit-config\n");
1148     
1149      output = backticks ("orbit-config --version");
1150     
1151      if (output == NULL)
1152        {
1153          g_free (pkg);
1154          return NULL;
1155        }
1156
1157      p = output;
1158
1159      while (*p && isspace ((guchar)*p))
1160        ++p;
1161
1162      if (*p == '\0')
1163        {
1164          /* empty output */
1165          g_free (output);
1166          g_free (pkg);
1167          return NULL;
1168        }
1169
1170      /* only heuristic; find a number or . */
1171      while (*p && ! (isdigit ((guchar)*p) || *p == '.'))
1172        ++p;     
1173
1174      pkg->version = g_strdup (p);
1175
1176      g_free (output);
1177     
1178      pkg->name = g_strdup ("ORBit Client");
1179      pkg->key = g_strdup ("orbit-client");
1180      pkg->description = g_strdup ("ORBit Client Libraries");
1181
1182      output = backticks ("orbit-config --libs client");
1183      parse_libs (pkg, output, "orbit-config");
1184      g_free (output);
1185
1186      output = backticks ("orbit-config --cflags client");
1187      parse_cflags (pkg, output, "orbit-config");
1188      g_free (output);
1189
1190      return pkg;
1191    }
1192  else if (strcmp (name, "orbit-server") == 0)
1193    {
1194      char *output;
1195      char *p;
1196
1197      debug_spew ("Calling orbit-config\n");
1198     
1199      output = backticks ("orbit-config --version");
1200     
1201      if (output == NULL)
1202        {
1203          g_free (pkg);
1204          return NULL;
1205        }
1206
1207      p = output;
1208
1209      while (*p && isspace ((guchar)*p))
1210        ++p;
1211
1212      if (*p == '\0')
1213        {
1214          /* empty output */
1215          g_free (output);
1216          g_free (pkg);
1217          return NULL;
1218        }
1219
1220      /* only heuristic; find a number or . */
1221      while (*p && ! (isdigit ((guchar)*p) || *p == '.'))
1222        ++p;     
1223
1224      pkg->version = g_strdup (p);
1225
1226      g_free (output);
1227     
1228      pkg->name = g_strdup ("ORBit Server");
1229      pkg->key = g_strdup ("orbit-server");
1230      pkg->description = g_strdup ("ORBit Server Libraries");
1231
1232      output = backticks ("orbit-config --libs server");
1233      parse_libs (pkg, output, "orbit-config");
1234      g_free (output);
1235
1236      output = backticks ("orbit-config --cflags server");
1237      parse_cflags (pkg, output, "orbit-config");
1238      g_free (output);
1239
1240      return pkg;
1241    }
1242  else
1243    {
1244      /* Check for the module in gnome-config */
1245      char *output;
1246      char *p;
1247      char *command;
1248
1249      debug_spew ("Calling gnome-config\n");
1250     
1251      /* Annoyingly, --modversion doesn't return a failure
1252       * code if the lib is unknown, so we have to use --libs
1253       * for that.
1254       */
1255     
1256      command = g_strdup_printf ("gnome-config --libs %s",
1257                                 name);
1258     
1259      if (!try_command (command))
1260        {
1261          g_free (command);
1262          g_free (pkg);
1263          return NULL;
1264        }
1265      else
1266        g_free (command);
1267     
1268      command = g_strdup_printf ("gnome-config --modversion %s",
1269                                 name);
1270     
1271      output = backticks (command);
1272      g_free (command);
1273      if (output == NULL)
1274        {
1275          g_free (pkg);
1276          return NULL;
1277        }
1278     
1279      /* Unknown modules give "Unknown library `foo'" from gnome-config
1280       * (but on stderr so this is useless, nevermind)
1281       */
1282      if (strstr (output, "Unknown") || *output == '\0')
1283        {
1284          g_free (output);
1285          g_free (pkg);
1286          return NULL;
1287        }
1288
1289      /* gnome-config --modversion gnomeui outputs e.g. "gnome-libs-1.2.4"
1290       * or libglade-0.12
1291       */
1292      p = output;
1293
1294      while (*p && isspace ((guchar)*p))
1295        ++p;
1296
1297      if (*p == '\0')
1298        {
1299          /* empty output */
1300          g_free (output);
1301          g_free (pkg);
1302          return NULL;
1303        }
1304
1305      /* only heuristic; find a number or . */
1306      while (*p && ! (isdigit ((guchar)*p) || *p == '.'))
1307        ++p;     
1308
1309      pkg->version = g_strdup (p);
1310
1311      g_free (output);
1312     
1313      /* Strip newline */
1314      p = pkg->version;
1315      while (*p)
1316        {
1317          if (*p == '\n')
1318            *p = '\0';
1319
1320          ++p;
1321        }
1322     
1323      pkg->name = g_strdup (name);
1324      pkg->key = g_strdup (name);
1325      pkg->description = g_strdup ("No description");
1326
1327      command = g_strdup_printf ("gnome-config --libs %s", name);
1328      output = backticks (command);
1329      g_free (command);
1330      parse_libs (pkg, output, "gnome-config");
1331      g_free (output);
1332
1333      command = g_strdup_printf ("gnome-config --cflags %s", name);
1334      output = backticks (command);
1335      g_free (command);
1336      parse_cflags (pkg, output, "gnome-config");
1337      g_free (output);
1338
1339      return pkg;
1340    }
1341#endif
1342}
Note: See TracBrowser for help on using the repository browser.