diff options
Diffstat (limited to 'Build/source/texk/kpathsea/tex-make.c')
-rw-r--r-- | Build/source/texk/kpathsea/tex-make.c | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/Build/source/texk/kpathsea/tex-make.c b/Build/source/texk/kpathsea/tex-make.c new file mode 100644 index 00000000000..530c6ffe535 --- /dev/null +++ b/Build/source/texk/kpathsea/tex-make.c @@ -0,0 +1,556 @@ +/* tex-make.c: run external programs to make TeX-related files. + + Copyright 1993, 1994, 1995, 1996, 1997, 2008-2020 Karl Berry. + Copyright 1997, 1998, 2001-05 Olaf Weber. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this library; if not, see <http://www.gnu.org/licenses/>. */ + +#include <kpathsea/config.h> + +#include <kpathsea/c-fopen.h> +#include <kpathsea/c-pathch.h> +#include <kpathsea/db.h> +#include <kpathsea/fn.h> +#include <kpathsea/magstep.h> +#include <kpathsea/readable.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/variable.h> + +#if !defined (AMIGA) && !(defined (MSDOS) && !defined(__DJGPP__)) && !defined (WIN32) +#include <sys/wait.h> +#endif + + +/* We set the envvar MAKETEX_MAG, which is part of the default spec for + MakeTeXPK above, based on KPATHSEA_DPI and MAKETEX_BASE_DPI. */ + +static void +set_maketex_mag (kpathsea kpse) +{ + char q[MAX_INT_LENGTH * 3 + 3]; + int m; + string dpi_str = getenv ("KPATHSEA_DPI"); + string bdpi_str = getenv ("MAKETEX_BASE_DPI"); + unsigned dpi = dpi_str ? atoi (dpi_str) : 0; + unsigned bdpi = bdpi_str ? atoi (bdpi_str) : 0; + + /* If the environment variables aren't set, it's a bug. */ + assert (dpi != 0 && bdpi != 0); + + /* Fix up for roundoff error. Hopefully the driver has already fixed + up DPI, but may as well be safe, and also get the magstep number. */ + (void) kpathsea_magstep_fix (kpse, dpi, bdpi, &m); + + if (m == 0) { + if (bdpi <= 4000) { + sprintf(q, "%u+%u/%u", dpi / bdpi, dpi % bdpi, bdpi); + } else { + unsigned f = bdpi/4000; + unsigned r = bdpi%4000; + + if (f > 1) { + if (r > 0) { + sprintf(q, "%u+%u/(%u*%u+%u)", + dpi/bdpi, dpi%bdpi, f, (bdpi - r)/f, r); + } else { + sprintf(q, "%u+%u/(%u*%u)", dpi/bdpi, dpi%bdpi, f, bdpi/f); + } + } else { + sprintf(q, "%u+%u/(4000+%u)", dpi/bdpi, dpi%bdpi, r); + } + } + } else { + /* m is encoded with LSB being a ``half'' bit (see magstep.h). Are + we making an assumption here about two's complement? Probably. + In any case, if m is negative, we have to put in the sign + explicitly, since m/2==0 if m==-1. */ + const_string sign = ""; + if (m < 0) { + m *= -1; + sign = "-"; + } + sprintf(q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5); + } + kpathsea_xputenv (kpse, "MAKETEX_MAG", q); +} + +/* This mktex... program was disabled, or the script failed. If this + was a font creation (according to FORMAT), append CMD + to a file missfont.log in the current directory. */ + +static void +misstex (kpathsea kpse, kpse_file_format_type format, string *args) +{ + string *s; + + /* If we weren't trying to make a font, do nothing. Maybe should + allow people to specify what they want recorded? */ + if (format != kpse_gf_format + && format != kpse_pk_format + && format != kpse_any_glyph_format + && format != kpse_tfm_format + && format != kpse_vf_format) + return; + + /* If this is the first time, have to open the log file. But don't + bother logging anything if they were discarding errors. */ + if (!kpse->missfont && !kpse->make_tex_discard_errors) { + const_string missfont_name = kpathsea_var_value (kpse, "MISSFONT_LOG"); + if (!missfont_name || *missfont_name == '1') { + missfont_name = "missfont.log"; /* take default name */ + } else if (missfont_name + && (*missfont_name == 0 || *missfont_name == '0')) { + missfont_name = NULL; /* user requested no missfont.log */ + } /* else use user's name */ + + kpse->missfont + = missfont_name ? fopen (missfont_name, FOPEN_A_MODE) : NULL; + if (!kpse->missfont && kpathsea_var_value (kpse, "TEXMFOUTPUT")) { + missfont_name = concat3 (kpathsea_var_value (kpse, "TEXMFOUTPUT"), + DIR_SEP_STRING, missfont_name); + kpse->missfont = fopen (missfont_name, FOPEN_A_MODE); + } + + if (kpse->missfont) + fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n", + missfont_name); + } + + /* Write the command if we have a log file. */ + if (kpse->missfont) { + fputs (args[0], kpse->missfont); + for (s = &args[1]; *s != NULL; s++) { + putc(' ', kpse->missfont); + fputs (*s, kpse->missfont); + } + putc ('\n', kpse->missfont); + } +} + + +/* Assume the script outputs the filename it creates (and nothing + else) on standard output; hence, we run the script with `popen'. */ + +static string +maketex (kpathsea kpse, kpse_file_format_type format, string* args) +{ + /* New implementation, use fork/exec pair instead of popen, since + * the latter is virtually impossible to make safe. + */ + unsigned len; + string *s; + string ret = NULL; + string fn; +#if defined(WIN32) + char fullbin[256], *wrp; + + wrp = kpathsea_var_value(kpse, "SELFAUTOLOC"); + if(wrp == NULL) { + fprintf(stderr, "I cannot get SELFAUTOLOC\n"); + exit(100); + } + + strcpy(fullbin, wrp); + free(wrp); + for(wrp=fullbin; *wrp; wrp++) { + if(*wrp == '/') *wrp = '\\'; + } + strcat(fullbin, "\\"); + strcat(fullbin, args[0]); +#endif + if (!kpse->make_tex_discard_errors) { + fprintf (stderr, "\nkpathsea: Running"); + for (s = &args[0]; *s != NULL; s++) + fprintf (stderr, " %s", *s); + fputc('\n', stderr); + } + +#if defined (AMIGA) + /* Amiga has a different interface. */ + { + string cmd; + string newcmd; + cmd = xstrdup(args[0]); + for (s = &args[1]; *s != NULL; s++) { + newcmd = concat(cmd, *s); + free (cmd); + cmd = newcmd; + } + ret = system(cmd) == 0 ? getenv ("LAST_FONT_CREATED"): NULL; + free (cmd); + } +#elif defined (MSDOS) && !defined(__DJGPP__) +#error Implement new MSDOS mktex call interface here +#else /* WIN32 or Unix */ + { +#if defined (WIN32) + /* spawnvp(_P_NOWAIT, ...) and pipe --ak 2002/12/15 */ + + unsigned long nexitcode = STILL_ACTIVE; + HANDLE hchild; + int hstdout, childpipe[2]; + int hstderr = -1; + FILE *Hnul = NULL; + + fn = NULL; + + if(_pipe(childpipe, 1024, O_TEXT | _O_NOINHERIT) == -1) { + perror("kpathsea: pipe()"); + goto labeldone; + } + + hstdout = _dup(fileno(stdout)); + if(_dup2(childpipe[1], fileno(stdout)) != 0) { + close(hstdout); + close(childpipe[0]); + close(childpipe[1]); + goto labeldone; + } + + close(childpipe[1]); + + if(kpse->make_tex_discard_errors) { + Hnul = fopen("nul", "w"); + if(!Hnul) { + perror("kpathsea: fopen(\"nul\")"); + } + else { + hstderr = _dup(fileno(stderr)); + _dup2(fileno(Hnul), fileno(stderr)); + } + } + fprintf(stderr, "\nThe command name is %s\n", fullbin); + hchild = (HANDLE)_spawnvp(_P_NOWAIT, fullbin, (const char * const *) args); + + _dup2(hstdout, fileno(stdout)); + close(hstdout); + + if(hchild == (HANDLE)(-1)) { + close(childpipe[0]); + goto labeldone; + } + + if(hchild) { + char buf[1024+1]; + int num; + + fn = xstrdup(""); + while(nexitcode == STILL_ACTIVE) { + num = read(childpipe[0], buf, sizeof(buf)-1); + if(num) { + string newfn; + buf[num] = '\0'; + newfn = concat(fn, buf); + free(fn); + fn = newfn; + } + if(!GetExitCodeProcess(hchild, &nexitcode)) { + fn = NULL; + close(childpipe[0]); + goto labeldone; + } + } + close(childpipe[0]); + } + + labeldone: + if(kpse->make_tex_discard_errors && Hnul) { + _dup2(hstderr, fileno(stderr)); + close(hstderr); + fclose(Hnul); + } +#else /* !WIN32 */ + /* Standard input for the child. Set to /dev/null */ + int childin; + /* Standard output for the child, what we're interested in. */ + int childout[2]; + /* Standard error for the child, same as parent or /dev/null */ + int childerr; + /* Child pid. */ + pid_t childpid; + + /* Open the channels that the child will use. */ + /* A fairly horrible uses of gotos for here for the error case. */ + if ((childin = open("/dev/null", O_RDONLY)) < 0) { + perror("kpathsea: open(\"/dev/null\", O_RDONLY)"); + goto error_childin; + } + if (pipe(childout) < 0) { + perror("kpathsea: pipe()"); + goto error_childout; + } + if ((childerr = open("/dev/null", O_WRONLY)) < 0) { + perror("kpathsea: open(\"/dev/null\", O_WRONLY)"); + goto error_childerr; + } + if ((childpid = fork()) < 0) { + perror("kpathsea: fork()"); + close(childerr); + error_childerr: + close(childout[0]); + close(childout[1]); + error_childout: + close(childin); + error_childin: + fn = NULL; + } else if (childpid == 0) { + /* Child + * + * We can use vfork, provided we're careful about what we + * do here: do not return from this function, do not modify + * variables, call _exit if there is a problem. + * + * Complete setting up the file descriptors. + * We use dup(2) so the order in which we do this matters. + */ + close(childout[0]); + /* stdin -- the child will not receive input from this */ + if (childin != 0) { + close(0); + if (dup(childin) != 0) { + perror("kpathsea: dup(2) failed for stdin"); + close(childin); + _exit(1); + } + close(childin); + } + /* stdout -- the output of the child's action */ + if (childout[1] != 1) { + close(1); + if (dup(childout[1]) != 1) { + perror("kpathsea: dup(2) failed for stdout"); + close(childout[1]); + _exit(1); + } + close(childout[1]); + } + /* stderr -- use /dev/null if we discard errors */ + if (childerr != 2) { + if (kpse->make_tex_discard_errors) { + close(2); + if (dup(childerr) != 2) { + perror("kpathsea: dup(2) failed for stderr"); + close(childerr); + _exit(1); + } + } + close(childerr); + } + /* FIXME: We could/should close all other file descriptors as well. */ + /* exec -- on failure a call of _exit(2) it is the only option */ + if (execvp(args[0], args)) + perror(args[0]); + _exit(1); + } else { + /* Parent */ + char buf[1024+1]; + int num; + + /* Clean up child file descriptors that we won't use anyway. */ + close(childin); + close(childout[1]); + close(childerr); + /* Get stdout of child from the pipe. */ + fn = xstrdup(""); + while ((num = read(childout[0],buf,sizeof(buf)-1)) != 0) { + if (num == -1) { + if (errno != EINTR) { + perror("kpathsea: read()"); + break; + } + } else { + string newfn; + buf[num] = '\0'; + newfn = concat(fn, buf); + free(fn); + fn = newfn; + } + } + /* End of file on pipe, child should have exited at this point. */ + close(childout[0]); + /* We don't really care about the exit status at this point. */ + wait(NULL); + } +#endif /* !WIN32 */ + + if (fn) { + len = strlen(fn); + + /* Remove trailing newlines and returns. */ + while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) { + fn[len - 1] = '\0'; + len--; + } + + ret = len == 0 ? NULL : kpathsea_readable_file (kpse, fn); + if (!ret && len > 1) { + WARNING2 ("kpathsea: %s output `%s' instead of a filename", + args[0], fn); + } + + /* Free the name if we're not returning it. */ + if (fn != ret) + free (fn); + } + } +#endif /* WIN32 or Unix */ + + if (ret == NULL) + misstex (kpse, format, args); + else + kpathsea_db_insert (kpse, ret); + + return ret; +} + + + +/* Create BASE in FORMAT and return the generated filename, or + return NULL. We used to emit warnings for names we declined to pass + on to the scripts, but such names are common with system fonts, so + now we are silent (just returning NULL). That is arguably better + behavior anyway. Presumably the caller always reports "font not + found" anyway. */ + +string +kpathsea_make_tex (kpathsea kpse, kpse_file_format_type format, + const_string base) +{ + kpse_format_info_type spec; /* some compilers lack struct initialization */ + string ret = NULL; + + spec = kpse->format_info[format]; + if (!spec.type) { /* Not initialized yet? */ + kpathsea_init_format (kpse, format); + spec = kpse->format_info[format]; + } + + if (spec.program && spec.program_enabled_p) { + /* See the documentation for the envvars we're dealing with here. */ + /* Number of arguments is spec.argc + 1, plus the trailing NULL. */ + string *args = XTALLOC (spec.argc + 2, string); + /* Helpers */ + int argnum; + int i; + + /* Check whether the name we were given is likely to be a problem. + All could be fixed in the scripts and/or invocation, but in + practice our names are simple, so let's err on the side of strictness: + - may not start with a hyphen + - allowed are: alphanumeric, underscore, hyphen, period, plus + - also allowed: DIRSEP, as we can be fed that when creating pk fonts + + For example, system fonts are likely to contain spaces, and + (for filename lookups) be enclosed in square brackets. We don't + want to try calling our mktex* scripts on those. + */ + if (base[0] == '-' /* || IS_DIR_SEP(base[0]) */) { + return NULL; + } + for (i = 0; base[i]; i++) { + if (!ISALNUM(base[i]) + && base[i] != '-' + && base[i] != '+' + && base[i] != '_' + && base[i] != '.' + && !IS_DIR_SEP(base[i])) + { + return NULL; + } + } + + if (format == kpse_gf_format + || format == kpse_pk_format + || format == kpse_any_glyph_format) + set_maketex_mag (kpse); + + /* Here's an awful kludge: if the mode is `/', mktexpk recognizes + it as a special case. `kpse_prog_init' sets it to this in the + first place when no mode is otherwise specified; this is so + when the user defines a resolution, they don't also have to + specify a mode; instead, mktexpk's guesses will take over. + They use / for the value because then when it is expanded as + part of the PKFONTS et al. path values, we'll wind up searching + all the pk directories. We put $MAKETEX_MODE in the path + values in the first place so that sites with two different + devices with the same resolution can find the right fonts; but + such sites are uncommon, so they shouldn't make things harder + for everyone else. */ + for (argnum = 0; argnum < spec.argc; argnum++) { + args[argnum] = kpathsea_var_expand (kpse, spec.argv[argnum]); + } + args[argnum++] = xstrdup(base); + args[argnum] = NULL; + + ret = maketex (kpse, format, args); + + for (argnum = 0; args[argnum] != NULL; argnum++) + free (args[argnum]); + free (args); + } + + return ret; +} + +#if defined (KPSE_COMPAT_API) +string +kpse_make_tex (kpse_file_format_type format, const_string base) +{ + return kpathsea_make_tex (kpse_def, format, base); +} +#endif + + +#ifdef TEST + +void +test_make_tex (kpathsea kpse, kpse_file_format_type fmt, const_string base) +{ + string answer; + + printf ("\nAttempting %s in format %d:\n", base, fmt); + + answer = kpathsea_make_tex (kpse, fmt, base); + puts (answer ? answer : "(nil)"); +} + + +int +main (int argc, char **argv) +{ + kpathsea kpse = xcalloc(1, sizeof(kpathsea_instance)); + kpathsea_set_program_name(kpse, argv[0], NULL); + kpathsea_xputenv (kpse, "KPATHSEA_DPI", "781"); /* call mktexpk */ + kpathsea_xputenv (kpse,"MAKETEX_BASE_DPI", "300"); /* call mktexpk */ + kpathsea_set_program_enabled(kpse, kpse_pk_format, 1, kpse_src_env); + test_make_tex (kpse, kpse_pk_format, "cmr10"); + + /* Fail with mktextfm. */ + kpathsea_set_program_enabled(kpse, kpse_tfm_format, 1, kpse_src_env); + test_make_tex (kpse, kpse_tfm_format, "foozler99"); + + /* Call something disabled. */ + test_make_tex (kpse, kpse_bst_format, "no-way"); + + return 0; +} + +#endif /* TEST */ + + +/* +Local variables: +standalone-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a" +End: +*/ |