summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/util.cc
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/util.cc')
-rw-r--r--Build/source/utils/asymptote/util.cc514
1 files changed, 514 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/util.cc b/Build/source/utils/asymptote/util.cc
new file mode 100644
index 00000000000..81398491c00
--- /dev/null
+++ b/Build/source/utils/asymptote/util.cc
@@ -0,0 +1,514 @@
+/*****
+ * util.cc
+ * John Bowman
+ *
+ * A place for useful utility functions.
+ *****/
+
+#ifdef __CYGWIN__
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include <cassert>
+#include <iostream>
+#include <cstdio>
+#include <cfloat>
+#include <sstream>
+#include <cerrno>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <cstring>
+#include <algorithm>
+#include <dirent.h>
+
+#include "util.h"
+#include "settings.h"
+#include "errormsg.h"
+#include "camperror.h"
+#include "interact.h"
+#include "locate.h"
+
+using namespace settings;
+using camp::reportError;
+
+bool False=false;
+
+namespace vm {
+void error(const char* message);
+}
+
+#if __GNUC__
+#include <cxxabi.h>
+string demangle(const char *s)
+{
+ int status;
+ char *demangled = abi::__cxa_demangle(s,NULL,NULL,&status);
+ if (status == 0 && demangled) {
+ string str(demangled);
+ free(demangled);
+ return str;
+ } else if (status == -2) {
+ free(demangled);
+ return s;
+ } else {
+ free(demangled);
+ return string("Unknown(") + s + ")";
+ }
+}
+#else
+string demangle(const char* s)
+{
+ return s;
+}
+#endif
+
+char *Strdup(string s)
+{
+ size_t size=s.size()+1;
+ char *dest=new(UseGC) char[size];
+ std::memcpy(dest,s.c_str(),size*sizeof(char));
+ return dest;
+}
+
+char *StrdupNoGC(string s)
+{
+ size_t size=s.size()+1;
+ char *dest=new char[size];
+ std::memcpy(dest,s.c_str(),size*sizeof(char));
+ return dest;
+}
+
+char *StrdupMalloc(string s)
+{
+ size_t size=s.size()+1;
+ char *dest=(char *) std::malloc(size);
+ std::memcpy(dest,s.c_str(),size*sizeof(char));
+ return dest;
+}
+
+string stripDir(string name)
+{
+ size_t p;
+#ifdef __MSDOS__
+ p=name.rfind('\\');
+ if(p < string::npos) name.erase(0,p+1);
+#endif
+ p=name.rfind('/');
+ if(p < string::npos) name.erase(0,p+1);
+ return name;
+}
+
+string stripFile(string name)
+{
+ size_t p;
+ bool dir=false;
+#ifdef __MSDOS__
+ p=name.rfind('\\');
+ if(p < string::npos) {
+ dir=true;
+ while(p > 0 && name[p-1] == '\\') --p;
+ name.erase(p+1);
+ }
+#endif
+ p=name.rfind('/');
+ if(p < string::npos) {
+ dir=true;
+ while(p > 0 && name[p-1] == '/') --p;
+ name.erase(p+1);
+ }
+
+ return dir ? name : "";
+}
+
+string stripExt(string name, const string& ext)
+{
+ string suffix="."+ext;
+ size_t p=name.rfind(suffix);
+ size_t n=suffix.length();
+ if(n == 1 || p == name.length()-n)
+ return name.substr(0,p);
+ else return name;
+}
+
+void backslashToSlash(string& s)
+{
+ size_t p;
+ while((p=s.find('\\')) < string::npos)
+ s[p]='/';
+}
+
+void spaceToUnderscore(string& s)
+{
+ size_t p;
+ while((p=s.find(' ')) < string::npos)
+ s[p]='_';
+}
+
+string Getenv(const char *name, bool msdos)
+{
+ char *s=getenv(name);
+ if(!s) return "";
+ string S=string(s);
+ if(msdos) backslashToSlash(S);
+ return S;
+}
+
+void readDisabled()
+{
+ camp::reportError("Read from other directories disabled; override with option -globalread");
+}
+
+void writeDisabled()
+{
+ camp::reportError("Write to other directories disabled; override with option -globalwrite");
+}
+
+string cleanpath(string name)
+{
+ string dir=stripFile(name);
+ name=stripDir(name);
+ spaceToUnderscore(name);
+ return dir+name;
+}
+
+string inpath(string name)
+{
+ bool global=globalread();
+ string dir=stripFile(name);
+ if(global && !dir.empty()) return name;
+ string indir=stripFile(outname());
+ if(!(global || dir.empty() || dir == indir)) readDisabled();
+ return stripDir(name);
+}
+
+string outpath(string name)
+{
+ bool global=globalwrite();
+ string dir=stripFile(name);
+ if(global && !dir.empty()) return name;
+ string outdir=stripFile(outname());
+ if(!(global || dir.empty() || dir == outdir)) writeDisabled();
+ return outdir+stripDir(name);
+}
+
+string buildname(string name, string suffix, string aux)
+{
+ name=stripExt(outpath(name),defaultformat())+aux;
+ if(!suffix.empty()) name += "."+suffix;
+ return name;
+}
+
+string auxname(string filename, string suffix)
+{
+ return buildname(filename,suffix,"_");
+}
+
+sighandler_t Signal(int signum, sighandler_t handler)
+{
+ struct sigaction action,oldaction;
+ action.sa_handler=handler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags=0;
+ return sigaction(signum,&action,&oldaction) == 0 ? oldaction.sa_handler :
+ SIG_ERR;
+}
+
+void push_split(mem::vector<string>& a, const string& S)
+{
+ const char *p=S.c_str();
+ string s;
+ char c;
+ while((c=*(p++))) {
+ if(c == ' ') {
+ if(s.size() > 0) {
+ a.push_back(s);
+ s.clear();
+ }
+ } else s += c;
+ }
+ if(s.size() > 0)
+ a.push_back(s);
+}
+
+char **args(const mem::vector<string>& s, bool quiet)
+{
+ size_t count=s.size();
+
+ char **argv=NULL;
+ argv=new char*[count+1];
+ for(size_t i=0; i < count; ++i)
+ argv[i]=StrdupNoGC(s[i]);
+
+ if(!quiet && settings::verbose > 1) {
+ cerr << argv[0];
+ for(size_t i=1; i < count; ++i) cerr << " " << argv[i];
+ cerr << endl;
+ }
+
+ argv[count]=NULL;
+ return argv;
+}
+
+void execError(const char *command, const char *hint, const char *application)
+{
+ cerr << "Cannot execute " << command << endl;
+ if(*application == 0) application=hint;
+ if(hint) {
+ string s=string(hint);
+ transform(s.begin(), s.end(), s.begin(), toupper);
+ cerr << "Please put in a file " << getSetting<string>("config")
+ << ": " << endl << endl
+ << "import settings;" << endl
+ << hint << "=\"LOCATION\";" << endl << endl
+ << "where LOCATION specifies the location of "
+ << application << "." << endl << endl
+ << "Alternatively, set the environment variable ASYMPTOTE_" << s
+ << endl << "or use the command line option -" << hint
+ << "=\"LOCATION\". For further details, see" << endl
+ << "https://asymptote.sourceforge.io/doc/Configuring.html" << endl
+ << "https://asymptote.sourceforge.io/doc/Search-paths.html" << endl;
+ }
+}
+
+// quiet: 0=none; 1=suppress stdout; 2=suppress stdout+stderr.
+int System(const mem::vector<string> &command, int quiet, bool wait,
+ const char *hint, const char *application, int *ppid)
+{
+ int status;
+
+ cout.flush(); // Flush stdout to avoid duplicate output.
+
+ char **argv=args(command);
+
+ int pid=fork();
+ if(pid == -1)
+ camp::reportError("Cannot fork process");
+
+ if(pid == 0) {
+ if(interact::interactive) signal(SIGINT,SIG_IGN);
+ if(quiet) {
+ static int null=creat("/dev/null",O_WRONLY);
+ close(STDOUT_FILENO);
+ dup2(null,STDOUT_FILENO);
+ if(quiet == 2) {
+ close(STDERR_FILENO);
+ dup2(null,STDERR_FILENO);
+ }
+ }
+ if(argv) {
+ execvp(argv[0],argv);
+ execError(argv[0],hint,application);
+ _exit(-1);
+ }
+ }
+
+ if(ppid) *ppid=pid;
+ for(;;) {
+ if(waitpid(pid, &status, wait ? 0 : WNOHANG) == -1) {
+ if(errno == ECHILD) return 0;
+ if(errno != EINTR) {
+ if(quiet < 2) {
+ ostringstream msg;
+ msg << "Command failed: ";
+ for(size_t i=0; i < command.size(); ++i) msg << command[i] << " ";
+ camp::reportError(msg);
+ }
+ }
+ } else {
+ if(!wait) return 0;
+ if(WIFEXITED(status)) {
+ if(argv) {
+ char **p=argv;
+ char *s;
+ while((s=*(p++)) != NULL)
+ delete [] s;
+ delete [] argv;
+ }
+ return WEXITSTATUS(status);
+ } else {
+ if(quiet < 2) {
+ ostringstream msg;
+ msg << "Command exited abnormally: ";
+ for(size_t i=0; i < command.size(); ++i) msg << command[i] << " ";
+ camp::reportError(msg);
+ }
+ }
+ }
+ }
+}
+
+string stripblanklines(const string& s)
+{
+ string S=string(s);
+ bool blank=true;
+ const char *t=S.c_str();
+ size_t len=S.length();
+
+ for(size_t i=0; i < len; i++) {
+ if(t[i] == '\n') {
+ if(blank) S[i]=' ';
+ else blank=true;
+ } else if(t[i] != '\t' && t[i] != ' ') blank=false;
+ }
+ return S;
+}
+
+char *startpath=NULL;
+
+void noPath()
+{
+ camp::reportError("Cannot get current path");
+}
+
+char *getPath(char *p)
+{
+#ifdef MAXPATHLEN
+ static size_t size = MAXPATHLEN;
+#else
+ static size_t size = 1024;
+#endif
+ if(!p) p=new(UseGC) char[size];
+ if(!p) noPath();
+ else while(getcwd(p,size) == NULL) {
+ if(errno == ERANGE) {
+ size *= 2;
+ p=new(UseGC) char[size];
+ } else {noPath(); p=NULL;}
+ }
+ return p;
+}
+
+const char *setPath(const char *s, bool quiet)
+{
+ if(startpath == NULL) startpath=getPath(startpath);
+ if(s == NULL || *s == 0) s=startpath;
+ int rc=chdir(s);
+ if(rc != 0) {
+ ostringstream buf;
+ buf << "Cannot change to directory '" << s << "'";
+ camp::reportError(buf);
+ }
+ char *p=getPath();
+ if(p && (!interact::interactive || quiet) && verbose > 1)
+ cout << "cd " << p << endl;
+ return p;
+}
+
+void fatal(const char *msg, const char *s=NULL)
+{
+ ostringstream buf;
+ buf << msg;
+ if(s) {
+ buf << " " << getPath();
+ if(*s) buf << "/";
+ buf << s;
+ }
+ buf << ": " << strerror(errno) << endl;
+ camp::reportError(buf);
+}
+
+void empty_current_dir()
+{
+ static struct stat buf;
+ DIR *dir=opendir(".");
+ if(dir == NULL) fatal("Cannot open directory","");
+ dirent *p;
+ while((p=readdir(dir)) != NULL) {
+ if(strcmp(p->d_name,".") == 0 || strcmp(p->d_name,"..") == 0) continue;
+ if(lstat(p->d_name,&buf) == 0) {
+ if(S_ISDIR(buf.st_mode)) {
+ if(chdir(p->d_name)) fatal("Cannot change directory to",p->d_name);
+ empty_current_dir();
+ if(chdir(".."))
+ fatal("Cannot change to parent directory of",p->d_name);
+ if(rmdir(p->d_name))
+ fatal("Cannot remove directory",p->d_name);
+ } else {
+ if(unlink(p->d_name)) fatal("Cannot remove file",p->d_name);
+ }
+ }
+ }
+ if(closedir(dir)) fatal("Invalid current directory stream descriptor");
+}
+
+void recursive_delete(char *name)
+{
+ static struct stat buf;
+ if(lstat(name,&buf) == 0) {
+ if(S_ISDIR(buf.st_mode)) {
+ const char *path=getPath();
+ if(chdir(name)) fatal("Cannot change directory to",name);
+ empty_current_dir();
+ if(chdir(path)) fatal("Cannot change to directory","");
+ if(rmdir(name)) fatal("Cannot remove directory", name);
+ }
+ else unlink(name);
+ }
+}
+
+void push_command(mem::vector<string>& a, const string& s)
+{
+ a.push_back(s);
+#ifdef __MSDOS__
+ if(s == "cmd") {
+ a.push_back("/c");
+ a.push_back("start");
+ a.push_back("\"\"");
+ }
+#endif
+}
+
+void popupHelp() {
+ // If the popped-up help is already running, pid stores the pid of the viewer.
+ static int pid=0;
+
+ // Status is ignored.
+ static int status=0;
+
+ // If the help viewer isn't running (or its last run has termined), launch the
+ // viewer again.
+ if (pid==0 || (waitpid(pid, &status, WNOHANG) == pid)) {
+ mem::vector<string> cmd;
+ push_command(cmd,getSetting<string>("pdfviewer"));
+ string viewerOptions=getSetting<string>("pdfviewerOptions");
+ if(!viewerOptions.empty())
+ cmd.push_back(viewerOptions);
+ cmd.push_back(docdir+dirsep+"asymptote.pdf");
+ status=System(cmd,0,false,"pdfviewer","your PDF viewer",&pid);
+ }
+}
+
+const char *intrange="integer argument is outside valid range";
+const char *uintrange="integer argument is outside valid unsigned range";
+
+unsigned unsignedcast(Int n)
+{
+ if(n < 0 || n/2 > INT_MAX)
+ vm::error(uintrange);
+ return (unsigned) n;
+}
+
+unsignedInt unsignedIntcast(Int n)
+{
+ if(n < 0)
+ vm::error(uintrange);
+ return (unsignedInt) n;
+}
+
+int intcast(Int n)
+{
+ if(Abs(n) > INT_MAX)
+ vm::error(intrange);
+ return (int) n;
+}
+
+Int Intcast(unsignedInt n)
+{
+ if(n > (unsignedInt) Int_MAX)
+ vm::error(intrange);
+ return (Int) n;
+}