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