1 /* Execute a C# program.
2    Copyright (C) 2003-2020 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 <https://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 __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    mono       mono
74    clix       sscli
75 
76    With Mono, the MONO_PATH is a colon separated list of pathnames. (On
77    Windows: semicolon separated list of pathnames.)
78 
79    We try the CIL interpreters in the following order:
80      1. "mono", because it is a partially free system but doesn't integrate
81         well with Unix.
82      2. "clix", although it is not free, because it is a kind of "reference
83         implementation" of C#.
84    But the order can be changed through the --enable-csharp configuration
85    option.
86  */
87 
88 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)89 execute_csharp_using_mono (const char *assembly_path,
90                            const char * const *libdirs,
91                            unsigned int libdirs_count,
92                            const char * const *args, unsigned int nargs,
93                            bool verbose, bool quiet,
94                            execute_fn *executer, void *private_data)
95 {
96   static bool mono_tested;
97   static bool mono_present;
98 
99   if (!mono_tested)
100     {
101       /* Test for presence of mono:
102          "mono --version >/dev/null 2>/dev/null"  */
103       char *argv[3];
104       int exitstatus;
105 
106       argv[0] = "mono";
107       argv[1] = "--version";
108       argv[2] = NULL;
109       exitstatus = execute ("mono", "mono", argv, false, false, true, true,
110                             true, false, NULL);
111       mono_present = (exitstatus == 0);
112       mono_tested = true;
113     }
114 
115   if (mono_present)
116     {
117       char *old_monopath;
118       char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *));
119       unsigned int i;
120       bool err;
121 
122       /* Set MONO_PATH.  */
123       old_monopath = set_monopath (libdirs, libdirs_count, false, verbose);
124 
125       argv[0] = "mono";
126       argv[1] = (char *) assembly_path;
127       for (i = 0; i <= nargs; i++)
128         argv[2 + i] = (char *) args[i];
129 
130       if (verbose)
131         {
132           char *command = shell_quote_argv (argv);
133           printf ("%s\n", command);
134           free (command);
135         }
136 
137       err = executer ("mono", "mono", argv, private_data);
138 
139       /* Reset MONO_PATH.  */
140       reset_monopath (old_monopath);
141 
142       freea (argv);
143 
144       return err;
145     }
146   else
147     return -1;
148 }
149 
150 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)151 execute_csharp_using_sscli (const char *assembly_path,
152                             const char * const *libdirs,
153                             unsigned int libdirs_count,
154                             const char * const *args, unsigned int nargs,
155                             bool verbose, bool quiet,
156                             execute_fn *executer, void *private_data)
157 {
158   static bool clix_tested;
159   static bool clix_present;
160 
161   if (!clix_tested)
162     {
163       /* Test for presence of clix:
164          "clix >/dev/null 2>/dev/null ; test $? = 1"  */
165       char *argv[2];
166       int exitstatus;
167 
168       argv[0] = "clix";
169       argv[1] = NULL;
170       exitstatus = execute ("clix", "clix", argv, false, false, true, true,
171                             true, false, NULL);
172       clix_present = (exitstatus == 0 || exitstatus == 1);
173       clix_tested = true;
174     }
175 
176   if (clix_present)
177     {
178       char *old_clixpath;
179       char **argv = (char **) xmalloca ((2 + nargs + 1) * sizeof (char *));
180       unsigned int i;
181       bool err;
182 
183       /* Set clix' PATH variable.  */
184       old_clixpath = set_clixpath (libdirs, libdirs_count, false, verbose);
185 
186       argv[0] = "clix";
187       argv[1] = (char *) assembly_path;
188       for (i = 0; i <= nargs; i++)
189         argv[2 + i] = (char *) args[i];
190 
191       if (verbose)
192         {
193           char *command = shell_quote_argv (argv);
194           printf ("%s\n", command);
195           free (command);
196         }
197 
198       err = executer ("clix", "clix", argv, private_data);
199 
200       /* Reset clix' PATH variable.  */
201       reset_clixpath (old_clixpath);
202 
203       freea (argv);
204 
205       return err;
206     }
207   else
208     return -1;
209 }
210 
211 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)212 execute_csharp_program (const char *assembly_path,
213                         const char * const *libdirs,
214                         unsigned int libdirs_count,
215                         const char * const *args,
216                         bool verbose, bool quiet,
217                         execute_fn *executer, void *private_data)
218 {
219   unsigned int nargs;
220   int result;
221 
222   /* Count args.  */
223   {
224     const char * const *arg;
225 
226     for (nargs = 0, arg = args; *arg != NULL; nargs++, arg++)
227      ;
228   }
229 
230   /* First try the C# implementation specified through --enable-csharp.  */
231 #if CSHARP_CHOICE_MONO
232   result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
233                                       args, nargs, verbose, quiet,
234                                       executer, private_data);
235   if (result >= 0)
236     return (bool) result;
237 #endif
238 
239   /* Then try the remaining C# implementations in our standard order.  */
240 #if !CSHARP_CHOICE_MONO
241   result = execute_csharp_using_mono (assembly_path, libdirs, libdirs_count,
242                                       args, nargs, verbose, quiet,
243                                       executer, private_data);
244   if (result >= 0)
245     return (bool) result;
246 #endif
247 
248   result = execute_csharp_using_sscli (assembly_path, libdirs, libdirs_count,
249                                        args, nargs, verbose, quiet,
250                                        executer, private_data);
251   if (result >= 0)
252     return (bool) result;
253 
254   if (!quiet)
255     error (0, 0, _("C# virtual machine not found, try installing mono"));
256   return true;
257 }
258