1 /*****
2 * util.cc
3 * John Bowman
4 *
5 * A place for useful utility functions.
6 *****/
7
8 #include <cassert>
9 #include <iostream>
10 #include <cstdio>
11 #include <cfloat>
12 #include <sstream>
13 #include <cerrno>
14 #include <sys/wait.h>
15 #include <sys/param.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <cstring>
21 #include <algorithm>
22
23 #include "util.h"
24 #include "settings.h"
25 #include "errormsg.h"
26 #include "camperror.h"
27 #include "interact.h"
28
29 using namespace settings;
30
31 bool False=false;
32
33 namespace vm {
34 void error(const char* message);
35 }
36
37 #if __GNUC__
38 #include <cxxabi.h>
demangle(const char * s)39 string demangle(const char *s)
40 {
41 int status;
42 char *demangled = abi::__cxa_demangle(s,NULL,NULL,&status);
43 if (status == 0 && demangled) {
44 string str(demangled);
45 free(demangled);
46 return str;
47 } else if (status == -2) {
48 free(demangled);
49 return s;
50 } else {
51 free(demangled);
52 return string("Unknown(") + s + ")";
53 }
54 }
55 #else
demangle(const char * s)56 string demangle(const char* s)
57 {
58 return s;
59 }
60 #endif
61
Strdup(string s)62 char *Strdup(string s)
63 {
64 size_t size=s.size()+1;
65 char *dest=new(UseGC) char[size];
66 std::memcpy(dest,s.c_str(),size*sizeof(char));
67 return dest;
68 }
69
StrdupNoGC(string s)70 char *StrdupNoGC(string s)
71 {
72 size_t size=s.size()+1;
73 char *dest=new char[size];
74 std::memcpy(dest,s.c_str(),size*sizeof(char));
75 return dest;
76 }
77
StrdupMalloc(string s)78 char *StrdupMalloc(string s)
79 {
80 size_t size=s.size()+1;
81 char *dest=(char *) std::malloc(size);
82 std::memcpy(dest,s.c_str(),size*sizeof(char));
83 return dest;
84 }
85
stripDir(string name)86 string stripDir(string name)
87 {
88 size_t p;
89 #ifdef __MSDOS__
90 p=name.rfind('\\');
91 if(p < string::npos) name.erase(0,p+1);
92 #endif
93 p=name.rfind('/');
94 if(p < string::npos) name.erase(0,p+1);
95 return name;
96 }
97
stripFile(string name)98 string stripFile(string name)
99 {
100 size_t p;
101 bool dir=false;
102 #ifdef __MSDOS__
103 p=name.rfind('\\');
104 if(p < string::npos) {
105 dir=true;
106 while(p > 0 && name[p-1] == '\\') --p;
107 name.erase(p+1);
108 }
109 #endif
110 p=name.rfind('/');
111 if(p < string::npos) {
112 dir=true;
113 while(p > 0 && name[p-1] == '/') --p;
114 name.erase(p+1);
115 }
116
117 return dir ? name : "";
118 }
119
stripExt(string name,const string & ext)120 string stripExt(string name, const string& ext)
121 {
122 string suffix="."+ext;
123 size_t p=name.rfind(suffix);
124 size_t n=suffix.length();
125 if(n == 1 || p == name.length()-n)
126 return name.substr(0,p);
127 else return name;
128 }
129
backslashToSlash(string & s)130 void backslashToSlash(string& s)
131 {
132 size_t p;
133 while((p=s.find('\\')) < string::npos)
134 s[p]='/';
135 }
136
spaceToUnderscore(string & s)137 void spaceToUnderscore(string& s)
138 {
139 size_t p;
140 while((p=s.find(' ')) < string::npos)
141 s[p]='_';
142 }
143
Getenv(const char * name,bool msdos)144 string Getenv(const char *name, bool msdos)
145 {
146 char *s=getenv(name);
147 if(!s) return "";
148 string S=string(s);
149 if(msdos) backslashToSlash(S);
150 return S;
151 }
152
writeDisabled()153 void writeDisabled()
154 {
155 camp::reportError("Write to other directories disabled; override with option -globalwrite");
156 }
157
cleanpath(string name)158 string cleanpath(string name)
159 {
160 string dir=stripFile(name);
161 name=stripDir(name);
162 spaceToUnderscore(name);
163 return dir+name;
164 }
165
outpath(string name)166 string outpath(string name)
167 {
168 bool global=globalwrite();
169 string dir=stripFile(name);
170 if(global && !dir.empty()) return name;
171 string outdir=stripFile(outname());
172 if(!(global || dir.empty() || dir == outdir)) writeDisabled();
173 return outdir+stripDir(name);
174 }
175
buildname(string name,string suffix,string aux)176 string buildname(string name, string suffix, string aux)
177 {
178 name=stripExt(outpath(name),defaultformat())+aux;
179 if(!suffix.empty()) name += "."+suffix;
180 return name;
181 }
182
auxname(string filename,string suffix)183 string auxname(string filename, string suffix)
184 {
185 return buildname(filename,suffix,"_");
186 }
187
Signal(int signum,sighandler_t handler)188 sighandler_t Signal(int signum, sighandler_t handler)
189 {
190 struct sigaction action,oldaction;
191 action.sa_handler=handler;
192 sigemptyset(&action.sa_mask);
193 action.sa_flags=0;
194 return sigaction(signum,&action,&oldaction) == 0 ? oldaction.sa_handler :
195 SIG_ERR;
196 }
197
push_split(mem::vector<string> & a,const string & S)198 void push_split(mem::vector<string>& a, const string& S)
199 {
200 const char *p=S.c_str();
201 string s;
202 char c;
203 while((c=*(p++))) {
204 if(c == ' ') {
205 if(s.size() > 0) {
206 a.push_back(s);
207 s.clear();
208 }
209 } else s += c;
210 }
211 if(s.size() > 0)
212 a.push_back(s);
213 }
214
args(const mem::vector<string> & s,bool quiet)215 char **args(const mem::vector<string>& s, bool quiet)
216 {
217 size_t count=s.size();
218
219 char **argv=NULL;
220 argv=new char*[count+1];
221 for(size_t i=0; i < count; ++i)
222 argv[i]=StrdupNoGC(s[i]);
223
224 if(!quiet && settings::verbose > 1) {
225 cerr << argv[0];
226 for(size_t i=1; i < count; ++i) cerr << " " << argv[i];
227 cerr << endl;
228 }
229
230 argv[count]=NULL;
231 return argv;
232 }
233
execError(const char * command,const char * hint,const char * application)234 void execError(const char *command, const char *hint, const char *application)
235 {
236 cerr << "Cannot execute " << command << endl;
237 if(*application == 0) application=hint;
238 if(hint) {
239 string s=string(hint);
240 transform(s.begin(), s.end(), s.begin(), toupper);
241 cerr << "Please put in a file " << getSetting<string>("config")
242 << ": " << endl << endl
243 << "import settings;" << endl
244 << hint << "=\"LOCATION\";" << endl << endl
245 << "where LOCATION specifies the location of "
246 << application << "." << endl << endl
247 << "Alternatively, set the environment variable ASYMPTOTE_" << s
248 << endl << "or use the command line option -" << hint
249 << "=\"LOCATION\". For further details, see" << endl
250 << "http://asymptote.sourceforge.net/doc/Configuring.html" << endl
251 << "http://asymptote.sourceforge.net/doc/Search-paths.html" << endl;
252 }
253 }
254
255 // quiet: 0=none; 1=suppress stdout; 2=suppress stdout+stderr.
System(const mem::vector<string> & command,int quiet,bool wait,const char * hint,const char * application,int * ppid)256 int System(const mem::vector<string> &command, int quiet, bool wait,
257 const char *hint, const char *application, int *ppid)
258 {
259 int status;
260
261 cout.flush(); // Flush stdout to avoid duplicate output.
262
263 char **argv=args(command);
264
265 int pid=fork();
266 if(pid == -1)
267 camp::reportError("Cannot fork process");
268
269 if(pid == 0) {
270 if(interact::interactive) signal(SIGINT,SIG_IGN);
271 if(quiet) {
272 static int null=creat("/dev/null",O_WRONLY);
273 close(STDOUT_FILENO);
274 dup2(null,STDOUT_FILENO);
275 if(quiet == 2) {
276 close(STDERR_FILENO);
277 dup2(null,STDERR_FILENO);
278 }
279 }
280 if(argv) {
281 execvp(argv[0],argv);
282 execError(argv[0],hint,application);
283 _exit(-1);
284 }
285 }
286
287 if(ppid) *ppid=pid;
288 for(;;) {
289 if(waitpid(pid, &status, wait ? 0 : WNOHANG) == -1) {
290 if(errno == ECHILD) return 0;
291 if(errno != EINTR) {
292 if(quiet < 2) {
293 ostringstream msg;
294 msg << "Command failed: ";
295 for(size_t i=0; i < command.size(); ++i) msg << command[i] << " ";
296 camp::reportError(msg);
297 }
298 }
299 } else {
300 if(!wait) return 0;
301 if(WIFEXITED(status)) {
302 if(argv) {
303 char **p=argv;
304 char *s;
305 while((s=*(p++)) != NULL)
306 delete [] s;
307 delete [] argv;
308 }
309 return WEXITSTATUS(status);
310 } else {
311 if(quiet < 2) {
312 ostringstream msg;
313 msg << "Command exited abnormally: ";
314 for(size_t i=0; i < command.size(); ++i) msg << command[i] << " ";
315 camp::reportError(msg);
316 }
317 }
318 }
319 }
320 }
321
stripblanklines(const string & s)322 string stripblanklines(const string& s)
323 {
324 string S=string(s);
325 bool blank=true;
326 const char *t=S.c_str();
327 size_t len=S.length();
328
329 for(size_t i=0; i < len; i++) {
330 if(t[i] == '\n') {
331 if(blank) S[i]=' ';
332 else blank=true;
333 } else if(t[i] != '\t' && t[i] != ' ') blank=false;
334 }
335 return S;
336 }
337
338 char *startpath=NULL;
339
noPath()340 void noPath()
341 {
342 camp::reportError("Cannot get current path");
343 }
344
getPath(char * p)345 char *getPath(char *p)
346 {
347 #ifdef MAXPATHLEN
348 static size_t size = MAXPATHLEN;
349 #else
350 static size_t size = 1024;
351 #endif
352 if(!p) p=new(UseGC) char[size];
353 if(!p) noPath();
354 else while(getcwd(p,size) == NULL) {
355 if(errno == ERANGE) {
356 size *= 2;
357 p=new(UseGC) char[size];
358 } else {noPath(); p=NULL;}
359 }
360 return p;
361 }
362
setPath(const char * s,bool quiet)363 const char *setPath(const char *s, bool quiet)
364 {
365 if(startpath == NULL) startpath=getPath(startpath);
366 if(s == NULL || *s == 0) s=startpath;
367 int rc=chdir(s);
368 if(rc != 0) {
369 ostringstream buf;
370 buf << "Cannot change to directory '" << s << "'";
371 camp::reportError(buf);
372 }
373 char *p=getPath();
374 if(p && (!interact::interactive || quiet) && verbose > 1)
375 cout << "cd " << p << endl;
376 return p;
377 }
378
push_command(mem::vector<string> & a,const string & s)379 void push_command(mem::vector<string>& a, const string& s)
380 {
381 a.push_back(s);
382 #ifdef __MSDOS__
383 if(s == "cmd") {
384 a.push_back("/c");
385 a.push_back("start");
386 a.push_back("\"\"");
387 }
388 #endif
389 }
390
popupHelp()391 void popupHelp() {
392 // If the popped-up help is already running, pid stores the pid of the viewer.
393 static int pid=0;
394
395 // Status is ignored.
396 static int status=0;
397
398 // If the help viewer isn't running (or its last run has termined), launch the
399 // viewer again.
400 if (pid==0 || (waitpid(pid, &status, WNOHANG) == pid)) {
401 mem::vector<string> cmd;
402 push_command(cmd,getSetting<string>("pdfviewer"));
403 string viewerOptions=getSetting<string>("pdfviewerOptions");
404 if(!viewerOptions.empty())
405 cmd.push_back(viewerOptions);
406 cmd.push_back(docdir+dirsep+"asymptote.pdf");
407 status=System(cmd,0,false,"pdfviewer","your PDF viewer",&pid);
408 }
409 }
410
411 const char *intrange="integer argument is outside valid range";
412 const char *uintrange="integer argument is outside valid unsigned range";
413
unsignedcast(Int n)414 unsigned unsignedcast(Int n)
415 {
416 if(n < 0 || n/2 > INT_MAX)
417 vm::error(uintrange);
418 return (unsigned) n;
419 }
420
unsignedIntcast(Int n)421 unsignedInt unsignedIntcast(Int n)
422 {
423 if(n < 0)
424 vm::error(uintrange);
425 return (unsignedInt) n;
426 }
427
intcast(Int n)428 int intcast(Int n)
429 {
430 if(Abs(n) > INT_MAX)
431 vm::error(intrange);
432 return (int) n;
433 }
434
Intcast(unsignedInt n)435 Int Intcast(unsignedInt n)
436 {
437 if(n > (unsignedInt) Int_MAX)
438 vm::error(intrange);
439 return (Int) n;
440 }
441