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