/************************************************************************* ** FileFinder.cpp ** ** ** ** This file is part of dvisvgm -- the DVI to SVG converter ** ** Copyright (C) 2005-2013 Martin Gieseking ** ** ** ** 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 3 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. ** ** ** ** You should have received a copy of the GNU General Public License ** ** along with this program; if not, see . ** *************************************************************************/ #include #include #include #include #include "FileFinder.h" #include "FileSystem.h" #include "FontMap.h" #include "Message.h" #include "Process.h" #ifdef MIKTEX #include "MessageException.h" #include "MiKTeXCom.h" static MiKTeXCom *miktex=0; #else // unfortunately, the kpathsea headers are not C++-ready, // so we have to wrap it with some ugly code namespace KPS { extern "C" { #include } } using namespace KPS; #endif // --------------------------------------------------- static bool _initialized = false; static bool _mktex_enabled = false; // --------------------------------------------------- static const char* find_file (const std::string &fname, const char *ftype); static const char* find_mapped_file (std::string fname); static const char* mktex (const std::string &fname); /** Initializes the file finder. This function must be called before any other * FileFinder function. * @param[in] argv0 argv[0] of main() function * @param[in] progname name of application using the FileFinder * @param[in] enable_mktexmf if true, tfm and mf file generation is activated */ void FileFinder::init (const char *argv0, const char *progname, bool enable_mktexmf) { if (_initialized) return; _mktex_enabled = enable_mktexmf; #ifdef MIKTEX miktex = new MiKTeXCom; #else kpse_set_program_name(argv0, progname); // enable tfm and mf generation (actually invoked by calls of kpse_make_tex) kpse_set_program_enabled(kpse_tfm_format, 1, kpse_src_env); kpse_set_program_enabled(kpse_mf_format, 1, kpse_src_env); kpse_make_tex_discard_errors = false; // don't suppress messages of mktexFOO tools #ifdef TEXLIVEWIN32 texlive_gs_init(); #endif #endif _initialized = true; } /** Cleans up the FileFinder. This function must be called before leaving the * application's main() function. */ void FileFinder::finish () { #ifdef MIKTEX if (miktex) { delete miktex; miktex = 0; } #endif _initialized = false; } /** Returns the version string of the underlying file searching library (kpathsea, MiKTeX) */ std::string FileFinder::version () { #ifdef MIKTEX bool autoinit=false; try { if (!_initialized) { init("", "", false); autoinit = true; } std::string ret = miktex->getVersion(); if (autoinit) finish(); return ret; } catch (MessageException &e) { if (autoinit) finish(); return "not available"; } #else if (const char *v = strrchr(KPSEVERSION, ' ')) return v+1; #endif return "unknown"; } /** Determines filetype by the filename extension and calls kpse_find_file * to actually look up the file. * @param[in] fname name of file to look up * @param[in] ftype expected file format of file fname; if 0, it's derived from the filename suffix * @return file path on success, 0 otherwise */ static const char* find_file (const std::string &fname, const char *ftype) { if (!_initialized || fname.empty()) return 0; std::string ext; if (ftype) ext = ftype; else { size_t pos = fname.rfind('.'); if (pos == std::string::npos) return 0; // no extension and no file type => no search ext = fname.substr(pos+1); } static std::string buf; #ifdef MIKTEX if (ext == "dll" || ext == "exe") { // lookup dll and exe files in the MiKTeX bin directory first buf = miktex->getBinDir() + "/" + fname; if (FileSystem::exists(buf.c_str())) return buf.c_str(); } else if (ext == "cmap") { // The MiKTeX SDK doesn't support the lookup of files without suffix (yet), thus // it's not possible to find cmap files which usually don't have a suffix. In order // to work around this, we try to lookup the files by calling kpsewhich. Process process("kpsewhich", std::string("-format=cmap ")+fname); process.run(&buf); return buf.empty() ? 0 : buf.c_str(); } return miktex->findFile(fname.c_str()); #else #ifdef TEXLIVEWIN32 if (ext == "exe") { // lookup exe files in directory where dvisvgm is located if (const char *path = kpse_var_value("SELFAUTOLOC")) { buf = std::string(path) + "/" + fname; return FileSystem::exists(buf.c_str()) ? buf.c_str() : 0; } return 0; } #endif static std::map types; if (types.empty()) { types["tfm"] = kpse_tfm_format; types["pfb"] = kpse_type1_format; types["vf"] = kpse_vf_format; types["mf"] = kpse_mf_format; types["ttc"] = kpse_truetype_format; types["ttf"] = kpse_truetype_format; types["otf"] = kpse_opentype_format; types["map"] = kpse_fontmap_format; types["cmap"] = kpse_cmap_format; types["sty"] = kpse_tex_format; types["enc"] = kpse_enc_format; types["pro"] = kpse_tex_ps_header_format; types["sfd"] = kpse_sfd_format; } std::map::iterator it = types.find(ext.c_str()); if (it == types.end()) return 0; if (char *path = kpse_find_file(fname.c_str(), it->second, 0)) { // In the current version of libkpathsea, each call of kpse_find_file produces // a memory leak since the path buffer is not freed. I don't think we can do // anything against it here... buf = path; std::free(path); return buf.c_str(); } return 0; #endif } /** Checks whether the given file is mapped to a different name and if the * file can be found under this name. * @param[in] fname name of file to look up * @param[in] fontmap font mappings * @return file path on success, 0 otherwise */ static const char* find_mapped_file (std::string fname) { size_t pos = fname.rfind('.'); if (pos == std::string::npos) return 0; const std::string ext = fname.substr(pos+1); // file extension const std::string base = fname.substr(0, pos); if (const FontMap::Entry *entry = FontMap::instance().lookup(base)) { const char *path=0; if (entry->fontname.find('.') != std::string::npos) // does the mapped filename has an extension? path = find_file(entry->fontname, 0); // look for that file else { // otherwise, use extension of unmapped file fname = entry->fontname + "." + ext; (path = find_file(fname, 0)) || (path = mktex(fname)); } return path; } return 0; } /** Runs external mktexFOO tool to create missing tfm or mf file. * @param[in] fname name of file to build * @return file path on success, 0 otherwise */ static const char* mktex (const std::string &fname) { if (!_initialized) return 0; size_t pos = fname.rfind('.'); if (!_mktex_enabled || pos == std::string::npos) return 0; std::string ext = fname.substr(pos+1); // file extension if (ext != "tfm" && ext != "mf") return 0; const char *path = 0; #ifdef MIKTEX // maketfm and makemf are located in miktex/bin which is in the search PATH std::string toolname = (ext == "tfm" ? "miktex-maketfm" : "miktex-makemf"); system((toolname+".exe "+fname).c_str()); path = find_file(fname, 0); #else kpse_file_format_type type = (ext == "tfm" ? kpse_tfm_format : kpse_mf_format); path = kpse_make_tex(type, fname.c_str()); #endif return path; } /** Searches a file in the TeX directory tree. * If the file doesn't exist, maximal two further steps are applied * (if "extended" is true): * - checks whether the filename is mapped to a different name and returns * the path to that name * - in case of tfm or mf files: invokes the external mktextfm/mktexmf tool * to create the missing file * @param[in] fname name of file to look up * @param[in] ftype type/format of file to look up * @param[in] extended if true, use fontmap lookup and mktexFOO calls * @return path to file on success, 0 otherwise */ const char* FileFinder::lookup (const std::string &fname, const char *ftype, bool extended) { const char *path; if ((path = find_file(fname, ftype)) || (extended && ((path = find_mapped_file(fname)) || (path = mktex(fname))))) return path; return 0; }