summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c
diff options
context:
space:
mode:
authorTaco Hoekwater <taco@elvenkind.com>2006-11-28 21:26:37 +0000
committerTaco Hoekwater <taco@elvenkind.com>2006-11-28 21:26:37 +0000
commit770fb6e8d77cc13083b6c645fe974752e08cf40f (patch)
tree35ba0116a5dd195961a7349109116a0a89784865 /Build/source/texk/web2c
parenteeb03517a1a5a2a8bd1eb73106b2c6c5d0ab6d8a (diff)
A C version of makempx
git-svn-id: svn://tug.org/texlive/trunk@2556 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c')
-rw-r--r--Build/source/texk/web2c/mpware/makempx.c718
1 files changed, 718 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mpware/makempx.c b/Build/source/texk/web2c/mpware/makempx.c
new file mode 100644
index 00000000000..be567f48a31
--- /dev/null
+++ b/Build/source/texk/web2c/mpware/makempx.c
@@ -0,0 +1,718 @@
+/* $Id$
+
+ Make an MPX file from the labels in a MetaPost source file,
+ using mpto and either dvitomp (TeX) or dmp (troff).
+
+ Started from a shell script initially based on John Hobby's original
+ version, that was then translated to C by Akira Kakuto (Aug 1997,
+ Aug 2001), and updated and largely rewritten by Taco Hoekwater (Nov 2006).
+
+ Public Domain.
+
+ indent -kr -psl
+*/
+
+/* Differences between the script and this C version:
+
+ * The script trapped HUP, INT, QUIT and TERM for cleaning up
+ temporary files. This is a refinement, and not portable.
+
+ * The command 'newer' became a function.
+
+ * The script put its own directory in front of the
+ executable search PATH. This is not portable either, and
+ it seems a safe bet that normal users do not have 'mpto',
+ 'dvitomp', or 'dmp' commands in their path.
+
+ * The command-line '-troff' now also accepts an optional argument.
+
+ * The troff infile for error diagnostics is renamed "mpxerr.i",
+ not plain "mpxerr".
+
+ * The script deleted mpx*.* in the cleanup process. That is a bit
+ too hard (requires reading the directory contents). Instead,
+ the 'to be expected' filenames are deleted.
+
+ * There is a -debug switch, preventing the removal of tmp files
+*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#ifdef WIN32
+#include <io.h>
+#include <process.h>
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+
+#if !defined(HAVE_MKSTEMP) && ! defined (HAVE_MKTEMP)
+#include "time.h"
+#endif
+
+#include <kpathsea/kpathsea.h>
+#include "c-auto.h"
+
+#define INLINE_NEWER 1
+
+/* We may have high-res timers in struct stat. If we do, use them. */
+#if HAVE_ST_MTIM
+#define ISNEWER(S,T) (S.st_mtim.tv_sec > T.st_mtim.tv_sec || \
+ (S.st_mtim.tv_sec == T.st_mtim.tv_sec && \
+ S.st_mtim.tv_nsec >= T.st_mtim.tv_nsec))
+#else
+#define ISNEWER(S,T) (S.st_mtime >= T.st_mtime)
+#endif
+
+#ifdef WIN32
+#define nuldev "nul"
+#define ACCESS_MODE 4
+#define DUP _dup
+#define DUPP _dup2
+#define uexit exit
+#else
+#define nuldev "/dev/null"
+#define ACCESS_MODE R_OK
+#define DUP dup
+#define DUPP dup2
+#define uexit _exit
+#endif
+
+#define version "0.992"
+
+#define ERRLOG "mpxerr.log"
+#define TEXERR "mpxerr.tex"
+#define DVIERR "mpxerr.dvi"
+#define TROFF_INERR "mpxerr.i"
+#define TROFF_OUTERR "mpxerr.t"
+#define DMP "dmp"
+#define DVITOMP "dvitomp"
+#define NEWER "newer"
+#define MPTO "mpto"
+#define MPTOTEXOPT "-tex"
+#define MPTOTROPT "-troff"
+#define TROFF "soelim | eqn -Tps -d$$ | troff -Tps"
+#define TEX "tex"
+
+#define LNAM 255
+#define SNAM 128
+#define COMMAND_PARTS 10
+
+#define ARGUMENT_IS(a) (!strncmp(av[curarg],(a),strlen((a))))
+
+#define TMPNAME_EXT(a,b) { strcpy(a,tmpname); strcat(a,b); }
+
+#define RENAME(a,b) { if (debug) fprintf(stderr,"%s: renaming %s to %s\n",progname,a,b); \
+ rename(a,b); }
+
+#define split_command(a,b) do_split_command(a,b,' ')
+
+#define split_pipes(a,b) do_split_command(a,b,'|')
+
+/* Temporary files */
+char mp_i[SNAM], mp_t[SNAM], mp_tmp[SNAM], mp_log[SNAM];
+
+char *mp_tex = NULL;
+
+int debug = 0;
+
+char *progname;
+
+
+void
+mess(char *progname)
+{
+ fprintf(stderr, "Try `%s --help' for more information.\n", progname);
+ uexit(1);
+}
+
+void
+erasetmp(void)
+{
+ char wrk[SNAM];
+ char *p;
+ if (debug)
+ return;
+
+ if (mp_tex != NULL) {
+ remove(mp_tex);
+ strcpy(wrk, mp_tex);
+ if ((p = strrchr(wrk, '.'))) {
+ *p = '\0';
+ strcat(wrk, ".aux");
+ remove(wrk);
+ *p = '\0';
+ strcat(wrk, ".pdf");
+ remove(wrk);
+ }
+ }
+ if (mp_i[0]) {
+ remove(mp_i);
+ }
+ if (mp_t[0]) {
+ remove(mp_t);
+ }
+ if (mp_tmp[0]) {
+ remove(mp_tmp);
+ }
+ if (mp_log[0]) {
+ remove(mp_log);
+ }
+}
+
+FILE *
+makempx_xfopen(char *name, char *mode)
+{
+ FILE *ret;
+ if (!(ret = fopen(name, mode))) {
+ if (*mode == 'r') {
+ fprintf(stderr, "Cannot open %s for reading.\n", name);
+ } else {
+ fprintf(stderr, "Cannot open %s for writing.\n", name);
+ }
+ erasetmp();
+ uexit(1);
+ }
+ return ret;
+}
+
+
+int
+run_command(int count, char **cmdl)
+{
+ int i;
+ char *cmd;
+ int retcode;
+ char **options = NULL;
+
+ /* return non-zero by default, signalling an error */
+ retcode = -1;
+
+ if (count < 1 || cmdl == NULL || cmdl[0] == NULL)
+ return retcode;
+
+ options = xmalloc(sizeof(char *) * (count + 1));
+ cmd = xstrdup(cmdl[0]);
+
+ for (i = 0; i < count; i++)
+ options[i] = cmdl[i];
+ options[count] = NULL;
+ if (debug) {
+ fprintf(stderr, "%s: running command", progname);
+ for (i = 0; i < count; i++)
+ fprintf(stderr, " %s", options[i]);
+ fprintf(stderr, "\n");
+ }
+#ifndef WIN32
+ {
+ pid_t child = fork();
+ if (child >= 0) {
+ if (child == 0) {
+ execvp(cmd, options);
+ } else
+ wait(&retcode);
+ }
+ free(cmd);
+ free(options);
+ }
+ return WEXITSTATUS(retcode);
+#else
+ retcode = spawnvp(P_WAIT, cmd, options);
+ free(cmd);
+ free(options);
+ return retcode;
+#endif
+}
+
+int
+do_split_command(char *maincmd, char **cmdline, char target)
+{
+ char *piece;
+ char *cmd;
+ int ret = 0;
+ int i;
+ int in_string = 0;
+ if (strlen(maincmd) == 0)
+ return 0;
+ cmd = xstrdup(maincmd);
+ i = 0;
+ while (cmd[i] == ' ')
+ i++;
+ piece = cmd;
+ for (; i <= strlen(maincmd); i++) {
+ if (in_string == 1) {
+ if (cmd[i] == '"') {
+ in_string = 0;
+ }
+ } else if (in_string == 2) {
+ if (cmd[i] == '\'') {
+ in_string = 0;
+ }
+ } else {
+ if (cmd[i] == '"') {
+ in_string = 1;
+ } else if (cmd[i] == '\'') {
+ in_string = 2;
+ } else if (cmd[i] == target) {
+ cmd[i] = 0;
+ if (ret == COMMAND_PARTS) {
+ fprintf(stderr,
+ "%s: Executable command too complex.\n",
+ progname);
+ mess(progname);
+ }
+ cmdline[ret++] = xstrdup(piece);
+ while (i < strlen(maincmd) && cmd[(i + 1)] == ' ')
+ i++;
+ piece = cmd + i + 1;
+ }
+ }
+ }
+ if (*piece) {
+ if (ret == COMMAND_PARTS) {
+ fprintf(stderr, "%s: Executable command too complex.\n",
+ progname);
+ mess(progname);
+ }
+ cmdline[ret++] = xstrdup(piece);
+ }
+ return ret;
+}
+
+
+int
+do_newer(char *source, char *target)
+{
+#ifndef INLINE_NEWER
+ char *cmdline[] = { NEWER, source, target };
+ return run_command(3, cmdline);
+#else
+ struct stat source_stat, target_stat;
+
+ /* check the target file */
+ if (stat(target, &target_stat) < 0)
+ return 0; /* true */
+
+ /* check the source file */
+ if (stat(source, &source_stat) < 0)
+ return 1; /* false */
+
+ if (ISNEWER(source_stat, target_stat))
+ return 0;
+
+ return 1;
+#endif
+}
+
+#define MBUF 512
+
+void
+prepare_tex(char *texname, char *tmpname)
+{
+ char buffer[MBUF];
+ char *tmpfile = NULL;
+ char *texkeep = NULL; /* for debugging */
+ char *mptexpre = NULL;
+ int i;
+ FILE *fr, *fw;
+
+ mptexpre = kpse_var_value("MPTEXPRE");
+
+ if (!mptexpre)
+ mptexpre = xstrdup("mptexpre.tex");
+
+ if (!access(mptexpre, ACCESS_MODE)) {
+ tmpfile = concat(tmpname, ".tmp");
+ fr = makempx_xfopen(mptexpre, "r");
+ fw = makempx_xfopen(tmpfile, "wb");
+
+ while ((i = fread(buffer, 1, MBUF, fr)))
+ fwrite(buffer, 1, i, fw);
+ fclose(fr);
+ fr = makempx_xfopen(texname, "r");
+ while ((i = fread(buffer, 1, MBUF, fr)))
+ fwrite(buffer, 1, i, fw);
+ fclose(fr);
+ fclose(fw);
+ if (debug) {
+ texkeep = concat(texname, ".keep");
+ RENAME(texname, texkeep);
+ } else {
+ remove(texname);
+ }
+ RENAME(tmpfile, texname);
+ }
+}
+
+int
+main(int ac, char **av)
+{
+ int mpmode = 0;
+ char tmpname[] = "mpXXXXXX";
+
+ char *mpfile = NULL, *mpxfile = NULL;
+ char whatever_to_mpx[SNAM], infile[SNAM], inerror[SNAM];
+
+ char *tmpstring = NULL;
+
+ char **cmdline;
+ char **cmdbits;
+
+ int cmdlength = 1;
+ int cmdbitlength = 1;
+
+ int retcode, i, sav_o, sav_e, sav_i;
+
+ FILE *fr, *fw, *few, *fnulr, *fnulw;
+ int curarg = 0;
+
+ /* TeX command name */
+ char maincmd[(LNAM + 1)];
+
+
+ cmdline = xmalloc(sizeof(char *) * COMMAND_PARTS);
+ cmdbits = xmalloc(sizeof(char *) * COMMAND_PARTS);
+ for (i = 0; i < COMMAND_PARTS; i++) {
+ cmdline[i] = NULL;
+ cmdbits[i] = NULL;
+ }
+
+ progname = av[0];
+
+ /* Initialize buffer for temporary file names */
+ for (i = 0; i < SNAM; i++) {
+ mp_i[i] = '\0';
+ mp_t[i] = '\0';
+ mp_tmp[i] = '\0';
+ mp_log[i] = '\0';
+ }
+
+ kpse_set_progname(progname);
+
+ /* Set TeX command */
+ if ((tmpstring = kpse_var_value("TEX")))
+ strncpy(maincmd, tmpstring, LNAM);
+ else if ((tmpstring = kpse_var_value("MPXMAINCMD")))
+ strncpy(maincmd, tmpstring, LNAM);
+ else
+ strncpy(maincmd, TEX, LNAM);
+ if (tmpstring)
+ free(tmpstring);
+
+ strncat(maincmd, " --parse-first-line --interaction=nonstopmode",
+ (LNAM - strlen(maincmd)));
+
+ while (curarg < (ac - 1)) {
+
+ curarg++;
+
+ if (ARGUMENT_IS("-help") || ARGUMENT_IS("--help")) {
+
+ fprintf(stderr,
+ "Usage: %s [-tex[=<program>]|-troff[=<program>]] MPFILE MPXFILE.\n",
+ av[0]);
+ fputs
+ (" If MPXFILE is older than MPFILE, translate the labels from the MetaPost\n"
+ " input file MPFILE to low-level commands in MPXFILE, by running\n"
+ " " MPTO " " MPTOTEXOPT ", " TEX ", and " DVITOMP "\n"
+ " by default; or, if -troff is specified,\n" " " MPTO
+ " " MPTOTROPT ", " TROFF ", and " DMP ".\n\n"
+ " The current directory is used for writing temporary files. Errors are\n"
+ " left in mpxerr.{tex,log,dvi}.\n\n"
+ " If the file named in $MPTEXPRE (mptexpre.tex by default) exists, it is\n"
+ " prepended to the output in tex mode.\n\n"
+ "Email bug reports to metapost@tug.org.\n", stderr);
+ uexit(0);
+
+ } else if (ARGUMENT_IS("-version") || ARGUMENT_IS("--version")) {
+
+ fprintf(stdout, "%s %s\n", progname, version);
+ fputs
+ ("There is NO warranty. This program is in the public domain.\n"
+ "Original author: John Hobby.\n"
+ "Current maintainer: Taco Hoekwater.\n", stdout);
+ uexit(0);
+
+ } else if (ARGUMENT_IS("-debug") || ARGUMENT_IS("--debug")) {
+
+ debug = 1;
+
+ } else if (ARGUMENT_IS("-troff") || ARGUMENT_IS("--troff")) {
+
+ mpmode = 1;
+
+ if (ARGUMENT_IS("-troff=") || ARGUMENT_IS("--troff=")) {
+ i = 7;
+ if (*(av[curarg] + i) == '=')
+ i++;
+ if (*(av[curarg] + i) == '\'' || *(av[curarg] + i) == '\"') {
+ strncpy(maincmd, av[curarg] + i + 1, LNAM);
+ *(maincmd + strlen(maincmd)) = 0;
+ } else {
+ strncpy(maincmd, av[curarg] + i, LNAM);
+ }
+ } else {
+ strncpy(maincmd, TROFF, LNAM);
+ }
+
+ } else if (ARGUMENT_IS("-tex") || ARGUMENT_IS("--tex")) {
+
+ mpmode = 0;
+
+ if (ARGUMENT_IS("-tex=") || ARGUMENT_IS("--tex=")) {
+ i = 5;
+ if (*(av[curarg] + i) == '=')
+ i++;
+ if (*(av[curarg] + i) == '\'' || *(av[curarg] + i) == '\"') {
+ strncpy(maincmd, av[curarg] + i + 1, LNAM);
+ *(maincmd + strlen(maincmd)) = 0;
+ } else {
+ strncpy(maincmd, av[curarg] + i, LNAM);
+ }
+ }
+
+ } else if (ARGUMENT_IS("-")) {
+ fprintf(stderr, "%s: Invalid option: %s.\n", progname,
+ av[curarg]);
+ mess(progname);
+ } else {
+ if (mpfile == NULL) {
+
+ if (strchr(av[curarg], ' ') && (av[curarg][0] != '\"'))
+ mpfile = concat3("\"", av[curarg], "\"");
+ else
+ mpfile = strdup(av[curarg]);
+
+ } else if (mpxfile == NULL) {
+
+ if (strchr(av[curarg], ' ') && (av[curarg][0] != '\"'))
+ mpxfile = concat3("\"", av[curarg], "\"");
+ else
+ mpxfile = strdup(av[curarg]);
+
+ } else {
+ fprintf(stderr, "%s: Extra argument %s.\n", progname,
+ av[curarg]);
+ mess(progname);
+ }
+ }
+ }
+
+ if (mpfile == NULL || mpxfile == NULL) {
+ fprintf(stderr, "%s: Need exactly two file arguments.\n",
+ progname);
+ mess(progname);
+ }
+
+ /* Check if mpfile is newer than mpxfile */
+
+ retcode = do_newer(mpfile, mpxfile);
+
+ /* If MPX file is up-to-date or if MP file does not exist, do nothing. */
+ if (retcode)
+ uexit(0);
+
+#ifdef HAVE_MKSTEMP
+ i = mkstemp(tmpname);
+ if (i == -1)
+ uexit(1);
+ close(i);
+ remove(tmpname);
+#else
+#ifdef HAVE_MKTEMP
+ tmpstring = mktemp(tmpname);
+ if ((tmpstring == NULL) || strlen(tmpname) == 0)
+ uexit(1);
+ /* this should not really be needed, but better
+ safe than sorry. */
+ if (tmpstring != tmpname) {
+ i = strlen(tmpstring);
+ if (i > 8) i = 8;
+ strncpy(tmpname, tmpstring, i);
+ }
+#else
+ sprintf(tmpname, "mp%06d", (time(NULL) % 1000000));
+#endif
+#endif
+
+ mp_tex = concat(tmpname, ".tex");
+
+ /* step 1: */
+
+ fw = makempx_xfopen(mp_tex, "wb");
+ few = makempx_xfopen(ERRLOG, "wb");
+
+ cmdline[0] = MPTO;
+ cmdline[1] = (mpmode == 0 ? MPTOTEXOPT : MPTOTROPT);
+ cmdline[2] = mpfile;
+
+ sav_o = DUP(fileno(stdout));
+ DUPP(fileno(fw), fileno(stdout));
+ sav_e = DUP(fileno(stderr));
+ DUPP(fileno(few), fileno(stderr));
+
+ retcode = run_command(3, cmdline);
+
+ DUPP(sav_o, fileno(stdout));
+ close(sav_o);
+ fclose(fw);
+
+ DUPP(sav_e, fileno(stderr));
+ close(sav_e);
+ fclose(few);
+
+ if (retcode) {
+ fprintf(stderr, "%s: Command failed: %s %s %s\n",
+ progname, cmdline[0], cmdline[1], cmdline[2]);
+ erasetmp();
+ uexit(1);
+ }
+
+ /* step 2: */
+
+ if (mpmode == 0) { /* TeX mode */
+
+ prepare_tex(mp_tex, tmpname); /* check for mptexpre.tex */
+
+ if (strlen(maincmd) > (LNAM - strlen(mp_tex) - 1)) {
+ fprintf(stderr, "%s: Command too complex: %s\n", progname,
+ maincmd);
+ erasetmp();
+ uexit(1);
+ }
+ strcat(maincmd, " ");
+ strcat(maincmd, mp_tex);
+ cmdlength = split_command(maincmd, cmdline);
+
+ fnulr = makempx_xfopen(nuldev, "r");
+ sav_i = DUP(fileno(stdin));
+ DUPP(fileno(fnulr), fileno(stdin));
+
+ fnulw = makempx_xfopen(nuldev, "w");
+ sav_o = DUP(fileno(stdout));
+ DUPP(fileno(fnulw), fileno(stdout));
+
+ retcode = run_command(cmdlength, cmdline);
+
+ DUPP(sav_i, fileno(stdin));
+ close(sav_i);
+ fclose(fnulr);
+ DUPP(sav_o, fileno(stdout));
+ close(sav_o);
+ fclose(fnulw);
+
+ TMPNAME_EXT(mp_log, ".log");
+
+ if (!retcode) {
+ strcpy(whatever_to_mpx, DVITOMP);
+ TMPNAME_EXT(infile, ".dvi");
+ strcpy(inerror, DVIERR);
+ } else {
+ RENAME(mp_tex, TEXERR);
+ RENAME(mp_log, ERRLOG);
+ fprintf(stderr, "%s: Command failed:", progname);
+ for (i = 0; i < (cmdlength - 1); i++)
+ fprintf(stderr, " %s", cmdline[i]);
+ fprintf(stderr, " %s", TEXERR);
+ fprintf(stderr, "; see mpxerr.log\n");
+ erasetmp();
+ uexit(2);
+ }
+ } else if (mpmode == 1) { /* troff mode */
+
+ TMPNAME_EXT(mp_i, ".i");
+ RENAME(mp_tex, mp_i);
+ TMPNAME_EXT(mp_t, ".t");
+ fr = makempx_xfopen(mp_i, "r");
+ TMPNAME_EXT(mp_tmp, ".tmp");
+ fw = makempx_xfopen(mp_tmp, "wb");
+
+ /* split the command in bits */
+ cmdbitlength = split_pipes(maincmd, cmdbits);
+
+ for (i = 0; i < cmdbitlength; i++) {
+ cmdlength = split_command(cmdbits[i], cmdline);
+
+ sav_i = DUP(fileno(stdin));
+ sav_o = DUP(fileno(stdout));
+ DUPP(fileno(fr), fileno(stdin));
+ DUPP(fileno(fw), fileno(stdout));
+
+ retcode = run_command(cmdlength, cmdline);
+
+ DUPP(sav_i, fileno(stdin));
+ close(sav_i);
+ DUPP(sav_o, fileno(stdout));
+ close(sav_o);
+ fclose(fr);
+ fclose(fw);
+ if (retcode) {
+ RENAME(mp_i, TROFF_INERR);
+
+ fprintf(stderr, "%s: Command failed:", progname);
+ for (i = 0; i < cmdlength; i++) {
+ if (strcmp(cmdline[i], mp_i) == 0)
+ fprintf(stderr, " %s", TROFF_INERR);
+ else
+ fprintf(stderr, " %s", cmdline[i]);
+ }
+ fprintf(stderr, "\n");
+ erasetmp();
+ uexit(2);
+ }
+ if (i < cmdbitlength - 1) {
+ if (i % 2 == 0) {
+ fr = makempx_xfopen(mp_tmp, "r");
+ fw = makempx_xfopen(mp_t, "wb");
+ strcpy(infile, mp_t);
+ } else {
+ fr = makempx_xfopen(mp_t, "r");
+ fw = makempx_xfopen(mp_tmp, "wb");
+ strcpy(infile, mp_tmp);
+ }
+ }
+ }
+ strcpy(whatever_to_mpx, DMP);
+ strcpy(inerror, TROFF_OUTERR);
+ }
+
+ /* Step 3: */
+
+ fw = makempx_xfopen(ERRLOG, "wb");
+
+ sav_o = DUP(fileno(stdout));
+ DUPP(fileno(fw), fileno(stdout));
+
+ cmdline[0] = whatever_to_mpx;
+ cmdline[1] = infile;
+ cmdline[2] = mpxfile;
+ retcode = run_command(3, cmdline);
+
+ DUPP(sav_o, fileno(stdout));
+ close(sav_o);
+ fclose(fw);
+
+ if (retcode) {
+ RENAME(infile, inerror);
+ if (mpmode == 1)
+ RENAME(mp_i, TROFF_INERR);
+ if (!debug)
+ remove(mpxfile);
+ fprintf(stderr, "%s: Command failed: %s %s %s\n",
+ progname, whatever_to_mpx, inerror, mpxfile);
+ erasetmp();
+ uexit(3);
+ }
+ if (!debug) {
+ remove(ERRLOG);
+ remove(infile);
+ }
+ erasetmp();
+ uexit(0);
+}