1 /* tex-make.c: Run external programs to make TeX-related files.
3 Copyright 1997, 98, 2001-05 Olaf Weber.
4 Copyright 1993, 94, 95, 96, 97 Karl Berry.
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.
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.
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
22 #include <kpathsea/config.h>
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>
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
38 boolean kpse_make_tex_discard_errors = false;
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. */
44 set_maketex_mag P1H(void)
46 char q[MAX_INT_LENGTH * 3 + 3];
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;
53 /* If the environment variables aren't set, it's a bug. */
54 assert (dpi != 0 && bdpi != 0);
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);
62 sprintf(q, "%u+%u/%u", dpi / bdpi, dpi % bdpi, bdpi);
64 unsigned f = bdpi/4000;
65 unsigned r = bdpi%4000;
69 sprintf(q, "%u+%u/(%u*%u+%u)",
70 dpi/bdpi, dpi%bdpi, f, (bdpi - r)/f, r);
72 sprintf(q, "%u+%u/(%u*%u)", dpi/bdpi, dpi%bdpi, f, bdpi/f);
75 sprintf(q, "%u+%u/(4000+%u)", dpi/bdpi, dpi%bdpi, r);
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 = "";
88 sprintf(q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5);
90 xputenv ("MAKETEX_MAG", q);
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. */
98 misstex P2C(kpse_file_format_type, format, string *, args)
100 static FILE *missfont = NULL;
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)
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 */
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,
127 missfont = fopen (missfont_name, FOPEN_A_MODE);
131 fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n",
135 /* Write the command if we have a log file. */
137 fputs (args[0], missfont);
138 for (s = &args[1]; *s != NULL; s++) {
140 fputs (*s, missfont);
142 putc ('\n', missfont);
147 /* Assume the script outputs the filename it creates (and nothing
148 else) on standard output; hence, we run the script with `popen'. */
151 maketex P2C(kpse_file_format_type, format, string*, args)
153 /* New implementation, use fork/exec pair instead of popen, since
154 * the latter is virtually impossible to make safe.
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);
169 /* Amiga has a different interface. */
173 cmd = xstrdup(args[0]);
174 for (s = &args[1]; *s != NULL; s++) {
175 newcmd = concat(cmd, *s);
179 ret = system(cmd) == 0 ? getenv ("LAST_FONT_CREATED"): NULL;
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. */
192 PROCESS_INFORMATION pi;
194 HANDLE child_in, child_out, child_err;
195 HANDLE father_in, father_out_dup;
197 SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
198 string new_cmd = NULL, app_name = NULL;
202 extern char *quote_args(char **argv);
204 if (look_for_cmd(args[0], &app_name) == FALSE) {
209 /* Compute the command line */
210 new_cmd = quote_args(args);
212 /* We need this handle to duplicate other handles */
213 current_pid = GetCurrentProcess();
215 ZeroMemory( &si, sizeof(STARTUPINFO) );
216 si.cb = sizeof(STARTUPINFO);
217 si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW ;
218 si.wShowWindow = /* 0 */ SW_HIDE ;
221 child_in = CreateFile("nul",
223 FILE_SHARE_READ | FILE_SHARE_WRITE,
224 &sa, /* non inheritable */
226 FILE_ATTRIBUTE_NORMAL,
228 si.hStdInput = child_in;
230 if (CreatePipe(&father_in, &child_out, NULL, 0) == FALSE) {
231 fprintf(stderr, "popen: error CreatePipe\n");
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);
242 CloseHandle(child_out);
243 si.hStdOutput = father_out_dup;
246 if (kpse_make_tex_discard_errors) {
247 child_err = CreateFile("nul",
249 FILE_SHARE_READ | FILE_SHARE_WRITE,
250 &sa, /* non inheritable */
252 FILE_ATTRIBUTE_NORMAL,
256 DuplicateHandle(current_pid, GetStdHandle(STD_ERROR_HANDLE),
257 current_pid, &child_err,
259 DUPLICATE_SAME_ACCESS);
261 si.hStdError = child_err;
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 */
275 FATAL2("kpathsea: CreateProcess() failed for `%s' (Error %x)\n", new_cmd, GetLastError());
278 CloseHandle(child_in);
279 CloseHandle(father_out_dup);
280 CloseHandle(child_err);
282 /* Only the process handle is needed */
283 CloseHandle(pi.hThread);
285 /* Get stdout of child from the pipe. */
287 while (ReadFile(father_in,buf,sizeof(buf)-1, &num, NULL) != 0
290 if (GetLastError() != ERROR_BROKEN_PIPE) {
291 FATAL2("kpathsea: read() error code for `%s' (Error %d)", new_cmd, GetLastError());
297 newfn = concat(fn, buf);
302 /* End of file on pipe, child should have exited at this point. */
303 CloseHandle(father_in);
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());
310 CloseHandle(pi.hProcess);
312 if (new_cmd) free(new_cmd);
313 if (app_name) free(app_name);
318 /* Remove trailing newlines and returns. */
319 while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
324 ret = len == 0 ? NULL : kpse_readable_file (fn);
325 if (!ret && len > 1) {
326 WARNING2 ("kpathsea: %s output `%s' instead of a filename",
330 /* Free the name if we're not returning it. */
339 /* Standard input for the child. Set to /dev/null */
341 /* Standard output for the child, what we're interested in. */
343 /* Standard error for the child, same as parent or /dev/null */
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)");
354 if (pipe(childout) < 0) {
355 perror("kpathsea: pipe()");
358 if ((childerr = open("/dev/null", O_WRONLY)) < 0) {
359 perror("kpathsea: open(\"/dev/null\", O_WRONLY)");
362 if ((childpid = fork()) < 0) {
363 perror("kpathsea: fork()");
372 } else if (childpid == 0) {
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.
379 * Complete setting up the file descriptors.
380 * We use dup(2) so the order in which we do this matters.
383 /* stdin -- the child will not receive input from this */
389 /* stdout -- the output of the child's action */
390 if (childout[1] != 1) {
395 /* stderr -- use /dev/null if we discard errors */
397 if (kpse_make_tex_discard_errors) {
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))
413 /* Clean up child file descriptors that we won't use anyway. */
417 /* Get stdout of child from the pipe. */
419 while ((num = read(childout[0],buf,sizeof(buf)-1)) != 0) {
421 if (errno != EINTR) {
422 perror("kpathsea: read()");
428 newfn = concat(fn, buf);
433 /* End of file on pipe, child should have exited at this point. */
435 /* We don't really care about the exit status at this point. */
442 /* Remove trailing newlines and returns. */
443 while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
448 ret = len == 0 ? NULL : kpse_readable_file (fn);
449 if (!ret && len > 1) {
450 WARNING2("kpathsea: %s output `%s' instead of a filename",
454 /* Free the name if we're not returning it. */
464 misstex (format, args);
466 kpse_db_insert (ret);
472 /* Create BASE in FORMAT and return the generated filename, or
476 kpse_make_tex P2C(kpse_file_format_type, format, const_string, base)
478 kpse_format_info_type spec; /* some compilers lack struct initialization */
481 spec = kpse_format_info[format];
482 if (!spec.type) { /* Not initialized yet? */
483 kpse_init_format (format);
484 spec = kpse_format_info[format];
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);
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.
503 if (base[0] == '-' /* || IS_DIR_SEP(base[0]) */) {
504 fprintf(stderr, "kpathsea: Illegal fontname `%s': starts with '%c'\n",
508 for (i = 0; base[i]; i++) {
509 if (!ISALNUM(base[i])
514 && !IS_DIR_SEP(base[i]))
516 fprintf(stderr, "kpathsea: Illegal fontname `%s': contains '%c'\n",
522 if (format == kpse_gf_format
523 || format == kpse_pk_format
524 || format == kpse_any_glyph_format)
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]);
542 args[argnum++] = xstrdup(base);
545 ret = maketex (format, args);
547 for (argnum = 0; args[argnum] != NULL; argnum++)
558 test_make_tex (kpse_file_format_type fmt, const_string base)
562 printf ("\nAttempting %s in format %d:\n", base, fmt);
564 answer = kpse_make_tex (fmt, base);
565 puts (answer ? answer : "(nil)");
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");
577 /* Fail with mktextfm. */
578 KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_tfm_format]) = true;
579 test_make_tex (kpse_tfm_format, "foozler99");
581 /* Call something disabled. */
582 test_make_tex (kpse_bst_format, "no-way");
592 test-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a"