Imported Upstream version 2.9.0
[debian/cc1111] / support / Util / MySystem.c
1 /*-------------------------------------------------------------------------
2   MySystem - SDCC Support function
3
4              Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
5
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version.
10
11    This program 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
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20    In other words, you are welcome to use, share and improve this program.
21    You are forbidden to forbid anyone else to use, share and improve
22    what you give them.   Help stamp out software-hoarding!
23 -------------------------------------------------------------------------*/
24
25 #ifdef _WIN32
26 #undef DATADIR
27 #include <windows.h>
28 /* avoid DATADIR definition clash :-( */
29 #include <io.h>
30 #else
31 #include <unistd.h>
32 #endif
33 #include <ctype.h>
34 #include "SDCCglobl.h"
35 #include "SDCCutil.h"
36 #include "MySystem.h"
37 #include "newalloc.h"
38
39
40 set *binPathSet = NULL; /* set of binary paths */
41
42
43 /*!
44  * get command and arguments from command line
45  */
46
47 static void
48 split_command(const char *cmd_line, char **command, char **params)
49 {
50   const char *p, *cmd_start;
51   char delim;
52   char *str;
53   unsigned len;
54
55   /* skip leading spaces */
56   for (p = cmd_line; isspace(*p); p++)
57     ;
58
59   /* get command */
60   switch (*p) {
61   case '\'':
62   case '"':
63     delim = *p;
64     cmd_start = ++p;
65     break;
66
67   default:
68     delim = ' ';
69     cmd_start = p;
70   }
71
72   if (delim == ' ') {
73     while (*p != '\0' && !isspace(*p))
74       p++;
75   }
76   else {
77     while (*p != '\0' && *p != delim)
78       p++;
79   }
80
81   if (command != NULL) {
82     len = p - cmd_start;
83     str = Safe_alloc(len + 1);
84     strncpy(str, cmd_start, len);
85     str[len] = '\0';
86     *command = str;
87   }
88
89   p++;
90
91   /* skip spaces before parameters */
92   while (isspace(*p))
93     p++;
94
95   /* get parameters */
96   if (params != NULL)
97     *params = Safe_strdup(p);
98 }
99
100
101 /*!
102  * find the command:
103  * 1) if the command is specified by path, try it
104  * 2) try to find the command in predefined path's
105  * 3) trust on $PATH
106  */
107
108 #ifdef _WIN32
109 /* WIN32 version */
110
111 /*
112  * I don't like this solution, but unfortunately cmd.exe and command.com
113  * don't accept something like this:
114  * "program" "argument"
115  * Cmd.exe accepts the following:
116  * ""program" "argument""
117  * but command.com doesn't.
118  * The following is accepted by both:
119  * program "argument"
120  *
121  * So the most portable WIN32 solution is to use GetShortPathName() for
122  * program to get rid of spaces, so that quotes are not needed :-(
123  * Using spawnvp() instead of system() is more portable cross platform approach,
124  * but then also a substitute for _popen() should be developed...
125  */
126
127 #define EXE_EXT ".exe"
128
129 /*!
130  * merge command and parameters to command line
131  */
132
133 static char *
134 merge_command(const char *command, const char *params)
135 {
136   /* allocate extra space for ' ' and '\0' */
137   char *cmd_line = (char *)Safe_alloc(strlen(command) + strlen(params) + 2);
138   sprintf(cmd_line, "%s %s", command, params);
139
140   return cmd_line;
141 }
142
143
144 /*!
145  * check if path/command exist by converting it to short file name
146  * if it exists, compose with args and return it
147  */
148
149 static char *
150 compose_command_line(const char *path, const char *command, const char *args)
151 {
152   unsigned len;
153   char cmdPath[PATH_MAX];
154   char shortPath[PATH_MAX];
155
156   if (path != NULL)
157     SNPRINTF(cmdPath, sizeof cmdPath,
158       "%s" DIR_SEPARATOR_STRING "%s", path, command);
159   else
160     strncpyz(cmdPath, command, sizeof cmdPath);
161
162   /* Try if cmdPath or cmdPath.exe exist by converting it to the short path name */
163   len = GetShortPathName(cmdPath, shortPath, sizeof shortPath);
164   assert(len < sizeof shortPath);
165   if (0 == len) {
166     len = GetShortPathName(strncatz(cmdPath, EXE_EXT, sizeof cmdPath), shortPath, sizeof shortPath);
167     assert(len < sizeof shortPath);
168   }
169   if (0 != len) {
170     /* compose the command line */
171     return merge_command(shortPath, args);
172   }
173   else {
174     /* path/command not found */
175     return NULL;
176   }
177 }
178
179
180 static char *
181 get_path(const char *cmd)
182 {
183   char *cmdLine;
184   char *command;
185   char *args;
186   char *path;
187
188   /* get the command */
189   split_command(cmd, &command, &args);
190
191   if (NULL == (cmdLine = compose_command_line(NULL, command, args))) {
192     /* not an absolute path: try to find the command in predefined binary paths */
193     if (NULL != (path = (char *)setFirstItem(binPathSet))) {
194       while (NULL == (cmdLine  = compose_command_line(path, command, args)) &&
195         NULL != (path = (char *)setNextItem(binPathSet)))
196         ;
197     }
198
199     if (NULL == cmdLine) {
200       /* didn't found the command in predefined binary paths: try with PATH */
201       char *envPath;
202
203       if (NULL != (envPath = getenv("PATH"))) {
204         /* make a local copy; strtok() will modify it */
205         envPath = Safe_strdup(envPath);
206
207         if (NULL != (path = strtok(envPath, ";"))) {
208           while (NULL == (cmdLine = compose_command_line(path, command, args)) &&
209            NULL != (path = strtok(NULL, ";")))
210            ;
211         }
212
213         Safe_free(envPath);
214       }
215     }
216
217     /* didn't found it; probably this won't help neither :-( */
218     if (NULL == cmdLine)
219       cmdLine = merge_command(command, args);
220   }
221
222   Safe_free(command);
223   Safe_free(args);
224
225   return cmdLine;
226 }
227
228 #else
229 /* *nix version */
230
231 /*!
232  * merge command and parameters to command line
233  */
234
235 static char *
236 merge_command(const char *command, const char *params)
237 {
238   /* allocate extra space for 2x'"', ' ' and '\0' */
239   char *cmd_line = (char *)Safe_alloc(strlen(command) + strlen(params) + 4);
240   sprintf(cmd_line, "\"%s\" %s", command, params);
241   return cmd_line;
242 }
243
244
245 /*!
246  * check if the path is relative or absolute (if contains the dir separator)
247  */
248
249 static int
250 has_path(const char *path)
251 {
252   return dbuf_splitPath(path, NULL, NULL);
253 }
254
255
256 static char *
257 get_path(const char *cmd)
258 {
259   char *cmdLine = NULL;
260   char *command;
261   char *args;
262   char *path;
263   char cmdPath[PATH_MAX];
264
265
266   /* get the command */
267   split_command(cmd, &command, &args);
268
269   if (!has_path(command)) {
270     /* try to find the command in predefined binary paths */
271     if (NULL != (path = (char *)setFirstItem(binPathSet))) {
272       do
273       {
274         SNPRINTF(cmdPath, sizeof cmdPath,
275           "%s" DIR_SEPARATOR_STRING "%s", path, command);
276
277         /* Try if cmdPath */
278         if (0 == access(cmdPath, X_OK)) {
279           /* compose the command line */
280           cmdLine = merge_command(cmdPath, args);
281           break;
282         }
283       } while (NULL != (path = (char *)setNextItem(binPathSet)));
284     }
285     if (NULL == cmdLine)
286       cmdLine = merge_command(command, args);
287
288     Safe_free(command);
289     Safe_free(args);
290
291     return cmdLine;
292   }
293   else {
294     /*
295      * the command is defined with absolute path:
296      * just return it
297      */
298     Safe_free(command);
299     Safe_free(args);
300
301     return Safe_strdup(cmd);
302   }
303 }
304 #endif
305
306
307 /*!
308  * call an external program with arguements
309  */
310
311 int
312 my_system(const char *cmd)
313 {
314   int e;
315   char *cmdLine = get_path(cmd);
316
317   assert(NULL != cmdLine);
318
319   if (options.verboseExec) {
320       printf("+ %s\n", cmdLine);
321   }
322
323   e = system(cmdLine);
324
325   if (options.verboseExec && e) {
326       printf("+ %s returned errorcode %d\n", cmdLine, e);
327   }
328
329   Safe_free(cmdLine);
330
331   return e;
332 }
333
334
335 /*!
336  * pipe an external program with arguements
337  */
338
339 #ifdef _WIN32
340 #define popen_read(cmd) _popen((cmd), "rt")
341 #else
342 #define popen_read(cmd) popen((cmd), "r")
343 #endif
344
345 FILE *
346 my_popen(const char *cmd)
347 {
348   FILE *fp;
349   char *cmdLine = get_path(cmd);
350
351   assert(NULL != cmdLine);
352
353   if (options.verboseExec) {
354       printf("+ %s\n", cmdLine);
355   }
356
357   fp = popen_read(cmdLine);
358   Safe_free(cmdLine);
359
360   return fp;
361 }