Back to index

tetex-bin  3.0
tex-make.c
Go to the documentation of this file.
00001 /* tex-make.c: Run external programs to make TeX-related files.
00002 
00003 Copyright (C) 1993, 94, 95, 96, 97 Karl Berry.
00004 
00005 This library is free software; you can redistribute it and/or
00006 modify it under the terms of the GNU Library General Public
00007 License as published by the Free Software Foundation; either
00008 version 2 of the License, or (at your option) any later version.
00009 
00010 This library is distributed in the hope that it will be useful,
00011 but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 Library General Public License for more details.
00014 
00015 You should have received a copy of the GNU Library General Public
00016 License along with this library; if not, write to the Free Software
00017 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00018 
00019 #include <kpathsea/config.h>
00020 
00021 #include <kpathsea/c-fopen.h>
00022 #include <kpathsea/c-pathch.h>
00023 #include <kpathsea/concatn.h>
00024 #include <kpathsea/db.h>
00025 #include <kpathsea/fn.h>
00026 #include <kpathsea/magstep.h>
00027 #include <kpathsea/readable.h>
00028 #include <kpathsea/tex-make.h>
00029 #include <kpathsea/variable.h>
00030 
00031 
00032 /* We never throw away stdout, since that is supposed to be the filename
00033    found, if all is successful.  This variable controls whether stderr
00034    is thrown away.  */
00035 boolean kpse_make_tex_discard_errors = false;
00036 
00037 /* We set the envvar MAKETEX_MAG, which is part of the default spec for
00038    MakeTeXPK above, based on KPATHSEA_DPI and MAKETEX_BASE_DPI.  */
00039 
00040 static void
00041 set_maketex_mag P1H(void)
00042 {
00043   char q[MAX_INT_LENGTH * 3 + 3];
00044   int m;
00045   string dpi_str = getenv ("KPATHSEA_DPI");
00046   string bdpi_str = getenv ("MAKETEX_BASE_DPI");
00047   unsigned dpi = dpi_str ? atoi (dpi_str) : 0;
00048   unsigned bdpi = bdpi_str ? atoi (bdpi_str) : 0;
00049 
00050   /* If the environment variables aren't set, it's a bug.  */
00051   assert (dpi != 0 && bdpi != 0);
00052   
00053   /* Fix up for roundoff error.  Hopefully the driver has already fixed
00054      up DPI, but may as well be safe, and also get the magstep number.  */
00055   (void) kpse_magstep_fix (dpi, bdpi, &m);
00056   
00057   if (m == 0)
00058     sprintf (q, "%d+%d/%d", dpi / bdpi, dpi % bdpi, bdpi);
00059   else
00060     { /* m is encoded with LSB being a ``half'' bit (see magstep.h).  Are
00061          we making an assumption here about two's complement?  Probably.
00062          In any case, if m is negative, we have to put in the sign
00063          explicitly, since m/2==0 if m==-1.  */
00064       const_string sign = "";
00065       if (m < 0)
00066         {
00067           m *= -1;
00068           sign = "-";
00069         }
00070       sprintf (q, "magstep\\(%s%d.%d\\)", sign, m / 2, (m & 1) * 5);
00071     }  
00072   xputenv ("MAKETEX_MAG", q);
00073 }
00074 
00075 /* This mktex... program was disabled, or the script failed.  If this
00076    was a font creation (according to FORMAT), append CMD
00077    to a file missfont.log in the current directory.  */
00078 
00079 static void
00080 misstex P2C(kpse_file_format_type, format,  string *, args)
00081 {
00082   static FILE *missfont = NULL;
00083   string *s;
00084   
00085   /* If we weren't trying to make a font, do nothing.  Maybe should
00086      allow people to specify what they want recorded?  */
00087   if (format != kpse_gf_format
00088       && format != kpse_pk_format
00089       && format != kpse_any_glyph_format
00090       && format != kpse_tfm_format
00091       && format != kpse_vf_format)
00092     return;
00093 
00094   /* If this is the first time, have to open the log file.  But don't
00095      bother logging anything if they were discarding errors.  */
00096   if (!missfont && !kpse_make_tex_discard_errors) {
00097     const_string missfont_name = kpse_var_value ("MISSFONT_LOG");
00098     if (!missfont_name || *missfont_name == '1') {
00099       missfont_name = "missfont.log"; /* take default name */
00100     } else if (missfont_name
00101                && (*missfont_name == 0 || *missfont_name == '0')) {
00102       missfont_name = NULL; /* user requested no missfont.log */
00103     } /* else use user's name */
00104 
00105     missfont = missfont_name ? fopen (missfont_name, FOPEN_A_MODE) : NULL;
00106     if (!missfont && kpse_var_value ("TEXMFOUTPUT")) {
00107       missfont_name = concat3 (kpse_var_value ("TEXMFOUTPUT"), DIR_SEP_STRING,
00108                                missfont_name);
00109       missfont = fopen (missfont_name, FOPEN_A_MODE);
00110     }
00111 
00112     if (missfont)
00113       fprintf (stderr, "kpathsea: Appending font creation commands to %s.\n",
00114                missfont_name);
00115   }
00116   
00117   /* Write the command if we have a log file.  */
00118   if (missfont) {
00119     fputs (args[0], missfont);
00120     for (s = &args[1]; *s != NULL; s++) {
00121       putc(' ', missfont);
00122       fputs (*s, missfont);
00123     }
00124     putc ('\n', missfont);
00125   }
00126 }  
00127 
00128 
00129 /* Assume the script outputs the filename it creates (and nothing
00130    else) on standard output; hence, we run the script with `popen'.  */
00131 
00132 static string
00133 maketex P2C(kpse_file_format_type, format, string*, args)
00134 {
00135   /* New implementation, use fork/exec pair instead of popen, since
00136    * the latter is virtually impossible to make safe.
00137    */
00138   unsigned len;
00139   string *s;
00140   string ret;
00141   string fn;
00142   
00143   if (!kpse_make_tex_discard_errors) {
00144     fprintf (stderr, "kpathsea: Running");
00145     for (s = &args[0]; *s != NULL; s++)
00146       fprintf (stderr, " %s", *s);
00147     fputc('\n', stderr);
00148   }
00149 
00150 #if defined (AMIGA)
00151   /* Amiga has a different interface. */
00152   {
00153     string cmd;
00154     string newcmd;
00155     cmd = xstrdup(args[0]);
00156     for (s = &args[1];  *s != NULL; s++) {
00157       newcmd = concat(cmd, *s);
00158       free (cmd);
00159       cmd = newcmd;
00160     }
00161     ret = system(cmd) == 0 ? getenv ("LAST_FONT_CREATED"): NULL;
00162     free (cmd);
00163   }
00164 #elif defined (MSDOS) && !defined(DJGPP)
00165 #error Implement new MSDOS mktex call interface here
00166 #elif defined (WIN32)
00167   /* We would vastly prefer to link directly with mktex.c here.
00168      Unfortunately, it is not quite possible because kpathsea
00169      is not reentrant. The progname is expected to be set in mktex.c
00170      and various initialisations occur. So to be safe, we implement
00171      a call sequence equivalent to the Unix one. */
00172   {
00173     STARTUPINFO si;
00174     PROCESS_INFORMATION pi;
00175 
00176     HANDLE child_in, child_out, child_err;
00177     HANDLE father_in, father_out_dup;
00178     HANDLE current_pid;
00179     SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
00180     string new_cmd = NULL, app_name = NULL;
00181 
00182     char buf[1024+1];
00183     int num;
00184     extern char *quote_args(char **argv);
00185 
00186     if (look_for_cmd(args[0], &app_name) == FALSE) {
00187       ret = NULL;
00188       goto error_exit;
00189     }
00190 
00191     /* Compute the command line */
00192     new_cmd = quote_args(args);
00193 
00194     /* We need this handle to duplicate other handles */
00195     current_pid = GetCurrentProcess();
00196 
00197     ZeroMemory( &si, sizeof(STARTUPINFO) );
00198     si.cb = sizeof(STARTUPINFO);
00199     si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW ;
00200     si.wShowWindow = /* 0 */ SW_HIDE ;
00201 
00202     /* Child stdin */
00203     child_in = CreateFile("nul",
00204                           GENERIC_READ,
00205                           FILE_SHARE_READ | FILE_SHARE_WRITE,
00206                           &sa,  /* non inheritable */
00207                           OPEN_EXISTING,
00208                           FILE_ATTRIBUTE_NORMAL,
00209                           NULL);
00210     si.hStdInput = child_in;
00211 
00212     if (CreatePipe(&father_in, &child_out, NULL, 0) == FALSE) {
00213       fprintf(stderr, "popen: error CreatePipe\n");
00214       goto error_exit;
00215     }
00216     if (DuplicateHandle(current_pid, child_out,
00217                         current_pid, &father_out_dup,
00218                         0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE) {
00219       fprintf(stderr, "popen: error DuplicateHandle father_in\n");
00220       CloseHandle(father_in);
00221       CloseHandle(child_out);
00222       goto error_exit;
00223     }
00224     CloseHandle(child_out);
00225     si.hStdOutput = father_out_dup;
00226 
00227     /* Child stderr */
00228     if (kpse_make_tex_discard_errors) {
00229       child_err = CreateFile("nul",
00230                              GENERIC_WRITE,
00231                              FILE_SHARE_READ | FILE_SHARE_WRITE,
00232                              &sa,       /* non inheritable */
00233                              OPEN_EXISTING,
00234                              FILE_ATTRIBUTE_NORMAL,
00235                              NULL);
00236     }
00237     else {
00238       DuplicateHandle(current_pid, GetStdHandle(STD_ERROR_HANDLE),
00239                       current_pid, &child_err,
00240                       0, TRUE,
00241                       DUPLICATE_SAME_ACCESS);
00242     }
00243     si.hStdError = child_err;
00244 
00245     /* creating child process */
00246     if (CreateProcess(app_name, /* pointer to name of executable module */
00247                       new_cmd,  /* pointer to command line string */
00248                       NULL,     /* pointer to process security attributes */
00249                       NULL,     /* pointer to thread security attributes */
00250                       TRUE,     /* handle inheritance flag */
00251                       0,                /* creation flags */
00252                       NULL,     /* pointer to environment */
00253                       NULL,     /* pointer to current directory */
00254                       &si,      /* pointer to STARTUPINFO */
00255                       &pi               /* pointer to PROCESS_INFORMATION */
00256                       ) == 0) {
00257       FATAL2("kpathsea: CreateProcess() failed for `%s' (Error %x)\n", new_cmd, GetLastError());
00258     }
00259 
00260     CloseHandle(child_in);
00261     CloseHandle(father_out_dup);
00262     CloseHandle(child_err);
00263 
00264     /* Only the process handle is needed */
00265     CloseHandle(pi.hThread);
00266 
00267     /* Get stdout of child from the pipe. */
00268     fn = xstrdup("");
00269     while (ReadFile(father_in,buf,sizeof(buf)-1, &num, NULL) != 0
00270            && num > 0) {
00271       if (num <= 0) {
00272         if (GetLastError() != ERROR_BROKEN_PIPE) {
00273           FATAL2("kpathsea: read() error code for `%s' (Error %d)", new_cmd, GetLastError());
00274           break;
00275         }
00276       } else {
00277         string newfn;
00278         buf[num] = '\0';
00279         newfn = concat(fn, buf);
00280         free(fn);
00281         fn = newfn;
00282       }
00283     }
00284     /* End of file on pipe, child should have exited at this point. */
00285     CloseHandle(father_in);
00286 
00287     if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
00288       WARNING2("kpathsea: failed to wait for process termination: %s (Error %d)\n",
00289                new_cmd, GetLastError());
00290     }
00291 
00292     CloseHandle(pi.hProcess);
00293 
00294     if (new_cmd) free(new_cmd);
00295     if (app_name) free(app_name);
00296 
00297     if (fn) {
00298       len = strlen(fn);
00299 
00300       /* Remove trailing newlines and returns.  */
00301       while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
00302         fn[len - 1] = '\0';
00303         len--;
00304       }
00305 
00306       ret = len == 0 ? NULL : kpse_readable_file (fn);
00307       if (!ret && len > 1) {
00308         WARNING1 ("kpathsea: mktexpk output `%s' instead of a filename", fn);
00309       }
00310 
00311       /* Free the name if we're not returning it.  */
00312       if (fn != ret)
00313         free (fn);
00314     }
00315   error_exit:
00316     ;
00317   }
00318 #else
00319   {
00320     /* Standard input for the child.  Set to /dev/null */
00321     int childin;
00322     /* Standard output for the child, what we're interested in. */
00323     int childout[2];
00324     /* Standard error for the child, same as parent or /dev/null */
00325     int childerr;
00326     /* Child pid. */
00327     pid_t childpid;
00328 
00329     /* Open the channels that the child will use. */
00330     /* A fairly horrible uses of gotos for here for the error case. */
00331     if ((childin = open("/dev/null", O_RDONLY)) < 0) {
00332       perror("kpathsea: open(\"/dev/null\", O_RDONLY)");
00333       goto error_childin;
00334     }
00335     if (pipe(childout) < 0) {
00336       perror("kpathsea: pipe()");
00337       goto error_childout;
00338     }
00339     if ((childerr = open("/dev/null", O_WRONLY)) < 0) {
00340       perror("kpathsea: open(\"/dev/null\", O_WRONLY)");
00341       goto error_childerr;
00342     }
00343     if ((childpid = fork()) < 0) {
00344       perror("kpathsea: fork()");
00345       close(childerr);
00346      error_childerr:
00347       close(childout[0]);
00348       close(childout[1]);
00349      error_childout:
00350       close(childin);
00351      error_childin:
00352       fn = NULL;
00353     } else if (childpid == 0) {
00354       /* Child
00355        *
00356        * We can use vfork, provided we're careful about what we
00357        * do here: do not return from this function, do not modify
00358        * variables, call _exit if there is a problem.
00359        *
00360        * Complete setting up the file descriptors.
00361        * We use dup(2) so the order in which we do this matters.
00362        */
00363       close(childout[0]);
00364       /* stdin -- the child will not receive input from this */
00365       if (childin != 0) {
00366         close(0);
00367         dup(childin);
00368         close(childin);
00369       }
00370       /* stdout -- the output of the child's action */
00371       if (childout[1] != 1) {
00372         close(1);
00373         dup(childout[1]);
00374         close(childout[1]);
00375       }
00376       /* stderr -- use /dev/null if we discard errors */
00377       if (childerr != 2) {
00378         if (kpse_make_tex_discard_errors) {
00379           close(2);
00380           dup(childerr);
00381         }
00382         close(childerr);
00383       }
00384       /* FIXME: We could/should close all other file descriptors as well. */
00385       /* exec -- on failure a call of _exit(2) it is the only option */
00386       if (execvp(args[0], args))
00387         perror(args[0]);
00388       _exit(1);
00389     } else {
00390       /* Parent */
00391       char buf[1024+1];
00392       int num;
00393       int status;
00394 
00395       /* Clean up child file descriptors that we won't use anyway. */
00396       close(childin);
00397       close(childout[1]);
00398       close(childerr);
00399       /* Get stdout of child from the pipe. */
00400       fn = xstrdup("");
00401       while ((num = read(childout[0],buf,sizeof(buf)-1)) != 0) {
00402         if (num == -1) {
00403           if (errno != EINTR) {
00404             perror("kpathsea: read()");
00405             break;
00406           }
00407         } else {
00408           string newfn;
00409           buf[num] = '\0';
00410           newfn = concat(fn, buf);
00411           free(fn);
00412           fn = newfn;
00413         }
00414       }
00415       /* End of file on pipe, child should have exited at this point. */
00416       close(childout[0]);
00417       /* We don't really care about the exit status at this point. */
00418       wait(NULL);
00419     }
00420 
00421     if (fn) {
00422       len = strlen(fn);
00423 
00424       /* Remove trailing newlines and returns.  */
00425       while (len && (fn[len - 1] == '\n' || fn[len - 1] == '\r')) {
00426         fn[len - 1] = '\0';
00427         len--;
00428       }
00429 
00430       ret = len == 0 ? NULL : kpse_readable_file (fn);
00431       if (!ret && len > 1) {
00432         WARNING1 ("kpathsea: mktexpk output `%s' instead of a filename", fn);
00433       }
00434 
00435       /* Free the name if we're not returning it.  */
00436       if (fn != ret)
00437         free (fn);
00438     } else {
00439       ret = NULL;
00440     }
00441   }
00442 #endif
00443 
00444   if (ret == NULL)
00445     misstex (format, args);
00446   else
00447     kpse_db_insert (ret);
00448   
00449   return ret;
00450 }
00451 
00452 
00453 /* Create BASE in FORMAT and return the generated filename, or
00454    return NULL.  */
00455 
00456 string
00457 kpse_make_tex P2C(kpse_file_format_type, format,  const_string, base)
00458 {
00459   kpse_format_info_type spec; /* some compilers lack struct initialization */
00460   string ret = NULL;
00461   
00462   spec = kpse_format_info[format];
00463   if (!spec.type) { /* Not initialized yet? */
00464     kpse_init_format (format);
00465     spec = kpse_format_info[format];
00466   }
00467 
00468   if (spec.program && spec.program_enabled_p) {
00469     /* See the documentation for the envvars we're dealing with here.  */
00470     /* Number of arguments is spec.argc + 1, plus the trailing NULL. */
00471     string *args = XTALLOC (spec.argc + 2, string);
00472     /* Helpers */
00473     int argnum;
00474     int i;
00475     
00476     /* FIXME
00477      * Check whether the name we were given is likely to be a problem.
00478      * Right now we err on the side of strictness:
00479      * - may not start with a hyphen (fixable in the scripts).
00480      * - allowed are: alphanumeric, underscore, hyphen, period, plus
00481      * ? also allowed DIRSEP, as we can be fed that when creating pk fonts
00482      * No doubt some possibilities were overlooked.
00483      */
00484     if (base[0] == '-' /* || IS_DIR_SEP(base[0])  */) {
00485       fprintf(stderr, "kpathsea: Illegal fontname `%s': starts with '%c'\n",
00486               base, base[0]);
00487       return NULL;
00488     }
00489     for (i = 0; base[i]; i++) {
00490       if (!ISALNUM(base[i])
00491           && base[i] != '-'
00492           && base[i] != '+'
00493           && base[i] != '_'
00494           && base[i] != '.'
00495           && !IS_DIR_SEP(base[i]))
00496       {
00497         fprintf(stderr, "kpathsea: Illegal fontname `%s': contains '%c'\n",
00498                 base, base[i]);
00499         return NULL;
00500       }
00501     }
00502 
00503     if (format == kpse_gf_format
00504         || format == kpse_pk_format
00505         || format == kpse_any_glyph_format)
00506       set_maketex_mag ();
00507 
00508     /* Here's an awful kludge: if the mode is `/', mktexpk recognizes
00509        it as a special case.  `kpse_prog_init' sets it to this in the
00510        first place when no mode is otherwise specified; this is so
00511        when the user defines a resolution, they don't also have to
00512        specify a mode; instead, mktexpk's guesses will take over.
00513        They use / for the value because then when it is expanded as
00514        part of the PKFONTS et al. path values, we'll wind up searching
00515        all the pk directories.  We put $MAKETEX_MODE in the path
00516        values in the first place so that sites with two different
00517        devices with the same resolution can find the right fonts; but
00518        such sites are uncommon, so they shouldn't make things harder
00519        for everyone else.  */
00520     for (argnum = 0; argnum < spec.argc; argnum++) {
00521       args[argnum] = kpse_var_expand (spec.argv[argnum]);
00522     }
00523     args[argnum++] = xstrdup(base);
00524     args[argnum] = NULL;
00525 
00526     ret = maketex (format, args);
00527 
00528     for (argnum = 0; args[argnum] != NULL; argnum++)
00529       free (args[argnum]);
00530     free (args);
00531   }
00532 
00533   return ret;
00534 }
00535 
00536 #ifdef TEST
00537 
00538 void
00539 test_make_tex (kpse_file_format_type fmt, const_string base)
00540 {
00541   string answer;
00542   
00543   printf ("\nAttempting %s in format %d:\n", base, fmt);
00544 
00545   answer = kpse_make_tex (fmt, base);
00546   puts (answer ? answer : "(nil)");
00547 }
00548 
00549 
00550 int
00551 main ()
00552 {
00553   xputenv ("KPATHSEA_DPI", "781"); /* call mktexpk */
00554   xputenv ("MAKETEX_BASE_DPI", "300"); /* call mktexpk */
00555   KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_pk_format]) = true;
00556   test_make_tex (kpse_pk_format, "cmr10");
00557 
00558   /* Fail with mktextfm.  */
00559   KPSE_MAKE_SPEC_ENABLED (kpse_make_specs[kpse_tfm_format]) = true;
00560   test_make_tex (kpse_tfm_format, "foozler99");
00561   
00562   /* Call something disabled.  */
00563   test_make_tex (kpse_bst_format, "no-way");
00564   
00565   return 0;
00566 }
00567 
00568 #endif /* TEST */
00569 
00570 
00571 /*
00572 Local variables:
00573 test-compile-command: "gcc -g -I. -I.. -DTEST tex-make.c kpathsea.a"
00574 End:
00575 */