summaryrefslogtreecommitdiff
path: root/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc')
-rw-r--r--Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc821
1 files changed, 821 insertions, 0 deletions
diff --git a/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc
new file mode 100644
index 00000000000..7ce4e512e84
--- /dev/null
+++ b/Build/source/texk/lcdf-typetools/lcdf-typetools-2.104/otftotfm/automatic.cc
@@ -0,0 +1,821 @@
+/* automatic.{cc,hh} -- code for automatic mode and interfacing with kpathsea
+ *
+ * Copyright (c) 2003-2014 Eddie Kohler
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version. This program 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 General
+ * Public License for more details.
+ */
+
+#include <config.h>
+#include "automatic.hh"
+#include "kpseinterface.h"
+#include "util.hh"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <sys/stat.h>
+#include <sys/types.h>
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+#if HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+#include <lcdf/error.hh>
+#include <lcdf/straccum.hh>
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#include <algorithm>
+
+#ifdef WIN32
+#ifdef _MSC_VER
+# include <io.h>
+# include <direct.h>
+#endif
+# define mkdir(dir, access) mkdir(dir)
+# define COPY_CMD "copy"
+# define CMD_SEP "&"
+#else
+# define COPY_CMD "cp"
+# define CMD_SEP ";"
+#endif
+
+/* kpathsea may already have defined this */
+#ifndef DEV_NULL
+# ifdef WIN32
+# define DEV_NULL "NUL"
+# else
+# define DEV_NULL "/dev/null"
+# endif
+#endif
+
+#if HAVE_AUTO_T1DOTLESSJ
+enum { T1DOTLESSJ_EXIT_J_NODOT = 2 };
+#endif
+
+static String odir[NUMODIR];
+static String typeface;
+static String vendor;
+static String map_file;
+#define DEFAULT_VENDOR "lcdftools"
+#define DEFAULT_TYPEFACE "unknown"
+
+static const struct {
+ const char *name;
+ const char *envvar;
+ const char *texdir;
+} odir_info[] = {
+ { "encoding", "ENCODINGDESTDIR", "#fonts/enc/dvips/@#dvips/@" },
+ { "TFM", "TFMDESTDIR", "fonts/tfm/%" },
+ { "PL", "PLDESTDIR", "fonts/pl/%" },
+ { "VF", "VFDESTDIR", "fonts/vf/%" },
+ { "VPL", "VPLDESTDIR", "fonts/vpl/%" },
+ { "Type 1", "T1DESTDIR", "fonts/type1/%" },
+ { "DVIPS map", "DVIPS directory", "#fonts/map/dvips/@#dvips/@" },
+ { "DVIPS updmap", "DVIPS directory", "dvips" },
+ { "TrueType", "TTFDESTDIR", "fonts/truetype/%" },
+ { "OpenType", "OPENTYPEDESTDIR", "fonts/opentype/%" },
+ { "Type 42", "T42DESTDIR", "fonts/type42/%" }
+};
+
+#if HAVE_KPATHSEA
+static String odir_kpathsea[NUMODIR];
+
+static bool writable_texdir_tried = false;
+static String writable_texdir; // always ends with directory separator
+static int tds_1_1 = -1;
+
+static bool mktexupd_tried = false;
+static String mktexupd;
+
+static String
+kpsei_string(char* x)
+{
+ String s(x);
+ free((void*)x);
+ return s;
+}
+
+static void
+look_for_writable_texdir(const char *path_variable, bool create)
+{
+ String path = kpsei_string(kpsei_path_expand(path_variable));
+ while (path && !writable_texdir) {
+ const char* colon = std::find(path.begin(), path.end(), kpsei_env_sep_char);
+ String texdir = path.substring(path.begin(), colon);
+ path = path.substring(colon + 1, path.end());
+ if (access(texdir.c_str(), W_OK) >= 0)
+ writable_texdir = texdir;
+ else if (create && errno != EACCES && mkdir(texdir.c_str(), 0777) >= 0)
+ // create file if it doesn't exist already
+ writable_texdir = texdir;
+ }
+ if (writable_texdir && writable_texdir.back() != '/')
+ writable_texdir += "/";
+}
+
+static void
+find_writable_texdir(ErrorHandler *errh, const char *)
+{
+#if defined(W32TEX)
+// W32TeX does not have TEXMFVAR
+ char *p = kpsei_var_value("TEXMFVAR");
+ if (p == NULL) // W32TeX
+ look_for_writable_texdir("$TEXMFLOCAL", true);
+ else { // TeXLive
+ free (p);
+ look_for_writable_texdir("$TEXMFVAR", true);
+ }
+#else
+ look_for_writable_texdir("$TEXMFVAR", true);
+#endif
+ if (!writable_texdir)
+ look_for_writable_texdir("$VARTEXMF", false);
+ if (!writable_texdir)
+ look_for_writable_texdir("$TEXMF", false);
+ if (!writable_texdir) {
+ errh->warning("no writable directory found in $TEXMFVAR or $TEXMF");
+ errh->message("(You probably need to set your TEXMF environment variable; see\n\
+the manual for more information. The current TEXMF path is\n\
+%<%s%>.)", kpsei_string(kpsei_path_expand("$TEXMF")).c_str());
+ }
+ writable_texdir_tried = true;
+}
+
+static String
+get_vendor()
+{
+ return (vendor ? vendor : String(DEFAULT_VENDOR));
+}
+
+static String
+get_typeface()
+{
+ return (typeface ? typeface : String(DEFAULT_TYPEFACE));
+}
+#endif
+
+bool
+set_vendor(const String &s)
+{
+ bool had = (bool) vendor;
+ vendor = s;
+ return !had;
+}
+
+bool
+set_typeface(const String &s, bool override)
+{
+ bool had = (bool) typeface;
+ if (!had || override)
+ typeface = s;
+ return !had;
+}
+
+String
+getodir(int o, ErrorHandler *errh)
+{
+ assert(o >= 0 && o < NUMODIR);
+
+ if (!odir[o] && automatic && odir_info[o].envvar)
+ odir[o] = getenv(odir_info[o].envvar);
+
+#if HAVE_KPATHSEA
+ if (!odir[o] && automatic && !writable_texdir_tried)
+ find_writable_texdir(errh, odir_info[o].name);
+
+ if (!odir[o] && automatic && writable_texdir) {
+ String suffix = odir_info[o].texdir;
+
+ // May need to behave differently on TDS 1.1 rather than TDS 1.0.
+ if (suffix[0] == '#') {
+ // check type of TDS
+ if (tds_1_1 < 0) {
+ // using a procedure suggested by Olaf Weber
+ String encfonts = kpsei_string(kpsei_path_expand("$TEXMFMAIN/fonts/enc"));
+ if (!encfonts)
+ encfonts = kpsei_string(kpsei_path_expand("$TEXMFDIST/fonts/enc"));
+ tds_1_1 = (encfonts != String());
+ }
+ if (tds_1_1 == 0)
+ suffix = suffix.substring(std::find(suffix.begin() + 1, suffix.end(), '#') + 1, suffix.end());
+ else
+ suffix = suffix.substring(suffix.begin() + 1, std::find(suffix.begin() + 1, suffix.end(), '#'));
+ }
+
+ String dir = writable_texdir + suffix;
+
+ if (dir.back() == '%')
+ dir = dir.substring(0, -1) + get_vendor() + "/" + get_typeface();
+ else if (dir.back() == '@')
+ dir = dir.substring(0, -1) + get_vendor();
+
+ // create parent directories as appropriate
+ int slash = writable_texdir.length() - 1;
+ while (access(dir.c_str(), F_OK) < 0 && slash < dir.length()) {
+ if ((slash = dir.find_left('/', slash + 1)) < 0)
+ slash = dir.length();
+ String subdir = dir.substring(0, slash);
+ if (access(subdir.c_str(), F_OK) < 0
+ && !no_create
+ && mkdir(subdir.c_str(), 0777) < 0)
+ goto kpathsea_done;
+ }
+
+ // that's our answer
+ odir[o] = dir;
+ odir_kpathsea[o] = dir;
+ }
+ kpathsea_done:
+#endif
+
+ if (!odir[o]) {
+ if (automatic) {
+ errh->warning("%s not specified, placing %s files in %<.%>", odir_info[o].envvar, odir_info[o].name);
+#if !HAVE_KPATHSEA
+ static int kpathsea_warning = 0;
+ if (++kpathsea_warning == 1)
+ errh->message("(This version of otftotfm lacks $TEXMF directory support.)");
+#endif
+ }
+ odir[o] = ".";
+ }
+
+ while (odir[o].length() && odir[o].back() == '/')
+ odir[o] = odir[o].substring(0, -1);
+
+ if (verbose)
+ errh->message("placing %s files in %<%s%>", odir_info[o].name, odir[o].c_str());
+ return odir[o];
+}
+
+bool
+setodir(int o, const String &value)
+{
+ assert(o >= 0 && o < NUMODIR);
+ bool had = (bool) odir[o];
+ odir[o] = value;
+ return !had;
+}
+
+const char *
+odirname(int o)
+{
+ assert(o >= 0 && o < NUMODIR);
+ return odir_info[o].name;
+}
+
+#if HAVE_KPATHSEA
+static bool
+file_in_kpathsea_odir(int o, const String &file)
+{
+ return odir_kpathsea[o]
+ && file.length() > odir[o].length()
+ && memcmp(file.data(), odir[o].data(), odir[o].length()) == 0
+ && file[odir[o].length()] == '/';
+}
+#endif
+
+void
+update_odir(int o, String file, ErrorHandler *errh)
+{
+ assert(o >= 0 && o < NUMODIR);
+#if HAVE_KPATHSEA
+ if (file.find_left('/') < 0)
+ file = odir[o] + "/" + file;
+
+ // exit if this directory was not found via kpathsea, or the file is not
+ // in the kpathsea directory
+ if (!file_in_kpathsea_odir(o, file))
+ return;
+
+ assert(writable_texdir && writable_texdir.length() <= odir[o].length()
+ && memcmp(file.data(), writable_texdir.data(), writable_texdir.length()) == 0);
+
+ // divide the filename into portions
+ // file == writable_texdir + directory + file
+ file = file.substring(writable_texdir.length());
+ while (file && file[0] == '/')
+ file = file.substring(1);
+ int last_slash = file.find_right('/');
+ String directory = (last_slash >= 0 ? file.substring(0, last_slash) : String());
+ file = file.substring(last_slash >= 0 ? last_slash + 1 : 0);
+ if (!file) // no filename to update
+ return;
+
+ // return if nocreate
+ if (no_create) {
+ errh->message("would update %sls-R for %s/%s", writable_texdir.c_str(), directory.c_str(), file.c_str());
+ return;
+ } else if (verbose)
+ errh->message("updating %sls-R for %s/%s", writable_texdir.c_str(), directory.c_str(), file.c_str());
+
+ // try to update ls-R ourselves, rather than running mktexupd --
+ // mktexupd's runtime is painful: a half second to update a file
+ String ls_r = writable_texdir + "ls-R";
+ bool success = false;
+ if (access(ls_r.c_str(), R_OK) >= 0) // make sure it already exists
+ if (FILE *f = fopen(ls_r.c_str(), "ab")) {
+ fprintf(f, "./%s:\n%s\n", directory.c_str(), file.c_str());
+ success = true;
+ fclose(f);
+ }
+
+ // otherwise, run mktexupd
+ if (!success && writable_texdir.find_left('\'') < 0 && directory.find_left('\'') < 0 && file.find_left('\'') < 0) {
+ // look for mktexupd script
+ if (!mktexupd_tried) {
+#ifdef WIN32
+ mktexupd = "mktexupd";
+#else
+ mktexupd = kpsei_string(kpsei_find_file("mktexupd", KPSEI_FMT_WEB2C));
+#endif
+ mktexupd_tried = true;
+ }
+
+ // run script
+ if (mktexupd) {
+ String command = mktexupd + " " + shell_quote(writable_texdir + directory) + " " + shell_quote(file);
+ int retval = system(command.c_str());
+ if (retval == 127)
+ errh->error("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->error("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (retval != 0)
+ errh->error("%<%s%> failed", command.c_str());
+ }
+ }
+#else
+ (void) file, (void) errh;
+#endif
+}
+
+bool
+set_map_file(const String &s)
+{
+ bool had = (bool) map_file;
+ map_file = s;
+ return !had;
+}
+
+String
+installed_type1(const String &otf_filename, const String &ps_fontname, bool allow_generate, ErrorHandler *errh)
+{
+ (void) otf_filename, (void) allow_generate, (void) errh;
+
+ if (!ps_fontname)
+ return String();
+
+#if HAVE_KPATHSEA
+# if HAVE_AUTO_CFFTOT1
+ if (!(force && allow_generate && otf_filename && otf_filename != "-" && getodir(O_TYPE1, errh))) {
+# endif
+ // look for .pfb and .pfa
+ String file, path;
+ if ((file = ps_fontname + ".pfb", path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TYPE1)))
+ || (file = ps_fontname + ".pfa", path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TYPE1)))) {
+ if (path == "./" + file || path == file) {
+ if (verbose)
+ errh->message("ignoring Type 1 file %s found with kpathsea in %<.%>", path.c_str());
+ } else {
+ if (verbose)
+ errh->message("Type 1 file %s found with kpathsea at %s", file.c_str(), path.c_str());
+ return path;
+ }
+ }
+# if HAVE_AUTO_CFFTOT1
+ }
+# endif
+#endif
+
+#if HAVE_AUTO_CFFTOT1
+ // if not found, and can generate on the fly, run cfftot1
+ if (allow_generate && otf_filename && otf_filename != "-" && getodir(O_TYPE1, errh)) {
+ String pfb_filename = odir[O_TYPE1] + "/" + ps_fontname + ".pfb";
+ if (pfb_filename.find_left('\'') >= 0 || otf_filename.find_left('\'') >= 0)
+ return String();
+ String command = "cfftot1 " + shell_quote(otf_filename) + " -n " + shell_quote(ps_fontname) + " " + shell_quote(pfb_filename);
+ int retval = mysystem(command.c_str(), errh);
+ if (retval == 127)
+ errh->error("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->error("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (retval != 0)
+ errh->error("%<%s%> failed", command.c_str());
+ if (retval == 0) {
+ update_odir(O_TYPE1, pfb_filename, errh);
+ return pfb_filename;
+ }
+ }
+#endif
+
+ return String();
+}
+
+String
+installed_type1_dotlessj(const String &otf_filename, const String &ps_fontname, bool allow_generate, ErrorHandler *errh)
+{
+ (void) otf_filename, (void) allow_generate, (void) errh;
+
+ if (!ps_fontname)
+ return String();
+ if (verbose)
+ errh->message("searching for dotless-j font for %s", ps_fontname.c_str());
+
+ String j_ps_fontname = ps_fontname + "LCDFJ";
+
+#if HAVE_KPATHSEA
+# if HAVE_AUTO_T1DOTLESSJ
+ if (!(force && allow_generate && getodir(O_TYPE1, errh))) {
+# endif
+ // look for existing .pfb or .pfa
+ String file, path;
+ if ((file = j_ps_fontname + ".pfb", path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TYPE1)))
+ || (file = j_ps_fontname + ".pfa", path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TYPE1)))) {
+ // ignore versions in the current directory
+ if (path == "./" + file || path == file) {
+ if (verbose)
+ errh->message("ignoring Type 1 file %s found with kpathsea in %<.%>", path.c_str());
+ } else {
+ if (verbose)
+ errh->message("Type 1 file %s found with kpathsea at %s", file.c_str(), path.c_str());
+ return path;
+ }
+ }
+# if HAVE_AUTO_T1DOTLESSJ
+ }
+# endif
+#endif
+
+#if HAVE_AUTO_T1DOTLESSJ
+ // if not found, and can generate on the fly, try running t1dotlessj
+ if (allow_generate && getodir(O_TYPE1, errh)) {
+ if (String base_filename = installed_type1(otf_filename, ps_fontname, allow_generate, errh)) {
+ String pfb_filename = odir[O_TYPE1] + "/" + j_ps_fontname + ".pfb";
+ if (pfb_filename.find_left('\'') >= 0 || base_filename.find_left('\'') >= 0)
+ return String();
+ String command = "t1dotlessj " + shell_quote(base_filename) + " -n " + shell_quote(j_ps_fontname) + " " + shell_quote(pfb_filename);
+ int retval = mysystem(command.c_str(), errh);
+ if (retval == 127)
+ errh->warning("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->warning("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (WEXITSTATUS(retval) == T1DOTLESSJ_EXIT_J_NODOT)
+ return String("\0", 1);
+ else if (retval != 0)
+ errh->warning("%<%s%> failed (%d)", command.c_str(), retval);
+ if (retval == 0) {
+ update_odir(O_TYPE1, pfb_filename, errh);
+ return pfb_filename;
+ } else
+ errh->warning("output font will not contain a dotless-j character");
+ }
+ }
+#endif
+
+ return String();
+}
+
+String
+installed_truetype(const String &ttf_filename, bool allow_generate, ErrorHandler *errh)
+{
+ String file = pathname_filename(ttf_filename);
+
+#if HAVE_KPATHSEA
+ if (!(force && allow_generate && ttf_filename && ttf_filename != "-" && getodir(O_TRUETYPE, errh))) {
+ if (String path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TRUETYPE))) {
+ if (path == "./" + file || path == file) {
+ if (verbose)
+ errh->message("ignoring TrueType file %s found with kpathsea in %<.%>", path.c_str());
+ } else {
+ if (verbose)
+ errh->message("TrueType file %s found with kpathsea at %s", file.c_str(), path.c_str());
+ return path;
+ }
+ }
+ }
+#endif
+
+ // perhaps generate type 42 in the future, for now just copy
+ if (allow_generate && ttf_filename && ttf_filename != "-" && getodir(O_TRUETYPE, errh)) {
+ String installed_ttf_filename = odir[O_TRUETYPE] + "/" + file;
+ if (installed_ttf_filename.find_left('\'') >= 0 || installed_ttf_filename.find_left('\"') >= 0)
+ return String();
+
+ int retval;
+ if (!same_filename(ttf_filename, installed_ttf_filename)) {
+ String command = COPY_CMD " " + shell_quote(ttf_filename) + " " + shell_quote(installed_ttf_filename);
+ retval = mysystem(command.c_str(), errh);
+ if (retval == 127)
+ errh->error("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->error("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (retval != 0)
+ errh->error("%<%s%> failed", command.c_str());
+ } else {
+ if (verbose)
+ errh->message("TrueType file %s already located in output directory", installed_ttf_filename.c_str());
+ retval = 0;
+ }
+
+ if (retval == 0) {
+ update_odir(O_TRUETYPE, installed_ttf_filename, errh);
+ return installed_ttf_filename;
+ }
+ }
+
+ return String();
+}
+
+String
+installed_type42(const String &ttf_filename, const String &ps_fontname, bool allow_generate, ErrorHandler *errh)
+{
+ (void) allow_generate, (void) ttf_filename, (void) errh;
+
+ if (!ps_fontname)
+ return String();
+
+#if HAVE_KPATHSEA
+# if HAVE_AUTO_TTFTOTYPE42
+ if (!(force && allow_generate && ttf_filename && ttf_filename != "-" && getodir(O_TYPE42, errh))) {
+# endif
+ // look for .pfb and .pfa
+ String file, path;
+ if ((file = ps_fontname + ".t42", path = kpsei_string(kpsei_find_file(file.c_str(), KPSEI_FMT_TYPE42)))) {
+ if (path == "./" + file || path == file) {
+ if (verbose)
+ errh->message("ignoring Type 42 file %s found with kpathsea in %<.%>", path.c_str());
+ } else {
+ if (verbose)
+ errh->message("Type 42 file %s found with kpathsea at %s", file.c_str(), path.c_str());
+ return path;
+ }
+ }
+# if HAVE_AUTO_TTFTOTYPE42
+ }
+# endif
+#endif
+
+#if HAVE_AUTO_TTFTOTYPE42
+ // if not found, and can generate on the fly, run ttftotype42
+ if (allow_generate && ttf_filename && ttf_filename != "-" && getodir(O_TYPE42, errh)) {
+ String t42_filename = odir[O_TYPE42] + "/" + ps_fontname + ".t42";
+ if (t42_filename.find_left('\'') >= 0 || ttf_filename.find_left('\'') >= 0)
+ return String();
+ String command = "ttftotype42 " + shell_quote(ttf_filename) + " " + shell_quote(t42_filename);
+ int retval = mysystem(command.c_str(), errh);
+ if (retval == 127)
+ errh->error("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->error("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (retval != 0)
+ errh->error("%<%s%> failed", command.c_str());
+ if (retval == 0) {
+ update_odir(O_TYPE42, t42_filename, errh);
+ return t42_filename;
+ }
+ }
+#endif
+
+ return String();
+}
+
+int
+update_autofont_map(const String &fontname, String mapline, ErrorHandler *errh)
+{
+#if HAVE_KPATHSEA
+ if (automatic && !map_file && getodir(O_MAP, errh))
+ map_file = odir[O_MAP] + "/" + get_vendor() + ".map";
+#endif
+
+ if (map_file == "" || map_file == "-")
+ fputs(mapline.c_str(), stdout);
+ else {
+ // report no_create/verbose
+ if (no_create) {
+ errh->message("would update %s for %s", map_file.c_str(), String(fontname).c_str());
+ return 0;
+ } else if (verbose)
+ errh->message("updating %s for %s", map_file.c_str(), String(fontname).c_str());
+
+ int fd = open(map_file.c_str(), O_RDWR | O_CREAT, 0666);
+ if (fd < 0)
+ return errh->lerror(map_file, "%s", strerror(errno));
+ FILE *f = fdopen(fd, "r+");
+ // NB: also change encoding logic if you change this code
+
+#if defined(F_SETLKW) && defined(HAVE_FTRUNCATE)
+ {
+ struct flock lock;
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+ int result;
+ while ((result = fcntl(fd, F_SETLKW, &lock)) < 0 && errno == EINTR)
+ /* try again */;
+ if (result < 0) {
+ result = errno;
+ fclose(f);
+ return errh->error("locking %s: %s", map_file.c_str(), strerror(result));
+ }
+ }
+#endif
+
+ // read old data from map file
+ StringAccum sa;
+ int amt;
+ do {
+ if (char *x = sa.reserve(8192)) {
+ amt = fread(x, 1, 8192, f);
+ sa.adjust_length(amt);
+ } else
+ amt = 0;
+ } while (amt != 0);
+ if (!feof(f))
+ return errh->error("%s: %s", map_file.c_str(), strerror(errno));
+ String text = sa.take_string();
+
+ // add comment if necessary
+ bool created = (!text);
+ if (created)
+ text = "% Automatically maintained by otftotfm or other programs. Do not edit.\n\n";
+ if (text.back() != '\n')
+ text += "\n";
+
+ // append old encodings
+ int fl = 0;
+ int nl = text.find_left('\n') + 1;
+ bool changed = created;
+ while (fl < text.length()) {
+ if (fl + fontname.length() + 1 < nl
+ && memcmp(text.data() + fl, fontname.data(), fontname.length()) == 0
+ && text[fl + fontname.length()] == ' ') {
+ // found the old name
+ if (text.substring(fl, nl - fl) == mapline) {
+ // duplicate of old name, don't change it
+ fclose(f);
+ if (verbose)
+ errh->message("%s unchanged", map_file.c_str());
+ return 0;
+ } else {
+ text = text.substring(0, fl) + text.substring(nl);
+ nl = fl;
+ changed = true;
+ }
+ }
+ fl = nl;
+ nl = text.find_left('\n', fl) + 1;
+ }
+
+ if (!mapline && !changed) {
+ // special case: empty mapline, unchanged file
+ if (verbose)
+ errh->message("%s unchanged", map_file.c_str());
+ } else {
+ // add our text
+ text += mapline;
+
+ // rewind file
+#if HAVE_FTRUNCATE
+ rewind(f);
+ if (ftruncate(fd, 0) < 0)
+#endif
+ {
+ fclose(f);
+ f = fopen(map_file.c_str(), "wb");
+ fd = fileno(f);
+ }
+
+ // write data
+ ignore_result(fwrite(text.data(), 1, text.length(), f));
+ }
+
+ fclose(f);
+
+ // inform about the new file if necessary
+ if (created)
+ update_odir(O_MAP, map_file, errh);
+
+#if HAVE_KPATHSEA && !WIN32
+ // run 'updmap' if present
+ String updmap_dir, updmap_file;
+ if (automatic && (output_flags & G_UPDMAP))
+ updmap_dir = getodir(O_MAP_PARENT, errh);
+ if (updmap_dir && (updmap_file = updmap_dir + "/updmap")
+ && access(updmap_file.c_str(), X_OK) >= 0) {
+ // want to run 'updmap' from its directory, can't use system()
+ if (verbose)
+ errh->message("running %s", updmap_file.c_str());
+
+ pid_t child = fork();
+ if (child < 0)
+ errh->fatal("%s during fork", strerror(errno));
+ else if (child == 0) {
+ // change to updmap directory, run it
+ if (chdir(updmap_dir.c_str()) < 0)
+ errh->fatal("%s: %s during chdir", updmap_dir.c_str(), strerror(errno));
+ if (execl("./updmap", updmap_file.c_str(), (const char*) 0) < 0)
+ errh->fatal("%s: %s during exec", updmap_file.c_str(), strerror(errno));
+ exit(1); // should never get here
+ }
+
+# if HAVE_WAITPID
+ // wait for updmap to finish
+ int status;
+ while (1) {
+ pid_t answer = waitpid(child, &status, 0);
+ if (answer >= 0)
+ break;
+ else if (errno != EINTR)
+ errh->fatal("%s during wait", strerror(errno));
+ }
+ if (!WIFEXITED(status))
+ errh->warning("%s exited abnormally", updmap_file.c_str());
+ else if (WEXITSTATUS(status) != 0)
+ errh->warning("%s exited with status %d", updmap_file.c_str(), WEXITSTATUS(status));
+# else
+# error "need waitpid() support: report this bug to the maintainer"
+# endif
+ goto ran_updmap;
+ }
+
+# if HAVE_AUTO_UPDMAP
+ // run system updmap
+ if (output_flags & G_UPDMAP) {
+ String filename = map_file;
+ int slash = filename.find_right('/');
+ if (slash >= 0)
+ filename = filename.substring(slash + 1);
+ String redirect = verbose ? " 1>&2" : " >" DEV_NULL " 2>&1";
+#if defined(W32TEX)
+// jtex_filetype is defined only in W32TeX
+ char *p = kpsei_var_value("jtex_filetype");
+ if (p != NULL) { // W32TeX
+ free(p);
+ String option = "--add ";
+ } else // TeXLive
+#endif
+ String option = "--enable Map ";
+ String command = "updmap --nomkmap " + option + shell_quote(filename) + redirect
+ + CMD_SEP " updmap" + redirect;
+ int retval = mysystem(command.c_str(), errh);
+ if (retval == 127)
+ errh->warning("could not run %<%s%>", command.c_str());
+ else if (retval < 0)
+ errh->warning("could not run %<%s%>: %s", command.c_str(), strerror(errno));
+ else if (retval != 0)
+ errh->warning("%<%s%> exited with status %d;\nrun it manually to check for errors", command.c_str(), WEXITSTATUS(retval));
+ goto ran_updmap;
+ }
+# endif
+
+ if (verbose)
+ errh->message("not running updmap");
+
+ ran_updmap: ;
+#endif
+ }
+
+ return 0;
+}
+
+String
+locate_encoding(String encfile, ErrorHandler *errh, bool literal)
+{
+ if (!encfile || encfile == "-")
+ return encfile;
+
+ if (!literal) {
+ int slash = encfile.find_right('/');
+ int dot = encfile.find_left('.', slash >= 0 ? slash : 0);
+ if (dot < 0)
+ if (String file = locate_encoding(encfile + ".enc", errh, true))
+ return file;
+ }
+
+#if HAVE_KPATHSEA
+ if (String file = kpsei_string(kpsei_find_file(encfile.c_str(), KPSEI_FMT_ENCODING))) {
+ if (verbose)
+ errh->message("encoding file %s found with kpathsea at %s", encfile.c_str(), file.c_str());
+ return file;
+ } else if (verbose)
+ errh->message("encoding file %s not found with kpathsea", encfile.c_str());
+#endif
+
+ if (access(encfile.c_str(), R_OK) >= 0)
+ return encfile;
+ else
+ return String();
+}