diff options
Diffstat (limited to 'Build/source/utils/asymptote/util.cc')
-rw-r--r-- | Build/source/utils/asymptote/util.cc | 386 |
1 files changed, 386 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..b9557870f91 --- /dev/null +++ b/Build/source/utils/asymptote/util.cc @@ -0,0 +1,386 @@ +/***** + * util.cc + * John Bowman + * + * A place for useful utility functions. + *****/ + +#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 <signal.h> +#include <cstring> +#include <algorithm> + +#include "util.h" +#include "settings.h" +#include "errormsg.h" +#include "camperror.h" +#include "interact.h" + +using namespace settings; + +bool False=false; + +namespace vm { +void error(const char* message); +} + +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 __CYGWIN__ + 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 __CYGWIN__ + p=name.rfind('\\'); + if(p < string::npos) { + dir=true; + name.erase(p+1); + } +#endif + p=name.rfind('/'); + if(p < string::npos) { + dir=true; + 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 writeDisabled() +{ + camp::reportError("Write/cd to other directories disabled; override with option -globalwrite"); +} + +bool globalwrite(string name) +{ + string outname=settings::outname(); + return (!outname.empty() && name.substr(0,outname.size()) == outname) || + globalwrite(); +} + +void checkLocal(string name) +{ + if(globalwrite(name)) return; +#ifdef __CYGWIN__ + if(name.rfind('\\') < string::npos) writeDisabled(); +#endif + if(name.rfind('/') < string::npos) writeDisabled(); + return; +} + +string buildname(string name, string suffix, string aux) +{ + if(!globalwrite(name)) + name=stripDir(name); + + name=stripExt(name,defaultformat()); + name += aux; + if(!suffix.empty()) name += "."+suffix; + return name; +} + +string auxname(string filename, string suffix) +{ + return buildname(filename,suffix,"_"); +} + +char **args(const char *command, bool quiet) +{ + if(command == NULL) return NULL; + + size_t n=0; + char **argv=NULL; + for(int pass=0; pass < 2; ++pass) { + if(pass) argv=new char*[n+1]; + ostringstream buf; + const char *p=command; + bool empty=true; + bool quote=false; + n=0; + char c; + while((c=*(p++))) { + if(!quote && c == ' ') { + if(!empty) { + if(pass) { + argv[n]=StrdupNoGC(buf.str()); + buf.str(""); + } + empty=true; + n++; + } + } else { + empty=false; + if(c == '\'') quote=!quote; + else if(pass) buf << c; + } + } + if(!empty) { + if(pass) argv[n]=StrdupNoGC(buf.str()); + n++; + } + } + + if(!quiet && settings::verbose > 1) { + cerr << argv[0]; + for(size_t m=1; m < n; ++m) cerr << " " << argv[m]; + cerr << endl; + } + + argv[n]=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 " << getSetting<string>("config") + << ": " << endl << endl + << "import settings;" << endl + << hint << "=\"PATH\";" << endl << endl + << "where PATH denotes the correct path to " + << application << "." << endl << endl + << "Alternatively, set the environment variable ASYMPTOTE_" << s + << endl << "or use the command line option -" << hint + << "=\"PATH\"" << endl; + } +} + +// quiet: 0=none; 1=suppress stdout; 2=suppress stdout+stderr. + +int System(const char *command, int quiet, bool wait, + const char *hint, const char *application, int *ppid) +{ + int status; + + if(!command) return 1; + + 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: " << command; + 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: " << command; + camp::reportError(msg); + } + } + } + } +} + +int System(const ostringstream& command, int quiet, bool wait, + const char *hint, const char *application, int *pid) +{ + return System(command.str().c_str(),quiet,wait,hint,application,pid); +} + +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) +{ + static size_t size=MAXPATHLEN; + 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 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)) { + ostringstream cmd; + cmd << "'" << getSetting<string>("pdfviewer") << "' " + << docdir << "/asymptote.pdf"; + status=System(cmd,0,false,"pdfviewer","your PDF viewer",&pid); + } +} + +unsigned unsignedcast(Int n) +{ + if(n < 0 || n/2 > INT_MAX) + vm::error("Unsigned integer argument is outside valid range"); + return (unsigned) n; +} + +int intcast(Int n) +{ + if(Abs(n) > INT_MAX) + vm::error("Integer argument is outside valid range"); + return (int) n; +} |