Initialize Tizen 2.3
[framework/multimedia/gstreamer0.10.git] / mobile / tools / gst-run.c
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 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <errno.h>
32 #include <glib.h>
33
34 /* global statics for option parsing */
35 static gboolean _print = FALSE;
36 static gchar *_arg_mm = NULL;
37 static gboolean _arg_list_mm = FALSE;
38
39 /* popt options table for the wrapper */
40 static GOptionEntry wrapper_options[] = {
41   {"print", 'p', 0, G_OPTION_ARG_NONE, &_print,
42       "print wrapped command line options", NULL},
43   {"gst-mm", 0, 0, G_OPTION_ARG_STRING, &_arg_mm,
44       "Force major/minor version", "VERSION"},
45   {"gst-list-mm", 0, 0, G_OPTION_ARG_NONE, &_arg_list_mm,
46       "List found major/minor versions", NULL},
47   {NULL}
48 };
49
50 /* print out the major/minor, which is the hash key */
51 static void
52 hash_print_key (gchar * key, gchar * value)
53 {
54   g_print ("%s\n", (gchar *) key);
55 }
56
57 /* return value like strcmp, but compares major/minor numerically */
58 static gint
59 compare_major_minor (const gchar * first, const gchar * second)
60 {
61   gchar **firsts, **seconds;
62   gint fmaj, fmin, smaj, smin;
63   gint ret = 0;
64
65   firsts = g_strsplit (first, ".", 0);
66   seconds = g_strsplit (second, ".", 0);
67
68   if (firsts[0] == NULL || firsts[1] == NULL) {
69     ret = -1;
70     goto beach;
71   }
72   if (seconds[0] == NULL || seconds[1] == NULL) {
73     ret = 1;
74     goto beach;
75   }
76
77   fmaj = atoi (firsts[0]);
78   fmin = atoi (firsts[1]);
79   smaj = atoi (seconds[0]);
80   smin = atoi (seconds[1]);
81
82   if (fmaj < smaj) {
83     ret = -1;
84     goto beach;
85   }
86   if (fmaj > smaj) {
87     ret = 1;
88     goto beach;
89   }
90
91   /* fmaj == smaj */
92   if (fmin < smin) {
93     ret = -1;
94     goto beach;
95   }
96   if (fmin > smin) {
97     ret = 1;
98     goto beach;
99   }
100   ret = 0;
101
102 beach:
103   g_strfreev (firsts);
104   g_strfreev (seconds);
105   return ret;
106 }
107
108 static void
109 find_highest_version (gchar * key, gchar * value, gchar ** highest)
110 {
111   if (*highest == NULL) {
112     /* first value, so just set it */
113     *highest = key;
114   }
115   if (compare_major_minor (key, *highest) > 0)
116     *highest = key;
117 }
118
119 /* Libtool creates shell scripts named "base" that calls actual binaries as
120  * .libs/lt-base.  If we detect this is a libtool script, unmangle so we
121  * find the right binaries */
122 static void
123 unmangle_libtool (gchar ** dir, gchar ** base)
124 {
125   gchar *new_dir, *new_base;
126
127   if (!*dir)
128     return;
129   if (!*base)
130     return;
131
132   /* We assume libtool when base starts with "lt-" and dir ends with ".libs".
133    * On Windows libtool doesn't seem to be adding "lt-" prefix. */
134 #ifndef G_OS_WIN32
135   if (!g_str_has_prefix (*base, "lt-"))
136     return;
137 #endif
138
139   if (!g_str_has_suffix (*dir, ".libs"))
140     return;
141
142 #ifndef G_OS_WIN32
143   new_base = g_strdup (&((*base)[3]));
144 #else
145   new_base = g_strdup (*base);
146 #endif
147   new_dir = g_path_get_dirname (*dir);
148   g_free (*base);
149   g_free (*dir);
150   *base = new_base;
151   *dir = new_dir;
152 }
153
154 /* Returns a directory path that contains the binary given as an argument.
155  * If the binary given contains a path, it gets looked for in that path.
156  * If it doesn't contain a path, it gets looked for in the standard path.
157  *
158  * The returned string is newly allocated.
159  */
160 static gchar *
161 get_dir_of_binary (const gchar * binary)
162 {
163   gchar *base, *dir;
164   gchar *full;
165
166   base = g_path_get_basename (binary);
167   dir = g_path_get_dirname (binary);
168
169   /* if putting these two together yields the same as binary,
170    * then we have the right breakup.  If not, it's because no path was
171    * specified which caused get_basename to return "." */
172   full = g_build_filename (dir, base, NULL);
173
174 #ifdef G_OS_WIN32
175
176   /* g_build_filename() should be using the last path separator used in the
177    * input according to the docs, but doesn't actually do that, so we have
178    * to fix up the result. */
179   {
180     gchar *tmp;
181
182     for (tmp = (gchar *) binary + strlen (binary) - 1; tmp >= binary; tmp--) {
183       if (*tmp == '/' || *tmp == '\\') {
184         full[strlen (dir)] = *tmp;
185         break;
186       }
187     }
188   }
189 #endif
190
191   if (strcmp (full, binary) != 0) {
192     if (strcmp (dir, ".") != 0) {
193       g_warning ("This should not happen, g_path_get_dirname () has changed.");
194       g_free (base);
195       g_free (dir);
196       g_free (full);
197       return NULL;
198     }
199
200     /* we know no path was specified, so search standard path for binary */
201     g_free (full);
202     full = g_find_program_in_path (base);
203     if (!full) {
204       g_warning ("This should not happen, %s not in standard path.", base);
205       g_free (base);
206       g_free (dir);
207       return NULL;
208     }
209   }
210
211   g_free (base);
212   g_free (dir);
213   dir = g_path_get_dirname (full);
214   g_free (full);
215
216   return dir;
217 }
218
219 /* Search the given directory for candidate binaries matching the base binary.
220  * Return a GHashTable of major/minor -> directory pairs
221  */
222 static GHashTable *
223 get_candidates (const gchar * dir, const gchar * base)
224 {
225   GDir *gdir;
226   GError *error = NULL;
227   const gchar *entry;
228   gchar *path;
229   gchar *suffix, *copy;
230
231   gchar *pattern;
232   GPatternSpec *spec, *specexe;
233
234   gchar **dirs;
235   gchar **cur;
236
237   GHashTable *candidates = NULL;
238
239   candidates = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
240
241   /* compile our pattern specs */
242   pattern = g_strdup_printf ("%s-*.*", base);
243   spec = g_pattern_spec_new (pattern);
244   g_free (pattern);
245   pattern = g_strdup_printf ("%s-*.*.exe", base);
246   specexe = g_pattern_spec_new (pattern);
247   g_free (pattern);
248
249   /* get all dirs from the path and prepend with given dir */
250   if (dir)
251     path = g_strdup_printf ("%s%c%s",
252         dir, G_SEARCHPATH_SEPARATOR, g_getenv ("PATH"));
253   else
254     path = (gchar *) g_getenv ("PATH");
255   dirs = g_strsplit (path, G_SEARCHPATH_SEPARATOR_S, 0);
256   if (dir)
257     g_free (path);
258
259   /* check all of these in reverse order by winding to bottom and going up  */
260   cur = &dirs[0];
261   while (*cur)
262     ++cur;
263
264   while (cur != &dirs[0]) {
265     --cur;
266     if (!g_file_test (*cur, G_FILE_TEST_EXISTS) ||
267         !g_file_test (*cur, G_FILE_TEST_IS_DIR)) {
268       continue;
269     }
270
271     gdir = g_dir_open (*cur, 0, &error);
272     if (!gdir) {
273       g_warning ("Could not open dir %s: %s", *cur, error->message);
274       g_error_free (error);
275       return NULL;
276     }
277     while ((entry = g_dir_read_name (gdir))) {
278       if (g_pattern_match_string (spec, entry)
279           || g_pattern_match_string (specexe, entry)) {
280         gchar *full;
281
282         /* is it executable ? */
283         full = g_build_filename (*cur, entry, NULL);
284         if (!g_file_test (full, G_FILE_TEST_IS_EXECUTABLE)) {
285           g_free (full);
286           continue;
287         }
288         g_free (full);
289
290         /* strip base and dash from it */
291         suffix = g_strdup (&(entry[strlen (base) + 1]));
292         copy = g_strdup (suffix);
293
294         /* strip possible .exe from copy */
295         if (g_strrstr (copy, ".exe"))
296           g_strrstr (copy, ".exe")[0] = '\0';
297
298         /* stricter pattern check: check if it only contains digits or dots */
299         g_strcanon (copy, "0123456789.", 'X');
300         if (strstr (copy, "X")) {
301           g_free (suffix);
302           g_free (copy);
303           continue;
304         }
305         g_free (copy);
306         g_hash_table_insert (candidates, suffix, g_strdup (*cur));
307       }
308     }
309   }
310
311   g_strfreev (dirs);
312   g_pattern_spec_free (spec);
313   return candidates;
314 }
315
316 int
317 main (int argc, char **argv)
318 {
319   GHashTable *candidates;
320   gchar *dir;
321   gchar *base;
322   gchar *highest = NULL;
323   gchar *binary;                /* actual binary we're going to run */
324   gchar *path = NULL;           /* and its path */
325   gchar *desc;
326   GOptionContext *ctx;
327   GError *err = NULL;
328
329   /* detect stuff */
330   dir = get_dir_of_binary (argv[0]);
331   base = g_path_get_basename (argv[0]);
332
333   /* parse command line options */
334   desc = g_strdup_printf ("wrapper to call versioned %s", base);
335   ctx = g_option_context_new (desc);
336   g_free (desc);
337   g_option_context_set_ignore_unknown_options (ctx, TRUE);
338   g_option_context_add_main_entries (ctx, wrapper_options, GETTEXT_PACKAGE);
339   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
340     g_print ("Error initializing: %s\n", err->message);
341     exit (1);
342   }
343   g_option_context_free (ctx);
344
345   /* unmangle libtool if necessary */
346   unmangle_libtool (&dir, &base);
347
348 #ifdef G_OS_WIN32
349   /* remove .exe suffix, otherwise we'll be looking for gst-blah.exe-*.* */
350   if (strlen (base) > 4 && g_str_has_suffix (base, ".exe")) {
351     base[strlen (base) - 4] = '\0';
352   }
353 #endif
354
355   /* get all candidate binaries */
356   candidates = get_candidates (dir, base);
357   g_free (dir);
358
359   /* only look for 0.10, we don't want 0.11 tools to be picked up accidentally
360    * (and from 0.11 on the unversioned tools are discontinued anyway) */
361   if (_arg_mm == NULL)
362     _arg_mm = GST_MAJORMINOR;
363
364   if (_arg_mm) {
365     /* if a version was forced, look it up in the hash table */
366     dir = g_hash_table_lookup (candidates, _arg_mm);
367     if (!dir) {
368       g_print ("ERROR: Major/minor %s of tool %s not found.\n", _arg_mm, base);
369       return 1;
370     }
371     binary = g_strdup_printf ("%s-%s", base, _arg_mm);
372   } else {
373     highest = NULL;
374
375     /* otherwise, just look up the highest version */
376     if (candidates) {
377       g_hash_table_foreach (candidates, (GHFunc) find_highest_version,
378           &highest);
379     }
380
381     if (highest == NULL) {
382       g_print ("ERROR: No version of tool %s found.\n", base);
383       return 1;
384     }
385     dir = g_hash_table_lookup (candidates, highest);
386     binary = g_strdup_printf ("%s-%s", base, highest);
387   }
388
389   g_free (base);
390
391   path = g_build_filename (dir, binary, NULL);
392   g_free (binary);
393
394   /* print out list of major/minors we found if asked for */
395   /* FIXME: do them in order by creating a GList of keys and sort them */
396   if (_arg_list_mm) {
397     g_hash_table_foreach (candidates, (GHFunc) hash_print_key, NULL);
398     g_hash_table_destroy (candidates);
399     return 0;
400   }
401
402   /* print out command line if asked for */
403   argv[0] = path;
404   if (_print) {
405     int i;
406
407     for (i = 0; i < argc; ++i) {
408       g_print ("%s", argv[i]);
409       if (i < argc - 1)
410         g_print (" ");
411     }
412     g_print ("\n");
413   }
414
415   /* execute */
416   if (execv (path, argv) == -1) {
417     g_warning ("Error executing %s: %s (%d)", path, g_strerror (errno), errno);
418   }
419   g_free (path);
420
421   return 0;
422 }