/*****
* settings.cc
* Andy Hammerlindl 2004/05/10
*
* Declares a list of global variables that act as settings in the system.
*****/
#include <iostream>
#include <iomanip>
#include <cstdlib>
#include <cerrno>
#include <sys/stat.h>
#include <cfloat>
#include <clocale>
#include <algorithm>
#if defined(_WIN32)
#include <Windows.h>
#include <io.h>
#define isatty _isatty
#else
#include <unistd.h>
#endif
#include "common.h"
#if HAVE_GNU_GETOPT_H
#include <getopt.h>
#else
#include "getopt.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"
#include "glrender.h"
#ifdef HAVE_LIBCURSES
extern "C" {
#ifdef HAVE_NCURSES_CURSES_H
#define USE_SETUPTERM
#include <ncurses/curses.h>
#include <ncurses/term.h>
#elif HAVE_NCURSES_H
#define USE_SETUPTERM
#include <ncurses.h>
#include <term.h>
#elif HAVE_CURSES_H
#include <curses.h>
#if defined(HAVE_TERM_H)
#define USE_SETUPTERM
#include <term.h>
#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
string systemDir=ASYMPTOTE_SYSDIR;
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<string>
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<BYTE> 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<string>(
reinterpret_cast<char const*>(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<CHAR> 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<string> 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;
bool globalwrite() {return globalWrite || !safe;}
bool globalread() {return globalRead || !safe;}
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<array *>("suppress");
size_t size=checkArray(Warn);
if(s.empty()) return;
for(size_t i=0; i < size; i++)
if(vm::read<string>(Warn,i) == s) return;
Warn->push(s);
}
void Warn(const string& s)
{
array *Warn=getSetting<array *>("suppress");
size_t size=checkArray(Warn);
for(size_t i=0; i < size; i++)
if(vm::read<string>(Warn,i) == s)
(*Warn).erase((*Warn).begin()+i,(*Warn).begin()+i+1);
}
bool warn(const string& s)
{
if(debug) return true;
array *Warn=getSetting<array *>("suppress");
size_t size=checkArray(Warn);
for(size_t i=0; i < size; i++)
if(vm::read<string>(Warn,i) == s) return false;
return true;
}
// The dictionaries of long options and short options.
struct option;
typedef mem::map<const string, option *> optionsMap_t;
optionsMap_t optionsMap;
typedef mem::map<const char, option *> 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 desciption.
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<itemSetting *>(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<boolSetting *> 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<string>(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<class T>
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<T>(optarg);
} catch (lexical::bad_cast&) {
error("option requires " + text + " as an argument");
return false;
}
return true;
}
};
template<class T>
string description(string desc, T defaultValue)
{
return desc.empty() ? "" : desc+" ["+String(defaultValue)+"]";
}
struct IntSetting : public dataSetting<Int> {
IntSetting(string name, char code,
string argname, string desc, Int defaultValue=0)
: dataSetting<Int>("an int", name, code, argname,
description(desc,defaultValue),
types::primInt(), defaultValue) {}
};
struct realSetting : public dataSetting<double> {
realSetting(string name, char code,
string argname, string desc, double defaultValue=0.0)
: dataSetting<double>("a real", name, code, argname,
description(desc,defaultValue),
types::primReal(), defaultValue) {}
};
struct pairSetting : public dataSetting<pair> {
pairSetting(string name, char code,
string argname, string desc, pair defaultValue=0.0)
: dataSetting<pair>("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<class T>
string stringCast(T x)
{
ostringstream buf;
buf.precision(DBL_DIG);
buf.setf(std::ios::boolalpha);
buf << x;
return string(buf.str());
}
template <class T>
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<T>(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<T>(ref);
}
};
struct boolrefSetting : public refSetting<bool> {
boolrefSetting(string name, char code, string desc, bool *ref,
bool Default=false)
: refSetting<bool>(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<Int> {
incrementSetting(string name, char code, string desc, Int *ref)
: refSetting<Int>(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 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 disabled;
const void feature(const char *s, bool enabled) {
if(enabled ^ disabled)
cerr << s << endl;
}
void features(bool enabled) {
disabled=!enabled;
cerr << endl << (disabled ? "DIS" : "EN") << "ABLED OPTIONS:" << endl;
bool glm=false;
bool gl=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 HAVE_GL
gl=true;
#endif
#ifdef HAVE_SSBO
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);
#ifdef HAVE_LIBOSMESA
feature("OpenGL 3D OSMesa offscreen rendering",gl);
#else
feature("OpenGL 3D OpenGL rendering",gl);
#endif
feature("SSBO GLSL 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 OpenGL in separate thread",usethreads);
}
bool getOption() {
version();
features(1);
features(0);
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<Int>(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;
}
errno=0;
}
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", 1.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("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 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,
"Iconify rendering window", 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("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 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 animations", false));
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 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("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<Int>("outpipe") < 0) {
cerr << "Missing outpipe." << endl;
exit(-1);
}
bool lspmode=getSetting<bool>("lsp");
if(numArgs() == 0 && !getSetting<bool>("listvariables") &&
getSetting<string>("command").empty() &&
(isatty(STDIN_FILENO) || xasy || lspmode))
interact::interactive=true;
if(getSetting<bool>("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<bool>("interactiveView");
else
return getSetting<bool>("batchView") &&
(numArgs() == 1 || getSetting<bool>("multipleView"));
}
bool trap()
{
if (interact::interactive)
return !getSetting<bool>("interactiveMask");
else
return !getSetting<bool>("batchMask");
}
string outname()
{
string name=getSetting<string>("outname");
if(name.empty() && interact::interactive) return standardprefix;
if(msdos) backslashToSlash(name);
return name;
}
string lookup(const string& symbol)
{
string s;
mem::vector<string> 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<string>("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<string>("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<string>("sysdir");
if(sysdir != "")
searchPath.push_back(sysdir);
searchPath.push_back(docdir+"/examples");
}
void SetPageDimensions() {
string paperType=getSetting<string>("papertype");
if(paperType.empty() &&
getSetting<double>("paperwidth") != 0.0 &&
getSetting<double>("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<string>("tex")) ? "pdf" : "eps";
}
string defaultformat()
{
string format=getSetting<string>("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<string>("texcommand");
return command.empty() ? getSetting<string>("tex") : command;
}
string texprogram()
{
string path=getSetting<string>("texpath");
string engine=texcommand();
return path.empty() ? engine : (string) (path+"/"+engine);
}
Int getScroll()
{
Int scroll=settings::getSetting<Int>("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<bool>("autoplain");
bool listvariables=getSetting<bool>("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<bool>("quiet");
// Make configuration and history directory
initDir();
Int Verbose=verbose;
string sysdir=getSetting<string>("sysdir");
resetOptions();
// Read user configuration file.
setPath();
string filename=getSetting<string>("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<Int>("outpipe") == 2) // Redirect cerr to cout
std::cerr.rdbuf(std::cout.rdbuf());
Setting("sysdir")=sysdir;
if(docdir.empty())
docdir=getSetting<string>("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<double>("paperwidth") != 0.0 &&
getSetting<double>("paperheight") != 0.0)
Setting("papertype")=string("");
SetPageDimensions();
setInteractive();
}
}