1 /* tex-make.c: run external programs to make TeX-related files.
2 
3    Copyright 1993, 1994, 1995, 1996, 1997, 2008-2013 Karl Berry.
4    Copyright 1997, 1998, 2001-05 Olaf Weber.
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 License
17    along with this library; if not, see <http://www.gnu.org/licenses/>.  */
18 
19 #include <kpathsea/config.h>
20 
21 #include <kpathsea/c-fopen.h>
22 #include <kpathsea/c-pathch.h>
23 #include <kpathsea/db.h>
24 #include <kpathsea/fn.h>
25 #include <kpathsea/magstep.h>
26 #include <kpathsea/readable.h>
27 #include <kpathsea/tex-make.h>
28 #include <kpathsea/variable.h>
29 
30 #if !defined (AMIGA) && !(defined (MSDOS) && !defined(__DJGPP__)) && !defined (WIN32)
31 #include <sys/wait.h>
32 #endif
33 
34 
35 /* We set the envvar MAKETEX_MAG, which is part of the default spec for
36    MakeTeXPK above, based on KPATHSEA_DPI and MAKETEX_BASE_DPI.  */
37 
38 static void
set_maketex_mag(kpathsea kpse)39 set_maketex_mag (kpathsea kpse)
40 {
41   char q[MAX_INT_LENGTH * 3 + 3];
42   int m;
43   string dpi_str = getenv ("KPATHSEA_DPI");
44   string bdpi_str = getenv ("MAKETEX_BASE_DPI");
45   unsigned dpi = dpi_str ? atoi (dpi_str) : 0;
46   unsigned bdpi = bdpi_str ? atoi (bdpi_str) : 0;
47 
48   /* If the environment variables aren't set, it's a bug.  */
49   assert (dpi != 0 && bdpi != 0);
50 
51   /* Fix up for roundoff error.  Hopefully the driver has already fixed
52      up DPI, but may as well be safe, and also get the magstep number.  */
53   (void) kpathsea_magstep_fix (kpse, dpi, bdpi, &m);
54 
55   if (m == 0) {
56       if (bdpi <= 4000) {
57           sprintf(q, "%u+%u/%u", dpi / bdpi, dpi % bdpi, bdpi);
58       } else {
59           unsigned f = bdpi/4000;
60           unsigned r = bdpi%4000;
61 
62           if (f > 1) {
63               if (r > 0) {
64                   sprintf(q, "%u+%u/(%u*%u+%u)",
65                           dpi/bdpi, dpi%bdpi, f, (bdpi - r)/f, r);
66               } else {
67                   sprintf(q, "%u+%u/(%u*%u)", dpi/bdpi, dpi%bdpi, f, bdpi/f);
68               }
69           } else {
70               sprintf(q, "%u+%u/(4000+%u)", dpi/bdpi, dpi%bdpi, r);
71           }
72       }
73   } else {
74       /* m is encoded with LSB being a ``half'' bit (see magstep.h).  Are
75          we making an assumption here about two's complement?  Probably.
76          In any case, if m is negative, we have to put in the sign
77          explicitly, since m/2==0 if m==-1.  */
78       const_string sign = "";
79       if (m < 0) {
80           m *= -1;
81           sign = "-";
82       }
83       sprintf(q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5);
84   }
85   kpathsea_xputenv (kpse, "MAKETEX_MAG", q);
86 }
87 
88 /* This mktex... program was disabled, or the script failed.  If this
89    was a font creation (according to FORMAT), append CMD
90    to a file missfont.log in the current directory.  */
91 
92 static void
misstex(kpathsea kpse,kpse_file_format_type format,string * args)93 misstex (kpathsea kpse, kpse_file_format_type format,  string *args)
94 {
95   string *s;
96 
97   /* If we weren't trying to make a font, do nothing.  Maybe should
98      allow people to specify what they want recorded?  */
99   if (format != kpse_gf_format
100       && format != kpse_pk_format
101       && format != kpse_any_glyph_format
102       && format != kpse_tfm_format
103       && format != kpse_vf_format)
104     return;
105 
106   /* If this is the first time, have to open the log file.  But don't
107      bother logging anything if they were discarding errors.  */
108   if (!kpse->missfont && !kpse->make_tex_discard_errors) {
109     const_string missfont_name = kpathsea_var_value (kpse, "MISSFONT_LOG");
110     if (!missfont_name || *missfont_name == '1') {
111       missfont_name = "missfont.log"; /* take default name */
112     } else if (missfont_name
113                && (*missfont_name == 0 || *missfont_name == '0')) {
114       missfont_name = NULL; /* user requested no missfont.log */
115     } /* else use user's name */
116 
117     kpse->missfont
118       = missfont_name ? fopen (missfont_name, FOPEN_A_MODE) : NULL;
119     if (!kpse->missfont && kpathsea_var_value (kpse, "TEXMFOUTPUT")) {
120       missfont_name = concat3 (kpathsea_var_value (kpse, "TEXMFOUTPUT"),
121                                DIR_SEP_STRING, missfont_name);
122       kpse->missfont = fopen (missfont_name, FOPEN_A_MODE);
123     }
124 
125     if (kpse->missfont)
126       fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n",
127                missfont_name);
128   }
129 
130   /* Write the command if we have a log file.  */
131   if (kpse->missfont) {
132     fputs (args[0], kpse->missfont);
133     for (s = &args[1]; *s != NULL; s++) {
134       putc(' ', kpse->missfont);
135       fputs (*s, kpse->missfont);
136     }
137     putc ('\n', kpse->missfont);
138   }
139 }
140 
141 
142 /* Assume the script outputs the filename it creates (and nothing
143    else) on standard output; hence, we run the script with `popen'.  */
144 
145 static string
maketex(kpathsea kpse,kpse_file_format_type format,string * args)146 maketex (kpathsea kpse, kpse_file_format_type format, string* args)
147 {
148   /* New implementation, use fork/exec pair instead of popen, since
149    * the latter is virtually impossible to make safe.
150    */
151   unsigned len;
152   string *s;
153   string ret = NULL;
154   string fn;
155 #if defined(WIN32)
156   char   fullbin[256], *wrp;
157 
158   wrp = kpathsea_var_value(kpse, "SELFAUTOLOC");
159   if(wrp == NULL) {
160      fprintf(stderr, "I cannot get SELFAUTOLOC\n");
161      exit(100);
162   }
163 
164   strcpy(fullbin, wrp);
165   free(wrp);
166   for(wrp=fullbin; *wrp; wrp++) {
167      if(*wrp == '/') *wrp = '\\';
168   }
169   strcat(fullbin, "\\");
170   strcat(fullbin, args[0]);
171 #endif
172   if (!kpse->make_tex_discard_errors) {
173     fprintf (stderr, "\nkpathsea: Running");
174     for (s = &args[0]; *s != NULL; s++)
175       fprintf (stderr, " %s", *s);
176     fputc('\n', stderr);
177   }
178 
179 #if defined (AMIGA)
180   /* Amiga has a different interface. */
181   {
182     string cmd;
183     string newcmd;
184     cmd = xstrdup(args[0]);
185     for (s = &args[1];  *s != NULL; s++) {
186       newcmd = concat(cmd, *s);
187       free (cmd);
188       cmd = newcmd;
189     }
190     ret = system(cmd) == 0 ? getenv ("LAST_FONT_CREATED"): NULL;
191     free (cmd);
192   }
193 #elif defined (MSDOS) && !defined(__DJGPP__)
194 #error Implement new MSDOS mktex call interface here
195 #else /* WIN32 or Unix */
196   {
197 #if defined (WIN32)
198     /* spawnvp(_P_NOWAIT, ...) and pipe --ak 2002/12/15 */
199 
200     unsigned long nexitcode = STILL_ACTIVE;
201     HANDLE hchild;
202     int hstdout, childpipe[2];
203     int hstderr = -1;
204     FILE *Hnul = NULL;
205 
206     fn = NULL;
207 
208     if(_pipe(childpipe, 1024, O_TEXT | _O_NOINHERIT) == -1) {
209       perror("kpathsea: pipe()");
210       goto labeldone;
211     }
212 
213     hstdout = _dup(fileno(stdout));
214     if(_dup2(childpipe[1], fileno(stdout)) != 0) {
215       close(hstdout);
216       close(childpipe[0]);
217       close(childpipe[1]);
218       goto labeldone;
219     }
220 
221     close(childpipe[1]);
222 
223     if(kpse->make_tex_discard_errors) {
224       Hnul = fopen("nul", "w");
225       if(!Hnul) {
226         perror("kpathsea: fopen(\"nul\")");
227       }
228       else {
229         hstderr = _dup(fileno(stderr));
230         _dup2(fileno(Hnul), fileno(stderr));
231       }
232     }
233     fprintf(stderr, "\nThe command name is %s\n", fullbin);
234     hchild = (HANDLE)spawnvp(_P_NOWAIT, fullbin, (const char * const *) args);
235 
236     _dup2(hstdout, fileno(stdout));
237     close(hstdout);
238 
239     if((int)hchild == -1) {
240       close(childpipe[0]);
241       goto labeldone;
242     }
243 
244     if(hchild) {
245       char buf[1024+1];
246       int num;
247 
248       fn = xstrdup("");
249       while(nexitcode == STILL_ACTIVE) {
250         num = read(childpipe[0], buf, sizeof(buf)-1);
251         if(num) {
252           string newfn;
253           buf[num] = '\0';
254           newfn = concat(fn, buf);
255           free(fn);
256           fn = newfn;
257         }
258         if(!GetExitCodeProcess(hchild, &nexitcode)) {
259           fn = NULL;
260           close(childpipe[0]);
261           goto labeldone;
262         }
263       }
264       close(childpipe[0]);
265     }
266 
267  labeldone:
268     if(kpse->make_tex_discard_errors && Hnul) {
269        _dup2(hstderr, fileno(stderr));
270        close(hstderr);
271        fclose(Hnul);
272     }
273 #else /* !WIN32 */
274     /* Standard input for the child.  Set to /dev/null */
275     int childin;
276     /* Standard output for the child, what we're interested in. */
277     int childout[2];
278     /* Standard error for the child, same as parent or /dev/null */
279     int childerr;
280     /* Child pid. */
281     pid_t childpid;
282 
283     /* Open the channels that the child will use. */
284     /* A fairly horrible uses of gotos for here for the error case. */
285     if ((childin = open("/dev/null", O_RDONLY)) < 0) {
286       perror("kpathsea: open(\"/dev/null\", O_RDONLY)");
287       goto error_childin;
288     }
289     if (pipe(childout) < 0) {
290       perror("kpathsea: pipe()");
291       goto error_childout;
292     }
293     if ((childerr = open("/dev/null", O_WRONLY)) < 0) {
294       perror("kpathsea: open(\"/dev/null\", O_WRONLY)");
295       goto error_childerr;
296     }
297     if ((childpid = fork()) < 0) {
298       perror("kpathsea: fork()");
299       close(childerr);
300      error_childerr:
301       close(childout[0]);
302       close(childout[1]);
303      error_childout:
304       close(childin);
305      error_childin:
306       fn = NULL;
307     } else if (childpid == 0) {
308       /* Child
309        *
310        * We can use vfork, provided we're careful about what we
311        * do here: do not return from this function, do not modify
312        * variables, call _exit if there is a problem.
313        *
314        * Complete setting up the file descriptors.
315        * We use dup(2) so the order in which we do this matters.
316        */
317       close(childout[0]);
318       /* stdin -- the child will not receive input from this */
319       if (childin != 0) {
320         close(0);
321         dup(childin);
322         close(childin);
323       }
324       /* stdout -- the output of the child's action */
325       if (childout[1] != 1) {
326         close(1);
327         dup(childout[1]);
328         close(childout[1]);
329       }
330       /* stderr -- use /dev/null if we discard errors */
331       if (childerr != 2) {
332         if (kpse->make_tex_discard_errors) {
333           close(2);
334           dup(childerr);
335         }
336         close(childerr);
337       }
338       /* FIXME: We could/should close all other file descriptors as well. */
339       /* exec -- on failure a call of _exit(2) it is the only option */
340       if (execvp(args[0], args))
341         perror(args[0]);
342       _exit(1);
343     } else {
344       /* Parent */
345       char buf[1024+1];
346       int num;
347 
348       /* Clean up child file descriptors that we won't use anyway. */
349       close(childin);
350       close(childout[1]);
351       close(childerr);
352       /* Get stdout of child from the pipe. */
353       fn = xstrdup("");
354       while ((num = read(childout[0],buf,sizeof(buf)-1)) != 0) {
355         if (num == -1) {
356           if (errno != EINTR) {
357             perror("kpathsea: read()");
358             break;
359           }
360         } else {
361           string newfn;
362           buf[num] = '\0';
363           newfn = concat(fn, buf);
364           free(fn);
365           fn = newfn;
366         }
367       }
368       /* End of file on pipe, child should have exited at this point. */
369       close(childout[0]);
370       /* We don't really care about the exit status at this point. */
371       wait(NULL);
372     }
373 #endif /* !WIN32 */
374 
375     if (fn) {
376       len = strlen(fn);
377 
378       /* Remove trailing newlines and returns.  */
379       while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
380         fn[len - 1] = '\0';
381         len--;
382       }
383 
384       ret = len == 0 ? NULL : kpathsea_readable_file (kpse, fn);
385       if (!ret && len > 1) {
386         WARNING2 ("kpathsea: %s output `%s' instead of a filename",
387                   args[0], fn);
388       }
389 
390       /* Free the name if we're not returning it.  */
391       if (fn != ret)
392         free (fn);
393     }
394   }
395 #endif /* WIN32 or Unix */
396 
397   if (ret == NULL)
398       misstex (kpse, format, args);
399   else
400       kpathsea_db_insert (kpse, ret);
401 
402   return ret;
403 }
404 
405 
406 
407 /* Create BASE in FORMAT and return the generated filename, or
408    return NULL.  */
409 
410 string
kpathsea_make_tex(kpathsea kpse,kpse_file_format_type format,const_string base)411 kpathsea_make_tex (kpathsea kpse, kpse_file_format_type format,
412                    const_string base)
413 {
414   kpse_format_info_type spec; /* some compilers lack struct initialization */
415   string ret = NULL;
416 
417   spec = kpse->format_info[format];
418   if (!spec.type) { /* Not initialized yet? */
419     kpathsea_init_format (kpse, format);
420     spec = kpse->format_info[format];
421   }
422 
423   if (spec.program && spec.program_enabled_p) {
424     /* See the documentation for the envvars we're dealing with here.  */
425     /* Number of arguments is spec.argc + 1, plus the trailing NULL. */
426     string *args = XTALLOC (spec.argc + 2, string);
427     /* Helpers */
428     int argnum;
429     int i;
430 
431     /* FIXME
432      * Check whether the name we were given is likely to be a problem.
433      * Right now we err on the side of strictness:
434      * - may not start with a hyphen (fixable in the scripts).
435      * - allowed are: alphanumeric, underscore, hyphen, period, plus
436      * ? also allowed DIRSEP, as we can be fed that when creating pk fonts
437      * No doubt some possibilities were overlooked.
438      */
439     if (base[0] == '-' /* || IS_DIR_SEP(base[0])  */) {
440       fprintf(stderr, "kpathsea:make_tex: Invalid fontname `%s', starts with '%c'\n",
441               base, base[0]);
442       return NULL;
443     }
444     for (i = 0; base[i]; i++) {
445       if (!ISALNUM(base[i])
446           && base[i] != '-'
447           && base[i] != '+'
448           && base[i] != '_'
449           && base[i] != '.'
450           && !IS_DIR_SEP(base[i]))
451       {
452         fprintf(stderr, "kpathsea:make_tex: Invalid fontname `%s', contains '%c'\n",
453                 base, base[i]);
454         return NULL;
455       }
456     }
457 
458     if (format == kpse_gf_format
459         || format == kpse_pk_format
460         || format == kpse_any_glyph_format)
461       set_maketex_mag (kpse);
462 
463     /* Here's an awful kludge: if the mode is `/', mktexpk recognizes
464        it as a special case.  `kpse_prog_init' sets it to this in the
465        first place when no mode is otherwise specified; this is so
466        when the user defines a resolution, they don't also have to
467        specify a mode; instead, mktexpk's guesses will take over.
468        They use / for the value because then when it is expanded as
469        part of the PKFONTS et al. path values, we'll wind up searching
470        all the pk directories.  We put $MAKETEX_MODE in the path
471        values in the first place so that sites with two different
472        devices with the same resolution can find the right fonts; but
473        such sites are uncommon, so they shouldn't make things harder
474        for everyone else.  */
475     for (argnum = 0; argnum < spec.argc; argnum++) {
476         args[argnum] = kpathsea_var_expand (kpse, spec.argv[argnum]);
477     }
478     args[argnum++] = xstrdup(base);
479     args[argnum] = NULL;
480 
481     ret = maketex (kpse, format, args);
482 
483     for (argnum = 0; args[argnum] != NULL; argnum++)
484       free (args[argnum]);
485     free (args);
486   }
487 
488   return ret;
489 }
490 
491 #if defined (KPSE_COMPAT_API)
492 string
kpse_make_tex(kpse_file_format_type format,const_string base)493 kpse_make_tex (kpse_file_format_type format,  const_string base)
494 {
495   return kpathsea_make_tex (kpse_def, format, base);
496 }
497 #endif
498 
499 
500 #ifdef TEST
501 
502 void
test_make_tex(kpathsea kpse,kpse_file_format_type fmt,const_string base)503 test_make_tex (kpathsea kpse, kpse_file_format_type fmt, const_string base)
504 {
505   string answer;
506 
507   printf ("\nAttempting %s in format %d:\n", base, fmt);
508 
509   answer = kpathsea_make_tex (kpse, fmt, base);
510   puts (answer ? answer : "(nil)");
511 }
512 
513 
514 int
main(int argc,char ** argv)515 main (int argc, char **argv)
516 {
517   kpathsea kpse = xcalloc(1, sizeof(kpathsea_instance));
518   kpathsea_set_program_name(kpse, argv[0], NULL);
519   kpathsea_xputenv (kpse, "KPATHSEA_DPI", "781"); /* call mktexpk */
520   kpathsea_xputenv (kpse,"MAKETEX_BASE_DPI", "300"); /* call mktexpk */
521   kpathsea_set_program_enabled(kpse, kpse_pk_format, 1, kpse_src_env);
522   test_make_tex (kpse, kpse_pk_format, "cmr10");
523 
524   /* Fail with mktextfm.  */
525   kpathsea_set_program_enabled(kpse, kpse_tfm_format, 1, kpse_src_env);
526   test_make_tex (kpse, kpse_tfm_format, "foozler99");
527 
528   /* Call something disabled.  */
529   test_make_tex (kpse, kpse_bst_format, "no-way");
530 
531   return 0;
532 }
533 
534 #endif /* TEST */
535 
536 
537 /*
538 Local variables:
539 standalone-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a"
540 End:
541 */
542