1 /*
2     Gri - A language for scientific graphics programming
3     Copyright (C) 2008 Daniel Kelley
4 
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; version 3 of the License, or
8     (at your option) any later version.
9 
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14 
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19 
20 //#define DEBUG_RESOLVE_PATH
21 //#define DEBUG_RE
22 //#define DEBUG_UNDERSCORE
23 
24 #include <string>
25 #include <ctype.h>
26 #include <math.h>
27 #include <stdio.h>
28 #include <stdarg.h>
29 #include <limits.h>
30 #if !defined(IS_MINGW32)
31 #include <pwd.h>
32 #include <paths.h>
33 #endif
34 #include <sys/types.h>
35 
36 #if defined(MSDOS)		// need all these?
37 #include <stdlib.h>
38 #include <conio.h>
39 #include <dir.h>
40 #include <io.h>
41 #include <process.h>
42 #include <signal.h>
43 #endif
44 
45 #include "extern.hh"
46 #include "debug.hh"
47 #include "defaults.hh"
48 #include "gr.hh"
49 #include "superus.hh"
50 #include "GriTimer.hh"
51 
52 #ifdef DEBUG_RE
53 static void     show_pattern(const char *target, int tlen, int star, int plus);
54 #endif
55 extern double   strtod(const char *, char **);
56 
57 bool
get_normal_number(const char * s,double * d)58 get_normal_number(const char *s, double *d)
59 {
60 	char *ptr = NULL;
61 	*d = strtod(s, &ptr);
62 	if (*ptr == '\0') {
63 		// Normal number; check for infinity/not-a-number
64 #if defined(HAVE_ISNAN) && defined(HAVE_ISINF)
65 #if !defined(__MACHTEN__)
66 		extern double _grMissingValue;
67 		if (isinf(*d) || isnan(*d)) {
68 		        extern bool _grMissingValueUsed;
69 		        if (!_grMissingValueUsed) // turn on missing-value, if it's off
70 			        gr_set_missing_value(1.0e22);
71 			*d = (double) _grMissingValue;
72 		}
73 #endif
74 #endif
75 		return true;
76 	} else {
77 		return false;
78 	}
79 }
80 
81 // This warning business is too hard.  Just ignore it for now.
82 #if 1
83 bool
get_number_with_underscores(const char * s,double * value)84 get_number_with_underscores(const char *s, double *value)
85 {
86 	const char *si = s;
87 	static std::string ss;	// keep in storage
88 	ss.assign("");
89 	unsigned int slen = strlen(s);
90 	if (!isdigit(*si) && *si != '.' && *si != '+' && *si != '-')
91 		return false;
92 	if (*si == '.' && !isdigit(*(si + 1)))
93 		return false;
94 	for (unsigned int i = 0; i < slen; i++) {
95 		if (isdigit(*si) || *si == '.' || *si == '-' || *si == '+'
96 		    || *si == 'e' || *si == 'E' || *si == 'd' || *si == 'D') {
97 			ss += *si;
98 		} else if (*si == '_') {
99 			;
100 		} else {
101 			return false;
102 		}
103 		si++;
104 	}
105 	if (get_normal_number(ss.c_str(), value))
106 		return true;
107 	else
108 		return false;
109 }
110 #else
111 bool
get_number_with_underscores(const char * s,double * value)112 get_number_with_underscores(const char *s, double *value)
113 {
114 	const char *si = s;
115 	static std::string ss;	// keep in storage
116 	ss.assign("");
117 	unsigned int slen = strlen(s);
118 	unsigned int last_underline = 0;
119 	bool have_underline = false;
120 	bool have_decimal = false;
121 	if (!isdigit(*si) && *si != '.' && *si != '+' && *si != '-')
122 		return false;
123 	if (*si == '.' && !isdigit(*(si + 1)))
124 		return false;
125 	for (unsigned int i = 0; i < slen; i++) {
126 		if (isdigit(*si) || *si == '-' || *si == '+') {
127 			ss += *si;
128 		} else if (*si == '.') {
129 			ss += *si;
130 			if (have_underline && ((i - 1 - last_underline) != 3))
131 				warning("misplaced _ before decimal place in `\\", s, "'", "\\");
132 			last_underline = i; // pretend
133 			have_underline = false;
134 			have_decimal = true;
135 		} else if (*si == 'e' || *si == 'E' || *si == 'd' || *si == 'D') {
136 			ss += *si;
137 			if (have_underline && ((i - 1 - last_underline) != 3))
138 				warning("misplaced _ before exponent indicator in `\\", s, "'", "\\");
139 			last_underline = i;
140 			have_underline = false;	// reset
141 		} else if (*si == '_') {
142 			if (have_underline && ((i -1 - last_underline) != 3))
143 				warning("misplaced _ in numerical constant `\\", s, "'", "\\");
144 			last_underline = i;
145 			have_underline = true;
146 		} else {
147 #ifdef DEBUG_UNDERSCORE
148 			printf("%s:%d NOT A NUMBER '%s'\n", __FILE__,__LINE__,s);
149 #endif
150 			return false;
151 		}
152 		si++;
153 	}
154 #ifdef DEBUG_UNDERSCORE
155 	printf("%s:%d last_underscore %d    end at %d\n", __FILE__,__LINE__,last_underline, slen);
156 #endif
157 	if (have_underline && (slen - 1 - last_underline != 3)) {
158 		warning("misplaced _ in constant `\\", s, "'", "\\");
159 	}
160 #ifdef DEBUG_UNDERSCORE
161 	printf("%s:%d OK, translated '%s' -> '%s'\n", __FILE__,__LINE__,s,ss.c_str());
162 #endif
163 	if (get_normal_number(ss.c_str(), value)) {
164 #ifdef DEBUG_UNDERSCORE
165 		printf("%s:%d Right this is a number '%s' -> '%s' -> %e\n", __FILE__,__LINE__,s,ss.c_str(), *value);
166 #endif
167 		return true;
168 	}
169 #ifdef DEBUG_UNDERSCORE
170 	printf("%s:%d Not a number '%s' -> '%s'\n", __FILE__,__LINE__,s,ss.c_str());
171 #endif
172 	return false;
173 }
174 #endif
175 
176 bool
get_coded_value(const std::string & name,int level,std::string & result)177 get_coded_value(const std::string& name, int level, std::string& result)
178 {
179 	//printf("DEBUG %s:%d get_coded_value(<%s>,%d)\n",__FILE__,__LINE__,name.c_str(),level);
180 	int mark_above = level + 1;
181 	if (name[0] == '.') {
182 		int index = index_of_variable(name.c_str(), level);
183 		//printf("DEBUG %s:%d index %d\n",__FILE__,__LINE__,index);
184 		if (index < 0) {
185 			// No such variable known
186 			return false;
187 		}
188 		char buf[100]; // BUG: could be too short
189 		sprintf(buf, "%g", variableStack[index].get_value());
190 		result.assign(buf);
191 		return true;
192 	} else if (name[0] == '\\'){
193 		int mark = 0;
194 		int index;
195 		for (index = 0; index < int(synonymStack.size()); index++) {
196 			const char *n = synonymStack[index].get_name();
197 			if (*n == '\0')
198 				if (++mark == mark_above)
199 					break;
200 		}
201 		if (mark != mark_above) {
202 			//printf("DEBUG %s:%d no match for <%s>\n",__FILE__,__LINE__,name.c_str());
203 			return false;
204 		}
205 		//printf("DEBUG %s:%d index %d\n",__FILE__,__LINE__,index);
206 		for (int i = index - 1; i >= 0; i--) {
207 			if (synonymStack[i].get_name() == name) {
208 				//printf("DEBUG %s:%d match at i= %d\n",__FILE__,__LINE__,i);
209 				result.assign(synonymStack[i].get_value());
210 				return true;
211 			}
212 		}
213 	} else {
214 		err("Internal error in synonyms.cc; cannot decode `\\", name.c_str(), "'", "\\");
215 		return false;
216 	}
217         return true;
218 }
219 
220 bool
is_coded_string(const std::string & s,std::string & name,int * mark_level)221 is_coded_string(const std::string&s, std::string& name, int* mark_level)
222 {
223 	//printf("DEBUG %s:%d is_coded_string <%s>\n",__FILE__,__LINE__,s.c_str());
224 
225 	//for (unsigned int i = 0; i < s.size(); i++) printf("\ts[%d] = '%c'\n",i,s[i]);
226 
227 	if (s.size() < 33) { // this long even if var and level each have only 1 character
228 		return false;
229 	}
230 	char cname[100];	// BUG: may be too short
231 	int ml = 0;
232 	if (2 != sscanf(s.c_str(), AMPERSAND_CODING, cname, &ml)) {
233 		//printf("DEBUG: %s:%d problem ... cname [%s]  level %d\n",cname, ml);
234 		return false;
235 	}
236 	name.assign(cname);
237 	*mark_level = ml;
238 	//printf("DEBUG %s:%d decoded name <%s> level %d\n",__FILE__,__LINE__,name.c_str(),*mark_level);
239 	return true;
240 }
241 
242 
243 bool
marker_draw()244 marker_draw() // put a marker on top
245 {
246 	GriVariable markVar("", 0.0);
247 	variableStack.push_back(markVar);
248 	GriSynonym markSyn("", "");
249 	synonymStack.push_back(markSyn);
250 	return true;
251 }
252 int
marker_count()253 marker_count() // -1 if error
254 {
255 	int nv = 0;
256 	int vlen = variableStack.size();
257 	for (int i = vlen - 1; i >= 0; i--)
258 		if (*variableStack[i].get_name() == '\0')
259 			nv++;
260 
261 	int ns = 0;
262 	int slen = synonymStack.size();
263 	for (int ii = slen - 1; ii >= 0; ii--)
264 		if (*synonymStack[ii].get_name() == '\0')
265 			ns++;
266 	if (ns == nv)
267 		return ns;
268 	return -1;		// error
269 }
270 bool
marker_erase()271 marker_erase() // erase top marker; return false if there is none
272 {
273 	bool ok = false;
274 	int vlen = variableStack.size();
275 	for (int i = vlen - 1; i >= 0; i--) {
276 		if (*variableStack[i].get_name() == '\0') {
277 			variableStack.erase(variableStack.begin() + i);
278 			ok = true;
279 			break;
280 		}
281 	}
282 	if (ok) {
283 		ok = false;
284 		int slen = synonymStack.size();
285 		for (int i = slen - 1; i >= 0; i--) {
286 			if (*synonymStack[i].get_name() == '\0') {
287 				synonymStack.erase(synonymStack.begin() + i);
288 				ok = true;
289 				break;
290 			}
291 		}
292 	}
293 	return ok;
294 }
295 
296 // Keep this in static area, so as not to waste time with
297 // multiple allocation/deallocation for scratch strings
298 static std::string tmp_string;
299 
300 bool
get_nth_word(const std::string & s,unsigned int which,std::string & result)301 get_nth_word(const std::string& s, unsigned int which, std::string& result)
302 {
303 	char *w[MAX_nword];	// BUG: wasteful
304 	unsigned int nw;
305 	char *cpy = strdup(s.c_str());
306 	chop_into_words(cpy, w, &nw, MAX_nword);
307 	if (nw <= which)
308 		return false;
309 	result.assign(w[which]);
310 	free(cpy);
311 	return true;
312 }
313 
314 unsigned int
get_number_of_words(const std::string & s)315 get_number_of_words(const std::string& s)
316 {
317 	if (s.size() == 0)
318 		return 0;
319 	char *w[MAX_nword];	// BUG: wasteful
320 	unsigned int nw;
321 	char *cpy = strdup(s.c_str());
322 	chop_into_words(cpy, w, &nw, MAX_nword);
323 	free(cpy);
324 	return nw;
325 
326 }
327 
328 
329 bool
is_assignment_op(const char * s)330 is_assignment_op(const char *s)
331 {
332 	if (!strcmp(s, "="))
333 		return true;
334 	if (!strcmp(s, "+="))
335 		return true;
336 	if (!strcmp(s, "*="))
337 		return true;
338 	if (!strcmp(s, "-="))
339 		return true;
340 	if (!strcmp(s, "/="))
341 		return true;
342 	if (!strcmp(s, "^="))
343 		return true;
344 	if (!strcmp(s, "_="))
345 		return true;
346 	return false;
347 }
348 
349 
350 // Return true if string is all space/tab chars
351 bool
string_is_blank(const char * s)352 string_is_blank(const char *s)
353 {
354 	int len = strlen(s);
355 	for (int i = 0; i < len; i++)
356 		if (!isspace(s[i]))
357 			return false;
358 	return true;
359 }
360 
361 // Returns number column data that are missing in *any* variable
362 unsigned int
number_missing_cols()363 number_missing_cols()
364 {
365 	// Only examine other columns if they have same length
366 	unsigned int length = _colX.size();
367 	bool do_y      = _colY.size()      == length;
368 	bool do_z      = _colZ.size()      == length;
369 	bool do_U      = _colU.size()      == length;
370 	bool do_V      = _colV.size()      == length;
371 	bool do_WEIGHT = _colWEIGHT.size() == length;
372 	unsigned int missing = 0;
373 	for (unsigned int i = 0; i < length; i++) {
374 		if (             gr_missing(_colX[i]))      { missing++; continue; }
375 		if (do_y      && gr_missing(_colY[i]))	    { missing++; continue; }
376 		if (do_z      && gr_missing(_colZ[i]))	    { missing++; continue; }
377 		if (do_U      && gr_missing(_colU[i]))	    { missing++; continue; }
378 		if (do_V      && gr_missing(_colV[i]))	    { missing++; continue; }
379 		if (do_WEIGHT && gr_missing(_colWEIGHT[i])) { missing++; continue; }
380 	}
381 	return missing;
382 }
383 
384 /* Return true if string starts and ends with character `"' */
385 bool
quoted(const char * s)386 quoted(const char *s)
387 {
388 	int len = strlen(s);
389 	if (len < 2)
390 		return false;
391 	return (*s == '"' && *(s + len - 1) == '"') ? true : false;
392 }
393 
394 /* Return transformed -- see code that calls it. */
395 double
quantize(double x,int levels,double dx)396 quantize(double x, int levels, double dx)
397 {
398 	if (dx < 0.0)
399 		dx = -dx;
400 	if (dx)
401 		return (dx * levels / (1.0 + levels) * (floor(1.0 + x / dx)));
402 	else
403 		return x;
404 }
405 
406 /* Change escaped quotes in word to quotes.  Strlen changed! */
407 void
remove_esc_quotes(char * w)408 remove_esc_quotes(char *w)
409 {
410 	int             i, max = strlen(w);
411 	char            last;
412 	last = ' ';
413 	for (i = 0; i < max; i++) {
414 		if (w[i] == '\\' && w[i + 1] == '"' && last != '\\') {
415 			/* gobble rest up */
416 			int             j;
417 			for (j = i; j < max; j++) {
418 				w[j] = w[j + 1];
419 			}
420 		} else {
421 			last = w[i];
422 		}
423 	}
424 }
425 
426 void
show_words()427 show_words()
428 {
429 	if (_nword > 0) {
430 		printf("DEBUG: ");
431 		for (unsigned int i = 0; i < _nword; i++) {
432 			printf("%d\"%s\" ", i, _word[i]);
433 		}
434 		printf("\n");
435 	} else {
436 		printf("DEBUG: commandline is blank\n");
437 	}
438 
439 }
440 
441 // Is the i-th word of _word[] equal to given word? (return 0 if too few
442 // words)
443 bool
word_is(int i,const char * word)444 word_is(int i, const char *word)
445 {
446 	return ((-1 < i && i < int(_nword) && !strcmp(word, _word[i])) ? true : false);
447 }
448 
449 void
check_psfile()450 check_psfile()
451 {
452 	extern output_file_type _output_file_type;
453 	if (_output_file_type == postscript) {
454 		extern FILE *_grPS;
455 		if (ferror(_grPS)) {
456 			/* never returns */
457 			fatal_err(" IO error on PostScript output file\n");
458 		}
459 	}
460 }
461 
462 bool
delete_file(const char * filename)463 delete_file(const char *filename)
464 {
465 #if defined(HAVE_UNISTD_H)
466 	return !(unlink(filename));
467 #else  // will have to delete it 'manually'
468 	char            sys_cmd[200];
469 #if defined(VMS)
470 	sprintf(sys_cmd, "DEL %s;*", filename);
471 	call_the_OS(sys_cmd, __FILE__, __LINE__);
472 #elif defined(MSDOS)
473 	sprintf(sys_cmd, "DEL %s", filename);
474 	call_the_OS(sys_cmd, __FILE__, __LINE__);
475 #else
476 	sprintf(sys_cmd, "rm %s", filename);
477 	call_the_OS(sys_cmd, __FILE__, __LINE__);
478 #endif
479 #endif // whether have unlink
480 	return true;
481 }
482 
483 /*
484  * skip_space () -- return number of spaces at start of string. (A space may
485  * be SPC, TAB, etc.)
486  */
487 int
skip_space(const char * s)488 skip_space(const char *s)
489 {
490 	int             i = 0;
491 	while (isspace(*(s + i)) && *(s + i) != '\0')
492 		i++;
493 	return i;
494 }
495 
496 // skip_nonspace () -- return number of non-spaces at start of string. (A
497 // space may be SPC, TAB, etc.)
498 int
skip_nonspace(const char * s)499 skip_nonspace(const char *s)
500 {
501 	int             i = 0;
502 	while (!isspace(*(s + i)) && *(s + i) != '\0')
503 		i++;
504 	return i;
505 }
506 
507 // 'get env \a SHELL'
508 bool
get_envCmd()509 get_envCmd()
510 {
511 	if (_nword != 4) {
512 		NUMBER_WORDS_ERROR;
513 		demonstrate_command_usage();
514 		return false;
515 	}
516 	std::string the_syn(_word[2]);
517 	un_double_quote(the_syn);
518 	un_double_slash(the_syn);
519 	de_reference(the_syn);
520 	if (!is_syn(the_syn)) {
521 		err("No synonym name given");
522 		demonstrate_command_usage();
523 		return false;
524 	}
525 	std::string the_env_var(_word[3]);
526 	un_double_quote(the_env_var);
527 	de_reference(the_env_var);
528 	char *result = egetenv(the_env_var.c_str());
529 	if (result == NULL) {
530 		if (!put_syn(the_syn.c_str(), "", true)) {
531 			gr_Error("Ran out of storage");
532 			return false;
533 		}
534 		warning("No environment variable called `\\", the_env_var.c_str(), "' exits.", "\\");
535 	} else {
536 		if (!put_syn(the_syn.c_str(), result, true)) {
537 			gr_Error("Ran out of storage");
538 			return false;
539 		}
540 	}
541 	return true;
542 }
543 
544 
545 /* Paste a single character onto the end of a string. */
546 void
strcat_c(char * s,int c)547 strcat_c(char *s, int c)
548 {
549 	int             slen = strlen(s);
550 	s[slen] = c;
551 	s[++slen] = '\0';
552 }
553 
554 bool
is_punctuation(int c)555 is_punctuation(int c)
556 {
557 	return ((c == ' ' || c == '\t' ||
558 		 c == '!' ||
559 		 c == '@' ||
560 		 c == '#' ||
561 		 c == '$' ||
562 		 c == '%' ||
563 		 c == '^' ||
564 		 c == '&' ||
565 		 c == '*' ||
566 		 c == '(' || c == ')' ||
567 		 c == '-' ||
568 		 c == '+' || c == '=' ||
569 		 c == '~' || c == '`' ||
570 		 c == '{' || c == '[' ||
571 		 c == '}' || c == ']' ||
572 		 c == '\\' ||
573 		 c == ':' || c == ';' ||
574 		 c == ',' || c == '<' ||
575 		 c == '"' || c == '<' ||
576 		 c == '.' || c == '>' ||
577 		 c == '/' || c == '?'
578 		) ? true : false);
579 }
580 
581 bool
full_path_name(std::string & f)582 full_path_name(std::string& f)
583 {
584 	if (f[0] == '~') {
585 		if (f[1] == '/') {
586 			f.STRINGERASE(0, 1);
587 			f.insert(0, egetenv("HOME"));
588 			return true;
589 		} else {
590 #if !defined(IS_MINGW32)
591 			size_t name_end = f.find("/");
592 			if (name_end == STRING_NPOS)
593 				name_end = f.size();
594 			std::string username = f.substr(1, name_end - 1);
595 			struct passwd *pw_entry;
596 			pw_entry = getpwnam(username.c_str());
597 			f.STRINGERASE(0, username.size() + 1);
598 			f.insert(0, pw_entry->pw_dir);
599 			return true;
600 #else
601 			return false;
602 #endif
603 		}
604 	} else if (f[0] == '.') {
605 		char wd[1024], *ptr = wd; // BUG: may not be long enough
606 		ptr = getcwd(ptr, 1023);
607 		if (ptr) {
608 			f.STRINGERASE(0, 1);
609 			f.insert(0, wd);
610 			//printf("GOT DIR %s\n", wd);
611 		} else {
612 			;
613 			//printf("CANNOT get cwd\n");
614 		}
615 	}
616 	return true;
617 }
618 
619 bool
resolve_filename(std::string & f,bool trace_path,char c_or_d)620 resolve_filename(std::string& f, bool trace_path, char c_or_d)
621 {
622 	unsigned int len;
623 	if (f[0] == '"') {
624 		f.STRINGERASE(0,1);
625 		len = f.size();
626 		if (len < 2)
627 			return false;
628 		if (f[len - 1] != '"')
629 			return false;
630 		f.STRINGERASE(len-1, 1);
631 	}
632 	// Change any escaped quotes to quotes (not sure why
633 	// anybody would do this, but what the heck).
634 	unsigned int i;
635 	len = f.size();
636 	for (i = 1; i < len; i++) {
637 		if (f[i] == '"' && f[i-1] == '\\') {
638 			f.STRINGERASE(i - 1, 1);
639 			if (--len == 0)
640 				return false;
641 		}
642 	}
643 	if (f[0] == '~') {
644 		if (f[1] == '/') {
645 			f.STRINGERASE(0, 1);
646 			f.insert(0, egetenv("HOME"));
647 			return true;
648 		} else {
649 #if !defined(IS_MINGW32)
650 			size_t name_end = f.find("/");
651 			if (name_end == STRING_NPOS)
652 				name_end = f.size();
653 			std::string username = f.substr(1, name_end - 1);
654 			struct passwd *pw_entry;
655 			pw_entry = getpwnam(username.c_str());
656 			f.STRINGERASE(0, username.size() + 1);
657 			f.insert(0, pw_entry->pw_dir);
658 			return true;
659 #else
660 			return false;
661 #endif
662 		}
663 	}
664 
665 	// BUG: probably should substitute any env-vars here, e.g. $HOME
666 
667 	// Done, unless we have to trace the path ...
668 	if (!trace_path)
669 		return true;
670 
671 	// ... but can even skip that, if the pathname is complete already ...
672 	if (f[0] == '.' || f[0] == '/')
673 		return true;
674 
675 	// ... ok, now we know we should trace!
676 	std::string path;
677 	if (c_or_d == 'c') {
678 		if (!get_syn("\\.path_commands.", path)) {
679 			err("Internal error in utility.cc:resolve_filename() -- cannot determine value of \\.path_commands\n");
680 			return false;
681 		}
682 	} else if (c_or_d == 'd') {
683 		if (!get_syn("\\.path_data.", path)) {
684 			err("Internal error in utility.cc:resolve_filename() -- cannot determine value of \\.path_data\n");
685 			return false;
686 		}
687 	} else {
688 		err("Internal error in utility.cc:resolve_filename() -- c_or_d has unacceptable value\n");
689 		return false;
690 	}
691 	if (path.size() < 1)
692 		return true;	// BUG: is this what I want for empty path?
693 	// HAD: path.assign(GRIINPUTS); // in defaults.hh ".:/usr/local/lib/gri"
694 	std::string::size_type start = 0;
695 	std::string::size_type colon;
696 #ifdef DEBUG_RESOLVE_PATH
697 	printf("DEBUG(%s:%d) resolve_filename has path '%s'\n",__FILE__,__LINE__,path.c_str());
698 #endif
699 	do {
700 		colon = path.find(":", start);
701 #ifdef DEBUG_RESOLVE_PATH
702 		printf("DEBUG(%s:%d) resolve_filename top of loop colon=%d  start=%d string='%s'\n",__FILE__,__LINE__,int(colon),int(start),path.c_str()+start);
703 #endif
704 		std::string test_file = path.substr(start, colon - start);
705 #ifdef DEBUG_RESOLVE_PATH
706 		printf("DEBUG(%s:%d) resolve_filename isolated    colon=%d  start=%d string='%s'\n",__FILE__,__LINE__,int(colon),int(start),test_file.c_str());
707 #endif
708 		test_file.append("/");
709 		test_file.append(f);
710 #ifdef DEBUG_RESOLVE_PATH
711 		printf("DEBUG(%s:%d) resolve_filename trying file named '%s'\n", __FILE__,__LINE__,test_file.c_str());
712 #endif
713 		FILE *fp = fopen(test_file.c_str(), "r");
714 		if (fp != NULL) {
715 			fclose(fp);
716 			f = test_file;
717 			return true;
718 		}
719 		start = colon + 1; // skip the ':'
720 	} while (colon != STRING_NPOS);
721 
722 	// Well, we just can't find this file.  Too bad.
723 	return false;
724 }
725 
726 
727 
728 char *
pwd()729 pwd()
730 {
731 #if HAVE_GETCWD
732 	static char msg[1024];
733 	if (NULL == getcwd(msg, 1024))
734 		return (char *)"";
735 	else
736 		return msg;
737 #elif defined(VMS)		/* vms version braindead */
738 	return "";
739 #elif defined(MSDOS)		/* msdos version braindead */
740 	return "";
741 #elif !defined(HAVE_POPEN)
742 	//err("Cannot do `pwd' because computer lacks popen() subroutine");
743 	return "";
744 #else
745 	char            msg[1024];
746 	FILE           *pipefile = (FILE *) popen("pwd", "r");
747 	if (pipefile) {
748 		char *result;
749 		if (1 == fscanf(pipefile, "%s", msg)) {
750 			pclose(pipefile);
751 			result = new char[1 + strlen(msg)];
752 			if (!result) OUT_OF_MEMORY;
753 			strcpy(result, msg);
754 			return result;
755 		} else {
756 			pclose(pipefile);
757 			warning("Can't determine name of working directory; using `.' instead.");
758 			result = new char[2];
759 			if (!result) OUT_OF_MEMORY;
760 			strcpy(result, ".");
761 			return result;
762 		}
763 	} else {
764 		return ".";
765 	}
766 #endif
767 }
768 
769 char*
egetenv(const char * s)770 egetenv(const char *s)
771 {
772 #if defined(HAVE_GETENV)
773 	char *rval = (char *)"";
774 #endif
775 	if (!strcmp(s, "PWD")) {
776 		return (char *) pwd();
777 	} else if (!strcmp(s, "USER")) {
778 #if defined(HAVE_GETENV)
779 		rval = (char *)getenv(s);
780 		if (rval == NULL)
781 			return (char *)"";
782 		else
783 			return rval;
784 #else
785 		return (char *)"unknown";
786 #endif
787 	} else if (!strcmp(s, "SYSTEM")) {
788 #if defined(VMS)
789 		return (char *)"vax";
790 #elif defined(MSDOS)
791 		return (char *)"msdos";
792 #else
793 		return (char *)"unix";
794 #endif
795 	} else if (!strcmp(s, "HOST")) {
796 #if defined(HAVE_GETENV)
797 		rval = (char *)getenv(s);
798 		if (rval == NULL)
799 			return (char *)"";
800 		else
801 			return rval;
802 #else
803 		return (char *)"unknown";
804 #endif
805 	} else if (!strcmp(s, "HOME")) {
806 #if defined(HAVE_GETENV)
807 		rval = (char *)getenv(s);
808 		if (rval == NULL)
809 			return (char *)"";
810 		else
811 			return rval;
812 #else
813 		return (char *)"unknown";
814 #endif
815 	} else if (!strcmp(s, "PAGER")) {
816 #if defined(HAVE_GETENV)
817 		rval = (char *)getenv(s);
818 		if (rval == NULL)
819 			return (char *)"";
820 		else
821 			return rval;
822 #else
823 		return (char *)"unknown";
824 #endif
825 	} else if (!strcmp(s, "GRIINPUTS")) {
826 #if defined(HAVE_GETENV)
827 		rval = (char *)getenv(s);
828 		if (rval == NULL)
829 			return (char *)"";
830 		else
831 			return rval;
832 #else
833 		return "";
834 #endif
835 	} else {
836 #if defined(HAVE_GETENV)
837 		rval = (char *)getenv(s);
838 		if (rval == NULL)
839 			return (char*)"";
840 		else
841 			return rval;
842 #else
843 		return "unknown";
844 #endif
845 	}
846 }
847 
848 /* sprintfCmd() -- print things into a string */
849 #define N 20
850 bool
sprintfCmd()851 sprintfCmd()
852 {
853 	char           *fmt;
854 	char            msg[1024];
855 	double          x[N];
856 	if (_nword < 4) {
857 		err("`sprintf \\synonym \"format\"' requires variable list to process");
858 		return false;
859 	}
860 	*(_word[2] + strlen(_word[2]) - 1) = '\0';
861 	fmt = _word[2] + 1;
862 	for (unsigned int i = 3; i < _nword; i++) {
863 		getdnum(_word[i], &x[i - 3]);
864 	}
865 	switch (_nword) {
866 	case 4:
867 		sprintf(msg, fmt,
868 			x[0]);
869 		break;
870 	case 5:
871 		sprintf(msg, fmt,
872 			x[0], x[1]);
873 		break;
874 	case 6:
875 		sprintf(msg, fmt,
876 			x[0], x[1], x[2]);
877 		break;
878 	case 7:
879 		sprintf(msg, fmt,
880 			x[0], x[1], x[2], x[3]);
881 		break;
882 	case 8:
883 		sprintf(msg, fmt,
884 			x[0], x[1], x[2], x[3], x[4]);
885 		break;
886 	case 9:
887 		sprintf(msg, fmt,
888 			x[0], x[1], x[2], x[3], x[4],
889 			x[5]);
890 		break;
891 	case 10:
892 		sprintf(msg, fmt,
893 			x[0], x[1], x[2], x[3], x[4],
894 			x[5], x[6]);
895 		break;
896 	case 11:
897 		sprintf(msg, fmt,
898 			x[0], x[1], x[2], x[3], x[4],
899 			x[5], x[6], x[7]);
900 		break;
901 	case 12:
902 		sprintf(msg, fmt,
903 			x[0], x[1], x[2], x[3], x[4],
904 			x[5], x[6], x[7], x[8]);
905 		break;
906 	case 13:
907 		sprintf(msg, fmt,
908 			x[0], x[1], x[2], x[3], x[4],
909 			x[5], x[6], x[7], x[8], x[9]);
910 		break;
911 	case 14:
912 		sprintf(msg, fmt,
913 			x[0], x[1], x[2], x[3], x[4],
914 			x[5], x[6], x[7], x[8], x[9],
915 			x[10]);
916 		break;
917 	case 15:
918 		sprintf(msg, fmt,
919 			x[0], x[1], x[2], x[3], x[4],
920 			x[5], x[6], x[7], x[8], x[9],
921 			x[10], x[11]);
922 		break;
923 	case 16:
924 		sprintf(msg, fmt,
925 			x[0], x[1], x[2], x[3], x[4],
926 			x[5], x[6], x[7], x[8], x[9],
927 			x[10], x[11], x[12]);
928 		break;
929 	case 17:
930 		sprintf(msg, fmt,
931 			x[0], x[1], x[2], x[3], x[4],
932 			x[5], x[6], x[7], x[8], x[9],
933 			x[10], x[11], x[12], x[13]);
934 		break;
935 	case 18:
936 		sprintf(msg, fmt,
937 			x[0], x[1], x[2], x[3], x[4],
938 			x[5], x[6], x[7], x[8], x[9],
939 			x[10], x[11], x[12], x[13], x[14]);
940 		break;
941 	case 19:
942 		sprintf(msg, fmt,
943 			x[0], x[1], x[2], x[3], x[4],
944 			x[5], x[6], x[7], x[8], x[9],
945 			x[10], x[11], x[12], x[13], x[14],
946 			x[15]);
947 		break;
948 	case 20:
949 		sprintf(msg, fmt,
950 			x[0], x[1], x[2], x[3], x[4],
951 			x[5], x[6], x[7], x[8], x[9],
952 			x[10], x[11], x[12], x[13], x[14],
953 			x[15], x[16]);
954 		break;
955 	case 21:
956 		sprintf(msg, fmt,
957 			x[0], x[1], x[2], x[3], x[4],
958 			x[5], x[6], x[7], x[8], x[9],
959 			x[10], x[11], x[12], x[13], x[14],
960 			x[15], x[16], x[17]);
961 		break;
962 	case 22:
963 		sprintf(msg, fmt,
964 			x[0], x[1], x[2], x[3], x[4],
965 			x[5], x[6], x[7], x[8], x[9],
966 			x[10], x[11], x[12], x[13], x[14],
967 			x[15], x[16], x[17], x[18]);
968 		break;
969 	case 23:
970 		sprintf(msg, fmt,
971 			x[0], x[1], x[2], x[3], x[4],
972 			x[5], x[6], x[7], x[8], x[9],
973 			x[10], x[11], x[12], x[13], x[14],
974 			x[15], x[16], x[17], x[18], x[19]);
975 		break;
976 	default:
977 		err("Can only do `sprintf' for 1-20 variables");
978 		return false;
979 	}
980 	std::string w1(_word[1]);
981 	de_reference(w1);
982 	if (!is_syn(w1)) {
983 		demonstrate_command_usage();
984 		err("Second word of command must be a synonym-name'");
985 		return false;
986 	}
987 	if (!put_syn(w1.c_str(), msg, true)) {
988 		gr_Error("Ran out of storage");
989 		return false;
990 	}
991 	return true;
992 }
993 
994 #undef N
995 
996 /*
997  * get (double) values from a list of words (typically the words in the cmd
998  * line).  Typically this is used in getting optional parameters from
999  * commands, and _dstack[] is used for objects. RETURN number of objects
1000  * actually found
1001  */
1002 int
get_cmd_values(char ** w,int nw,const char * key,int nobjects,double * objects)1003 get_cmd_values(char ** w, int nw, const char *key, int nobjects, double *objects)
1004 {
1005 	int             istart = -1, i, found = 0, iobject;
1006 	if (nobjects > _num_dstackMAX)
1007 		return 0;
1008 	for (i = 0; i < nw; i++)
1009 		if (!strcmp(w[i], key)) {
1010 			istart = i + 1;
1011 			break;
1012 		}
1013 	if (istart == -1)
1014 		return 0;		/* 'key' not present */
1015 	if (istart + nobjects > nw)
1016 		return -1;		/* too few */
1017 	for (i = istart, iobject = 0; iobject < nobjects; i++, iobject++) {
1018 		if (true != getdnum(w[i], &objects[iobject]))
1019 			return found;
1020 		found++;
1021 	}
1022 	return found;
1023 }
1024 
1025 char
last_character(const char * s)1026 last_character(const char *s)
1027 {
1028 	return s[strlen(s) - 1];
1029 }
1030 
1031 // getinum -- return 0 if can't get number
1032 bool
getinum(const char * s,int * i)1033 getinum(const char *s, int *i)
1034 {
1035 	if (*s == '\0')
1036 		return 0;
1037 	extern double   _grMissingValue;
1038 	double          d;
1039 	char           *ptr = NULL;
1040 	d = _grMissingValue;
1041 	*i = (int) strtod(s, &ptr);
1042 	if (*ptr == '\0') {
1043 		// It was a normal number with no problems in reading.
1044 		return true;
1045 	}
1046 	// Cannot read as a normal number.  Check to see if it's a variable or
1047 	// NaN/Inf.
1048 	if (is_var(s)) {
1049 		if (get_var(s, &d)) {
1050 			*i = (int) d;
1051 			return true;
1052 		} else {
1053 			if (!skipping_through_if()) {
1054 				err("variable `\\", s, "' is not defined yet", "\\");
1055 				return false;
1056 			} else {
1057 				*i = (int) _grMissingValue;
1058 				return true;
1059 			}
1060 		}
1061 	} else if (is_syn(s)) {
1062 		std::string syn_value;
1063 		bool exists = get_syn(s, syn_value);
1064 		//printf("DEBUG %s:%d '%s' exists= %d  value [%s]\n",__FILE__,__LINE__,s,exists,syn_value.c_str());
1065 		if (exists) {
1066 			const char* vptr = syn_value.c_str();
1067 			ptr = NULL;	// reset this
1068 			*i = int(strtod(vptr, &ptr));
1069 			if (*ptr == '\0') {
1070 				return true;
1071 			} else {
1072 				return false;
1073 			}
1074 		} else {
1075 			return false;
1076 		}
1077 	} else if (!strcmp(s, "-NaN")
1078 		   || !strcmp(s, "NaN")
1079 		   || !strcmp(s, "Inf")
1080 		   || !strcmp(s, "Infinity")
1081 		   || !strcmp(s, "-Inf")
1082 		   || !strcmp(s, "-Infinity")
1083 		) {
1084 		*i = (int) (_grMissingValue);
1085 		return true;
1086 	} else {
1087 		// Maybe it's a fortran 'D' exponential
1088 		tmp_string.assign(s);
1089 		int loc;
1090 		loc=tmp_string.find('d'); if (-1!=loc) tmp_string.replace(loc,1,"e");
1091 		loc=tmp_string.find('D'); if (-1!=loc) tmp_string.replace(loc,1,"e");
1092 		*i = (int)strtod(tmp_string.c_str(), &ptr);
1093 		if (*ptr == '\0')
1094 			return true;
1095 	}
1096 	// It's a mystery.  report error, fatal or nonfatal depending on
1097 	// whether the user is interested in errors.
1098 	*i = (int) _grMissingValue;
1099 	if (!_ignore_error) {
1100 		if (isalpha(s[1])) {
1101 			err("Cannot interpret `\\",
1102 			    s,
1103 			    "' as a number.\n       Did you forget the final \".\" in a variable name?",
1104 			    "\\");
1105 		} else {
1106 			err("Cannot interpret `\\",
1107 			    s,
1108 			    "' as a number.",
1109 			    "\\");
1110 		}
1111 	}
1112 	return false;
1113 }
1114 
1115 // getdnum -- return 0 if can't get number
1116 bool
getdnum(const char * s,double * d)1117 getdnum(const char *s, double *d)
1118 {
1119 	if (*s == '\0')
1120 		return false;
1121 	if (get_normal_number(s, d)) {
1122 #ifdef DEBUG_UNDERSCORE
1123 		printf("%s:%d normal number '%s' = %e\n", __FILE__,__LINE__,s, *d);
1124 #endif
1125 		return true;
1126 	}
1127 	if (get_number_with_underscores(s, d)) {
1128 #ifdef DEBUG_UNDERSCORE
1129 		printf("%s:%d decoded underline '%s' to be %e\n", __FILE__,__LINE__,s, *d);
1130 #endif
1131 		return true;
1132 	}
1133 #ifdef DEBUG_UNDERSCORE
1134 	printf("%s:%d OK, not any kinda number '%s'\n", __FILE__,__LINE__,s);
1135 #endif
1136 	// Cannot read as a normal number.  Check to see if it's a variable or
1137 	// NaN/Inf.
1138 	extern double _grMissingValue;
1139 	if (is_var(s)) {
1140 		if (get_var(s, d)) {
1141 			return true;
1142 		} else {
1143 			if (!skipping_through_if()) {
1144 				err("variable `\\", s, "' is not defined yet", "\\");
1145 				return false;
1146 			} else {
1147 				*d = _grMissingValue;
1148 				return true;
1149 			}
1150 		}
1151 #if 1				// vsn 2.6.0 [2001-feb-18]
1152 	} else if (is_syn(s)) {
1153 		std::string syn_value;
1154 		bool exists = get_syn(s, syn_value);
1155 		//printf("DEBUG %s:%d '%s' exists= %d  value [%s]\n",__FILE__,__LINE__,s,exists,syn_value.c_str());
1156 		if (exists) {
1157 			const char* vptr = syn_value.c_str();
1158 			char *ptr;
1159 			ptr = NULL;	// reset this
1160 			*d = strtod(vptr, &ptr);
1161 			if (*ptr == '\0') {
1162 				return true;
1163 			} else {
1164 				return false;
1165 			}
1166 		} else {
1167 			return false;
1168 		}
1169 #endif
1170 	} else if (!strcmp(s, "-NaN") || !strcmp(s, "NaN") || !strcmp(s, "-Inf") || !strcmp(s, "Inf")) {
1171 		*d = _grMissingValue;
1172 		return true;
1173 	} else {
1174 		// Maybe it's a fortran 'D' exponential
1175 		tmp_string.assign(s);
1176 		int loc;
1177 		loc=tmp_string.find('d'); if (-1!=loc) tmp_string.replace(loc,1,"e");
1178 		loc=tmp_string.find('D'); if (-1!=loc) tmp_string.replace(loc,1,"e");
1179 		char *ptr;
1180 		*d = (double)strtod(tmp_string.c_str(), &ptr);
1181 		if (*ptr == '\0')
1182 			return true;
1183 	}
1184 	// It's a mystery.  report error, fatal or nonfatal depending on
1185 	// whether the user is interested in errors.
1186 	*d = (double) _grMissingValue;
1187 	if (!_ignore_error) {
1188 		if (isalpha(s[1])) {
1189 			err("Cannot interpret `\\",
1190 			    s,
1191 			    "' as a number.\n       Did you forget the final \".\" in a variable name?",
1192 			    "\\");
1193 		} else {
1194 			err("Cannot interpret `\\",
1195 			    s,
1196 			    "' as a number.",
1197 			    "\\");
1198 		}
1199 	}
1200 	return false;
1201 }
1202 
1203 /*
1204  * fatal_err () -- print error message.  If the first (string) argument ends
1205  * in '\\', then several strings follow, ended by a string consisting only of
1206  * "\\".
1207  */
1208 void
fatal_err(const char * str,...)1209 fatal_err(const char *str,...)
1210 {
1211 	char            msg[1024];
1212 	bool            several = false;
1213 	int             len;
1214 	char *            p;
1215 	va_list         ap;
1216 	if (!_error_in_cmd && _gri_beep)
1217 		gr_textput("\007");
1218 
1219 	if (str != NULL) {
1220 		va_start(ap, str);
1221 		strcpy(msg, str);
1222 		len = strlen(msg);
1223 		if (msg[len - 1] == '\\') {
1224 			msg[len - 1] = '\0';
1225 			several = true;
1226 		}
1227 		gr_textput("FATAL ERROR: ");
1228 		do {
1229 			gr_textput(msg);
1230 			if (several) {
1231 				p = va_arg(ap, char *);
1232 				strcpy(msg, p);
1233 			}
1234 		}
1235 		while (several && strcmp(msg, "\\"));
1236 		gr_textput("\n");
1237 		_error_in_cmd = true;
1238 		va_end(ap);
1239 	}
1240 	//
1241 	// Print file:line so emacs mode can locate error
1242 	//
1243 	if (superuser() & FLAG_AUT1) {
1244 		// Remove quotes around source indicator
1245 		extern char source_indicator[];
1246 		sprintf(msg, " Error at %s\n", source_indicator);
1247 	} else {
1248 		if (block_level() > 0) {
1249 			if (block_source_file() != NULL) {
1250 				if (superuser() & FLAG_AUT1)printf("DEBUG utility.cc:fatal_err(): block_source_line()=%d\n",block_source_line());
1251 				sprintf(msg,
1252 					" Error at %s:%d\n",
1253 					block_source_file(),
1254 					block_source_line() - 1);
1255 				gr_textput(msg);
1256 			}
1257 		} else {
1258 			if (what_file() != NULL) {
1259 				sprintf(msg,
1260 					" Error at %s:%d\n",
1261 					what_file(),
1262 					what_line());
1263 				gr_textput(msg);
1264 			}
1265 		}
1266 	}
1267 	if (_error_action == 1)
1268 		gri_abort();
1269 	else
1270 		gri_exit(1);
1271 }
1272 
1273 const char *
what_file()1274 what_file()
1275 {
1276 	if (_cmdFILE.size() == 0)
1277 		return NULL;
1278 	if (block_level() > 0) {
1279 		if (_cmd_being_done < 1) {
1280 			return NULL;
1281 		} else {
1282 			return _command[_cmd_being_done_code[_cmd_being_done - 1]].filename;
1283 		}
1284 	} else {
1285 		return _cmdFILE.back().get_name();
1286 	}
1287 }
1288 
1289 int
what_line()1290 what_line()
1291 {
1292 	if (block_level() > 0) {
1293 		if (_cmd_being_done < 1) {
1294 			return -1;		/* note: what_file() will give NULL */
1295 		} else {
1296 			return
1297 				_command[_cmd_being_done_code[_cmd_being_done - 1]].fileline
1298 				+ block_offset_line() - 1;
1299 		}
1300 	} else {
1301 		return _cmdFILE.back().get_line();
1302 	}
1303 }
1304 
1305 /*
1306  * err () -- print error message.  If the first (string) argument ends in
1307  * '\\', then several strings follow, ended by a string consisting only of
1308  * "\\".
1309  */
1310 void
err(const char * str,...)1311 err(const char *str,...)
1312 {
1313 	char            msg[1024];
1314 	bool            several = false;
1315 	int             len;
1316 	char *            p;
1317 	va_list         ap;
1318 	if (!_error_in_cmd && _gri_beep)
1319 		gr_textput("\007");
1320 	if (str != NULL) {
1321 		va_start(ap, str);
1322 		strcpy(msg, str);
1323 		len = strlen(msg);
1324 		if (msg[len - 1] == '\\') {
1325 			msg[len - 1] = '\0';
1326 			several = true;
1327 		}
1328 		gr_textput("ERROR: ");
1329 		do {
1330 			gr_textput(msg);
1331 			if (several) {
1332 				p = va_arg(ap, char *);
1333 				strcpy(msg, p);
1334 			}
1335 		}
1336 		while (several && strcmp(msg, "\\"));
1337 		gr_textput("\n");
1338 	}
1339 	_error_in_cmd = true;
1340 	va_end(ap);
1341 }
1342 
1343 // warning () -- print warning message.  All arguments must be char*.
1344 // If the first argument ends in '\\', then several strings follow,
1345 // ended by a string consisting only of "\\".  If only string is '\\',
1346 // then this is a request to notify of number of repeats of last msg.
1347 void
warning(const char * s,...)1348 warning(const char *s,...)
1349 {
1350 	static          unsigned int msg_last_copies = 0;
1351 	static std::string   msg_last;	// must be static
1352 	static std::string msg;		// static only for efficiency if called a lot
1353 	bool            several = false;
1354 	int             len;
1355 	char           *p = NULL;
1356 	va_list         ap;
1357 	if (!_error_in_cmd && _gri_beep)
1358 		gr_textput("\007");
1359 	// Check for final wrap-up command
1360 	if (!strcmp(s, "\\\\")) {
1361 		if (msg_last_copies > 0) {
1362 			char buffer[100];
1363 			sprintf(buffer,
1364 				"         ... this warning was repeated %d times.\n",
1365 				++msg_last_copies);
1366 			gr_textput(buffer);
1367 		}
1368 		return;
1369 	}
1370 	if (s != NULL) {
1371 		va_start(ap, s);
1372 		len = strlen(s);
1373 		msg = s;
1374 		if (msg[len - 1] == '\\') {
1375 			msg.STRINGERASE(len - 1, 1);
1376 			several = true;
1377 		}
1378 		do {
1379 			if (several) {
1380 				p = va_arg(ap, char *);
1381 				if (strcmp(p, "\\"))
1382 					msg += p;
1383 			}
1384 		} while (several && strcmp(p, "\\"));
1385 		if (msg == msg_last) {
1386 			// Same as last msg.  Collect till a new one.
1387 			msg_last_copies++;
1388 		} else {
1389 			// Not same as last msg.
1390 			if (msg_last_copies > 0) {
1391 				// This msg repeated
1392 				char buffer[100];
1393 				sprintf(buffer,
1394 					"Warning: (The last warning was repeated %d times.)\n",
1395 					msg_last_copies + 1);
1396 				gr_textput(buffer);
1397 				// Now show current msg
1398 				gr_textput("Warning: ");
1399 				gr_textput(msg.c_str());
1400 				gr_textput("\n");
1401 			} else {
1402 				// First time for this msg
1403 				gr_textput("Warning: ");
1404 				gr_textput(msg.c_str());
1405 				gr_textput("\n");
1406 			}
1407 			msg_last_copies = 0;
1408 		}
1409 		msg_last = msg;
1410 	} va_end(ap);
1411 }
1412 
1413 /*
1414  * ExtractQuote() -- extract quote `sout' from string `s'. NOTE: You must
1415  * ensure sout is as long as s. RETURN VALUE: character position at end of
1416  * extracted quote, if all was OK.  Otherwise 0.
1417  *
1418  * Accepts both quoted strings '... "hi" ...' and unquoted strings '... \"hi\" ...'
1419  * but not combinations.
1420  */
1421 // RETURN VALUE:
1422 //      0  if no quoted thing found
1423 //     -1  if missing final-quote
1424 //     >0  OK; value 'i' is such that s[i] is just after the final quote
1425 int
ExtractQuote(const char * s,std::string & sout)1426 ExtractQuote(const char *s, std::string& sout)
1427 {
1428 	//printf("DEBUG %s:%d input string is '%s'\n",__FILE__,__LINE__,s);
1429 	int i = 0;
1430 	bool slash_quoted = false;
1431 	// Skip along to first quote ...
1432 	while (s[i] != '"') {
1433 		if (s[i] == '\0')
1434 			return 0; // never found any quoted items
1435 		i++;
1436 	}
1437 	if (i > 0 && s[i - 1] == '\\')
1438 		slash_quoted = true;
1439 
1440 	i++;			// skip the quote
1441 	// ... then copy along until find first un-escaped quote ...
1442 	while (s[i] != '\0') {
1443 		if (s[i] == '"') {
1444 			if (i > 0 && s[i - 1] == '\\') {
1445 				if (!slash_quoted) {
1446 					sout += '"';
1447 				} else {
1448 					sout.STRINGERASE(sout.size() - 1, 1); // trim it
1449 					//printf("DEBUG %s:%d RETURNING-A %d.  Extracted quote length %d as \n<%s>\n", __FILE__,__LINE__,i+1,sout.size(),sout.c_str());
1450 					return i + 1;
1451 				}
1452 			} else {
1453 				//printf("DEBUG %s:%d RETURNING-B %d\n<%s>\n\n",__FILE__,__LINE__,i+1,sout.c_str());
1454 				return i + 1;
1455 			}
1456 		} else {
1457 			sout += s[i];
1458 		}
1459 		i++;
1460 	}
1461 	if (s[i] == '\0') {
1462 		//printf("DEBUG %s:%d RETURNING-C -1.  Extracted quote length %d as \n<%s>\n", __FILE__,__LINE__,sout.size(),sout.c_str());
1463 		return -1;
1464 	}
1465 
1466 	// ... and return an index so the parser can do more
1467 	// work on 's' past the quote
1468 	//printf("DEBUG %s:%d RETURNING %d.  Extracted quote length %d as \n<%s>\n", __FILE__,__LINE__,i,sout.size(),sout.c_str());
1469 	return i;
1470 }
1471 
1472 // Make all trailing blanks, tabs, etc, into null chars
1473 void
remove_trailing_blanks(char * s)1474 remove_trailing_blanks(char *s)
1475 {
1476 	int             i = strlen(s);
1477 	while (--i > -1)
1478 		if (isspace(s[i]))
1479 			s[i] = '\0';
1480 		else
1481 			break;
1482 }
1483 // Make all trailing blanks, tabs, etc, into null chars
1484 void
remove_trailing_blanks(std::string & s)1485 remove_trailing_blanks(std::string& s)
1486 {
1487 	int i = s.length();
1488 	while (--i > -1)
1489 		if (!isspace(s[i]))
1490 			break;
1491 	//printf("BEFORE removing trailing blanks '%s' ... ", s.c_str());
1492 	s.STRINGERASE(i + 1);
1493 	//printf("AFTER '%s' ... ", s.c_str());
1494 }
1495 
1496 void
beep_terminal()1497 beep_terminal()
1498 {
1499 	if (_gri_beep)
1500 		fprintf(stderr, "\007");
1501 }
1502 
1503 /* matrix_limits() - find min/max of matrix */
1504 void
matrix_limits(double * min,double * max)1505 matrix_limits(double *min, double *max)
1506 {
1507 	bool            first = true;
1508 	double          f;
1509 	*min = *max = 0.0;
1510 	for (unsigned int c = 0; c < _num_xmatrix_data; c++) {
1511 		for (unsigned int r = 0; r < _num_ymatrix_data; r++) {
1512 			if (_legit_xy(c, r) == true) {
1513 				f = _f_xy(c, r);
1514 				if (first == true) {
1515 					*min = *max = f;
1516 					first = false;
1517 				}
1518 				if (f < *min)
1519 					*min = f;
1520 				if (*max < f)
1521 					*max = f;
1522 			}
1523 		}
1524 	}
1525 	if (first == true) {
1526 		*min = gr_currentmissingvalue();
1527 		*max = gr_currentmissingvalue();
1528 	}
1529 }
1530 
1531 /*
1532  * inside_box - see if data point inside clip box DESCRIPTION Returns 1 if
1533  * either: (1) clipping turned off or	(2) clipping on and inside axes or
1534  * (3) clipping on and inside (xl,xr)(yb,yt) box
1535  */
1536 bool
inside_box(double x,double y)1537 inside_box(double x, double y)
1538 {
1539 	extern char     _grTempString[];
1540 	extern gr_axis_properties _grTransform_x, _grTransform_y;
1541 	/* If logarithmic, ensure that positive value */
1542 	if (_grTransform_x == gr_axis_LOG && x <= 0.0)
1543 		return false;
1544 	if (_grTransform_y == gr_axis_LOG && y <= 0.0)
1545 		return false;
1546 	if (_clipData == 0) {	/* user did `set clip off' */
1547 		return true;
1548 	} else if (_clipData == 1) {/* user did `set clip on xl xr yb yt' */
1549 		if (!BETWEEN(_clipxleft, _clipxright, x)) {
1550 			if (_debugFlag & DEBUG_CLIPPED) {
1551 				sprintf(_grTempString, "Clipping (%g, %g)\n", x, y);
1552 				gr_textput(_grTempString);
1553 			}
1554 			return false;
1555 		}
1556 		if (!BETWEEN(_clipybottom, _clipytop, y)) {
1557 			if (_debugFlag & 0x01) {
1558 				sprintf(_grTempString, "Clipping (%g, %g)\n", x, y);
1559 				gr_textput(_grTempString);
1560 			}
1561 			return false;
1562 		}
1563 		return true;
1564 	} else {			/* user did `set clip on' */
1565 		if (!BETWEEN(_xleft, _xright, x)) {
1566 			if (_debugFlag & 0x01) {
1567 				sprintf(_grTempString, "Clipping (%g, %g)\n", x, y);
1568 				gr_textput(_grTempString);
1569 			}
1570 			return false;
1571 		}
1572 		if (!BETWEEN(_ybottom, _ytop, y)) {
1573 			if (_debugFlag & 0x01) {
1574 				sprintf(_grTempString, "Clipping (%g, %g)\n", x, y);
1575 				gr_textput(_grTempString);
1576 			}
1577 			return false;
1578 		}
1579 		return true;
1580 	}
1581 }
1582 
1583 bool
grid_exists()1584 grid_exists()
1585 {
1586 	Require(_xgrid_exists,
1587 		err("First `set x grid' or `read grid x'"));
1588 	Require(_ygrid_exists,
1589 		err("First `set y grid' or `read grid y'"));
1590 	Require(_grid_exists,
1591 		err("First `read grid data' or `convert columns to grid'"));
1592 	return true;
1593 }
1594 
1595 bool
scales_defined()1596 scales_defined()
1597 {
1598 	return ((_xscale_exists && _yscale_exists) ? true : false);
1599 }
1600 
1601 // Set environment for line drawing.
1602 bool
set_environment()1603 set_environment()
1604 {
1605 	// Update some things possibly ruined by other routines. BUG: I don't
1606 	// think most of this is required; Macintosh remants
1607 	gr_fontID       old_font = gr_currentfont();
1608 	double          fontsize = FONTSIZE_PT_DEFAULT;
1609 	double          linewidth = LINEWIDTH_DEFAULT;
1610 	double          symbolsize = SYMBOLSIZE_DEFAULT;
1611 	double          tic_direction = 0.0; // out
1612 	double          tic_size = 0.2;
1613 	double          xmargin = XMARGIN_DEFAULT;
1614 	double          ymargin = YMARGIN_DEFAULT;
1615 	double          xsize = XSIZE_DEFAULT;
1616 	double          ysize = YSIZE_DEFAULT;
1617 	if (!get_var("..fontsize..", &fontsize))
1618 		warning("(set_environment), ..fontsize.. undefined so using 12");
1619 	gr_setfontsize_pt(fontsize);
1620 	gr_setfont(old_font);	// weird, since already set
1621 	if (!get_var("..linewidth..", &linewidth))
1622 		warning("(set_environment), ..linewidth.. undefined so using default");
1623 	_griState.set_linewidth_line(linewidth);
1624 
1625 	if (!get_var("..linewidthsymbol..", &linewidth))
1626 		warning("(set_environment), ..linewidthsymbol.. undefined so using default");
1627 	_griState.set_linewidth_symbol(linewidth);
1628 
1629 	if (!get_var("..symbolsize..", &symbolsize))
1630 		warning("(set_environment) ..symbolsize.. undefined so using 5.0");
1631 	gr_setsymbolsize_cm(symbolsize);
1632 	if (!get_var("..tic_direction..", &tic_direction))
1633 		warning("(set_environment) ..tic_direction.. undefined so using OUT");
1634 	gr_setticdirection(int(floor(0.5 + tic_direction)) ? true : false);
1635 	if (!get_var("..tic_size..", &tic_size)) {
1636 		warning("(set_environment) ..tic_size.. undefined so using default (0.2cm)");
1637 		gr_setticsize_cm((double) TICSIZE_DEFAULT);
1638 	} else
1639 		gr_setticsize_cm(tic_size);
1640 	if (!get_var("..xmargin..", &xmargin))
1641 		warning("(set_environment) ..xmargin.. undefined so using default");
1642 	if (!get_var("..ymargin..", &ymargin))
1643 		warning("(set_environment) ..ymargin.. undefined so using default");
1644 	if (!get_var("..xsize..", &xsize))
1645 		warning("(set_environment) ..xsize.. undefined so using default");
1646 	if (!get_var("..ysize..", &ysize))
1647 		warning("(set_environment) ..ysize.. undefined so using default");
1648 	gr_setxtransform(_xtype);
1649 	gr_setxscale(xmargin, xmargin + xsize, _xleft, _xright);
1650 	gr_setytransform(_ytype);
1651 	gr_setyscale(ymargin, ymargin + ysize, _ybottom, _ytop);
1652 	gr_record_scale();
1653 	return true;
1654 }
1655 
1656 bool
draw_axes_if_needed()1657 draw_axes_if_needed()
1658 {
1659 	if (_need_x_axis && _need_y_axis)
1660 		draw_axes(_axesStyle, 0.0, (gr_axis_properties) gr_axis_LEFT, true);
1661 	return true;
1662 }
1663 
1664 bool
batch()1665 batch()
1666 {
1667 	double          batch;
1668 	get_var("..batch..", &batch);
1669 	if (batch)
1670 		return true;
1671 	else
1672 		return false;
1673 }
1674 
1675 unsigned int
superuser()1676 superuser()
1677 {
1678 	return _griState.superuser();
1679 }
1680 
1681 // display file on terminal
1682 void
more_file_to_terminal(const char * filename)1683 more_file_to_terminal(const char *filename)
1684 {
1685 	char            sys_cmd[100];
1686 #if defined(VMS)		// vax-vms machine
1687 	sprintf(sys_cmd, "TYPE %s/PAGE", filename);
1688 #elif defined(MSDOS)		// ibm-style msdos machine
1689 	GriString fn(filename);
1690 	fn.convert_slash_to_MSDOS();
1691 	sprintf(sys_cmd, "COMMAND.COM MORE < %s", fn.getValue());
1692 #else				// a neutral machine
1693 	char *egetenv(const char *);
1694 	char *pager = egetenv("PAGER");
1695 	if (*pager != '\0')
1696 		sprintf(sys_cmd, "%s %s", pager, filename);
1697 	else
1698 		sprintf(sys_cmd, "more %s", filename);
1699 #endif
1700 	if (((unsigned) superuser()) & FLAG_SYS) {
1701 		ShowStr("\nSending the following command to the operating system:\n");
1702 		ShowStr(sys_cmd);
1703 		ShowStr("\n");
1704 	}
1705 	call_the_OS(sys_cmd, __FILE__, __LINE__);
1706 }
1707 
1708 bool
demonstrate_command_usage()1709 demonstrate_command_usage()
1710 {
1711 	ShowStr("PROPER USAGE: `");
1712 	if (cmd_being_done() > -1) {
1713 		ShowStr(_command[cmd_being_done()].syntax);
1714 		ShowStr("'\n");
1715 	} else {
1716 		ShowStr(" unknown.\n");
1717 	}
1718 	return true;
1719 }
1720 
1721 // Regular expression search. This is limited; presently can match:
1722 
1723 // (1) characters; (2) alternative characters given in square brackets; (3)
1724 // multiple characters followed by '*' or by '+'; (4) the '\s' metacharacter
1725 // (for whitespace, either SPACE, TAB or NEWLINE).
1726 
1727 // Notes: (1) notation is standard for regular expressions, in the 'perl' style;
1728 // (2) '^' is not supported (implied anyway); (3) '+' not supported, but easy
1729 // if I ever need it; (4) () not supported.
1730 bool
re_compare(const char * s,const char * pattern)1731 re_compare(const char *s, const char *pattern)
1732 {
1733 	bool find_target(const char *pattern, int *pindex, int plen, std::string& target, int *star, int *plus);
1734 	int             slen = strlen(s);
1735 	int             plen = strlen(pattern);
1736 	int             sindex = 0;
1737 	int             pindex = 0;
1738 	int             star;	// is subpattern followed by '*'?
1739 	int             plus;	// is subpattern followed by '+'?
1740 	bool            need_new_target = true;
1741 	int             matches = 0;
1742 	// Search through pattern
1743 	std::string target("");
1744 	while (sindex < slen) {
1745 		bool            this_matches = false;
1746 		if (need_new_target) {
1747 			if (!find_target(pattern, &pindex, plen, target, &star, &plus)) {
1748 #ifdef DEBUG_RE
1749 				printf("ran out of pattern\n");
1750 #endif
1751 				return false;
1752 			}
1753 			matches = 0;
1754 		}
1755 		// Now see if source string matches
1756 #ifdef DEBUG_RE
1757 		printf("source[%d] = `%c'  ", sindex, s[sindex]);
1758 		show_pattern(target.c_str(), target.size(), star, plus);
1759 #endif
1760 		for (unsigned int tindex = 0; tindex < target.size(); tindex++) {
1761 			if (s[sindex] == target[tindex] || target[tindex] == '.') {
1762 				this_matches = true;
1763 				matches++;
1764 				break;
1765 			}
1766 		}
1767 		// See if match
1768 		if (this_matches) {
1769 			if (star || plus) {
1770 				need_new_target = false;
1771 			}
1772 		} else {
1773 			// Not match.  See if it was a repeat pattern ('*' or '+')
1774 			if (star) {
1775 				need_new_target = true;
1776 				sindex--;	// try again on this one
1777 			} else if (plus) {
1778 				if (matches < 1) {
1779 					return false;
1780 				}
1781 				need_new_target = true;
1782 				sindex--;	// try again on this one
1783 			} else {
1784 				return false;	// Failed match
1785 			}
1786 		}
1787 		sindex++;
1788 	}				// while (sindex < slen)
1789 	// Have matched all the way through the source string.  So we have a
1790 	// complete match if and only if the pattern string is now exhausted.
1791 	if (pindex == plen) {
1792 		return true;
1793 	}
1794 	// Some pattern left.  See if all remaining targets are '*' types; then
1795 	// have a match.
1796 	while (find_target(pattern, &pindex, plen, target, &star, &plus)) {
1797 		if (!star) {
1798 #ifdef DEBUG_RE
1799 			printf(" ... still some non-* target left: `%s'\n", target.c_str());
1800 #endif
1801 			return false;
1802 		}
1803 	}
1804 	// All remaining patterns (if any) were * type, so don't need to match
1805 	return true;
1806 }
1807 
1808 bool
find_target(const char * pattern,int * pindex,int plen,std::string & target,int * star,int * plus)1809 find_target(const char *pattern, int *pindex, int plen, std::string& target, int *star, int *plus)
1810 {
1811 	target = "";
1812 	/*
1813 	 * Determine present target, leaving *pindex pointing at next part of
1814 	 * pattern.
1815 	 */
1816 	*star = *plus = 0;
1817 	if (*pindex >= plen)
1818 		return false;
1819 	switch (pattern[*pindex]) {
1820 	case '\\':
1821 		/*
1822 		 * Check against list of known escapes
1823 		 */
1824 		switch (pattern[*pindex + 1]) {
1825 		case '\\':
1826 			target += '\\';
1827 			(*pindex)++;	/* the backslash */
1828 			(*pindex)++;	/* the '\\' */
1829 			break;
1830 		case 's':		/* whitespace as in perl */
1831 			target += ' ';
1832 			target += '\t';
1833 			//target += '\r';
1834 			target += '\n';
1835 			(*pindex)++;	/* the backslash */
1836 			(*pindex)++;	/* the 's' */
1837 			break;
1838 		default:
1839 			printf("unknown escape pattern in search string");
1840 			return false;
1841 		}
1842 		break;
1843 	case '[':
1844 		/*
1845 		 * List of alternatives.
1846 		 */
1847 		(*pindex)++;		/* skip the '[' */
1848 		while (pattern[*pindex] != ']' && pattern[*pindex] != '\0')
1849 			target += pattern[(*pindex)++];
1850 		(*pindex)++;		/* skip the ']' */
1851 		break;
1852 	default:
1853 		/*
1854 		 * A single character.
1855 		 */
1856 		target += pattern[(*pindex)++];
1857 	}
1858 	if (pattern[*pindex] == '*') {
1859 		*star = 1;
1860 		(*pindex)++;		/* skip the '*' */
1861 	} else if (pattern[*pindex] == '+') {
1862 		*plus = 1;
1863 		(*pindex)++;		/* skip the '+' */
1864 	}
1865 	return true;
1866 }
1867 
1868 #ifdef DEBUG_RE
1869 static void
show_pattern(const char * target,int tlen,int star,int plus)1870 show_pattern(const char *target, int tlen, int star, int plus)
1871 {
1872 	int             i;
1873 	printf("target: ");
1874 	for (i = 0; i < tlen; i++) {
1875 		printf(" `%c'", target[i]);
1876 	}
1877 	if (star)
1878 		printf("*");
1879 	else if (plus)
1880 		printf("+");
1881 	printf("\n");
1882 }
1883 #endif
1884 
1885 void
swap(double & a,double & b)1886 swap(double& a, double& b)
1887 {
1888 	double tmp = a;
1889 	a = b;
1890 	b = tmp;
1891 }
1892 
1893 // Indicate if this command will take a long time. Usage example:
1894 // GriTimer t;
1895 // for (i = 0; i < n; i++) {
1896 //     if (!warned)
1897 //         double frac = double(i) / double(n - 1);
1898 //         warned = warn_if_slow(&t, frac, "draw something");
1899 //     ... possibly slow code
1900 // }
1901 bool
warn_if_slow(GriTimer * t,double fraction_done,const char * cmd)1902 warn_if_slow(GriTimer *t, double fraction_done, const char *cmd)
1903 {
1904 	const double calibrate = 10.0; // wait this long to calibrate speed
1905 	double dt = t->elapsed_time();
1906 	extern char _grTempString[];
1907 	if (_chatty < 1)
1908 		return true;		// user does not want this
1909 	if (fraction_done == 0.0)
1910 		return false;
1911 	if (dt > calibrate) {
1912 		dt = dt / fraction_done;
1913 		if (dt > 3600.0) {
1914 			sprintf(_grTempString, "\n`%s':\n  This operation will take %.1g h; it is now %s",
1915 				cmd,
1916 				dt / 3600.0,
1917 				t->now_ascii());
1918 			ShowStr(_grTempString);
1919 		} else if (dt > 60.0) {
1920 			sprintf(_grTempString, "\n`%s':\n  This operation will take %.1g min; it is now %s",
1921 				cmd,
1922 				dt / 60.0,
1923 				t->now_ascii());
1924 			ShowStr(_grTempString);
1925 		} else if (dt > 15.0) {
1926 			sprintf(_grTempString, "\n`%s':\n  This operation will take %.0g sec; it is now %s",
1927 				cmd,
1928 				dt,
1929 				t->now_ascii());
1930 			ShowStr(_grTempString);
1931 		}
1932 		return true;
1933 	}
1934 	return false;
1935 }
1936 
1937 // Returns portion of filename after last '/' character, if
1938 // there is one, otherwise returns full filename.
1939 const char
filename_sans_dir(const char * fullfilename)1940 *filename_sans_dir(const char *fullfilename)
1941 {
1942 	int i, len = strlen(fullfilename);
1943 	for (i = len - 1; i > -1; i--) {
1944 		if (fullfilename[i] == '/') {
1945 			return fullfilename + i + 1;
1946 		}
1947 	}
1948 	return fullfilename;
1949 }
1950 
1951 void
bounding_box_display(const char * msg)1952 bounding_box_display(const char *msg)
1953 {
1954 	printf("%s\n", msg);
1955 	printf("bbox: (%f %f) (%f %f) cm\n",
1956 	       _bounding_box.llx(),_bounding_box.lly(),
1957 	       _bounding_box.urx(),_bounding_box.ury());
1958 }
1959 
1960 // Update bounding box (stored in cm on page)
1961 void
bounding_box_update(const rectangle & box)1962 bounding_box_update(const rectangle& box)
1963 {
1964 	// Only process if supplied bbox is nonzero in size
1965 #if 0
1966         printf("updating bounding box %f < x < %f     %f < y < %f\n", box.llx(), box.urx(), box.lly(), box.ury());
1967 #endif
1968 	if (box.llx()    != box.urx()
1969 	    || box.lly() != box.ury()) {
1970 		// If have existing bbox, see if this lies outside ...
1971 		if (_bounding_box.llx()    != _bounding_box.urx()
1972 		    || _bounding_box.lly() != _bounding_box.ury()) {
1973 			if (box.llx() < _bounding_box.llx())
1974 				_bounding_box.set_llx(box.llx());
1975 			if (box.lly() < _bounding_box.lly())
1976 				_bounding_box.set_lly(box.lly());
1977 			if (_bounding_box.urx() < box.urx())
1978 				_bounding_box.set_urx(box.urx());
1979 			if (_bounding_box.ury() < box.ury())
1980 				_bounding_box.set_ury(box.ury());
1981 		} else {		// ... else just copy it
1982 			_bounding_box.set_llx(box.llx());
1983 			_bounding_box.set_lly(box.lly());
1984 			_bounding_box.set_urx(box.urx());
1985 			_bounding_box.set_ury(box.ury());
1986 		}
1987 	}
1988 }
1989 
1990 double
vector_min(double * v,unsigned n)1991 vector_min(double *v, unsigned n)
1992 {
1993 	double return_value = v[0];
1994 	for (unsigned i = 1; i < n; i++)
1995 		if (v[i] < return_value)
1996 			return_value = v[i];
1997 	return return_value;
1998 }
1999 
2000 double
vector_max(double * v,unsigned n)2001 vector_max(double *v, unsigned n)
2002 {
2003 	double return_value = v[0];
2004 	for (unsigned i = 1; i < n; i++)
2005 		if (return_value < v[i])
2006 			return_value = v[i];
2007 	return return_value;
2008 }
2009 
2010 void
set_ps_color(char what)2011 set_ps_color(char what)		// what='p' for path or 't' for text
2012 {
2013 	extern output_file_type _output_file_type;
2014 	if (_output_file_type == postscript) {
2015 		extern FILE *_grPS;
2016 		extern bool _grWritePS;
2017 		if (!_grWritePS)
2018 			return;
2019 		double r, g, b;
2020 		if (what == 'p')
2021 			_griState.color_line().getRGB(&r, &g, &b);
2022 		else
2023 			_griState.color_text().getRGB(&r, &g, &b);
2024 		if (r == g && g == b) {
2025 			fprintf(_grPS, "%.3g g\n", r);
2026 			fprintf(_grPS, "%.3g G\n", r);
2027 		} else {
2028 			fprintf(_grPS, "%.3g %.3g %.3g rg\n", r, g, b);
2029 			fprintf(_grPS, "%.3g %.3g %.3g RG\n", r, g, b);
2030 		}
2031 	}
2032 }
2033 
2034 void
gri_abort()2035 gri_abort()
2036 {
2037 	close_data_files();
2038 	abort();
2039 }
2040 
2041 void
gri_exit(int code)2042 gri_exit(int code)
2043 {
2044 	close_data_files();
2045 	exit(code);
2046 }
2047 
2048 // Determine a 1-2-5 scaling for interval xl<x<xr, trying
2049 // to get n increments each of which is a multiple of 1, 2, or 5.
2050 // The results are xlr, ylr = the new range, which includes
2051 // the old range as a subset, and nr = the number of increments.
2052 void
gr_scale125(double xl,double xr,int n,double * xlr,double * xrr,int * nr)2053 gr_scale125(double xl, double xr, int n, double *xlr, double *xrr, int *nr)
2054 {
2055 	const int max_tries = 5;
2056 	int n_orig = n;
2057 	int tries = 0;
2058 	do {
2059 		double delta = fabs((xr - xl) / n);
2060 		if (delta == 0.0) {
2061 			*xlr = xl - 1.0;
2062 			*xrr = xr + 1.0;
2063 			*nr = 1;
2064 			return;
2065 		}
2066 		double order_of_magnitude = pow(10.0, floor(log10(delta)));
2067 		double delta125 = delta / order_of_magnitude;
2068 		if (delta125 < 2.0)
2069 			delta125 = 1.0;
2070 		else if (delta125 < 5.0)
2071 			delta125 = 2.0;
2072 		else
2073 			delta125 = 5.0;
2074 		delta125 *= order_of_magnitude;
2075 		if (xr > xl) {
2076 			*xlr = delta125 * (floor(xl / delta125));
2077 			*xrr = delta125 * (ceil(xr / delta125));
2078 			*nr = (int) floor(0.5 + (*xrr - *xlr) / delta125);
2079 		} else {
2080 			*xlr = delta125 * (ceil(xl / delta125));
2081 			*xrr = delta125 * (floor(xr / delta125));
2082 			*nr = (int) floor(0.5 - (*xrr - *xlr) / delta125);
2083 		}
2084 		n = int(0.8 * n);
2085 		if (n < 1)
2086 			n = 1;		// need at least 1 segment
2087 	} while ((*nr > int(1.75 * n_orig)) && ++tries < max_tries);
2088 }
2089 
2090 double
rho(double S,double T,double p)2091 rho(double S /* PSU */, double T /* in-situ degC */, double p /* dbar */)
2092 {
2093 	double          rho_w, Kw, Aw, Bw, p1, S12, ro, xkst;
2094 	rho_w = 999.842594 +
2095 		T * (6.793952e-2 +
2096 		     T * (-9.095290e-3 +
2097 			  T * (1.001685e-4 +
2098 			       T * (-1.120083e-6 + T * 6.536332e-9))));
2099 	Kw = 19652.21
2100 		+ T * (148.4206 +
2101 		       T * (-2.327105 +
2102 			    T * (1.360477e-2 - T * 5.155288e-5)));
2103 	Aw = 3.239908 +
2104 		T * (1.43713e-3 +
2105 		     T * (1.16092e-4 -
2106 			  T * 5.77905e-7));
2107 	Bw = 8.50935e-5 +
2108 		T * (-6.12293e-6 +
2109 		     T * 5.2787e-8);
2110 	p1 = 0.1 * p;
2111 	S12 = sqrt(S);
2112 	ro = rho_w +
2113 		S * (8.24493e-1 +
2114 		     T * (-4.0899e-3 +
2115 			  T * (7.6438e-5 +
2116 			       T * (-8.2467e-7 + T * 5.3875e-9))) +
2117 		     S12 * (-5.72466e-3 +
2118 			    T * (1.0227e-4 -
2119 				 T * 1.6546e-6) +
2120 			    S12 * 4.8314e-4));
2121 	xkst = Kw +
2122 		S * (54.6746 +
2123 		     T * (-0.603459 +
2124 			  T * (1.09987e-2 -
2125 			       T * 6.1670e-5)) +
2126 		     S12 * (7.944e-2 +
2127 			    T * (1.6483e-2 +
2128 				 T * (-5.3009e-4)))) +
2129 		p1 * (Aw +
2130 		      S * (2.2838e-3 +
2131 			   T * (-1.0981e-5 +
2132 				T * (-1.6078e-6)) +
2133 			   S12 * (1.91075e-4)) +
2134 		      p1 * (Bw +
2135 			    S * (-9.9348e-7 +
2136 				 T * (2.0816e-8 +
2137 				      T * (9.1697e-10)))));
2138 	return (ro / (1.0 - p1 / xkst));
2139 }
2140 
2141 double
pot_temp(double S,double t,double p,double pref)2142 pot_temp(double S, double t, double p, double pref)
2143 {
2144 	double dp, sq2;
2145 	double dt1, t1, q1;
2146 	double dt2, t2, q2;
2147 	double dt3, t3, q3;
2148 	double dt4, t4;
2149 
2150 	dp = pref - p;
2151 	sq2 = sqrt(2.);
2152 
2153 	dt1 = dp * lapse_rate(S, t, p);
2154 	q1 = dt1;
2155 	t1 = t + 0.5 * dt1;
2156 
2157 	dt2 = dp * lapse_rate(S, t1, p + 0.5 * dp);
2158 	q2 = (2. - sq2) * dt2 + (-2. + 3. / sq2) * q1;
2159 	t2 = t1 + (1. - 1. / sq2) * (dt2 - q1);
2160 
2161 	dt3 = dp * lapse_rate(S, t2, p + 0.5*dp);
2162 	q3 = (2 + sq2) * dt3 + (-2. - 3. / sq2) * q2;
2163 	t3 = t2 + (1. + 1. / sq2) * (dt3 - q2);
2164 
2165 	dt4 = dp * lapse_rate(S, t3, p + dp);
2166 	t4 = t3 + 1. / 6. * (dt4 - 2. * q3);
2167 
2168 	return t4;
2169 }
2170 
2171 /* From Unesco technical papers in marine science, number 44 (1983).
2172  *
2173  * SYNTAX        double lapse_rate(double S, double t, double p)
2174  *
2175  * UNITS         S in psu; t in degC; p in dbar;
2176  *
2177  * RETURN VALUE  adiabatic lapse rate in degC/dbar
2178  *
2179  * Note: used to compute potential temperature.
2180  */
2181 double
lapse_rate(double S,double t,double p)2182 lapse_rate(double S, double t, double p)
2183 {
2184 	const double a0 = 3.5803e-5;
2185 	const double a1 = 8.5258e-6;
2186 	const double a2 = -6.8360e-8;
2187 	const double a3 = 6.6228e-10;
2188 	const double b0 = 1.8932e-6;
2189 	const double b1 = -4.2393e-8;
2190 	const double c0 = 1.8741e-8;
2191 	const double c1 = -6.7795e-10;
2192 	const double c2 = 8.7330e-12;
2193 	const double c3 = -5.4481e-14;
2194 	const double d0 = -1.1351e-10;
2195 	const double d1 = 2.7759e-12;
2196 	const double e0 = -4.6206e-13;
2197 	const double e1 = 1.8676e-14;
2198 	const double e2 = -2.1687e-16;
2199 
2200 	double Gamma;
2201 
2202 	Gamma = a0 + t * (a1 + t * (a2 + t * a3))
2203 		+ (S - 35.) * (b0 + t * b1)
2204 		+ p * (c0 + t * (c1 + t * (c2 + t * c3))
2205 		       + (S - 35.) * (d0 + t * d1)
2206 		       + p * (e0 + t * (e1 + t * e2)));
2207 
2208 	return Gamma;
2209 }
2210 
2211 // Get name for temporary file (hide details of libraries here)
2212 char*
tmp_file_name()2213 tmp_file_name()
2214 {
2215 #if defined(HAVE_MKSTEMP)
2216 	static char rval[PATH_MAX];
2217 	int fd;
2218 	/*
2219 	 * Create the file safely and let caller scribble on it. Not
2220 	 * perfect but better than the alternative. (One could also
2221 	 * change the function to return a fd or FILE * instead of a
2222 	 * path, but that's quite invasive.)
2223 	 */
2224 	strcpy(rval, _PATH_TMP "griXXXXXX");
2225 	fd = mkstemp(rval);
2226 	if (fd < 0) {
2227 		return NULL;
2228 	}
2229 	close(fd);
2230 	return rval;
2231 #else
2232 #if defined(HAVE_TEMPNAM)
2233 	//	rval = tempnam("/usr/tmp", "gri");
2234 	char *rval = tempnam(NULL, "gri");
2235 	if (rval == NULL)
2236 		return NULL;
2237 	return rval;
2238 #else
2239 #if defined(HAVE_TMPNAM)
2240 	char *rval = tmpnam(NULL);
2241 	if (rval == NULL)
2242 		return NULL;
2243 	return rval;
2244 #else
2245 	return GRI_TMP_FILE;
2246 #endif
2247 #endif
2248 #endif
2249 }
2250 
2251 int
call_the_OS(const char * cmd,const char * calling_filename,int calling_line)2252 call_the_OS(const char* cmd, const char* calling_filename, int calling_line)
2253 {
2254 	std::string c(cmd);
2255 	clean_blanks_quotes(c);
2256 	c.append("\n");
2257 	if (((unsigned) superuser()) & FLAG_SYS) {
2258 		printf("Sending the following command to the operating system [ref: %s:%d]:\n%s\n",
2259 		       calling_filename, calling_line, c.c_str());
2260 	}
2261 	int status = system(c.c_str());
2262 	PUT_VAR("..exit_status..", double(status));
2263 	return status;
2264 }
2265 
2266 void
clean_blanks_quotes(std::string & c)2267 clean_blanks_quotes(std::string& c)
2268 {
2269 	// Trim any blanks at the start and end ...
2270 	while (isspace(c[0]))
2271 		c.STRINGERASE(0,1);
2272 	while (c.size() > 0 && isspace(c[-1 + c.size()]))
2273 		c.STRINGERASE(-1 + c.size(), 1);
2274 
2275 	// ... and, if the first nonblank symbol is a quote,
2276 	// then remove both it and a matching trailing quote.
2277 	if (c[0] == '"') {
2278 		c.STRINGERASE(0,1);
2279 		if (c.size() > 0 && c[-1 + c.size()] == '"')
2280 			c.STRINGERASE(-1 + c.size(), 1);
2281 	}
2282 }
2283 
2284 bool
is_even_integer(double v)2285 is_even_integer(double v)
2286 {
2287 	int iv = int(v);
2288 	if (double(iv) != v)
2289 		return false;	// not even an integer
2290 	int iiv = 2 * (iv / 2);
2291 	if (iv == iiv)
2292 		return true;
2293 	return false;
2294 }
2295 
2296 bool
is_odd_integer(double v)2297 is_odd_integer(double v)
2298 {
2299 	int iv = int(v);
2300 	if (double(iv) != v)
2301 		return false;	// not even an integer
2302 	int iiv = 2 * (iv / 2);
2303 	if (iv != iiv)
2304 		return true;
2305 	return false;
2306 }
2307 
2308 void
de_reference(std::string & syn)2309 de_reference(std::string& syn)
2310 {
2311 	//printf("%s:%d 1. de_reference (%s)...\n",__FILE__,__LINE__,syn.c_str());
2312 	if (syn[0] == '\\' && syn[1] == '@') {
2313 		std::string deref("\\");
2314 		deref.append(syn.substr(2, syn.size()));
2315 		//printf("2. deref= <%s>\n", deref.c_str());
2316 		std::string buf;
2317 		if (get_syn(deref.c_str(), buf)) {
2318 			syn.assign(buf);
2319 			//printf("3. syn= <%s>\n", syn.c_str());
2320 			if (syn[0] == '\\' && syn[1] == '\\')
2321 				syn.STRINGERASE(0, 1);
2322 			//printf("4. syn= <%s>\n", syn.c_str());
2323 		}
2324 	}
2325 	//printf("%s:%d de_reference returning MODIFIED TO <%s>\n",__FILE__,__LINE__,syn.c_str());
2326 }
2327 
2328 void
un_double_slash(std::string & word)2329 un_double_slash(std::string& word)	// change leading double-backslash to single-backslash
2330 {
2331 	if (word[0] == '\\' && word[1] == '\\')
2332 		word.STRINGERASE(0, 1);
2333 }
2334 
2335 void
2336 
un_double_quote(std::string & word)2337 un_double_quote(std::string& word)
2338 {
2339 	if (word[0] == '"')
2340 		if (word[word.size() - 1] == '"') {
2341 			word.STRINGERASE(word.size() - 1, 1);
2342 			word.STRINGERASE(0, 1);
2343 		}
2344 }
2345 
2346 void
fix_negative_zero(std::string & number)2347 fix_negative_zero(std::string& number) // change e.g. "-0" to "0", for axes
2348 {
2349         //#define DEBUG_FIX_NEGATIVE_ZERO
2350 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2351         number = "-" + number;
2352         printf("called fix_negative_zero(%s) [%s]\n", number.c_str(), number.c_str());
2353 #endif
2354 	unsigned int i;
2355         unsigned size = number.size();
2356         // find first non-blank character
2357         unsigned start = 0;
2358 	for (i = 0; i < size; i++) {
2359 		if (number[i] != ' ') {
2360                         start = i;
2361 			break;
2362                 }
2363         }
2364 	if (i == size || number[i] != '-')
2365 		return;
2366 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2367         printf("    first character is a minus.  start=%d\n    ", start);
2368 #endif
2369         // find last digit
2370         unsigned end = size - 1;
2371         for (i = end; i > start; i--) {
2372 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2373                 printf("c[%2d]='%c' ", i, number[i]);
2374 #endif
2375                 if (isdigit(number[i]) || number[i] == '.') {
2376                         end = i;
2377 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2378                         printf("\n");
2379 #endif
2380                         break;
2381                 }
2382         }
2383 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2384                         printf("    end=%d\n", end);
2385 #endif
2386         std::string portion = number.substr(start + 1, end - start); // just digits or decimals
2387 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2388         printf("    portion='%s'\n", portion.c_str());
2389 #endif
2390         // The 'portion' is now a middle portion consisting of digits and decimals
2391 	bool is_zero = true;
2392         for (i = 0; i < portion.size(); i++) {
2393                 if (!(portion[i] == '0' || portion[i] == '.')) {
2394                         is_zero = false;
2395                         break;
2396                 }
2397         }
2398         //printf("    is_zero=%d\n", is_zero);
2399 	if (is_zero) {
2400 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2401 		printf("    ERASING at start=%d\n", start);
2402 #endif
2403                 number[start] = ' ';
2404         }
2405 #ifdef DEBUG_FIX_NEGATIVE_ZERO
2406         printf("    returning '%s'\n", number.c_str());
2407 #endif
2408 #undef DEBUG_FIX_NEGATIVE_ZERO
2409 }
2410 
2411 bool
get_optionsCmd()2412 get_optionsCmd()
2413 {
2414 	DEBUG_FUNCTION_ENTRY;
2415 	DEBUG_MESSAGE("This command does NOTHING yet.\n");
2416 	DEBUG_MESSAGE("This command is NOT documented yet.\n");
2417 	if (_nword < 3 || _nword > 5) {
2418 		NUMBER_WORDS_ERROR;
2419 		demonstrate_command_usage();
2420 		return false;
2421 	}
2422 	bool keep = false;	// keep the unused options?
2423 	if (_nword == 4) {
2424 		if (word_is(3, "keep")) {
2425 			keep = true;
2426 		} else {
2427 			demonstrate_command_usage();
2428 			err("Cannot understand word `\\", _word[3], "'.  Expecting `keep' here, if anything", "\\");
2429 			DEBUG_FUNCTION_EXIT;
2430 			return false;
2431 		}
2432 	}
2433 	if (keep)
2434 		DEBUG_MESSAGE("Will keep unused options.\n")
2435 	else
2436 		DEBUG_MESSAGE("Will NOT keep unused options.\n")
2437 
2438 	std::string options(_word[2]);
2439 	// Remove containing quotes, if present
2440 	if (options[0] == '"')
2441 		options.STRINGERASE(0, 1);
2442 	if (options[options.size() - 1] == '"')
2443 		options.STRINGERASE(options.size() - 1, 1);
2444 
2445 	DEBUG_MESSAGE("Option-specification string was"); printf(" \"%s\"\n", options.c_str());
2446 	DEBUG_FUNCTION_EXIT;
2447 
2448 	return true;
2449 }
2450 
2451 
2452 // Byte swapping, from /usr/include/bits/byteswap.h on a linux box
2453 #define gri_bswap_constant_32(x) \
2454      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
2455       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
2456 unsigned int
endian_swap_uint(unsigned int v)2457 endian_swap_uint(unsigned int v)
2458 {
2459 	return gri_bswap_constant_32(v);
2460 }
2461 #undef gri_bswap_constant_32
2462 
2463 
2464 bool
gri_version_exceeds(unsigned int n1,unsigned int n2,unsigned int n3)2465 gri_version_exceeds(unsigned int n1, unsigned int n2, unsigned int n3)
2466 {
2467 	double v = n1 + n2 / 100.0 + n3 / 100000.0;
2468 	return _version > v;
2469 }
2470 
2471 bool
xy_to_pt(double xin,double yin,units u,double * xout,double * yout)2472 xy_to_pt(double xin, double yin, units u, double *xout, double *yout)
2473 {
2474 	if (u == units_user) {
2475 		gr_usertopt(xin, yin, xout, yout);
2476 	} else if (u == units_cm) {
2477 		*xout = xin * PT_PER_CM;
2478 		*yout = yin * PT_PER_CM;
2479 	} else if (u == units_pt) {
2480 		*xout = xin;
2481 		*yout = yin;
2482 	} else {		// impossible????
2483 		*xout = xin;
2484 		*yout = yin;
2485 	}
2486 	return true;
2487 }
2488 
2489 bool
xy_to_cm(double xin,double yin,units u,double * xout,double * yout)2490 xy_to_cm(double xin, double yin, units u, double *xout, double *yout)
2491 {
2492 	if (u == units_user) {
2493 		gr_usertocm(xin, yin, xout, yout);
2494 	} else if (u == units_pt) {
2495 		*xout = xin / PT_PER_CM;
2496 		*yout = yin / PT_PER_CM;
2497 	} else if (u == units_cm) {
2498 		*xout = xin;
2499 		*yout = yin;
2500 	} else {		// impossible??
2501 		*xout = xin;
2502 		*yout = yin;
2503 	}
2504 	return true;
2505 }
2506 
2507 void
fix_line_ending(char * line)2508 fix_line_ending(char *line)
2509 {
2510 	unsigned int len = strlen(line);
2511 	if (len > 2 && line[len - 2] == '\r') {
2512 		line[len - 2] = '\n';
2513 		line[len - 1] = '\0';
2514 	}
2515 }
2516 
2517 bool
is_column_name(const char * n)2518 is_column_name(const char* n)
2519 {
2520 	//printf("is_column_name(%s)\n", n);
2521 	if (strEQ(n, "u")
2522 	    || strEQ(n, "v")
2523 	    || strEQ(n, "weight")
2524 	    || strEQ(n, "x")
2525 	    || strEQ(n, "y")
2526 	    || strEQ(n, "z")
2527 	    || strEQ(n, "z")
2528 		)
2529 		return true;
2530 	else
2531 		return false;
2532 }
2533 
2534 #define ASSIGN_TO_COLUMN(i,v,c) \
2535 { \
2536 	if ((i) >= int((c).size())) { \
2537 		for (int ii = int((c).size()); ii <= (i); ii++) {\
2538 			(c).push_back(0.0); \
2539                 } \
2540 	        PUT_VAR("..num_col_data..", double(i));\
2541         } \
2542 	(c)[(i)] = (v); \
2543 	_columns_exist = true;\
2544 }
2545 
2546 bool
assign_to_column(int index,double value,const char * c)2547 assign_to_column(int index, double value, const char* c)
2548 {
2549 	if (index < 0)
2550 		return false;
2551 	//printf("assigning %f to  %s[%d]\n", value, c, index);
2552 	if (strEQ(c, "x"))
2553 		ASSIGN_TO_COLUMN(index, value, _colX);
2554 	if (strEQ(c, "y"))
2555 		ASSIGN_TO_COLUMN(index, value, _colY);
2556 	if (strEQ(c, "z"))
2557 		ASSIGN_TO_COLUMN(index, value, _colZ);
2558 	if (strEQ(c, "u"))
2559 		ASSIGN_TO_COLUMN(index, value, _colU);
2560 	if (strEQ(c, "v"))
2561 		ASSIGN_TO_COLUMN(index, value, _colV);
2562 	return true;
2563 	if (strEQ(c, "weight"))
2564 		ASSIGN_TO_COLUMN(index, value, _colWEIGHT);
2565 	return true;
2566 }
2567 
2568 double
gr_page_height_pt()2569 gr_page_height_pt()
2570 {
2571 	extern rectangle _page_size;
2572 	return (_page_size.ury() * PT_PER_CM);
2573 }
2574