tentative fix for issue 3 (ex 53)
[mplib] / src / texk / kpathsea / tex-make.c
1 /* tex-make.c: Run external programs to make TeX-related files.
2
3     Copyright 1997, 98, 2001-05 Olaf Weber.
4     Copyright 1993, 94, 95, 96, 97 Karl Berry.
5
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2.1 of the License, or (at your option) any later version.
10
11     This library 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 GNU
14     Lesser General Public License for more details.
15
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to the Free Software
18     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
20 */
21
22 #include <kpathsea/config.h>
23
24 #include <kpathsea/c-fopen.h>
25 #include <kpathsea/c-pathch.h>
26 #include <kpathsea/concatn.h>
27 #include <kpathsea/db.h>
28 #include <kpathsea/fn.h>
29 #include <kpathsea/magstep.h>
30 #include <kpathsea/readable.h>
31 #include <kpathsea/tex-make.h>
32 #include <kpathsea/variable.h>
33
34
35 /* We never throw away stdout, since that is supposed to be the filename
36    found, if all is successful.  This variable controls whether stderr
37    is thrown away.  */
38 boolean kpse_make_tex_discard_errors = false;
39 \f
40 /* We set the envvar MAKETEX_MAG, which is part of the default spec for
41    MakeTeXPK above, based on KPATHSEA_DPI and MAKETEX_BASE_DPI.  */
42
43 static void
44 set_maketex_mag P1H(void)
45 {
46   char q[MAX_INT_LENGTH * 3 + 3];
47   int m;
48   string dpi_str = getenv ("KPATHSEA_DPI");
49   string bdpi_str = getenv ("MAKETEX_BASE_DPI");
50   unsigned dpi = dpi_str ? atoi (dpi_str) : 0;
51   unsigned bdpi = bdpi_str ? atoi (bdpi_str) : 0;
52
53   /* If the environment variables aren't set, it's a bug.  */
54   assert (dpi != 0 && bdpi != 0);
55   
56   /* Fix up for roundoff error.  Hopefully the driver has already fixed
57      up DPI, but may as well be safe, and also get the magstep number.  */
58   (void) kpse_magstep_fix (dpi, bdpi, &m);
59   
60   if (m == 0) {
61       if (bdpi <= 4000) {
62           sprintf(q, "%u+%u/%u", dpi / bdpi, dpi % bdpi, bdpi);
63       } else {
64           unsigned f = bdpi/4000;
65           unsigned r = bdpi%4000;
66
67           if (f > 1) {
68               if (r > 0) {
69                   sprintf(q, "%u+%u/(%u*%u+%u)",
70                           dpi/bdpi, dpi%bdpi, f, (bdpi - r)/f, r);
71               } else {
72                   sprintf(q, "%u+%u/(%u*%u)", dpi/bdpi, dpi%bdpi, f, bdpi/f);
73               }
74           } else {
75               sprintf(q, "%u+%u/(4000+%u)", dpi/bdpi, dpi%bdpi, r);
76           }
77       }
78   } else {
79       /* m is encoded with LSB being a ``half'' bit (see magstep.h).  Are
80          we making an assumption here about two's complement?  Probably.
81          In any case, if m is negative, we have to put in the sign
82          explicitly, since m/2==0 if m==-1.  */
83       const_string sign = "";
84       if (m < 0) {
85           m *= -1;
86           sign = "-";
87       }
88       sprintf(q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5);
89   }
90   xputenv ("MAKETEX_MAG", q);
91 }
92 \f
93 /* This mktex... program was disabled, or the script failed.  If this
94    was a font creation (according to FORMAT), append CMD
95    to a file missfont.log in the current directory.  */
96
97 static void
98 misstex P2C(kpse_file_format_type, format,  string *, args)
99 {
100   static FILE *missfont = NULL;
101   string *s;
102   
103   /* If we weren't trying to make a font, do nothing.  Maybe should
104      allow people to specify what they want recorded?  */
105   if (format != kpse_gf_format
106       && format != kpse_pk_format
107       && format != kpse_any_glyph_format
108       && format != kpse_tfm_format
109       && format != kpse_vf_format)
110     return;
111
112   /* If this is the first time, have to open the log file.  But don't
113      bother logging anything if they were discarding errors.  */
114   if (!missfont && !kpse_make_tex_discard_errors) {
115     const_string missfont_name = kpse_var_value ("MISSFONT_LOG");
116     if (!missfont_name || *missfont_name == '1') {
117       missfont_name = "missfont.log"; /* take default name */
118     } else if (missfont_name
119                && (*missfont_name == 0 || *missfont_name == '0')) {
120       missfont_name = NULL; /* user requested no missfont.log */
121     } /* else use user's name */
122
123     missfont = missfont_name ? fopen (missfont_name, FOPEN_A_MODE) : NULL;
124     if (!missfont && kpse_var_value ("TEXMFOUTPUT")) {
125       missfont_name = concat3 (kpse_var_value ("TEXMFOUTPUT"), DIR_SEP_STRING,
126                                missfont_name);
127       missfont = fopen (missfont_name, FOPEN_A_MODE);
128     }
129
130     if (missfont)
131       fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n",
132                missfont_name);
133   }
134   
135   /* Write the command if we have a log file.  */
136   if (missfont) {
137     fputs (args[0], missfont);
138     for (s = &args[1]; *s != NULL; s++) {
139       putc(' ', missfont);
140       fputs (*s, missfont);
141     }
142     putc ('\n', missfont);
143   }
144 }  
145
146
147 /* Assume the script outputs the filename it creates (and nothing
148    else) on standard output; hence, we run the script with `popen'.  */
149
150 static string
151 maketex P2C(kpse_file_format_type, format, string*, args)
152 {
153   /* New implementation, use fork/exec pair instead of popen, since
154    * the latter is virtually impossible to make safe.
155    */
156   unsigned len;
157   string *s;
158   string ret;
159   string fn;
160   
161   if (!kpse_make_tex_discard_errors) {
162     fprintf (stderr, "kpathsea: Running");
163     for (s = &args[0]; *s != NULL; s++)
164       fprintf (stderr, " %s", *s);
165     fputc('\n', stderr);
166   }
167
168 #if defined (AMIGA)
169   /* Amiga has a different interface. */
170   {
171     string cmd;
172     string newcmd;
173     cmd = xstrdup(args[0]);
174     for (s = &args[1];  *s != NULL; s++) {
175       newcmd = concat(cmd, *s);
176       free (cmd);
177       cmd = newcmd;
178     }
179     ret = system(cmd) == 0 ? getenv ("LAST_FONT_CREATED"): NULL;
180     free (cmd);
181   }
182 #elif defined (MSDOS) && !defined(DJGPP)
183 #error Implement new MSDOS mktex call interface here
184 #elif defined (WIN32)
185   /* We would vastly prefer to link directly with mktex.c here.
186      Unfortunately, it is not quite possible because kpathsea
187      is not reentrant. The progname is expected to be set in mktex.c
188      and various initialisations occur. So to be safe, we implement
189      a call sequence equivalent to the Unix one. */
190   {
191     STARTUPINFO si;
192     PROCESS_INFORMATION pi;
193
194     HANDLE child_in, child_out, child_err;
195     HANDLE father_in, father_out_dup;
196     HANDLE current_pid;
197     SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
198     string new_cmd = NULL, app_name = NULL;
199
200     char buf[1024+1];
201     int num;
202     extern char *quote_args(char **argv);
203
204     if (look_for_cmd(args[0], &app_name) == FALSE) {
205       ret = NULL;
206       goto error_exit;
207     }
208
209     /* Compute the command line */
210     new_cmd = quote_args(args);
211
212     /* We need this handle to duplicate other handles */
213     current_pid = GetCurrentProcess();
214
215     ZeroMemory( &si, sizeof(STARTUPINFO) );
216     si.cb = sizeof(STARTUPINFO);
217     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW ;
218     si.wShowWindow = /* 0 */ SW_HIDE ;
219
220     /* Child stdin */
221     child_in = CreateFile("nul",
222                           GENERIC_READ,
223                           FILE_SHARE_READ | FILE_SHARE_WRITE,
224                           &sa,  /* non inheritable */
225                           OPEN_EXISTING,
226                           FILE_ATTRIBUTE_NORMAL,
227                           NULL);
228     si.hStdInput = child_in;
229
230     if (CreatePipe(&father_in, &child_out, NULL, 0) == FALSE) {
231       fprintf(stderr, "popen: error CreatePipe\n");
232       goto error_exit;
233     }
234     if (DuplicateHandle(current_pid, child_out,
235                         current_pid, &father_out_dup,
236                         0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE) {
237       fprintf(stderr, "popen: error DuplicateHandle father_in\n");
238       CloseHandle(father_in);
239       CloseHandle(child_out);
240       goto error_exit;
241     }
242     CloseHandle(child_out);
243     si.hStdOutput = father_out_dup;
244
245     /* Child stderr */
246     if (kpse_make_tex_discard_errors) {
247       child_err = CreateFile("nul",
248                              GENERIC_WRITE,
249                              FILE_SHARE_READ | FILE_SHARE_WRITE,
250                              &sa,       /* non inheritable */
251                              OPEN_EXISTING,
252                              FILE_ATTRIBUTE_NORMAL,
253                              NULL);
254     }
255     else {
256       DuplicateHandle(current_pid, GetStdHandle(STD_ERROR_HANDLE),
257                       current_pid, &child_err,
258                       0, TRUE,
259                       DUPLICATE_SAME_ACCESS);
260     }
261     si.hStdError = child_err;
262
263     /* creating child process */
264     if (CreateProcess(app_name, /* pointer to name of executable module */
265                       new_cmd,  /* pointer to command line string */
266                       NULL,     /* pointer to process security attributes */
267                       NULL,     /* pointer to thread security attributes */
268                       TRUE,     /* handle inheritance flag */
269                       0,                /* creation flags */
270                       NULL,     /* pointer to environment */
271                       NULL,     /* pointer to current directory */
272                       &si,      /* pointer to STARTUPINFO */
273                       &pi               /* pointer to PROCESS_INFORMATION */
274                       ) == 0) {
275       FATAL2("kpathsea: CreateProcess() failed for `%s' (Error %x)\n", new_cmd, GetLastError());
276     }
277
278     CloseHandle(child_in);
279     CloseHandle(father_out_dup);
280     CloseHandle(child_err);
281
282     /* Only the process handle is needed */
283     CloseHandle(pi.hThread);
284
285     /* Get stdout of child from the pipe. */
286     fn = xstrdup("");
287     while (ReadFile(father_in,buf,sizeof(buf)-1, &num, NULL) != 0
288            && num > 0) {
289       if (num <= 0) {
290         if (GetLastError() != ERROR_BROKEN_PIPE) {
291           FATAL2("kpathsea: read() error code for `%s' (Error %d)", new_cmd, GetLastError());
292           break;
293         }
294       } else {
295         string newfn;
296         buf[num] = '\0';
297         newfn = concat(fn, buf);
298         free(fn);
299         fn = newfn;
300       }
301     }
302     /* End of file on pipe, child should have exited at this point. */
303     CloseHandle(father_in);
304
305     if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
306       WARNING2("kpathsea: failed to wait for process termination: %s (Error %d)\n",
307                new_cmd, GetLastError());
308     }
309
310     CloseHandle(pi.hProcess);
311
312     if (new_cmd) free(new_cmd);
313     if (app_name) free(app_name);
314
315     if (fn) {
316       len = strlen(fn);
317
318       /* Remove trailing newlines and returns.  */
319       while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
320         fn[len - 1] = '\0';
321         len--;
322       }
323
324       ret = len == 0 ? NULL : kpse_readable_file (fn);
325       if (!ret && len > 1) {
326         WARNING2 ("kpathsea: %s output `%s' instead of a filename",
327                   new_cmd, fn);
328       }
329
330       /* Free the name if we're not returning it.  */
331       if (fn != ret)
332         free (fn);
333     }
334   error_exit:
335     ;
336   }
337 #else
338   {
339     /* Standard input for the child.  Set to /dev/null */
340     int childin;
341     /* Standard output for the child, what we're interested in. */
342     int childout[2];
343     /* Standard error for the child, same as parent or /dev/null */
344     int childerr;
345     /* Child pid. */
346     pid_t childpid;
347
348     /* Open the channels that the child will use. */
349     /* A fairly horrible uses of gotos for here for the error case. */
350     if ((childin = open("/dev/null", O_RDONLY)) < 0) {
351       perror("kpathsea: open(\"/dev/null\", O_RDONLY)");
352       goto error_childin;
353     }
354     if (pipe(childout) < 0) {
355       perror("kpathsea: pipe()");
356       goto error_childout;
357     }
358     if ((childerr = open("/dev/null", O_WRONLY)) < 0) {
359       perror("kpathsea: open(\"/dev/null\", O_WRONLY)");
360       goto error_childerr;
361     }
362     if ((childpid = fork()) < 0) {
363       perror("kpathsea: fork()");
364       close(childerr);
365      error_childerr:
366       close(childout[0]);
367       close(childout[1]);
368      error_childout:
369       close(childin);
370      error_childin:
371       fn = NULL;
372     } else if (childpid == 0) {
373       /* Child
374        *
375        * We can use vfork, provided we're careful about what we
376        * do here: do not return from this function, do not modify
377        * variables, call _exit if there is a problem.
378        *
379        * Complete setting up the file descriptors.
380        * We use dup(2) so the order in which we do this matters.
381        */
382       close(childout[0]);
383       /* stdin -- the child will not receive input from this */
384       if (childin != 0) {
385         close(0);
386         dup(childin);
387         close(childin);
388       }
389       /* stdout -- the output of the child's action */
390       if (childout[1] != 1) {
391         close(1);
392         dup(childout[1]);
393         close(childout[1]);
394       }
395       /* stderr -- use /dev/null if we discard errors */
396       if (childerr != 2) {
397         if (kpse_make_tex_discard_errors) {
398           close(2);
399           dup(childerr);
400         }
401         close(childerr);
402       }
403       /* FIXME: We could/should close all other file descriptors as well. */
404       /* exec -- on failure a call of _exit(2) it is the only option */
405       if (execvp(args[0], args))
406         perror(args[0]);
407       _exit(1);
408     } else {
409       /* Parent */
410       char buf[1024+1];
411       int num;
412
413       /* Clean up child file descriptors that we won't use anyway. */
414       close(childin);
415       close(childout[1]);
416       close(childerr);
417       /* Get stdout of child from the pipe. */
418       fn = xstrdup("");
419       while ((num = read(childout[0],buf,sizeof(buf)-1)) != 0) {
420         if (num == -1) {
421           if (errno != EINTR) {
422             perror("kpathsea: read()");
423             break;
424           }
425         } else {
426           string newfn;
427           buf[num] = '\0';
428           newfn = concat(fn, buf);
429           free(fn);
430           fn = newfn;
431         }
432       }
433       /* End of file on pipe, child should have exited at this point. */
434       close(childout[0]);
435       /* We don't really care about the exit status at this point. */
436       wait(NULL);
437     }
438
439     if (fn) {
440       len = strlen(fn);
441
442       /* Remove trailing newlines and returns.  */
443       while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
444         fn[len - 1] = '\0';
445         len--;
446       }
447
448       ret = len == 0 ? NULL : kpse_readable_file (fn);
449       if (!ret && len > 1) {
450         WARNING2("kpathsea: %s output `%s' instead of a filename",
451                  args[0], fn);
452       }
453
454       /* Free the name if we're not returning it.  */
455       if (fn != ret)
456         free (fn);
457     } else {
458       ret = NULL;
459     }
460   }
461 #endif
462
463   if (ret == NULL)
464     misstex (format, args);
465   else
466     kpse_db_insert (ret);
467   
468   return ret;
469 }
470
471
472 /* Create BASE in FORMAT and return the generated filename, or
473    return NULL.  */
474
475 string
476 kpse_make_tex P2C(kpse_file_format_type, format,  const_string, base)
477 {
478   kpse_format_info_type spec; /* some compilers lack struct initialization */
479   string ret = NULL;
480   
481   spec = kpse_format_info[format];
482   if (!spec.type) { /* Not initialized yet? */
483     kpse_init_format (format);
484     spec = kpse_format_info[format];
485   }
486
487   if (spec.program && spec.program_enabled_p) {
488     /* See the documentation for the envvars we're dealing with here.  */
489     /* Number of arguments is spec.argc + 1, plus the trailing NULL. */
490     string *args = XTALLOC (spec.argc + 2, string);
491     /* Helpers */
492     int argnum;
493     int i;
494     
495     /* FIXME
496      * Check whether the name we were given is likely to be a problem.
497      * Right now we err on the side of strictness:
498      * - may not start with a hyphen (fixable in the scripts).
499      * - allowed are: alphanumeric, underscore, hyphen, period, plus
500      * ? also allowed DIRSEP, as we can be fed that when creating pk fonts
501      * No doubt some possibilities were overlooked.
502      */
503     if (base[0] == '-' /* || IS_DIR_SEP(base[0])  */) {
504       fprintf(stderr, "kpathsea: Illegal fontname `%s': starts with '%c'\n",
505               base, base[0]);
506       return NULL;
507     }
508     for (i = 0; base[i]; i++) {
509       if (!ISALNUM(base[i])
510           && base[i] != '-'
511           && base[i] != '+'
512           && base[i] != '_'
513           && base[i] != '.'
514           && !IS_DIR_SEP(base[i]))
515       {
516         fprintf(stderr, "kpathsea: Illegal fontname `%s': contains '%c'\n",
517                 base, base[i]);
518         return NULL;
519       }
520     }
521
522     if (format == kpse_gf_format
523         || format == kpse_pk_format
524         || format == kpse_any_glyph_format)
525       set_maketex_mag ();
526
527     /* Here's an awful kludge: if the mode is `/', mktexpk recognizes
528        it as a special case.  `kpse_prog_init' sets it to this in the
529        first place when no mode is otherwise specified; this is so
530        when the user defines a resolution, they don't also have to
531        specify a mode; instead, mktexpk's guesses will take over.
532        They use / for the value because then when it is expanded as
533        part of the PKFONTS et al. path values, we'll wind up searching
534        all the pk directories.  We put $MAKETEX_MODE in the path
535        values in the first place so that sites with two different
536        devices with the same resolution can find the right fonts; but
537        such sites are uncommon, so they shouldn't make things harder
538        for everyone else.  */
539     for (argnum = 0; argnum < spec.argc; argnum++) {
540       args[argnum] = kpse_var_expand (spec.argv[argnum]);
541     }
542     args[argnum++] = xstrdup(base);
543     args[argnum] = NULL;
544
545     ret = maketex (format, args);
546
547     for (argnum = 0; args[argnum] != NULL; argnum++)
548       free (args[argnum]);
549     free (args);
550   }
551
552   return ret;
553 }
554 \f
555 #ifdef TEST
556
557 void
558 test_make_tex (kpse_file_format_type fmt, const_string base)
559 {
560   string answer;
561   
562   printf ("\nAttempting %s in format %d:\n", base, fmt);
563
564   answer = kpse_make_tex (fmt, base);
565   puts (answer ? answer : "(nil)");
566 }
567
568
569 int
570 main ()
571 {
572   xputenv ("KPATHSEA_DPI", "781"); /* call mktexpk */
573   xputenv ("MAKETEX_BASE_DPI", "300"); /* call mktexpk */
574   KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_pk_format]) = true;
575   test_make_tex (kpse_pk_format, "cmr10");
576
577   /* Fail with mktextfm.  */
578   KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_tfm_format]) = true;
579   test_make_tex (kpse_tfm_format, "foozler99");
580   
581   /* Call something disabled.  */
582   test_make_tex (kpse_bst_format, "no-way");
583   
584   return 0;
585 }
586
587 #endif /* TEST */
588
589
590 /*
591 Local variables:
592 test-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a"
593 End:
594 */