source: trunk/third/gstreamer/tools/gst-run.c @ 21005

Revision 21005, 9.8 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21004, which included commits to RCS files with non-trunk default branches.
Line 
1/* GStreamer
2 * Copyright (C) 2004 Thomas Vander Stichele <thomas@apestaart.org>
3 *
4 * gst-run.c: tool to launch GStreamer tools with correct major/minor
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22#ifdef HAVE_CONFIG_H
23#  include "config.h"
24#endif
25
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <errno.h>
30#include <popt.h>
31#include <glib.h>
32
33enum
34{
35  ARG_MM = 1,
36  ARG_LIST_MM,
37  ARG_PRINT,
38  ARG_HELP
39};
40
41/* global statics for option parsing */
42static gboolean _print = FALSE;
43static gchar *_arg_mm = NULL;
44static gboolean _arg_list_mm = FALSE;
45
46/* callback to parse arguments */
47static void
48popt_callback (poptContext context, enum poptCallbackReason reason,
49    const struct poptOption *option, const char *arg, void *data)
50{
51  if (reason == POPT_CALLBACK_REASON_OPTION) {
52    switch (option->val) {
53      case ARG_MM:
54        _arg_mm = g_strdup (arg);
55        break;
56      case ARG_LIST_MM:
57        _arg_list_mm = TRUE;
58        break;
59      case ARG_PRINT:
60        _print = TRUE;
61        break;
62      case ARG_HELP:
63        poptPrintHelp (context, stdout, 0);
64        g_print ("\n");
65        break;
66    }
67  } else {
68    g_print ("Unknown reason for callback\n");
69  }
70}
71
72/* popt options table for the wrapper */
73static struct poptOption wrapper_options[] = {
74  {NULL, '\0',
75        POPT_ARG_CALLBACK,
76      (void *) &popt_callback, 0, NULL, NULL},
77  {"help", '\0',
78        POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
79      NULL, ARG_HELP, ("Show help"), NULL},
80  {"?", '\0',
81        POPT_ARG_NONE | POPT_ARGFLAG_STRIP | POPT_ARGFLAG_ONEDASH
82        | POPT_ARGFLAG_DOC_HIDDEN,
83      NULL, ARG_HELP, NULL, NULL},
84  /* We cheat by specifying -p as long "p" with onedash, so that it
85     also gets stripped properly from our arg flags */
86  {"p", '\0',
87        POPT_ARG_NONE | POPT_ARGFLAG_STRIP | POPT_ARGFLAG_ONEDASH
88        | POPT_ARGFLAG_DOC_HIDDEN,
89      NULL, ARG_PRINT, NULL, NULL},
90  {"print", '\0',
91        POPT_ARG_NONE | POPT_ARGFLAG_STRIP,
92      NULL, ARG_PRINT, ("Print wrapped command line"), NULL},
93  {"gst-mm", '\0',
94        POPT_ARG_STRING | POPT_ARGFLAG_STRIP,
95      NULL, ARG_MM, ("Force major/minor version"), NULL},
96  {"gst-list-mm", '\0',
97        POPT_ARG_NONE | POPT_ARGFLAG_STRIP,
98      NULL, ARG_LIST_MM, ("List found major/minor versions"), NULL},
99  POPT_TABLEEND
100};
101
102/* helper table including our wrapper options */
103static struct poptOption options[] = {
104  {NULL, '\0', POPT_ARG_INCLUDE_TABLE, wrapper_options, 0,
105      "Wrapper options:", NULL},
106  POPT_TABLEEND
107};
108
109
110/* print out the major/minor, which is the hash key */
111static void
112hash_print_key (gchar * key, gchar * value)
113{
114  g_print ("%s\n", (gchar *) key);
115}
116
117static void
118find_highest_version (gchar * key, gchar * value, gchar ** highest)
119{
120  if (*highest == NULL) {
121    /* first value, so just set it */
122    *highest = key;
123  }
124  if (strcmp (key, *highest) > 0)
125    *highest = key;
126}
127
128/* Libtool creates shell scripts named "base" that calls actual binaries as
129 * .libs/lt-base.  If we detect this is a libtool script, unmangle so we
130 * find the right binaries */
131static void
132unmangle_libtool (gchar ** dir, gchar ** base)
133{
134  gchar *new_dir, *new_base;
135
136  if (!*dir)
137    return;
138  if (!*base)
139    return;
140
141  /* we assume libtool when base starts with lt- and dir ends with .libs */
142  if (!g_str_has_prefix (*base, "lt-"))
143    return;
144  if (!g_str_has_suffix (*dir, ".libs"))
145    return;
146
147  new_base = g_strdup (&((*base)[3]));
148  new_dir = g_path_get_dirname (*dir);
149  g_free (*base);
150  g_free (*dir);
151  *base = new_base;
152  *dir = new_dir;
153}
154
155/* Returns a directory path that contains the binary given as an argument.
156 * If the binary given contains a path, it gets looked for in that path.
157 * If it doesn't contain a path, it gets looked for in the standard path.
158 *
159 * The returned string is newly allocated.
160 */
161gchar *
162get_dir_of_binary (const gchar * binary)
163{
164  gchar *base, *dir;
165  gchar *full;
166
167  base = g_path_get_basename (binary);
168  dir = g_path_get_dirname (binary);
169
170  /* if putting these two together yields the same as binary,
171   * then we have the right breakup.  If not, it's because no path was
172   * specified which caused get_basename to return "." */
173  full = g_build_filename (dir, base, NULL);
174
175  if (strcmp (full, binary) != 0) {
176    if (strcmp (dir, ".") != 0) {
177      g_warning ("This should not happen, g_path_get_dirname () has changed.");
178      g_free (base);
179      g_free (dir);
180      g_free (full);
181      return NULL;
182    }
183
184    /* we know no path was specified, so search standard path for binary */
185    g_free (full);
186    full = g_find_program_in_path (base);
187    if (!full) {
188      g_warning ("This should not happen, %s not in standard path.", base);
189      g_free (base);
190      g_free (dir);
191      return NULL;
192    }
193  }
194
195  g_free (base);
196  g_free (dir);
197  dir = g_path_get_dirname (full);
198  g_free (full);
199
200  return dir;
201}
202
203/* Search the given directory for candidate binaries matching the base binary.
204 * Return a GHashTable of major/minor -> directory pairs
205 */
206GHashTable *
207get_candidates (const gchar * dir, const gchar * base)
208{
209  GDir *gdir;
210  GError *error = NULL;
211  const gchar *entry;
212  gchar *path;
213  gchar *suffix;
214
215  gchar *pattern;
216  GPatternSpec *spec;
217
218
219  gchar *test;
220
221  gchar **dirs;
222  gchar **cur;
223
224  GHashTable *candidates = NULL;
225
226  candidates = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
227
228  /* compile our pattern spec */
229  pattern = g_strdup_printf ("%s-*.*", base);
230  spec = g_pattern_spec_new (pattern);
231  g_free (pattern);
232
233  /* get all dirs from the path and prepend with given dir */
234  path = g_strdup_printf ("%s%c%s",
235      dir, G_SEARCHPATH_SEPARATOR, g_getenv ("PATH"));
236  dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0);
237  g_free (path);
238
239  /* check all of these in reverse order by winding to bottom and going up  */
240  cur = &dirs[0];
241  while (*cur)
242    ++cur;
243
244  while (cur != &dirs[0]) {
245    --cur;
246    if (!g_file_test (*cur, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))
247      continue;
248
249    gdir = g_dir_open (*cur, 0, &error);
250    if (!gdir) {
251      g_warning ("Could not open dir %s: %s", *cur, error->message);
252      g_error_free (error);
253      return NULL;
254    }
255    while ((entry = g_dir_read_name (gdir))) {
256      if (g_pattern_match_string (spec, entry)) {
257        gchar *full;
258
259        /* is it executable ? */
260        full = g_build_filename (*cur, entry, NULL);
261        if (!g_file_test (full, G_FILE_TEST_IS_EXECUTABLE)) {
262          g_free (full);
263          continue;
264        }
265        g_free (full);
266
267        /* strip base and dash from it */
268        suffix = g_strdup (&(entry[strlen (base) + 1]));
269
270        /* stricter pattern check: check if it only contains digits or dots */
271        test = g_strdup (suffix);
272        g_strcanon (test, "0123456789.", 'X');
273        if (strstr (test, "X")) {
274          g_free (test);
275          continue;
276        }
277        g_free (test);
278        g_hash_table_insert (candidates, suffix, g_strdup (*cur));
279      }
280    }
281  }
282
283  g_strfreev (dirs);
284  g_pattern_spec_free (spec);
285  return candidates;
286}
287
288int
289main (int argc, char **argv)
290{
291  GHashTable *candidates;
292  gchar *dir;
293  gchar *base;
294  gchar *highest = NULL;
295  gchar *binary;                /* actual binary we're going to run */
296  gchar *path = NULL;           /* and its path */
297  poptContext ctx;
298  int nextopt;
299
300  /* parse command line options */
301  ctx = poptGetContext ("gst-run", argc, (const char **) argv, options, 0);
302  poptReadDefaultConfig (ctx, TRUE);
303  while ((nextopt = poptGetNextOpt (ctx)) > 0)
304    /* keep looping to parse */ ;
305
306  argc = poptStrippedArgv (ctx, argc, argv);
307  argv[argc] = NULL;
308  poptFreeContext (ctx);
309
310  /* detect stuff */
311  dir = get_dir_of_binary (argv[0]);
312  base = g_path_get_basename (argv[0]);
313
314  /* unmangle libtool if necessary */
315  unmangle_libtool (&dir, &base);
316
317  /* get all candidate binaries */
318  candidates = get_candidates (dir, base);
319  g_free (dir);
320
321  if (_arg_mm) {
322    /* if a version was forced, look it up in the hash table */
323    dir = g_hash_table_lookup (candidates, _arg_mm);
324    if (!dir) {
325      g_print ("ERROR: Major/minor %s of tool %s not found.\n", _arg_mm, base);
326      return 1;
327    }
328    binary = g_strdup_printf ("%s-%s", base, _arg_mm);
329  } else {
330    highest = NULL;
331
332    /* otherwise, just look up the highest version */
333    g_hash_table_foreach (candidates, (GHFunc) find_highest_version, &highest);
334    if (highest == NULL) {
335      g_print ("ERROR: No version of tool %s not found.\n", base);
336      return 1;
337    }
338    dir = g_hash_table_lookup (candidates, highest);
339    binary = g_strdup_printf ("%s-%s", base, highest);
340  }
341
342  g_free (base);
343
344  path = g_build_filename (dir, binary, NULL);
345  g_free (binary);
346
347  /* print out list of major/minors we found if asked for */
348  /* FIXME: do them in order by creating a GList of keys and sort them */
349  if (_arg_list_mm) {
350    g_hash_table_foreach (candidates, (GHFunc) hash_print_key, NULL);
351    g_hash_table_destroy (candidates);
352    return 0;
353  }
354
355  /* print out command line if asked for */
356  argv[0] = path;
357  if (_print) {
358    int i;
359
360    for (i = 0; i < argc; ++i) {
361      g_print ("%s", argv[i]);
362      if (i < argc - 1)
363        g_print (" ");
364    }
365    g_print ("\n");
366  }
367
368  /* execute */
369  if (execv (path, argv) == -1) {
370    g_warning ("Error executing %s: %s (%d)", path, g_strerror (errno), errno);
371  }
372  g_free (path);
373
374  return 0;
375}
Note: See TracBrowser for help on using the repository browser.