dvitomp fix from Akira
[mplib] / src / texk / kpathsea / lookcmd.c
1 /* libc replacement functions for win32.
2
3 Copyright (C) 1998, 99 Free Software Foundation, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library 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 GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18
19 /*
20   This does make sense only under WIN32.
21   Functions:
22     - look_for_cmd() : locates an executable file
23     - parse_cmd_line() : splits a command with pipes and redirections
24     - build_cmd_line() : builds a command with pipes and redirections (useful ?)
25   */
26
27 /*
28   This part looks for the real location of the program invoked
29   by cmd. If it can find the program, that's good. Else 
30   command processor is invoked.
31 */
32
33 #ifdef __MINGW32__
34
35 #include <kpathsea/config.h>
36 #include <kpathsea/c-proto.h>
37 #include <kpathsea/win32lib.h>
38 #include <kpathsea/lib.h>
39 #include <kpathsea/concatn.h>
40 #include <errno.h>
41
42 BOOL 
43 look_for_cmd(const char *cmd, char **app, char **new)
44 {
45   char *env_path;
46   char *p, *q;
47   char pname[MAXPATHLEN], *fp;
48   char *suffixes[] = { ".bat", ".cmd", ".com", ".exe", NULL };
49   char **s;
50   char *app_name, *new_cmd;
51
52   BOOL go_on;
53
54   *new = *app = NULL;
55   new_cmd = app_name = NULL;
56
57   /* We should look for the application name along the PATH,
58      and decide to prepend "%COMSPEC% /c " or not to the command line.
59      Do nothing for the moment. */
60
61   /* Another way to do that would be to try CreateProcess first without
62      invoking cmd, and look at the error code. If it fails because of
63      command not found, try to prepend "cmd /c" to the cmd line.
64   */
65
66   /* Look for the application name */
67   for (p = (char *)cmd; *p && isspace(*p); p++);
68   if (*p == '"') {
69     q = ++p;
70     while(*p && *p != '"') p++;
71     if (*p == '\0') {
72       fprintf(stderr, "Look_for_cmd: malformed command (\" not terminated)\n");
73       return FALSE;
74     }
75   }
76   else
77     for (q = p; *p && !isspace(*p); p++);
78   /* q points to the beginning of appname, p to the last + 1 char */
79   if ((app_name = malloc(p - q + 1)) == NULL) {
80     fprintf(stderr, "Look_for_cmd: malloc(app_name) failed.\n");
81     return FALSE;
82   }
83   strncpy(app_name, q, p - q );
84   app_name[p - q] = '\0';
85   pname[0] = '\0';
86 #ifdef TRACE
87   fprintf(stderr, "popen: app_name = %s\n", app_name);
88 #endif
89
90   {
91     char *tmp = getenv("PATH");
92     env_path = xmalloc(strlen(tmp) + 3);
93     strcpy(env_path, ".;");
94     strcat(env_path, tmp);
95   }
96
97   /* Looking for appname on the path */
98   for (s = suffixes, go_on = TRUE; go_on; *s++) {
99     if (SearchPath(env_path,    /* Address of search path */
100                    app_name,    /* Address of filename */
101                    *s,          /* Address of extension */
102                    MAXPATHLEN,  /* Size of destination buffer */
103                    pname,       /* Address of destination buffer */
104                    &fp)         /* File part of app_name */
105         != 0) {
106 #ifdef TRACE
107       fprintf(stderr, "%s found with suffix %s\nin %s\n", app_name, *s, pname);
108 #endif
109       new_cmd = xstrdup(cmd);
110       free(app_name);
111       app_name = xstrdup(pname);
112       break;
113     }
114     go_on = (*s != NULL);
115   }
116   if (go_on == FALSE) {
117     /* the app_name was not found */
118 #ifdef TRACE
119     fprintf(stderr, "%s not found, concatenating comspec\n", app_name);
120 #endif
121     new_cmd = concatn(getenv("COMSPEC"), " /c ", cmd, NULL);
122     free(app_name);
123     app_name = NULL;
124   }
125   else {
126   }
127   if (env_path) free(env_path);
128
129   *new = new_cmd;
130   *app = app_name;
131
132   return TRUE;
133
134 }
135
136 /*
137   Command parser. Borrowed from DJGPP.
138  */
139
140 static BOOL __system_allow_multiple_cmds = FALSE;
141
142 typedef enum {
143   EMPTY,
144   WORDARG,
145   REDIR_INPUT,
146   REDIR_APPEND,
147   REDIR_OUTPUT,
148   PIPE,
149   SEMICOLON,
150   UNMATCHED_QUOTE,
151   EOL
152 } cmd_sym_t;
153
154 /* Return a copy of a word between BEG and (excluding) END with all
155    quoting characters removed from it.  */
156
157 static char *
158 __unquote (char *to, const char *beg, const char *end)
159 {
160   const char *s = beg;
161   char *d = to;
162   int quote = 0;
163
164   while (s < end)
165     {
166       switch (*s)
167         {
168         case '"':
169         case '\'':
170           if (!quote)
171             quote = *s;
172           else if (quote == *s)
173             quote = 0;
174           s++;
175           break;
176         case '\\':
177           if (s[1] == '"' || s[1] == '\''
178               || (s[1] == ';'
179                   && (__system_allow_multiple_cmds)))
180             s++;
181           /* Fall-through.  */
182         default:
183           *d++ = *s++;
184           break;
185         }
186     }
187
188   *d = 0;
189   return to;
190 }
191
192 /* A poor-man's lexical analyzer for simplified command processing.
193
194    It only knows about these:
195
196      redirection and pipe symbols
197      semi-colon `;' (that possibly ends a command)
198      argument quoting rules with quotes and `\'
199      whitespace delimiters of words (except in quoted args)
200
201    Returns the type of next symbol and pointers to its first and (one
202    after) the last characters.
203
204    Only `get_sym' and `unquote' should know about quoting rules.  */
205
206 static cmd_sym_t
207 get_sym (char *s, char **beg, char **end)
208 {
209   int in_a_word = 0;
210
211   while (isspace (*s))
212     s++;
213
214   *beg = s;
215   
216   do {
217     *end = s + 1;
218
219     if (in_a_word
220         && (!*s || strchr ("<>| \t\n", *s)
221             || ((__system_allow_multiple_cmds) && *s == ';')))
222       {
223         --*end;
224         return WORDARG;
225       }
226
227     switch (*s)
228       {
229       case '<':
230         return REDIR_INPUT;
231       case '>':
232         if (**end == '>')
233           {
234             ++*end;
235             return REDIR_APPEND;
236           }
237         return REDIR_OUTPUT;
238       case '|':
239         return PIPE;
240       case ';':
241         if (__system_allow_multiple_cmds)
242           return SEMICOLON;
243         else
244           in_a_word = 1;
245         break;
246       case '\0':
247         --*end;
248         return EOL;
249       case '\\':
250         if (s[1] == '"' || s[1] == '\''
251             || (s[1] == ';' && (__system_allow_multiple_cmds)))
252           s++;
253         in_a_word = 1;
254         break;
255       case '\'':
256       case '"':
257         {
258           char quote = *s++;
259
260           while (*s && *s != quote)
261             {
262               if (*s++ == '\\' && (*s == '"' || *s == '\''))
263                 s++;
264             }
265           *end = s;
266           if (!*s)
267             return UNMATCHED_QUOTE;
268           in_a_word = 1;
269           break;
270         }
271       default:
272         in_a_word = 1;
273         break;
274       }
275
276     s++;
277
278   } while (1);
279 }
280
281 /*
282   What we allow :
283   [cmd] [arg1] ... [argn] < [redinput] | [cmd2] | ... | [cmdn] > [redoutput]
284 */
285 void *parse_cmdline(char *line, char **input, char **output)
286 {
287   BOOL again, needcmd = TRUE, bSuccess = TRUE, append_out = FALSE;
288   char *beg = line, *end, *new_end;
289   cmd_sym_t token = EMPTY, prev_token = EMPTY;
290   int ncmd = 0, narg = 1;
291   char **fp;
292   char ***cmd;
293   char *dummy_input;                    /* So that we could pass NULL */
294   char *dummy_output;                   /* instead of a real ??put */
295
296   if (input == NULL) input = &dummy_input;
297   if (output == NULL) output = &dummy_output;
298
299   *input = NULL;
300   *output = NULL;
301   cmd = xmalloc(MAX_PIPES*sizeof(char **));
302   cmd[ncmd] = NULL;
303 #ifdef TRACE
304   fprintf(stderr, "line = %s\n", line);
305 #endif
306   do {
307     again = FALSE;
308     prev_token = token;
309     token = get_sym (beg, &beg, &end);  /* get next symbol */
310 #ifdef TRACE
311     fprintf(stderr, "token = %s\n", beg);
312 #endif  
313     switch (token) {
314     case WORDARG:
315       if (prev_token == REDIR_INPUT
316           || prev_token == REDIR_OUTPUT) {
317         fprintf(stderr, "Ambigous input/output redirect.");
318         bSuccess = FALSE;
319         goto leave;
320       }
321       /* First word we see is the program to run.  */
322       if (needcmd) {
323         narg = 1;
324         cmd[ncmd] = xmalloc(narg * sizeof(char *));
325         cmd[ncmd][narg - 1] = xmalloc(end - beg + 1);
326         __unquote (cmd[ncmd][narg - 1], beg, end); /* unquote and copy to prog */
327         if (cmd[ncmd][narg - 1][0] == '(') {
328           fprintf(stderr, "parse_cmdline(%s): Parenthesized groups not allowed.\n", line);
329           bSuccess = FALSE;
330           goto leave;
331         }
332         needcmd = FALSE;
333       }
334       else {
335         narg++;
336         cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *));
337         cmd[ncmd][narg - 1] = xmalloc(end - beg + 1);
338         __unquote (cmd[ncmd][narg - 1], beg, end); /* unquote and copy to prog */
339       }
340       beg = end; /* go forward */
341       again = TRUE;
342       break;
343
344     case REDIR_INPUT:
345     case REDIR_OUTPUT:
346     case REDIR_APPEND:
347       if (token == REDIR_INPUT) {
348         if (*input) {
349           fprintf(stderr, "Ambiguous input redirect.");
350           errno = EINVAL;
351           bSuccess = FALSE;
352           goto leave;
353         }
354         fp = input;
355       }
356       else if (token == REDIR_OUTPUT || token == REDIR_APPEND) {
357         if (*output) {
358           fprintf(stderr, "Ambiguous output redirect.");
359           errno = EINVAL;
360           bSuccess = FALSE;
361           goto leave;
362         }
363         fp = output;
364         if (token == REDIR_APPEND)
365           append_out = TRUE;
366       }
367       if (get_sym (end, &end, &new_end) != WORDARG) {
368         fprintf(stderr, "Target of redirect is not a filename.");
369         errno = EINVAL;
370         bSuccess = FALSE;
371         goto leave;
372       }
373       *fp = (char *)xmalloc (new_end - end + 1);
374       memcpy (*fp, end, new_end - end);
375       (*fp)[new_end - end] = '\0';
376       beg = new_end;
377       again = TRUE;
378       break;
379     case PIPE:
380       if (*output) {
381         fprintf(stderr, "Ambiguous output redirect.");
382         errno = EINVAL;
383         bSuccess = FALSE;
384         goto leave;
385       }
386       narg++;
387       cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *));
388       cmd[ncmd][narg - 1] = NULL;
389       ncmd++; 
390       needcmd = TRUE;
391       beg = end;
392       again = TRUE;
393       break;
394     case SEMICOLON:
395     case EOL:
396       if (needcmd) {
397         fprintf(stderr, "No command name seen.");
398         errno = EINVAL;
399         bSuccess = FALSE;
400         goto leave;
401       }
402       narg++;
403       cmd[ncmd] = xrealloc(cmd[ncmd], narg * sizeof(char *));
404       cmd[ncmd][narg - 1] = NULL;
405       ncmd++;
406       cmd[ncmd] = NULL;
407       again = FALSE;
408       break;
409           
410     case UNMATCHED_QUOTE:
411       fprintf(stderr, "Unmatched quote character.");
412       errno = EINVAL;
413       bSuccess = FALSE;
414       goto leave;
415     default:
416       fprintf(stderr, "I cannot grok this.");
417       errno = EINVAL;
418       bSuccess = FALSE;
419       goto leave;
420
421     }
422
423   }     while (again);
424
425  leave:
426   if (!bSuccess) {
427     int i;
428     char **p;
429     /* Need to free everything that was allocated */
430     for (i = 0; i < ncmd; i++) {
431       for (p = cmd[i]; *p; p++) 
432         free(*p);
433       free(cmd[i]);
434     }
435     if (cmd[ncmd]) {
436       for (i = 0; i < narg; i++)
437         free(cmd[ncmd][i]);
438       free(cmd[ncmd]);
439     }
440     free(cmd);
441     *cmd = NULL;
442   }
443   return cmd;
444 }
445
446 static char *
447 quote_elt(char *elt) 
448 {
449   char *p;
450   for (p = elt; *p; p++)
451     if (isspace(*p))
452       return concat3("\"", elt, "\"");
453
454   return xstrdup(elt);
455 }
456
457 /* static (commented out for mingw; SK) */ char *
458 quote_args(char **argv)
459 {
460   int i;
461   char *line = NULL, *new_line;
462   char *new_argv;
463
464   if (!argv)
465     return line;
466
467   line = quote_elt(argv[0]);
468   for (i = 1; argv[i]; i++) {
469     new_argv = quote_elt(argv[i]);
470     new_line = concat3(line, " ", new_argv);
471     free(line);
472     free(new_argv);
473     line = new_line;
474   }
475
476   return line;
477 }
478
479 char *
480 build_cmdline(char ***cmd, char *input, char *output)
481 {
482   int ncmd;
483   char *line = NULL, *new_line;
484
485   if (!cmd)
486     return line;
487
488   line = quote_args(cmd[0]);
489   if (input) {
490     new_line = concat3(line, " < ", quote_elt(input));
491     free(line);
492     line = new_line;
493   }
494   for(ncmd = 1; cmd[ncmd]; ncmd++) {
495     new_line = concat3(line, " | ", quote_args(cmd[ncmd]));
496     free(line);
497     line = new_line;
498   }
499
500   if (output) {
501     new_line = concat3(line, " > ", quote_elt(output));
502     free(line);
503     line = new_line;
504   }
505
506   return line;
507 }
508
509 #ifdef TEST
510 void 
511 display_cmd(char ***cmd, char *input, char *output, char *errput)
512 {
513   int ncmd = 0, narg;
514   
515   if (cmd) {
516     for (ncmd = 0; cmd[ncmd]; ncmd++) {
517       printf("cmd[%d] = %s ", ncmd, cmd[ncmd][0]);
518       for(narg = 1; cmd[ncmd][narg] != NULL; narg++)
519         printf("%s ", cmd[ncmd][narg]);
520       printf("\n");
521     }
522   }
523   printf("input = %s\n", (input ? input : "<stdin>"));
524   printf("output = %s\n", (output ? output : "<stdout>"));
525   printf("errput = %s\n", (errput ? errput : "<stderr>"));
526 }
527
528 main(int argc, char *argv[])
529 {
530   char *input;
531   char *output;
532   char ***cmd;
533   char *line;
534
535   printf("%s\n", line = "foo a b");
536   if (cmd = parse_cmdline(line, &input, &output)) {
537     display_cmd(cmd, input, output, NULL);
538   }
539   printf("%s\n", line = "foo < a > b");
540   if (cmd = parse_cmdline(line, &input, &output)) {
541     display_cmd(cmd, input, output, NULL);
542   }
543   printf("%s\n", line = "foo > a < b");
544   if (cmd = parse_cmdline(line, &input, &output)) {
545     display_cmd(cmd, input, output, NULL);
546   }
547   printf("%s\n", line = "foo | a b");
548   if (cmd = parse_cmdline(line, &input, &output)) {
549     display_cmd(cmd, input, output, NULL);
550   }
551   printf("%s\n", line = "foo a | b");
552   if (cmd = parse_cmdline(line, &input, &output)) {
553     display_cmd(cmd, input, output, NULL);
554   }
555   printf("%s\n", line = "foo | a > b");
556   if (cmd = parse_cmdline(line, &input, &output)) {
557     display_cmd(cmd, input, output, NULL);
558   }
559   printf("%s\n", line = "foo < a | b");
560   if (cmd = parse_cmdline(line, &input, &output)) {
561     display_cmd(cmd, input, output, NULL);
562   }
563   printf("%s\n", line = "foo < a | b");
564   if (cmd = parse_cmdline(line, &input, &output)) {
565     display_cmd(cmd, input, output, NULL);
566   }
567   printf("%s\n", line = "foo < a | b | c > d");
568   if (cmd = parse_cmdline(line, &input, &output)) {
569     display_cmd(cmd, input, output, NULL);
570   }
571   printf("%s\n", line = "foo > a | b < c");
572   if (cmd = parse_cmdline(line, &input, &output)) {
573     display_cmd(cmd, input, output, NULL);
574   }
575
576   printf("%s\n", line = "\"C:\\Program Files\\WinEdt\\WinEdt.exe\" -F \"[Open('%f');SelLine(%l,8)]\"");
577   if (cmd = parse_cmdline(line, &input, &output)) {
578     display_cmd(cmd, input, output, NULL);
579   }
580
581 }
582 #endif
583
584 #endif