1/* Execute a C# program.
2   Copyright (C) 2003-2006 Free Software Foundation, Inc.
3   Written by Bruno Haible <bruno@clisp.org>, 2003.
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2, or (at your option)
8   any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software Foundation,
17   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19#include <config.h>
20#include <alloca.h>
21
22/* Specification.  */
23#include "csharpexec.h"
24
25#include <stdio.h>
26#include <stdlib.h>
27
28#include "execute.h"
29#include "sh-quote.h"
30#include "xallocsa.h"
31#include "error.h"
32#include "gettext.h"
33
34/* Handling of MONO_PATH is just like Java CLASSPATH.  */
35#define CLASSPATHVAR "MONO_PATH"
36#define new_classpath new_monopath
37#define set_classpath set_monopath
38#define reset_classpath reset_monopath
39#include "classpath.h"
40#include "classpath.c"
41#undef reset_classpath
42#undef set_classpath
43#undef new_classpath
44#undef CLASSPATHVAR
45
46/* Handling of clix' PATH variable is just like Java CLASSPATH.  */
47#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__
48  /* Win32, Cygwin */
49  #define CLASSPATHVAR "PATH"
50#elif defined __APPLE__ && defined __MACH__
51  /* MacOS X */
52  #define CLASSPATHVAR "DYLD_LIBRARY_PATH"
53#else
54  /* Normal Unix */
55  #define CLASSPATHVAR "LD_LIBRARY_PATH"
56#endif
57#define new_classpath new_clixpath
58#define set_classpath set_clixpath
59#define reset_classpath reset_clixpath
60#include "classpath.h"
61#include "classpath.c"
62#undef reset_classpath
63#undef set_classpath
64#undef new_classpath
65#undef CLASSPATHVAR
66
67#define _(str) gettext (str)
68
69
70/* Survey of CIL interpreters.
71
72   Program    from
73
74   ilrun      pnet
75   mono       mono
76   clix       sscli
77
78   With Mono, the MONO_PATH is a colon separated list of pathnames. (On
79   Windows: semicolon separated list of pathnames.)
80
81   We try the CIL interpreters in the following order:
82     1. "ilrun", because it is a completely free system.
83     2. "mono", because it is a partially free system but doesn't integrate
84        well with Unix.
85     3. "clix", although it is not free, because it is a kind of "reference
86        implementation" of C#.
87   But the order can be changed through the --enable-csharp configuration
88   option.
89 */
90
91static int
92execute_csharp_using_pnet (const char *assembly_path,
93			   const char * const *libdirs,
94			   unsigned int libdirs_count,
95			   const char * const *args, unsigned int nargs,
96			   bool verbose, bool quiet,
97			   execute_fn *executer, void *private_data)
98{
99  static bool ilrun_tested;
100  static bool ilrun_present;
101
102  if (!ilrun_tested)
103    {
104      /* Test for presence of ilrun:
105	 "ilrun --version >/dev/null 2>/dev/null"  */
106      char *argv[3];
107      int exitstatus;
108
109      argv[0] = "ilrun";
110      argv[1] = "--version";
111      argv[2] = NULL;
112      exitstatus = execute ("ilrun", "ilrun", argv, false, false, true, true,
113			    true, false);
114      ilrun_present = (exitstatus == 0);
115      ilrun_tested = true;
116    }
117
118  if (ilrun_present)
119    {
120      unsigned int argc;
121      char **argv;
122      char **argp;
123      unsigned int i;
124      bool err;
125
126      argc = 1 + 2 * libdirs_count + 1 + nargs;
127      argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
128
129      argp = argv;
130      *argp++ = "ilrun";
131      for (i = 0; i < libdirs_count; i++)
132	{
133	  *argp++ = "-L";
134	  *argp++ = (char *) libdirs[i];
135	}
136      *argp++ = (char *) assembly_path;
137      for (i = 0; i < nargs; i++)
138	*argp++ = (char *) args[i];
139      *argp = NULL;
140      /* Ensure argv length was correctly calculated.  */
141      if (argp - argv != argc)
142	abort ();
143
144      if (verbose)
145	{
146	  char *command = shell_quote_argv (argv);
147	  printf ("%s\n", command);
148	  free (command);
149	}
150
151      err = executer ("ilrun", "ilrun", argv, private_data);
152
153      freesa (argv);
154
155      return err;
156    }
157  else
158    return -1;
159}
160
161static int
162execute_csharp_using_mono (const char *assembly_path,
163			   const char * const *libdirs,
164			   unsigned int libdirs_count,
165			   const char * const *args, unsigned int nargs,
166			   bool verbose, bool quiet,
167			   execute_fn *executer, void *private_data)
168{
169  static bool mono_tested;
170  static bool mono_present;
171
172  if (!mono_tested)
173    {
174      /* Test for presence of mono:
175	 "mono --version >/dev/null 2>/dev/null"  */
176      char *argv[3];
177      int exitstatus;
178
179      argv[0] = "mono";
180      argv[1] = "--version";
181      argv[2] = NULL;
182      exitstatus = execute ("mono", "mono", argv, false, false, true, true,
183			    true, false);
184      mono_present = (exitstatus == 0);
185      mono_tested = true;
186    }
187
188  if (mono_present)
189    {
190      char *old_monopath;
191      char **argv = (char **) xallocsa ((2 + nargs + 1) * sizeof (char *));
192      unsigned int i;
193      bool err;
194
195      /* Set MONO_PATH.  */
196      old_monopath = set_monopath (libdirs, libdirs_count, false, verbose);
197
198      argv[0] = "mono";
199      argv[1] = (char *) assembly_path;
200      for (i = 0; i <= nargs; i++)
201	argv[2 + i] = (char *) args[i];
202
203      if (verbose)
204	{
205	  char *command = shell_quote_argv (argv);
206	  printf ("%s\n", command);
207	  free (command);
208	}
209
210      err = executer ("mono", "mono", argv, private_data);
211
212      /* Reset MONO_PATH.  */
213      reset_monopath (old_monopath);
214
215      freesa (argv);
216
217      return err;
218    }
219  else
220    return -1;
221}
222
223static int
224execute_csharp_using_sscli (const char *assembly_path,
225			    const char * const *libdirs,
226			    unsigned int libdirs_count,
227			    const char * const *args, unsigned int nargs,
228			    bool verbose, bool quiet,
229			    execute_fn *executer, void *private_data)
230{
231  static bool clix_tested;
232  static bool clix_present;
233
234  if (!clix_tested)
235    {
236      /* Test for presence of clix:
237	 "clix >/dev/null 2>/dev/null ; test $? = 1"  */
238      char *argv[2];
239      int exitstatus;
240
241      argv[0] = "clix";
242      argv[1] = NULL;
243      exitstatus = execute ("clix", "clix", argv, false, false, true, true,
244			    true, false);
245      clix_present = (exitstatus == 0 || exitstatus == 1);
246      clix_tested = true;
247    }
248
249  if (clix_present)
250    {
251      char *old_clixpath;
252      char **argv = (char **) xallocsa ((2 + nargs + 1) * sizeof (char *));
253      unsigned int i;
254      bool err;
255
256      /* Set clix' PATH variable.  */
257      old_clixpath = set_clixpath (libdirs, libdirs_count, false, verbose);
258
259      argv[0] = "clix";
260      argv[1] = (char *) assembly_path;
261      for (i = 0; i <= nargs; i++)
262	argv[2 + i] = (char *) args[i];
263
264      if (verbose)
265	{
266	  char *command = shell_quote_argv (argv);
267	  printf ("%s\n", command);
268	  free (command);
269	}
270
271      err = executer ("clix", "clix", argv, private_data);
272
273      /* Reset clix' PATH variable.  */
274      reset_clixpath (old_clixpath);
275
276      freesa (argv);
277
278      return err;
279    }
280  else
281    return -1;
282}
283
284bool
285execute_csharp_program (const char *assembly_path,
286			const char * const *libdirs,
287			unsigned int libdirs_count,
288			const char * const *args,
289			bool verbose, bool quiet,
290			execute_fn *executer, void *private_data)
291{
292  unsigned int nargs;
293  int result;
294
295  /* Count args.  */
296  {
297    const char * const *arg;
298
299    for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++)
300     ;
301  }
302
303  /* First try the C# implementation specified through --enable-csharp.  */
304#if CSHARP_CHOICE_PNET
305  result = execute_csharp_using_pnet (assembly_path, libdirs, libdirs_count,
306				      args, nargs, verbose, quiet,
307				      executer, private_data);
308  if (result >= 0)
309    return (bool) result;
310#endif
311
312#if CSHARP_CHOICE_MONO
313  result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
314				      args, nargs, verbose, quiet,
315				      executer, private_data);
316  if (result >= 0)
317    return (bool) result;
318#endif
319
320  /* Then try the remaining C# implementations in our standard order.  */
321#if !CSHARP_CHOICE_PNET
322  result = execute_csharp_using_pnet (assembly_path, libdirs, libdirs_count,
323				      args, nargs, verbose, quiet,
324				      executer, private_data);
325  if (result >= 0)
326    return (bool) result;
327#endif
328
329#if !CSHARP_CHOICE_MONO
330  result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
331				      args, nargs, verbose, quiet,
332				      executer, private_data);
333  if (result >= 0)
334    return (bool) result;
335#endif
336
337  result = execute_csharp_using_sscli (assembly_path, libdirs, libdirs_count,
338				       args, nargs, verbose, quiet,
339				       executer, private_data);
340  if (result >= 0)
341    return (bool) result;
342
343  if (!quiet)
344    error (0, 0, _("C# virtual machine not found, try installing pnet"));
345  return true;
346}
347