b78d99aac17b20736a6e3eda39af189a3cfd3fe8
[fw/sdcc] / 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   if (strrchr(path, DIR_SEPARATOR_CHAR) == NULL)
253       return 0;
254
255   return 1;
256 }
257
258
259 static char *
260 get_path(const char *cmd)
261 {
262   char *cmdLine = NULL;
263   char *command;
264   char *args;
265   char *path;
266   char cmdPath[PATH_MAX];
267
268
269   /* get the command */
270   split_command(cmd, &command, &args);
271
272   if (!has_path(command)) {
273     /* try to find the command in predefined binary paths */
274     if (NULL != (path = (char *)setFirstItem(binPathSet))) {
275       do
276       {
277         SNPRINTF(cmdPath, sizeof cmdPath,
278           "%s" DIR_SEPARATOR_STRING "%s", path, command);
279
280         /* Try if cmdPath */
281         if (0 == access(cmdPath, X_OK)) {
282           /* compose the command line */
283           cmdLine = merge_command(cmdPath, args);
284           break;
285         }
286       } while (NULL != (path = (char *)setNextItem(binPathSet)));
287     }
288     if (NULL == cmdLine)
289       cmdLine = merge_command(command, args);
290
291     Safe_free(command);
292     Safe_free(args);
293
294     return cmdLine;
295   }
296   else {
297     /*
298      * the command is defined with absolute path:
299      * just return it
300      */
301     Safe_free(command);
302     Safe_free(args);
303
304     return Safe_strdup(cmd);
305   }
306 }
307 #endif
308
309
310 /*!
311  * call an external program with arguements
312  */
313
314 int
315 my_system(const char *cmd)
316 {
317   int e;
318   char *cmdLine = get_path(cmd);
319
320   assert(NULL != cmdLine);
321
322   if (options.verboseExec) {
323       printf("+ %s\n", cmdLine);
324   }
325
326   e = system(cmdLine);
327   Safe_free(cmdLine);
328
329   return e;
330 }
331
332
333 /*!
334  * pipe an external program with arguements
335  */
336
337 #ifdef _WIN32
338 #define popen_read(cmd) _popen((cmd), "rt")
339 #else
340 #define popen_read(cmd) popen((cmd), "r")
341 #endif
342
343 FILE *
344 my_popen(const char *cmd)
345 {
346   FILE *fp;
347   char *cmdLine = get_path(cmd);
348
349   assert(NULL != cmdLine);
350
351   if (options.verboseExec) {
352       printf("+ %s\n", cmdLine);
353   }
354
355   fp = popen_read(cmdLine);
356   Safe_free(cmdLine);
357
358   return fp;
359 }