1 // vim: noexpandtab tabstop=8 softtabstop=8 shiftwidth=8
2 /*
3     Gri - A language for scientific graphics programming
4     Copyright (C) 2010 Daniel Kelley
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include        <string>
22 #include	<ctype.h>
23 #include	<math.h>
24 #include	<stdio.h>
25 #include	<string.h>
26 #include	"gr.hh"
27 #include	"extern.hh"
28 #include	"image_ex.hh"
29 #include        "defaults.hh"
30 #include        "files.hh"
31 #include        "superus.hh"
32 #include        "gr_coll.hh"
33 #include        "GriState.hh"
34 
35 static inline bool between(double x, double lim0, double lim1);
36 extern char     _grTempString[];
37 void            reset_top_of_plot(void);
38 bool            ignore_initial_newline(void);
39 bool            set_ignore_initial_newlineCmd(void);
40 bool            set_image_colorscaleCmd(void);
41 bool            set_image_grayscaleCmd(void);
42 bool            set_image_grayscale_using_hist(void);
43 bool            set_image_missingCmd(void);
44 bool            set_image_rangeCmd(void);
45 bool            set_grid_missingCmd(void);
46 bool            set_grid_missing_curve(bool inside);
47 bool            mask_an_island(double *x, double *y, unsigned int n);
48 bool            set_y_axis_nameCmd(void);
49 // following shared with read.c
50 double          _input_data_window_x_min = 1.0;
51 double          _input_data_window_x_max = -1.0;
52 double          _input_data_window_y_min = 1.0;
53 double          _input_data_window_y_max = -1.0;
54 bool            _input_data_window_x_exists = false;
55 bool            _input_data_window_y_exists = false;
56 static bool     already_landscape = false;
57 static double   xleft, xright, xinc, ybottom, ytop, yinc;
58 static double   tmp, tmp2;
59 
between(double x,double lim0,double lim1)60 static inline bool between(double x, double lim0, double lim1)
61 {
62 	if (lim0 <= x && x < lim1)
63 		return true;
64 	if (lim1 <= x && x < lim0)
65 		return true;
66 	return false;
67 }
68 
69 void
reset_top_of_plot()70 reset_top_of_plot()
71 {
72 	double          margin, size;
73 	get_var("..ymargin..", &margin);
74 	get_var("..ysize..", &size);
75 	_top_of_plot = margin + size;
76 }
77 
78 static bool     ignore_initial_newline_flag = false;
79 bool
set_ignore_initial_newlineCmd()80 set_ignore_initial_newlineCmd()
81 {
82 	switch (_nword) {
83 	case 4:
84 		ignore_initial_newline_flag = true;
85 		break;
86 	case 5:
87 		if (!strcmp(_word[4], "off")) {
88 			ignore_initial_newline_flag = false;
89 			return true;
90 		} else {
91 			demonstrate_command_usage();
92 			return false;
93 		}
94 	default:
95 		demonstrate_command_usage();
96 		return false;
97 	}
98 	return false;
99 }
100 bool
ignore_initial_newline()101 ignore_initial_newline()
102 {
103 	return ((ignore_initial_newline_flag) ? true : false);
104 }
105 
106 bool
set_axes_styleCmd()107 set_axes_styleCmd()
108 {
109 	if (_nword < 3) {
110 		err("Too few words in `set axes'");
111 		return false;
112 	}
113 	if (strcmp(_word[2], "style")) {
114 		err("Third word of `set axes' must be \"style\".");
115 		return false;
116 	}
117 	if (_nword < 4) {
118 		err("`set axes style' to what?");
119 		return false;
120 	}
121 	// `set axes style offset [.distance_cm.]'
122 	if (_nword >= 4 && !strcmp(_word[3], "offset")) {
123 		double          tmp;
124 		if (_nword == 5) {
125 			if (!getdnum(_word[4], &tmp)) {
126 				READ_WORD_ERROR(".distance_cm.");
127 				return false;
128 			}
129 			_axes_offset = tmp;
130 		} else if (!get_var("..tic_size..", &tmp)) {
131 			err("Can't remember the tic size.");
132 			return false;
133 		}
134 		_axes_offset = tmp;
135 		return true;
136 	}
137 	switch (_nword) {
138 	case 4:
139 		// `set axes style .style.|rectangular|default|none'
140 		if (!strcmp(_word[3], "default")) {
141 			_axesStyle = 0;
142 			_axes_offset = 0.0;
143 			return true;
144 		} else if (!strcmp(_word[3], "none")) {
145 			_need_x_axis = false;
146 			_need_y_axis = false;
147 		} else if (!strcmp(_word[3], "rectangular")) {
148 			// `set axes style rectangular'
149 			_axesStyle = 0;
150 			return true;
151 		} else {
152 			// `set axes style .style.'
153 			if (!getdnum(_word[3], &tmp))
154 				return false;
155 			if (tmp < 0.0 || tmp > 2.0) {
156 				err("Ignoring bad axes type <0 or >2");
157 				return false;
158 			}
159 			_axesStyle = (int) floor(0.5 + tmp);
160 			return true;
161 		}
162 		break;
163 	default:
164 		NUMBER_WORDS_ERROR;
165 		return false;
166 	}
167 	return true;
168 }
169 
170 // Set arrow head halfwidth into ..arrowsize..; a positive value indicates
171 // size in cm; a negative value
172 bool
set_arrow_sizeCmd()173 set_arrow_sizeCmd()
174 {
175 	if (_nword < 3) {
176 		err("`set arrow' what?");
177 		return false;
178 	}
179 	if (!strcmp(_word[2], "size")) {
180 		if (_nword < 4) {
181 			err("`set arrow size' what?");
182 			return false;
183 		}
184 		switch (_nword) {
185 		case 4:
186 			// set arrow size .size.|default
187 			if (!strcmp(_word[3], "default")) {
188 				PUT_VAR("..arrowsize..", ARROWSIZE_DEFAULT);
189 				return true;
190 			} else {
191 				if (!getdnum(_word[3], &tmp)) {
192 					err("Can't read arrow size");
193 					return false;
194 				}
195 				if (tmp < 0.0) {
196 					err("Ignoring bad (negative) arrow size.");
197 					return false;
198 				}
199 				PUT_VAR("..arrowsize..", tmp);
200 				return true;
201 			}
202 			// NOT REACHED
203 		case 8:
204 			// `set arrow size as .num. percent of length'
205 			if (word_is(5, "percent") && word_is(6, "of") && word_is(7, "length")) {
206 				Require(getdnum(_word[4], &tmp), err("Can't read percentage"));
207 				Require(tmp >= 0.0, err("Ignoring bad (negative) percentage arrow size."));
208 				PUT_VAR("..arrowsize..", -tmp / 100.0);
209 				return true;
210 			} else {
211 				demonstrate_command_usage();
212 				err("Cannot understand command");
213 				return false;
214 			}
215 			// NOT REACHED
216 		default:
217 			demonstrate_command_usage();
218 			NUMBER_WORDS_ERROR;
219 			return false;
220 		}
221 	} else {
222 		err("`set arrow' what?");
223 		return false;
224 	}
225 	// NOT REACHED
226 }
227 
228 bool
set_arrow_typeCmd()229 set_arrow_typeCmd()
230 {
231 	if (_nword != 4) {
232 		demonstrate_command_usage();
233 		NUMBER_WORDS_ERROR;
234 		return false;
235 	}
236 	double tmp;
237 	if (!getdnum(_word[3], &tmp)) {
238 		READ_WORD_ERROR(".which.");
239 		return false;
240 	}
241 	_arrow_type = int(floor(0.5 + tmp));
242 	if (_arrow_type != 0 && _arrow_type != 1 && _arrow_type != 2) {
243 		err("Valid arrow types are 0, 1, and 2");
244 		return false;
245 	}
246 	return true;
247 }
248 
249 bool
set_beepCmd()250 set_beepCmd()
251 {
252 	if (_nword == 2 || !strcmp(_word[2], "on"))
253 		_gri_beep = true;
254 	else if (!strcmp(_word[2], "off"))
255 		_gri_beep = false;
256 	else {
257 		err("`set beep' what?");
258 		return false;
259 	}
260 	return true;
261 }
262 
263 //`set bounding box .xleft. .ybottom. .xright. .ytop. [cm|pt]'
264 bool
set_bounding_boxCmd()265 set_bounding_boxCmd()
266 {
267 	// Set to _bounding_box_user
268 	double ll_x, ll_y, ur_x, ur_y; // user-units
269 	double ll_x_cm, ll_y_cm, ur_x_cm, ur_y_cm; // points
270 	if (_nword == 7) {		// user-units
271 		if (!getdnum(_word[3], &ll_x)) {
272 			demonstrate_command_usage();
273 			err("Can't read .xleft. in `\\", _word[3], "'.", "\\");
274 			return false;
275 		}
276 		if (!getdnum(_word[4], &ll_y)) {
277 			demonstrate_command_usage();
278 			err("Can't read .ybottom. in `\\", _word[4], "'.", "\\");
279 			return false;
280 		}
281 		if (!getdnum(_word[5], &ur_x)) {
282 			demonstrate_command_usage();
283 			err("Can't read .xright. in `\\", _word[5], "'.", "\\");
284 			return false;
285 		}
286 		if (!getdnum(_word[6], &ur_y)) {
287 			demonstrate_command_usage();
288 			err("Can't read .ytop. in `\\", _word[6], "'.", "\\");
289 			return false;
290 		}
291 		double          xmargin = XMARGIN_DEFAULT;
292 		double          ymargin = YMARGIN_DEFAULT;
293 		double          xsize = XSIZE_DEFAULT;
294 		double          ysize = YSIZE_DEFAULT;
295 
296 		if (!get_var("..xmargin..", &xmargin))
297 			warning("(set_environment) ..xmargin.. undefined so using default");
298 		if (!get_var("..ymargin..", &ymargin))
299 			warning("(set_environment) ..ymargin.. undefined so using default");
300 		if (!get_var("..xsize..", &xsize))
301 			warning("(set_environment) ..xsize.. undefined so using default");
302 		if (!get_var("..ysize..", &ysize))
303 			warning("(set_environment) ..ysize.. undefined so using default");
304 		gr_setxtransform(_xtype);
305 		gr_setxscale(xmargin, xmargin + xsize, _xleft, _xright);
306 		gr_setytransform(_ytype);
307 		gr_setyscale(ymargin, ymargin + ysize, _ybottom, _ytop);
308 
309 		gr_usertocm(ll_x, ll_y, &ll_x_cm, &ll_y_cm);
310 		gr_usertocm(ur_x, ur_y, &ur_x_cm, &ur_y_cm);
311 
312 	} else if (_nword == 8) {
313 
314 		bool in_pt = false;
315 		if (word_is(7, "cm")) {
316 			in_pt = false;
317 		} else if (word_is(7, "pt")) {
318 			in_pt = true;
319 		} else {
320 			err("`set bounding box ...' expecting keyword `pt' or `cm' but got `\\", _word[7], "'", "\\");
321 			return false;
322 		}
323 
324 		if (!getdnum(_word[3], &ll_x_cm)) {
325 			demonstrate_command_usage();
326 			err("Can't read .xleft. in `\\", _word[3], "'.", "\\");
327 			return false;
328 		}
329 		if (!getdnum(_word[4], &ll_y_cm)) {
330 			demonstrate_command_usage();
331 			err("Can't read .ybottom. in `\\", _word[4], "'.", "\\");
332 			return false;
333 		}
334 		if (!getdnum(_word[5], &ur_x_cm)) {
335 			demonstrate_command_usage();
336 			err("Can't read .xright. in `\\", _word[5], "'.", "\\");
337 			return false;
338 		}
339 		if (!getdnum(_word[6], &ur_y_cm)) {
340 			demonstrate_command_usage();
341 			err("Can't read .ytop. in `\\", _word[6], "'.", "\\");
342 			return false;
343 		}
344 		if (in_pt) {
345 			ll_x_cm /= PT_PER_CM;	// convert points to cm
346 			ll_y_cm /= PT_PER_CM;
347 			ur_x_cm /= PT_PER_CM;
348 			ur_y_cm /= PT_PER_CM;
349 		}
350 
351 	} else {
352 		err("Must specify .xleft. .ybottom. .xright. .ytop. [cm]");
353 	}
354 	_bounding_box_user.set(ll_x_cm, ll_y_cm, ur_x_cm, ur_y_cm);
355 	_user_gave_bounding_box = true;
356 	return true;
357 }
358 
359 // set clip to curve [4 words]
360 bool
set_clipCmd()361 set_clipCmd()
362 {
363 	Require(_nword > 2, err("Must specify `set clip on' or `set clip off'"));
364 	if (_nword == 4 && word_is(2, "to") && word_is(3, "curve")) {
365 		unsigned int xlen = _colX.size();
366 		if (xlen < 1) {
367 			warning("`set clip to curve' noticed that no curve exists");
368 			return true;
369 		}
370 		unsigned int ylen = _colY.size();
371 		if (xlen != ylen) {
372 			warning("`set clip to curve' noticed that curve's x and y lengths disagree");
373 			return true;
374 		}
375 		double *xp = _colX.begin();
376 		double *yp = _colY.begin();
377 		gr_set_clip_ps_curve(xp, yp, xlen);
378 		return true;
379 	} else {
380 		if (!strcmp(_word[2], "postscript")) {
381 			// PostScript clipping
382 			if (_nword < 4) {
383 				err("`set clip postscript ...' needs >= 4 words.");
384 				return false;
385 			}
386 			if (!strcmp(_word[3], "off")) {
387 				gr_set_clip_ps_off();
388 				return true;
389 			} else if (!strcmp(_word[3], "on")) {
390 				double xl, xr, yb, yt;
391 				if (_nword == 4) { // set clip postscript on
392 					xl = _xleft;
393 					xr = _xright;
394 					yb = _ybottom;
395 					yt = _ytop;
396 					/*DEK*/
397 //					printf("DEBUG: will try to set clip to xl=%f  xr=%f  yb=%f  yt=%f\n",xl,xr,yb,yt);
398 					/*DEK*/
399 				} else if (_nword == 8) { // set clip postscript on .llx. ...
400 					if (!getdnum(_word[4], &xl)) {
401 						demonstrate_command_usage();
402 						err("Can't read .xleft. in `\\", _word[4], "'.", "\\");
403 						return false;
404 					}
405 					if (!getdnum(_word[5], &xr)) {
406 						demonstrate_command_usage();
407 						err("Can't read .xright. in `\\", _word[5], "'.", "\\");
408 						return false;
409 					}
410 					if (!getdnum(_word[6], &yb)) {
411 						demonstrate_command_usage();
412 						err("Can't read .ybottom. in `\\", _word[6], "'.", "\\");
413 						return false;
414 					}
415 					if (!getdnum(_word[7], &yt)) {
416 						demonstrate_command_usage();
417 						err("Can't read .ytop. in `\\", _word[7], "'.", "\\");
418 						return false;
419 					}
420 				} else {
421 					NUMBER_WORDS_ERROR;
422 					demonstrate_command_usage();
423 					return false;
424 				}
425 				set_environment();
426 				double llx, lly, urx, ury;
427 				gr_usertopt(xl, yb, &llx, &lly);
428 				gr_usertopt(xr, yt, &urx, &ury);
429 				gr_set_clip_ps_rect(llx, lly, urx, ury);
430 				return true;
431 			}
432 		} else if (!strcmp(_word[2], "on")) {
433 			// Non-PostScript clipping
434 			if (_nword == 3)
435 				_clipData = -1;
436 			else if (_nword == 7) {
437 				if (!getdnum(_word[3], &tmp)) {
438 					READ_WORD_ERROR(".xleft.");
439 					_clipData = 0;
440 					_clipxleft = _clipxright = _clipybottom = _clipytop = 0.0;
441 					return false;
442 				}
443 				_clipxleft = tmp;
444 				if (!getdnum(_word[4], &tmp)) {
445 					READ_WORD_ERROR(".xright.");
446 					_clipData = 0;
447 					_clipxleft = _clipxright = _clipybottom = _clipytop = 0.0;
448 					return false;
449 				}
450 				_clipxright = tmp;
451 				if (!getdnum(_word[5], &tmp)) {
452 					READ_WORD_ERROR(".ybottom.");
453 					_clipData = 0;
454 					_clipxleft = _clipxright = _clipybottom = _clipytop = 0.0;
455 					return false;
456 				}
457 				_clipybottom = tmp;
458 				if (!getdnum(_word[6], &tmp)) {
459 					READ_WORD_ERROR(".ytop.");
460 					_clipData = 0;
461 					_clipxleft = _clipxright = _clipybottom = _clipytop = 0.0;
462 					return false;
463 				}
464 				_clipytop = tmp;
465 				_clipData = 1;
466 			} else
467 				err("`set clip on' takes 4 parameters");
468 		} else if (!strcmp(_word[2], "off")) {
469 			gr_set_clip_ps_off();
470 			_clipData = 0;
471 		} else {
472 			err("Must specify `set clip on' or `set clip off'");
473 			return false;
474 		}
475 	}
476 	return true;
477 }
478 
479 #define CHECK_RGB_RANGE(item) {						\
480     if ((item) < 0.0) {							\
481 	warning("`set color rgb' value being clipped to range 0-1");	\
482 	(item) = 0.0;							\
483     } else if ((item) > 1.0) {						\
484 	warning("`set color rgb' value being clipped to range 0-1");	\
485         (item) = 1.0;							\
486     }									\
487 }
488 #define CHECK_HSB_RANGE(item) {						\
489     if ((item) < 0.0) {							\
490         warning("`set color hsb' value being clipped to range 0-1");	\
491         (item) = 0.0;							\
492     } else if ((item) > 1.0) {						\
493         warning("`set color hsb' value being clipped to range 0-1");	\
494         (item) = 1.0;							\
495     }									\
496 }
497 bool
set_colorCmd()498 set_colorCmd()
499 {
500 	double          red, green, blue;
501 	std::string cname;
502 	GriColor c;
503 	switch (_nword) {
504 	case 3:
505 		cname.assign(_word[2]);
506 		un_double_quote(cname);
507 		if (!look_up_color(cname.c_str(), &red, &green, &blue)) {
508 			unsigned int rhex, ghex, bhex;
509 			if (cname.size() == 6
510 			    && 1 == sscanf(cname.substr(0,2).c_str(), "%x", &rhex)
511 			    && 1 == sscanf(cname.substr(2,2).c_str(), "%x", &ghex)
512 			    && 1 == sscanf(cname.substr(4,2).c_str(), "%x", &bhex)) {
513 				red = rhex / 255.0;
514 				green = ghex / 255.0;
515 				blue = bhex / 255.0;
516 			} else {
517 				err("`set color' given unknown colorname `\\", cname.c_str(), "'.  Use command `show colornames' to see available colors.", "\\");
518 			return false;
519 			}
520 		}
521 		PUT_VAR("..red..", red);
522 		PUT_VAR("..green..", green);
523 		PUT_VAR("..blue..", blue);
524 		c.setRGB(red, green, blue);
525 		_griState.set_color_line(c);
526 		if (_griState.separate_text_color() == false)
527 			_griState.set_color_text(c);
528 		return true;
529 	case 6:
530 		if (!strcmp(_word[2], "rgb")) {
531 			// `set color rgb .red. .green. .blue.'
532 			Require(getdnum(_word[3], &red), READ_WORD_ERROR(".red."));
533 			Require(getdnum(_word[4], &green), READ_WORD_ERROR(".green."));
534 			Require(getdnum(_word[5], &blue), READ_WORD_ERROR(".blue."));
535 			// Clip if necessary
536 			CHECK_RGB_RANGE(red);
537 			CHECK_RGB_RANGE(green);
538 			CHECK_RGB_RANGE(blue);
539 			PUT_VAR("..red..", red);
540 			PUT_VAR("..green..", green);
541 			PUT_VAR("..blue..", blue);
542 			c.setRGB(red, green, blue);
543 			_griState.set_color_line(c);
544 			if (_griState.separate_text_color() == false)
545 				_griState.set_color_text(c);
546 			return true;
547 		} else if (!strcmp(_word[2], "hsb")) {
548 			// `set color hsb .hue. .saturation. .brightness.'
549 			double          hue, saturation, brightness;
550 			Require(getdnum(_word[3], &hue), READ_WORD_ERROR(".hue."));
551 			Require(getdnum(_word[4], &saturation), READ_WORD_ERROR(".saturation."));
552 			Require(getdnum(_word[5], &brightness), READ_WORD_ERROR(".brightness."));
553 			// Clip if necessary
554 			CHECK_HSB_RANGE(hue);
555 			CHECK_HSB_RANGE(saturation);
556 			CHECK_HSB_RANGE(brightness);
557 			gr_hsv2rgb(hue, saturation, brightness, &red, &green, &blue);
558 			PUT_VAR("..red..", red);
559 			PUT_VAR("..green..", green);
560 			PUT_VAR("..blue..", blue);
561 			c.setHSV(hue, saturation, brightness);
562 			_griState.set_color_line(c);
563 			if (_griState.separate_text_color() == false)
564 				_griState.set_color_text(c);
565 			return true;
566 		} else {
567 			err("Can't understand command.");
568 			demonstrate_command_usage();
569 			return false;
570 		}
571 	default:
572 		NUMBER_WORDS_ERROR;
573 		demonstrate_command_usage();
574 		return false;
575 	}
576 }
577 
578 
579 // `set colorname \name {rgb .red. .green. .blue.}|{hsb .hue. .saturation. .brightness.}
580 bool
set_colornameCmd()581 set_colornameCmd()
582 {
583 	double red, green, blue;
584 	switch (_nword) {
585 	case 7:
586 		if (strEQ(_word[3], "rgb")) {
587 			Require(getdnum(_word[4], &red), READ_WORD_ERROR(".red."));
588 			Require(getdnum(_word[5], &green), READ_WORD_ERROR(".green."));
589 			Require(getdnum(_word[6], &blue), READ_WORD_ERROR(".blue."));
590 		} else if (strEQ(_word[3], "hsb")) {
591 			double hue, saturation, brightness;
592 			Require(getdnum(_word[4], &hue), READ_WORD_ERROR(".hue."));
593 			Require(getdnum(_word[5], &saturation), READ_WORD_ERROR(".saturation."));
594 			Require(getdnum(_word[6], &brightness), READ_WORD_ERROR(".brightness."));
595 			hue = pin0_1(hue);
596 			saturation = pin0_1(saturation);
597 			brightness = pin0_1(brightness);
598 			gr_hsv2rgb(hue, saturation, brightness, &red, &green, &blue);
599 		} else {
600 			demonstrate_command_usage();
601 			err("word must be `rgb' or `hsb', not `\\", _word[3], "' as given.", "\\");
602 			return false;
603 		}
604 		red = pin0_1(red);
605 		green = pin0_1(green);
606 		blue = pin0_1(blue);
607 		create_color(_word[2], red, green, blue);
608 		//printf("set colorname '%s' %f %f %f\n", _word[2], red, green, blue);
609 		break;
610 	default:
611 		NUMBER_WORDS_ERROR;
612 		return false;
613 	}
614 	return true;
615 }
616 
617 bool
set_x_typeCmd()618 set_x_typeCmd()
619 {
620 	extern char      _xtype_map;
621 	switch (_nword) {
622 	case 5:
623 		if (word_is(3, "map")) {
624 			if (word_is(4, "E")) {
625 				_xtype_map = 'E';
626 			} else if (word_is(4, "W")) {
627 				_xtype_map = 'W';
628 			} else if (word_is(4, "S")) {
629 				_xtype_map = 'S';
630 			} else if (word_is(4, "N")) {
631 				_xtype_map = 'N';
632 			} else {
633 				err("X map type must be `E', `W', `S' or `N'");
634 				return false;
635 			}
636 		} else {
637 			err("Type must be `linear', `log', or `map'");
638 			return false;
639 		}
640 		break;
641 	case 4:
642 		if (!strcmp(_word[3], "linear")) {
643 			_xtype = gr_axis_LINEAR;
644 			_xtype_map = ' ';
645 		} else if (!strcmp(_word[3], "log")) {
646 			_xtype_map = ' ';
647 			if (_xtype == gr_axis_LOG)	// only change if necessary
648 				return true;
649 			if (!_xscale_exists) {
650 				_xtype = gr_axis_LOG;
651 				gr_setxtransform(_xtype);
652 				return true;
653 			}
654 			if ((_xright > _xleft) && (_xleft > 0.0) && (_xright > 0.0)) {
655 				_xinc = 1;
656 				PUT_VAR("..xleft..", _xleft = pow(10.0, floor(0.5 + log10(_xleft))));
657 				PUT_VAR("..xright..", _xright = pow(10.0, floor(0.5 + log10(_xright))));
658 				PUT_VAR("..xinc..", _xinc);
659 				_xtype = gr_axis_LOG;
660 			} else {
661 				err("\
662 Can't convert from linear x axis, present x axis has numbers <= 0.\n\
663 First `delete x scale', then `set x type log'.");
664 			}
665 		} else {
666 			err("Type must be `linear', `log', or `map'");
667 			return false;
668 		}
669 		break;
670 	default:
671 		NUMBER_WORDS_ERROR;
672 		return false;
673 	}
674 	gr_setxtransform(_xtype);
675 	return true;
676 }
677 
678 bool
set_y_typeCmd()679 set_y_typeCmd()
680 {
681 	extern char      _ytype_map;
682 	switch (_nword) {
683 	case 5:
684 		if (word_is(3, "map")) {
685 			if (word_is(4, "N")) {
686 				_ytype_map = 'N';
687 			} else if (word_is(4, "S")) {
688 				_ytype_map = 'S';
689 			} else if (word_is(4, "E")) {
690 				_ytype_map = 'E';
691 			} else if (word_is(4, "W")) {
692 				_ytype_map = 'W';
693 			} else {
694 				err("Y map type must be `E', `W', `S' or `N'");
695 				return false;
696 			}
697 		} else {
698 			err("Type must be `linear', `log', or `map'");
699 			return false;
700 		}
701 		break;
702 	case 4:
703 		if (!strcmp(_word[3], "linear")) {
704 			_ytype_map = ' ';
705 			_ytype = gr_axis_LINEAR;
706 		} else if (!strcmp(_word[3], "log")) {
707 			_ytype_map = ' ';
708 			if (_ytype == gr_axis_LOG) {	// only change if necessary
709 				return true;
710 			}
711 			if (!_yscale_exists) {
712 				_ytype = gr_axis_LOG;
713 				gr_setytransform(_ytype);
714 				return true;
715 			}
716 			if ((_ytop > _ybottom) && (_ytop > 0.0) && (_ybottom > 0.0)) {
717 				_yinc = 1;
718 				PUT_VAR("..ybottom..", _ybottom = pow(10.0, floor(0.5 + log10(_ybottom))));
719 				PUT_VAR("..ytop..", _ytop = pow(10.0, floor(0.5 + log10(_ytop))));
720 				PUT_VAR("..yinc..", _yinc);
721 				_ytype = gr_axis_LOG;
722 			} else {
723 				err("\
724 Can't convert from linear y axis, present x axis has numbers <= 0.\n\
725 First `delete y scale', then `set y type log'.");
726 			}
727 		} else {
728 			err("Type must be `linear', `log', or `map'");
729 			return false;
730 		}
731 		break;
732 	default:
733 		NUMBER_WORDS_ERROR;
734 		return false;
735 	}
736 	gr_setytransform(_ytype);
737 	return true;
738 }
739 
740 // `set z missing above|below .intercept. .slope.'
741 bool
set_z_missingCmd()742 set_z_missingCmd()
743 {
744 	double slope, intercept;
745 	typedef enum { above, below, dont_know } WHERE;
746 	WHERE where = dont_know;
747 	switch (_nword) {
748 	case 6:
749 		if (!getdnum(_word[4], &intercept))
750 			return false;
751 		if (!getdnum(_word[5], &slope))
752 			return false;
753 		if (word_is(3, "above"))
754 			where = above;
755 		else if (word_is(3, "below"))
756 			where = below;
757 		break;
758 	default:
759 		NUMBER_WORDS_ERROR;
760 		return false;
761 	}
762 	if (_colX.size() < 1) {
763 		err("First `read columns ... x'");
764 		return false;
765 	}
766 	if (_colY.size() < 1) {
767 		err("First `read columns ... y'");
768 		return false;
769 	}
770 	if (_colZ.size() < 1) {
771 		err("First `read columns ... z'");
772 		return false;
773 	}
774 	double missing = gr_currentmissingvalue();
775 	switch (where) {
776 	case above:
777 		for (unsigned int i = 0; i < _colX.size(); i++) {
778 			if (_colY[i] > intercept + slope * _colX[i])
779 				_colZ[i] = missing;
780 		}
781 		break;
782 	case below:
783 		for (unsigned int i = 0; i < _colX.size(); i++) {
784 			if (_colY[i] < intercept + slope * _colX[i])
785 				_colZ[i] = missing;
786 		}
787 		break;
788 	default:
789 		demonstrate_command_usage();
790 		err("Fourth word must be `above' or `below'");
791 		return false;
792 	}
793 
794 	return true;
795 }
796 
797 
798 
799 bool
set_font_colorCmd()800 set_font_colorCmd()
801 {
802 	double          red, green, blue;
803 	GriColor       c;
804 	switch (_nword) {
805 	case 4:
806 		if (!look_up_color(_word[3], &red, &green, &blue)) {
807 			err("`set font color' given unknown colorname `\\", _word[3], "'.  Use command `show colornames' to see available colors.", "\\");
808 			return false;
809 		}
810 		c.setRGB(red, green, blue);
811 		_griState.set_color_text(c);
812 		_griState.set_separate_text_color(true);
813 		return true;
814 	case 7:
815 		if (strEQ(_word[3], "rgb")) {
816 			// `set color rgb .red. .green. .blue.'
817 			Require(getdnum(_word[4], &red), READ_WORD_ERROR(".red."));
818 			Require(getdnum(_word[5], &green), READ_WORD_ERROR(".green."));
819 			Require(getdnum(_word[6], &blue), READ_WORD_ERROR(".blue."));
820 			// Clip if necessary
821 			if (red < 0.0) {
822 				warning("`set color rgb' value being clipped to 0");
823 				red = 0.0;
824 			} else if (red > 1.0) {
825 				warning("`set color rgb' value being clipped to 0");
826 				red = 1.0;
827 			}
828 			if (green < 0.0) {
829 				warning("`set color rgb' .green. value being clipped to 0");
830 				green = 0.0;
831 			} else if (green > 1.0) {
832 				warning("`set color rgb' value being clipped to 0");
833 				green = 1.0;
834 			}
835 			if (blue < 0.0) {
836 				warning("`set color rgb' value being clipped to 0");
837 				blue = 0.0;
838 			} else if (blue > 1.0) {
839 				warning("`set color rgb' value being clipped to 0");
840 				blue = 1.0;
841 			}
842 			_griState.set_separate_text_color(true);
843 			c.setRGB(red, green, blue);
844 			_griState.set_color_text(c);
845 			return true;
846 		} else if (strEQ(_word[3], "hsb")) {
847 			// `set color hsb .hue. .saturation. .brightness.'
848 			double          hue, saturation, brightness;
849 			Require(getdnum(_word[4], &hue), READ_WORD_ERROR(".hue."));
850 			Require(getdnum(_word[5], &saturation), READ_WORD_ERROR(".saturation."));
851 			Require(getdnum(_word[6], &brightness), READ_WORD_ERROR(".brightness."));
852 			// Clip if necessary
853 			if (hue < 0.0) {
854 				warning("`set color hsb' value being clipped to 0");
855 				hue = 0.0;
856 			} else if (hue > 1.0) {
857 				warning("`set color hsb' value being clipped to 0");
858 				hue = 1.0;
859 			}
860 			if (saturation < 0.0) {
861 				warning("`set color hsb' value being clipped to 0");
862 				saturation = 0.0;
863 			} else if (saturation > 1.0) {
864 				warning("`set color hsb' value being clipped to 0");
865 				saturation = 1.0;
866 			}
867 			if (brightness < 0.0) {
868 				warning("`set color hsb' value being clipped to 0");
869 				brightness = 0.0;
870 			} else if (brightness > 1.0) {
871 				warning("`set color hsb' value being clipped to 0");
872 				brightness = 1.0;
873 			}
874 			c.setHSV(hue, saturation, brightness);
875 			_griState.set_color_text(c);
876 			_griState.set_separate_text_color(true);
877 			return true;
878 		} else {
879 			err("Can't understand command.");
880 			demonstrate_command_usage();
881 			return false;
882 		}
883 	default:
884 		NUMBER_WORDS_ERROR;
885 		demonstrate_command_usage();
886 		return false;
887 	}
888 }
889 
890 // set font encoding PostscriptStandard|isolatin1
891 bool
set_font_encodingCmd()892 set_font_encodingCmd()
893 {
894 	if (_nword < 4) {
895 		demonstrate_command_usage();
896 		err("`set font encoding' needs to know which method to use ('PostscriptStandard' or 'isolatin1')");
897 		return false;
898 	}
899 	if (!strcmp(_word[3], "PostscriptStandard")) {
900 		gr_set_font_encoding(font_encoding_standard);
901 		return true;
902 	} else if (!strcmp(_word[3], "isolatin1")) {
903 		gr_set_font_encoding(font_encoding_isolatin1);
904 		return true;
905 	} else if (!strcmp(_word[3], "isolatinl")) {
906 		demonstrate_command_usage();
907 		err("`set font encoding' spelling error.  Did you mean `isolatin1' with a `one' at the end??");
908 		return false;
909 	} else {
910 		demonstrate_command_usage();
911 		err("`set font encoding' doesn't understand encoding method.  Choices are `PostscriptStandard' and `isolatin1'");
912 		return false;
913 	}
914 }
915 bool
set_font_sizeCmd()916 set_font_sizeCmd()
917 {
918 	if (_nword < 3) {
919 		err("`set font' what?");
920 		return false;
921 	}
922 	if (!strcmp(_word[2], "size")) {
923 		if (_nword < 4) {
924 			err("`set font size' what?");
925 			return false;
926 		}
927 		if (_nword > 5) {
928 			err("extra words in `set font size' command");
929 			return false;
930 		}
931 		if (!strcmp(_word[3], "default")) {
932 			PUT_VAR("..fontsize..", FONTSIZE_PT_DEFAULT);
933 			gr_setfontsize_pt(FONTSIZE_PT_DEFAULT);
934 			return false;
935 		}
936 		if (!getdnum(_word[3], &tmp))
937 			return false;
938 		if (_nword == 5) {
939 			if (!strcmp(_word[4], "cm"))
940 				tmp *= PT_PER_CM;
941 			else {
942 				err("last word in `set font size' unknown");
943 				return false;
944 			}
945 		}
946 		if (tmp < 0.0 || tmp > 200.0) {
947 			err("ignoring bad font size <0 or >200 cm");
948 			return false;
949 		}
950 		PUT_VAR("..fontsize..", tmp);
951 		gr_setfontsize_pt(tmp);
952 	} else {
953 		err("`set font' what?");
954 		return false;
955 	}
956 	return true;
957 }
958 
959 bool
set_font_toCmd()960 set_font_toCmd()
961 {
962 	if (_nword != 4) {
963 		demonstrate_command_usage();
964 		NUMBER_WORDS_ERROR;
965 		return false;
966 	}
967 	if (!strcmp(_word[3], "Courier"))
968 		gr_setfont(gr_font_Courier);
969 	else if (!strcmp(_word[3], "Helvetica"))
970 		gr_setfont(gr_font_Helvetica);
971 	else if (!strcmp(_word[3], "HelveticaBold"))
972 		gr_setfont(gr_font_HelveticaBold);
973 	else if (!strcmp(_word[3], "Palatino") || !strcmp(_word[3], "PalatinoRoman"))
974 		gr_setfont(gr_font_PalatinoRoman);
975 	else if (!strcmp(_word[3], "Symbol"))
976 		gr_setfont(gr_font_Symbol);
977 	else if (!strcmp(_word[3], "Century"))
978 		gr_setfont(gr_font_Century);
979 	else if (!strcmp(_word[3], "Times"))
980 		gr_setfont(gr_font_TimesRoman);
981 	else if (!strcmp(_word[3], "TimesRoman"))
982 		gr_setfont(gr_font_TimesRoman);
983 	else if (!strcmp(_word[3], "TimesBold"))
984 		gr_setfont(gr_font_TimesBold);
985 
986 	else if (*_word[3] == '\\') {
987 		demonstrate_command_usage();
988 		err("Font name, specified as `\\", _word[3], "' should not have a backslash at the start.", "\\");
989 		return false;
990 	} else {
991 		demonstrate_command_usage();
992 		err("Unknown font `\\", _word[3], "'.\n  Available fonts: Courier, Helvetica, HelveticaBold, Palatino, PalatinoRoman, Symbol, Times, TimesRoman, TimesBold", "Century", "\\");
993 		return false;
994 	}
995 	return true;
996 }
997 
998 bool
set_dashCmd()999 set_dashCmd()
1000 {
1001 	// Start by clearing existing dash list
1002 	_dash.erase(_dash.begin(), _dash.end()); // go to solid
1003 	// Solid line
1004 	if (word_is(_nword - 1, "off")) { // solid line
1005 		return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1006 #if 0
1007 		fprintf(_grPS, "[] 0 d\n");
1008 #endif
1009 		return true;		// solid line
1010 	}
1011 	if (_nword == 2) {
1012 		// `set dash'
1013 		_dash.push_back(0.2);
1014 		_dash.push_back(0.1);
1015 		return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1016 		return true;
1017 	}
1018 	if (_nword == 3) {
1019 		// `set dash .num.'
1020 		if (!getdnum(_word[2], &tmp)) {
1021 			demonstrate_command_usage();
1022 			READ_WORD_ERROR(".n.");
1023 			return false;
1024 		}
1025 		switch ((int) (0.5 + fabs((double) tmp))) {
1026 		case 0:
1027 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1028 		case 1:
1029 			_dash.push_back(0.2);
1030 			_dash.push_back(0.1);
1031 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1032 		case 2:
1033 			_dash.push_back(0.4);
1034 			_dash.push_back(0.1);
1035 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1036 		case 3:
1037 			_dash.push_back(0.6);
1038 			_dash.push_back(0.1);
1039 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1040 		case 4:
1041 			_dash.push_back(0.8);
1042 			_dash.push_back(0.1);
1043 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1044 		case 5:
1045 			_dash.push_back(1.0);
1046 			_dash.push_back(0.1);
1047 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1048 		case 10:
1049 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1050 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1051 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1052 		case 11:
1053 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1054 			_dash.push_back(2.0 * _griState.linewidth_line() / PT_PER_CM);
1055 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1056 		case 12:
1057 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1058 			_dash.push_back(3.0 * _griState.linewidth_line() / PT_PER_CM);
1059 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1060 		case 13:
1061 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1062 			_dash.push_back(4.0 * _griState.linewidth_line() / PT_PER_CM);
1063 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1064 		case 14:
1065 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1066 			_dash.push_back(5.0 * _griState.linewidth_line() / PT_PER_CM);
1067 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1068 		case 15:
1069 			_dash.push_back(_griState.linewidth_line() / PT_PER_CM);
1070 			_dash.push_back(6.0 * _griState.linewidth_line() / PT_PER_CM);
1071 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1072 		default:
1073 			_dash.push_back(0.2);
1074 			_dash.push_back(0.1);
1075 			return gr_set_dash(_dash); // BUG: needed? (set in GriPath anyway)
1076 		}
1077 		return gr_set_dash(_dash); // BUG: need this? (set in GriPath anyway)
1078 	} else {
1079 		// Long list of values
1080 		for (unsigned int i = 0; i < _nword - 2; i++) {
1081 			double tmp;
1082 			if (!getdnum(_word[2 + i], &tmp)) {
1083 				demonstrate_command_usage();
1084 				err("Cannot read all dash/blank values");
1085 				return false;
1086 			}
1087 			_dash.push_back(tmp);
1088 		}
1089 	}
1090 #if 0
1091 	fprintf(_grPS, "[");
1092 	for (unsigned int i = 0; i < _dash.size(); i++)
1093 		fprintf(_grPS, "%.3f ", _dash[i] * PT_PER_CM);
1094 	fprintf(_grPS, "] %d d\n", int(_dash.size()));
1095 #endif
1096 	return gr_set_dash(_dash); // BUG: need this? (set in GriPath anyway)
1097 }
1098 bool
gr_set_dash(const std::vector<double> dash)1099 gr_set_dash(const std::vector<double> dash)
1100 {
1101 	extern FILE *_grPS;
1102 	fprintf(_grPS, "[");
1103 	for (unsigned int i = 0; i < dash.size(); i++)
1104 		fprintf(_grPS, "%.3f ", dash[i] * PT_PER_CM);
1105 	fprintf(_grPS, "] %d d\n", int(dash.size()));
1106 	_griState.set_dash(dash);
1107 	//printf("DEBUG: gr_set_dash() stored a dash of length %ud\n",dash.size());
1108 	return true;
1109 }
1110 
1111 bool
set_ignoreCmd()1112 set_ignoreCmd()
1113 {
1114 	switch (_nword) {
1115 	case 4:
1116 		if (!strcmp(_word[1], "ignore")
1117 		    && !strcmp(_word[2], "error")
1118 		    && !strcmp(_word[3], "eof")) {
1119 			_ignore_eof = true;
1120 		} else {
1121 			demonstrate_command_usage();
1122 			err("Can't understand command.");
1123 		}
1124 		break;
1125 	default:
1126 		demonstrate_command_usage();
1127 		NUMBER_WORDS_ERROR;
1128 		break;
1129 	}
1130 	return false;
1131 }
1132 
1133 bool
set_input_data_windowCmd()1134 set_input_data_windowCmd()
1135 {
1136 	double minval, maxval;
1137 	switch (_nword) {
1138 	case 6:			// set input data window x|y off
1139 		if (strcmp(_word[5], "off")) {
1140 			demonstrate_command_usage();
1141 			err("In this context, the last word must be `off'.");
1142 			return false;
1143 		}
1144 		if (!strcmp(_word[4], "x"))
1145 			_input_data_window_x_exists = false;
1146 		else if (!strcmp(_word[4], "y"))
1147 			_input_data_window_y_exists = false;
1148 		else {
1149 			demonstrate_command_usage();
1150 			err("In this context, the fifth word must be \"x\" or \"y\".");
1151 			return false;
1152 		}
1153 		break;
1154 	case 7:
1155 		if (!strcmp(_word[4], "x")) {
1156 			if (!getdnum(_word[5], &minval)) {
1157 				demonstrate_command_usage();
1158 				err("Can't read x window .min.");
1159 				return false;
1160 			}
1161 			if (!getdnum(_word[6], &maxval)) {
1162 				demonstrate_command_usage();
1163 				err("Can't read x window .max.");
1164 				return false;
1165 			}
1166 			if (minval == maxval) {
1167 				demonstrate_command_usage();
1168 				err("Can't have input data window of zero width.");
1169 				return false;
1170 			}
1171 			if (minval < maxval) {
1172 				_input_data_window_x_min = minval;
1173 				_input_data_window_x_max = maxval;
1174 			} else {
1175 				_input_data_window_x_min = maxval;
1176 				_input_data_window_x_max = minval;
1177 			}
1178 			_input_data_window_x_exists = true;
1179 		} else if (!strcmp(_word[4], "y")) {
1180 			if (!getdnum(_word[5], &minval)) {
1181 				demonstrate_command_usage();
1182 				err("Can't read y window .min.");
1183 				return false;
1184 			}
1185 			if (!getdnum(_word[6], &maxval)) {
1186 				demonstrate_command_usage();
1187 				err("Can't read y window .max.");
1188 				return false;
1189 			}
1190 			if (minval == maxval) {
1191 				demonstrate_command_usage();
1192 				err("Can't have input data window of zero width.");
1193 				return false;
1194 			}
1195 			if (minval < maxval) {
1196 				_input_data_window_y_min = minval;
1197 				_input_data_window_y_max = maxval;
1198 			} else {
1199 				_input_data_window_y_min = maxval;
1200 				_input_data_window_y_max = minval;
1201 			}
1202 			_input_data_window_y_exists = true;
1203 		} else {
1204 			demonstrate_command_usage();
1205 			err("In this context, the fifth word must be \"x\" or \"y\".");
1206 			return false;
1207 		}
1208 		break;
1209 	default:
1210 		demonstrate_command_usage();
1211 		err("Can't understand command.");
1212 		return false;
1213 	}
1214 	return true;
1215 }
1216 
1217 bool
set_input_data_separatorCmd()1218 set_input_data_separatorCmd()
1219 {
1220 	if (_nword < 5) {
1221 		demonstrate_command_usage();
1222 		err("Must specify an input separator (either `TAB' or `default').");
1223 		return false;
1224 	}
1225 	if (_nword > 5) {
1226 		demonstrate_command_usage();
1227 		err("Too many words in `set input data separator' command");
1228 		return false;
1229 	}
1230 	if (!strcmp(_word[4], "default")) {
1231 		extern char _input_data_separator;
1232 		_input_data_separator = ' ';
1233 		return true;
1234 	} else if (!strcmp(_word[4], "TAB")) {
1235 		extern char _input_data_separator;
1236 		_input_data_separator = '\t';
1237 		return true;
1238 	} else {
1239 		demonstrate_command_usage();
1240 		err("`set input data separator' only understands `TAB' and `default', not ``\\", _word[4], "' as given", "\\");
1241 		return false;
1242 	}
1243 }
1244 bool
set_contour_formatCmd()1245 set_contour_formatCmd()
1246 {
1247 	if (_nword < 4) {
1248 		err("Must specify a format for `set contour format'");
1249 		return false;
1250 	}
1251 	if (!strcmp(_word[3], "default")) {
1252 		_contourFmt.assign(CONTOUR_FMT_DEFAULT);
1253 	} else {
1254 		if (*_word[3] == '"') {
1255 			int len = strlen(_word[3]);
1256 			if (len <= 1) {
1257 				_contourFmt.assign(CONTOUR_FMT_DEFAULT);
1258 			} else {
1259 				if (*(_word[3] + len - 1) == '"')
1260 					_contourFmt.assign(_word[3] + 1, len - 2);
1261 				else
1262 					_contourFmt.assign(_word[3] + 1, len - 1);
1263 			}
1264 		} else {
1265 			_contourFmt.assign(_word[3]);
1266 		}
1267 	}
1268 	return true;
1269 }
1270 
1271 // `set contour label position {.start_cm. .between_cm.}|default|centered'
1272 // 0       1     2               3         4   5  6
1273 // `set contour label for lines exceeding .x. cm'
1274 // 0       1     2   3     4         5   6  7
1275 bool
set_contour_labelCmd()1276 set_contour_labelCmd()
1277 {
1278 	extern double   _contour_space_first, _contour_space_later, _contour_minlength;	// <-> draw.c
1279 	extern bool     _contour_space_centered;	// <-> draw.c
1280 	if (word_is(3, "for")) {
1281 		// `set contour label for lines exceeding .x. cm'
1282 		Require(_nword == 8, NUMBER_WORDS_ERROR);
1283 		Require(getdnum(_word[6], &tmp), READ_WORD_ERROR(".x."));
1284 		_contour_minlength = tmp;
1285 	} else {
1286 		// `set contour label position ...'
1287 		switch (_nword) {
1288 		case 5:
1289 			if (word_is(4, "default")) {
1290 				_contour_space_first = -1.0;
1291 				_contour_space_later = -1.0;
1292 			} else if (word_is(4, "centered") || word_is(4, "centred")) {
1293 				_contour_space_centered = true;
1294 			} else {
1295 				demonstrate_command_usage();
1296 				err("Expecting `default', not '\\", _word[4], "'", "\\");
1297 				return false;
1298 			}
1299 			break;
1300 		case 6:
1301 			Require(getdnum(_word[4], &tmp), READ_WORD_ERROR(".start_cm."));
1302 			Require(getdnum(_word[5], &tmp2), READ_WORD_ERROR(".between_cm."));
1303 			_contour_space_first = tmp;
1304 			_contour_space_later = tmp2;
1305 			_contour_space_centered = false;
1306 			break;
1307 		default:
1308 			demonstrate_command_usage();
1309 			err("Can't understand command.");
1310 			return false;
1311 		}
1312 	}
1313 	return true;
1314 }
1315 
1316 // `set contour labels rotated|horizontal'
1317 // `set contour labels whiteunder|nowhiteunder'
1318 // 0       1      2                  3
1319 bool
set_contour_labelsCmd()1320 set_contour_labelsCmd()
1321 {
1322 	extern bool     _contour_label_rotated;	// <-> draw.c startup.c
1323 	extern bool     _contour_label_whiteunder;	// <-> grcntour.c startup.c
1324 	Require(_nword == 4, NUMBER_WORDS_ERROR);
1325 	if (word_is(3, "rotated"))
1326 		_contour_label_rotated = true;
1327 	else if (word_is(3, "horizontal"))
1328 		_contour_label_rotated = false;
1329 	else if (word_is(3, "whiteunder"))
1330 		_contour_label_whiteunder = true;
1331 	else if (word_is(3, "nowhiteunder"))
1332 		_contour_label_whiteunder = false;
1333 	else {
1334 		err("Last word of `set contour labels' must be `rotated|horizontal|whiteunder|nowhiteunder'");
1335 		return false;
1336 	}
1337 	return true;
1338 }
1339 
1340 // Flags for `set flag'
1341 #define NFLAG 100
1342 typedef struct {
1343 	std::string name;
1344 	bool   value;
1345 } FLAG;
1346 FLAG flag[NFLAG];
1347 unsigned int num_flags = 0;
1348 bool
set_flagCmd()1349 set_flagCmd()
1350 {
1351 	if (_nword != 3 && _nword != 4) {
1352 		demonstrate_command_usage();
1353 		NUMBER_WORDS_ERROR;
1354 		return false;
1355 	}
1356 	bool turn_on = true;
1357 	if (_nword == 4 && word_is(3, "off"))
1358 		turn_on = false;
1359 	unsigned int i;
1360 	for (i = 0; i < num_flags; i++) {
1361 		if (flag[i].name == _word[2]) {
1362 			flag[i].value = turn_on;
1363 			return true;
1364 		}
1365 	}
1366 	// It is a new flag
1367 	if (i >= NFLAG) OUT_OF_MEMORY;
1368 	num_flags = i;
1369 	flag[num_flags].name = _word[2];
1370 	flag[num_flags].value = turn_on;
1371 	num_flags++;
1372 	return true;
1373 }
1374 
1375 bool
get_flag(const char * name)1376 get_flag(const char *name)
1377 {
1378 	for (unsigned int i = 0; i < num_flags; i++)
1379 		if (flag[i].name == name)
1380 			return flag[i].value;
1381 	return false;		// unknown flag
1382 }
1383 void
show_flags()1384 show_flags()
1385 {
1386 	extern char _grTempString[];
1387 	if (num_flags) {
1388 		for (unsigned int i = 0; i < num_flags; i++) {
1389 			sprintf(_grTempString, "Flag `%s' is %d\n",
1390 				flag[i].name.c_str(), flag[i].value);
1391 			gr_textput(_grTempString);
1392 		}
1393 	} else {
1394 		gr_textput("No flags exist\n");
1395 	}
1396 }
1397 
1398 // set error action ...
1399 bool
set_error_actionCmd()1400 set_error_actionCmd()
1401 {
1402 	// set error action to core dump
1403 	//   0     1      2  3    4    5
1404 	if (_nword == 6
1405 	    && word_is(2, "action")
1406 	    && word_is(3, "to")
1407 	    && word_is(4, "core")
1408 	    && word_is(5, "dump")) {
1409 		_error_action = 1;
1410 		return true;
1411 	} else {
1412 		demonstrate_command_usage();
1413 		err("Wrong syntax");
1414 		return false;
1415 	}
1416 }
1417 
1418 bool
set_graylevelCmd()1419 set_graylevelCmd()
1420 {
1421 	double          tmp;
1422 	if (_nword != 3) {
1423 		err("`set graylevel' requires 1 parameter");
1424 		return false;
1425 	}
1426 	if (word_is(2, "black")) {
1427 		tmp = 0.0;
1428 	} else if (word_is(2, "white")) {
1429 		tmp = 1.0;
1430 	} else if (!getdnum(_word[2], &tmp)) {
1431 		READ_WORD_ERROR(".brightness.");
1432 		return false;
1433 	}
1434 	if (tmp < 0.0 || tmp > 1.0) {
1435 		err("Ignoring bad graylevel <0 or >1");
1436 		return false;
1437 	}
1438 	PUT_VAR("..graylevel..", tmp);
1439 	PUT_VAR("..red..", tmp);
1440 	PUT_VAR("..green..", tmp);
1441 	PUT_VAR("..blue..", tmp);
1442 	GriColor c;
1443 	c.setRGB(tmp, tmp, tmp);
1444 	_griState.set_color_line(c);
1445 	if (_griState.separate_text_color() == false)
1446 		_griState.set_color_text(c);
1447 	return true;
1448 }
1449 
1450 // If x is very close to xmin or xmax, assign to same
1451 double
tweak_to_limit(double x,double xmin,double xmax)1452 tweak_to_limit(double x, double xmin, double xmax)
1453 {
1454 	double          range = fabs(xmax - xmin);
1455 	if (fabs(x - xmin) < range / 1.0e4)
1456 		return xmin;
1457 	if (fabs(x - xmax) < range / 1.0e4)
1458 		return xmax;
1459 	return x;
1460 }
1461 
1462 // set image colorscale hsb .h. .s. .b. .im_value. hsb .h. .s. .b. .im_value.
1463 // [increment .value.]
1464 //
1465 // set image colorscale rgb .r. .g. .b. .im_value. rgb .r. .g. .b. .im_value.
1466 // [increment .value.]
1467 //
1468 // set image colorscale \name .im_value. \name .im_value. [increment .im_value.]
1469 //
1470 // 0     1          2   3   4   5   6          7   8   9  10  11           12 13
1471 // 14
1472 bool
set_image_colorscaleCmd()1473 set_image_colorscaleCmd()
1474 {
1475 	double           rA, rB, gA, gB, bA, bB, hA, hB, sA, sB, brA, brB;
1476 	double           r, g, b, h, s, br;
1477 	double           valA, valB, indexA, indexB, inc, delta_image = 0.0;
1478 	int             i, levels = 0;
1479 	GriColor::type color_model; // rgb hsv cmyk (GriColor.hh)
1480 	bool            gave_increment = false;
1481 	// check number of words, for the 2 forms and with/without increment
1482 	if (_nword != 13 && _nword != 15 && _nword != 7 && _nword != 9) {
1483 		demonstrate_command_usage();
1484 		NUMBER_WORDS_ERROR;
1485 		return false;
1486 	}
1487 	if (!image_range_exists()) {
1488 		err("First `set image range'");
1489 		return false;
1490 	}
1491 	// Extract endpoint values.
1492 	if (_nword == 7) {
1493 		// `set image colorscale \name .im_value. \name .im_value.'
1494 		if (!look_up_color(_word[3], &rA, &gA, &bA)) {
1495 			err("Unknown colorname `\\", _word[3], "'.  Use command `show colornames' to see available colors.", "\\");
1496 			return false;
1497 		}
1498 		if (!getdnum(_word[4], &valA))
1499 			return false;
1500 		if (!look_up_color(_word[5], &rB, &gB, &bB)) {
1501 			err("Unknown colorname `\\", _word[5], "'.  Use command `show colornames' to see available colors.", "\\");
1502 			return false;
1503 		}
1504 		if (!getdnum(_word[6], &valB))
1505 			return false;
1506 		gave_increment = false;
1507 		color_model = GriColor::rgb; // default
1508 	} else if (_nword == 9) {
1509                 // `set image colorscale \name .im_value. \name .im_value. increment
1510                 // .im_value.
1511 		if (!look_up_color(_word[3], &rA, &gA, &bA)) {
1512 			err("Unknown colorname `\\", _word[3], "'.  Use command `show colornames' to see available colors.", "\\");
1513 			return false;
1514 		}
1515 		if (!getdnum(_word[4], &valA))
1516 			return false;
1517 		if (!look_up_color(_word[5], &rB, &gB, &bB)) {
1518 			err("Unknown colorname `\\", _word[5], "'.  Use command `show colornames' to see available colors.", "\\");
1519 			return false;
1520 		}
1521 		if (!getdnum(_word[6], &valB))
1522 			return false;
1523 		if (!getdnum(_word[8], &inc))
1524 			return false;
1525 		gave_increment = true;
1526 		color_model = GriColor::rgb; // default
1527 	} else {		// BUG: will need to check word number if add cmyk
1528 		// Didn't give named color.  Must be rgb or hsb specification
1529 		if (word_is(3, "rgb")) {
1530 			if (!getdnum(_word[4], &rA))
1531 				return false;
1532 			if (!getdnum(_word[5], &gA))
1533 				return false;
1534 			if (!getdnum(_word[6], &bA))
1535 				return false;
1536 			if (!getdnum(_word[7], &valA))
1537 				return false;
1538 			color_model = GriColor::rgb;
1539 		} else if (word_is(3, "hsb")) {
1540 			if (!getdnum(_word[4], &hA))
1541 				return false;
1542 			if (!getdnum(_word[5], &sA))
1543 				return false;
1544 			if (!getdnum(_word[6], &brA))
1545 				return false;
1546 			if (!getdnum(_word[7], &valA))
1547 				return false;
1548 			color_model = GriColor::hsv;
1549 		} else {
1550 			err("Cannot understand first color specification `\\", _word[3], "'.", "\\");
1551 			return false;
1552 		}
1553 		// Insist that rgb and hsb not be intermixed, since that's how to
1554 		// decide on the blending algorithm
1555 		if (word_is(8, "rgb")) {
1556 			if (color_model != GriColor::rgb) {
1557 				err("Cannot mix color specifications, e.g. `rgb' with `hsb'");
1558 				return false;
1559 			}
1560 			if (!getdnum(_word[9], &rB))
1561 				return false;
1562 			if (!getdnum(_word[10], &gB))
1563 				return false;
1564 			if (!getdnum(_word[11], &bB))
1565 				return false;
1566 			if (!getdnum(_word[12], &valB))
1567 				return false;
1568 			if (rA < 0.0 || rA > 1.0
1569 			    || gA < 0.0 || gA > 1.0
1570 			    || bA < 0.0 || bA > 1.0) {
1571 				err("Require all RGB values to be in range 0.0 to 1.0");
1572 				return false;
1573 			}
1574 		} else if (word_is(8, "hsb")) {
1575 			if (color_model != GriColor::hsv) {
1576 				err("Cannot mix color specifications, e.g. `rgb' with `hsb'");
1577 				return false;
1578 			}
1579 			if (!getdnum(_word[9], &hB))
1580 				return false;
1581 			if (!getdnum(_word[10], &sB))
1582 				return false;
1583 			if (!getdnum(_word[11], &brB))
1584 				return false;
1585 			if (!getdnum(_word[12], &valB))
1586 				return false;
1587 			if (hB < 0.0 || hB > 1.0
1588 			    || sB < 0.0 || sB > 1.0
1589 			    || brB < 0.0 || brB > 1.0) {
1590 				err("Require all HSB values to be in range 0.0 to 1.0");
1591 				return false;
1592 			}
1593 		} else {
1594 			err("Cannot understand color specification `\\", _word[3], "'.", "\\");
1595 			return false;
1596 		}
1597 		// Get increment value if it exists.
1598 		if (word_is(13, "increment")) {
1599 			// then last word is the increment value
1600 			if (!getdnum(_word[14], &inc))
1601 				return false;
1602 			gave_increment = true;
1603 		}
1604 	}
1605 	// Check that endpoints distinct
1606 	if (valA == valB) {
1607 		err("Can't have equal image values at endpoints of `set colorscale'");
1608 		return false;
1609 	}
1610 	if (gave_increment) {
1611 		if (!well_ordered(valA, valB, inc))
1612 			inc = -inc;
1613 		if (!gr_multiple(fabs(valB - valA), fabs(inc), fabs(0.001 * inc))) {
1614 			err("\
1615 `set image colorscale ... requires range/increment to be integral to 0.1%");
1616 			return false;
1617 		}
1618 		delta_image = 255.0 * fabs(inc / (valA - valB));
1619 		levels = (int) floor(1.0e-5 + fabs((valA - valB) / inc));
1620 	}
1621 	// Calculate the transforms.  NB: indexA and indexB are of the
1622 	// indices of transform endpoints.  Do RGB and HSB separately, each
1623 	// interpolating linearly in its own space.  (NB: a nonlinear transform
1624 	// links RGB and HSB, so linear movement between equivalent endpoints in
1625 	// these two spaces yields nonequivalent values.)
1626 	//
1627 	indexA = 255.0 * (valA - _image0) / (_image255 - _image0);
1628 	indexB = 255.0 * (valB - _image0) / (_image255 - _image0);
1629 	if (color_model == GriColor::rgb) {
1630 		double           delta_r = delta_image * fabs(rA - rB);
1631 		double           delta_g = delta_image * fabs(gA - gB);
1632 		double           delta_b = delta_image * fabs(bA - bB);
1633 		// For each target imageTransform[], make a clipped, linear blend
1634 		// from (rA,gA,bA)  to (rB,gB,bB).  Possibly pass this linear blend
1635 		// to 'quantize', to make it into a staircase function.
1636 		for (i = 0; i < 256; i++) {
1637 			r = 255.0
1638 				* (rA + (rB - rA) * pin0_1((i - indexA) / (indexB - indexA)));
1639 			g = 255.0 *
1640 				(gA + (gB - gA) * pin0_1((i - indexA) / (indexB - indexA)));
1641 			b = 255.0 *
1642 				(bA + (bB - bA) * pin0_1((i - indexA) / (indexB - indexA)));
1643 			if (gave_increment) {
1644 				r = quantize(r, levels, delta_r);
1645 				g = quantize(g, levels, delta_g);
1646 				b = quantize(b, levels, delta_b);
1647 			}
1648 			_imageTransform[i] = (unsigned char) pin0_255(r);
1649 			_imageTransform[i + 256] = (unsigned char) pin0_255(g);
1650 			_imageTransform[i + 512] = (unsigned char) pin0_255(b);
1651 		}
1652 		// Ensure that endpoints match (rgb)A or (rgb)B, as appropriate.
1653 		if (indexA < indexB) {
1654 			_imageTransform[0] = (unsigned char) (255.0 * rA);
1655 			_imageTransform[256] = (unsigned char) (255.0 * gA);
1656 			_imageTransform[512] = (unsigned char) (255.0 * bA);
1657 			_imageTransform[0 + 255] = (unsigned char) (255.0 * rB);
1658 			_imageTransform[256 + 255] = (unsigned char) (255.0 * gB);
1659 			_imageTransform[512 + 255] = (unsigned char) (255.0 * bB);
1660 		} else {
1661 			_imageTransform[0] = (unsigned char) (255.0 * rB);
1662 			_imageTransform[256] = (unsigned char) (255.0 * gB);
1663 			_imageTransform[512] = (unsigned char) (255.0 * bB);
1664 			_imageTransform[0 + 255] = (unsigned char) (255.0 * rA);
1665 			_imageTransform[256 + 255] = (unsigned char) (255.0 * gA);
1666 			_imageTransform[512 + 255] = (unsigned char) (255.0 * bA);
1667 		}
1668 	} else if (color_model == GriColor::hsv) {
1669 		double           delta_h = delta_image * fabs(hA - hB);
1670 		double           delta_s = delta_image * fabs(sA - sB);
1671 		double           delta_br = delta_image * fabs(brA - brB);
1672 		// For each target imageTransform[], make a clipped, linear blend
1673 		// from (hA,sA,brA)  to (hB,sB,brB).  Possibly pass this linear blend
1674 		// to 'quantize', to make it into a staircase function.
1675 		for (i = 0; i < 256; i++) {
1676 			h = (hA + (hB - hA) * pin0_1((i - indexA) / (indexB - indexA)));
1677 			s = (sA + (sB - sA) * pin0_1((i - indexA) / (indexB - indexA)));
1678 			br = (brA + (brB - brA) * pin0_1((i - indexA) / (indexB - indexA)));
1679 			if (gave_increment) {
1680 				// Quantize HSB *before* conversion to RGB
1681 				h = quantize(255.0 * h, levels, delta_h) / 255.0;
1682 				s = quantize(255.0 * s, levels, delta_s) / 255.0;
1683 				br = quantize(255.0 * br, levels, delta_br) / 255.0;
1684 			}
1685 			gr_hsv2rgb(h, s, br, &r, &g, &b);
1686 			_imageTransform[i] = (unsigned char) pin0_255(255.0 * r);
1687 			_imageTransform[i + 256] = (unsigned char) pin0_255(255.0 * g);
1688 			_imageTransform[i + 512] = (unsigned char) pin0_255(255.0 * b);
1689 		}
1690 		// Ensure that endpoints match (rgb)A or (rgb)B, as appropriate.
1691 		gr_hsv2rgb(hA, sA, brA, &rA, &gA, &bA);
1692 		gr_hsv2rgb(hB, sB, brB, &rB, &gB, &bB);
1693 		if (indexA < indexB) {
1694 			_imageTransform[0] = (unsigned char) (255.0 * rA);
1695 			_imageTransform[256] = (unsigned char) (255.0 * gA);
1696 			_imageTransform[512] = (unsigned char) (255.0 * bA);
1697 			_imageTransform[0 + 255] = (unsigned char) (255.0 * rB);
1698 			_imageTransform[256 + 255] = (unsigned char) (255.0 * gB);
1699 			_imageTransform[512 + 255] = (unsigned char) (255.0 * bB);
1700 		} else {
1701 			_imageTransform[0] = (unsigned char) (255.0 * rB);
1702 			_imageTransform[256] = (unsigned char) (255.0 * gB);
1703 			_imageTransform[512] = (unsigned char) (255.0 * bB);
1704 			_imageTransform[0 + 255] = (unsigned char) (255.0 * rA);
1705 			_imageTransform[256 + 255] = (unsigned char) (255.0 * gA);
1706 			_imageTransform[512 + 255] = (unsigned char) (255.0 * bA);
1707 		}
1708 	} else {
1709 		err("Cannot handle this color model.  Only `rgb' and `hsb' are known at this time");
1710 		return false;
1711 	}
1712 	// All done.  Everything was OK.
1713 	_imageTransform_exists = true;
1714 	_image_color_model = rgb_model;
1715 	return true;
1716 }
1717 
1718 bool
set_image_grayscaleCmd()1719 set_image_grayscaleCmd()
1720 {
1721 	int             i;
1722 	double          indexA;
1723 	if (_nword < 3) {
1724 		err("`set image' what?");
1725 		return false;
1726 	}
1727 	// Do a (redundant?) check
1728 	if (!word_is(2, "grayscale") && !word_is(2, "greyscale")) {
1729 		err("`set image' what?");
1730 		return false;
1731 	}
1732 	if (!image_range_exists()) {
1733 		err("First `set image range'");
1734 		return false;
1735 	}
1736 	// `set image grayscale'
1737 	if (_nword == 3) {
1738 		if (!_image.storage_exists) {
1739 			err("First, you must read or create an image");
1740 			return false;
1741 		}
1742 		unsigned char valTmp;
1743 		unsigned char valA = *(_image.image);
1744 		unsigned char valB = valA;
1745 		for (unsigned int ij = 1; ij < _image.ras_width * _image.ras_height; ij++) {
1746 			valTmp = *(_image.image + ij);
1747 			if (valTmp < valA)
1748 				valA = valTmp;
1749 			if (valTmp > valB)
1750 				valB = valTmp;
1751 		}
1752 		indexA = pin0_255(255.0 * (valA - _image0) / (_image255 - _image0));
1753 		double scale = 255 / (valB - valA);
1754 		for (i = 0; i < 256; i++) {
1755 			_imageTransform[i] = (unsigned char) floor(double(pin0_255((int) floor(scale * (i - indexA)))));
1756 		}
1757 		_imageTransform[0] = (_imageTransform[0] <= 128) ? 0 : 255;
1758 		_imageTransform[255] = (_imageTransform[255] <= 128) ? 0 : 255;
1759 		_imageTransform_exists = true;
1760 		_image_color_model = bw_model;
1761                 //printf("_image0 %f _image255 %f valA %d  valB %d scale %f indexA %f\n", _image0, _image255, valA, valB, scale, indexA);
1762 		return true;
1763 	}
1764 	// `set image grayscale [black .bl. white .wh. [increment .inc.]]'
1765 	if (_nword > 3
1766 	    && (!strcmp(_word[3], "black")
1767 		|| !strcmp(_word[3], "white")
1768 		|| !strcmp(_word[3], "increment"))) {
1769 		double          inc = 0.0, valB, valA, scale;
1770 		bool            gave_increment = false;
1771 		if (1 == get_cmd_values(_word, _nword, "black", 1, _dstack))
1772 			valA = _dstack[0];
1773 		else {
1774 			err("Can't read .bl. in [white .wh. black .bl.]");
1775 			return false;
1776 		}
1777 		if (1 == get_cmd_values(_word, _nword, "white", 1, _dstack))
1778 			valB = _dstack[0];
1779 		else {
1780 			err("Can't read .wh. in [white .wh. black .bl.]");
1781 			return false;
1782 		}
1783 		if (1 == get_cmd_values(_word, _nword, "increment", 1, _dstack)) {
1784 			inc = _dstack[0];
1785 			gave_increment = true;
1786 		}
1787 		indexA = 255.0 * (valA - _image0) / (_image255 - _image0);
1788 		scale = (_image255 - _image0) / (valB - valA);
1789 		if (gave_increment) {
1790 			// Set up quantized gray levels.  For example, `set image
1791 			// grayscale white -1 black 1 increment 0.5' will set up 4 gray
1792 			// levels; thus, _imageTransform[] will be set up with 4 bands,
1793 			// containing the distinct values (1/5, 2/5, 3/5, 4/5) * 255.
1794 			//
1795 			// BUG -- this quantized graylevel code has had lots of bugs
1796 			double          delta_image;	// image change per level
1797 			int             levels;	// number of levels
1798 			if (!well_ordered(valA, valB, inc))
1799 				inc = -inc;
1800 			if (!gr_multiple(valB - valA, inc, 0.001 * inc)) {
1801 				err("\
1802 `set image grayscale black .bl. white .wh. increment .inc.'\n\
1803   has (.wh. - .bl.) not a multiple of .inc. to within 0.1%");
1804 				return false;
1805 			}
1806 			if (valA == valB) {
1807 				err("\
1808 `set image grayscale black .bl. white .wh. increment .inc.'\n\
1809   has .wh. = .bl., which is not allowed.");
1810 				return false;
1811 			}
1812 			delta_image = 255.0 * fabs(inc / (valB - valA));
1813 			levels = (int) floor(1.0e-5 + fabs((valB - valA) / inc));
1814 			for (i = 0; i < 256; i++) {
1815 				// The .001 below is to prevent rounding problems.
1816 				_imageTransform[i] = (unsigned char)
1817 					floor(double(pin0_255((int) floor(double(0.001 + quantize(scale * (i - indexA), levels, delta_image))))));
1818 			}
1819 			// BUG -- the following is a total kludge, because I could not
1820 			// find an adequate rounding macro
1821 			_imageTransform[0] = (_imageTransform[0] <= 128) ? 0 : 255;
1822 			_imageTransform[255] = (_imageTransform[255] <= 128) ? 0 : 255;
1823 			_imageTransform_exists = true;
1824 			_image_color_model = bw_model;
1825 			return true;
1826 		} else {
1827 			// Increment was not given.
1828 			for (i = 0; i < 256; i++) {
1829 				_imageTransform[i]
1830 					= (unsigned char) floor(double(pin0_255((int) floor(scale * (i - indexA)))));
1831 			}
1832 		}
1833 		_imageTransform_exists = true;
1834 		_image_color_model = bw_model;
1835 		return true;
1836 	}
1837 	demonstrate_command_usage();
1838 	err("Can't understand command.");
1839 	return false;
1840 }
1841 
1842 bool
set_image_grayscale_using_hist()1843 set_image_grayscale_using_hist()
1844 {
1845 	int             i;
1846 	double          sum = 0.0, range;
1847 	double          wh, bl;
1848 	if (!image_range_exists()) {
1849 		err("First `set image range'");
1850 		return false;
1851 	}
1852 	// Since parser got to this point, must at least match `set image
1853 	// grayscale using histogram'
1854 	if (_nword == 5) {
1855 		// `set image grayscale using histogram'
1856 		calculate_image_histogram();
1857 		// The range is sum(255) - sum(0), but sum(255) is unity, because
1858 		// _imageHist[] is defined that way, and sum(0) is _imageHist[0].
1859 		range = 1.0 - _imageHist[0];
1860 		if (range == 0.0)
1861 			range = 1.0;
1862 		for (i = 0; i < 256; i++) {
1863 			_imageTransform[i] = (unsigned char) floor(pin0_255(255.0 * (1.0 - (sum - _imageHist[0]) / range)));
1864 			sum += _imageHist[i];
1865 		}
1866 		_imageTransform_exists = true;
1867 	} else if (_nword == 9) {
1868 		// `set image grayscale using histogram black .bl. white .wh.'
1869 		int             start, end;
1870 		double          sum_to_end = 0.0, sum_to_start = 0.0;
1871 		if (1 == get_cmd_values(_word, _nword, "black", 1, _dstack)) {
1872 			bl = _dstack[0];
1873 		} else {
1874 			err("Can't read .bl. in [black .bl. white .wh.]");
1875 			return false;
1876 		}
1877 		if (1 == get_cmd_values(_word, _nword, "white", 1, _dstack)) {
1878 			wh = _dstack[0];
1879 		} else {
1880 			err("Can't read .wh. in [black .bl. white .wh.]");
1881 			return false;
1882 		}
1883 		calculate_image_histogram();
1884 		start = value_to_image(wh);	// pixel
1885 		end = value_to_image(bl);	// pixel
1886 		for (i = 0; i < start; i++)
1887 			sum_to_start += _imageHist[i];
1888 		for (i = 0; i < end; i++)
1889 			sum_to_end += _imageHist[i];
1890 		range = sum_to_end - sum_to_start;
1891 		if (range == 0.0)
1892 			range = 1.0;
1893 		for (i = 0; i < 256; i++) {
1894 			_imageTransform[i] = (unsigned char) floor(pin0_255(255.0 * (1.0 - (sum - sum_to_start) / range)));
1895 			sum += _imageHist[i];
1896 		}
1897 		_imageTransform_exists = true;
1898 	} else {
1899 		demonstrate_command_usage();
1900 		err("Can't understand command.");
1901 		return false;
1902 	}
1903 	_image_color_model = bw_model;
1904 	return true;
1905 }
1906 
1907 // `set image missing value color to white|black|{graylevel .brightness.}'
1908 bool
set_image_missingCmd()1909 set_image_missingCmd()
1910 {
1911 	if (!image_range_exists()) {
1912 		err("First `set image range'");
1913 		return false;
1914 	}
1915 	if (_nword == 7) { // `set image missing value color to white|black'
1916 		if (word_is(6, "white")) {
1917 			_image_missing_color_red = 1.0;
1918 			_image_missing_color_green = 1.0;
1919 			_image_missing_color_blue = 1.0;
1920 		} else if (word_is(6, "black")) {
1921 			_image_missing_color_red = 0.0;
1922 			_image_missing_color_green = 0.0;
1923 			_image_missing_color_blue = 0.0;
1924 		} else {
1925 			demonstrate_command_usage();
1926 			err("Unknown color specified for image missing-value.");
1927 			return false;
1928 		}
1929 	} else if (_nword == 10) {
1930 		// `set image missing value color to rgb .red. .green. .blue.'
1931 		if (!strcmp(_word[6], "rgb")) {
1932 			double red, green, blue;
1933 			Require(getdnum(_word[7], &red), READ_WORD_ERROR(".red."));
1934 			Require(getdnum(_word[8], &green), READ_WORD_ERROR(".green."));
1935 			Require(getdnum(_word[9], &blue), READ_WORD_ERROR(".blue."));
1936 			// Clip if necessary
1937 			CHECK_RGB_RANGE(red);
1938 			CHECK_RGB_RANGE(green);
1939 			CHECK_RGB_RANGE(blue);
1940 			_image_missing_color_red = red;
1941 			_image_missing_color_green = green;
1942 			_image_missing_color_blue = blue;
1943 		}
1944 	} else {
1945 		// `set image missing value color to {graylevel .brightness.}'
1946 		if (1 == get_cmd_values(_word, _nword, "graylevel", 1, _dstack)
1947 		    || 1 ==get_cmd_values(_word, _nword, "greylevel", 1, _dstack)) {
1948 			if (_dstack[0] < 0.0) {
1949 				warning("`set image missing value color': clipping graylevel to 0.0");
1950 				_image_missing_color_red = 0.0;
1951 				_image_missing_color_green = 0.0;
1952 				_image_missing_color_blue = 0.0;
1953 			} else if (_dstack[0] > 1.0) {
1954 				warning("`set image missing value color': clipping graylevel to 0.0");
1955 				_image_missing_color_red = 1.0;
1956 				_image_missing_color_green = 1.0;
1957 				_image_missing_color_blue = 1.0;
1958 			} else {
1959 				_image_missing_color_red = _dstack[0];
1960 				_image_missing_color_green = _dstack[0];
1961 				_image_missing_color_blue = _dstack[0];
1962 			}
1963 		} else {
1964 			demonstrate_command_usage();
1965 			err("Can't understand color specification.");
1966 			return false;
1967 		}
1968 	}
1969 	return true;
1970 }
1971 
1972 bool
set_image_rangeCmd()1973 set_image_rangeCmd()
1974 {
1975 	double          tmp1, tmp2;
1976 	switch (_nword) {
1977 	case 5:
1978 		if (!getdnum(_word[3], &tmp1))
1979 			return false;
1980 		if (!getdnum(_word[4], &tmp2))
1981 			return false;
1982 		_image0 = tmp1;
1983 		_image255 = tmp2;
1984 		return true;
1985 	default:
1986 		demonstrate_command_usage();
1987 		NUMBER_WORDS_ERROR;
1988 		return false;
1989 	}
1990 }
1991 
1992 // `set grid missing inside|outside       curve'
1993 // `set grid missing  above|below   .intercept. .slope'
1994 //    0    1       2            3             4      5
1995 bool
set_grid_missingCmd()1996 set_grid_missingCmd()
1997 {
1998 	double intercept, slope;
1999 	typedef enum { above, below, inside, outside, dont_know } WHERE;
2000 	WHERE where = dont_know;
2001 	switch(_nword) {
2002 	case 5:
2003 		if (word_is(3, "inside"))
2004 			where = inside;
2005 		else if (word_is(3, "outside"))
2006 			where = outside;
2007 		break;
2008 	case 6:
2009 		if (!getdnum(_word[4], &intercept))
2010 			return false;
2011 		if (!getdnum(_word[5], &slope))
2012 			return false;
2013 		if (word_is(3, "above"))
2014 			where = above;
2015 		else if (word_is(3, "below"))
2016 			where = below;
2017 		break;
2018 	default:
2019 		demonstrate_command_usage();
2020 		NUMBER_WORDS_ERROR;
2021 		return false;
2022 	}
2023 	if (!grid_exists())
2024 		return false;
2025 	unsigned int i, j;
2026 	switch (where) {
2027 	case above:
2028 		for (i = 0; i < _num_xmatrix_data; i++)
2029 			for (j = 0; j < _num_ymatrix_data; j++)
2030 				if (_ymatrix[j] > intercept + slope * _xmatrix[i])
2031 					_legit_xy(i, j) = false;
2032 		return true;		// ok
2033 	case below:
2034 		for (i = 0; i < _num_xmatrix_data; i++)
2035 			for (j = 0; j < _num_ymatrix_data; j++)
2036 				if (_ymatrix[j] < intercept + slope * _xmatrix[i])
2037 					_legit_xy(i, j) = false;
2038 		return true;		// ok
2039 	case inside:
2040 		return set_grid_missing_curve(true);
2041 	case outside:
2042 		return set_grid_missing_curve(false);
2043 	default:
2044 		demonstrate_command_usage();
2045 		err("Fourth word must be `above', `below', `inside' or `outside'");
2046 		return false;
2047 	}
2048 }
2049 
2050 bool
set_grid_missing_curve(bool inside)2051 set_grid_missing_curve(bool inside)
2052 {
2053 	if (!inside) {
2054 		err("'set grid missing outside curve' not allowed");
2055 		return false;
2056 	}
2057 	unsigned int imax = 0, start = 0;
2058 	for (unsigned int i = 0; i < _colX.size(); i++) {
2059 		imax = i;
2060 		if (gr_missingx(_colX[i])
2061 		    || gr_missingy(_colY[i])
2062 		    || i > _colX.size() - 1) {
2063 			if (!mask_an_island(_colX.begin() + start,
2064 					    _colY.begin() + start, i - start))
2065 				return false;	// problem
2066 			while ((gr_missingx(_colX[i]) || gr_missingy(_colY[i]))
2067 			       && i < _colX.size())
2068 				i++;
2069 			start = i;
2070 		}
2071 	}
2072 	if (imax <= _colX.size()) {
2073 		mask_an_island(_colX.begin() + start,
2074 			       _colY.begin() + start,
2075 			       imax - start);
2076 	}
2077 	return true;
2078 }
2079 
2080 bool
mask_an_island(double * x,double * y,unsigned int n)2081 mask_an_island(double *x, double *y, unsigned int n)
2082 {
2083 	unsigned right_edge = 0;
2084 	double xmax = x[0];
2085 	unsigned int i;
2086 	for (i = 1; i < n; i++) {
2087 		if (x[i] > xmax) {
2088 			xmax = x[i];
2089 			right_edge = i;
2090 		}
2091 	}
2092 //DEBUG printf("\nIsland:\n");
2093 //DEBUG for (i = 0; i < n; i++) {
2094 //DEBUG     printf("    %f %f\n", x[i], y[i]);
2095 //DEBUG }
2096 //DEBUG printf("Rotates to:\n");
2097 //DEBUG for (i = 0; i < n; i++) {
2098 //DEBUG     int ii = (i + right_edge) % n;
2099 //DEBUG     printf("    %f %f\n", x[ii], y[ii]);
2100 //DEBUG }
2101 	for (i = 0; i < n; i++) {
2102 		int ii = (i + right_edge) % n;
2103 		for (unsigned int ixm = 0; ixm < _num_xmatrix_data; ixm++) {
2104 			if (between(_xmatrix[ixm], x[(ii+1)%n], x[ii])) {
2105 //DEBUG printf("mask out ixm=%d at %f, bracketed by %f - %f\n", ixm, _xmatrix[ixm], x[(ii+1)%n],x[ii]);
2106 				for (int iym = (int)_num_ymatrix_data - 1; iym > -1; iym--) {
2107 					if (_ymatrix[iym] <=
2108 					    interpolate_linear(_xmatrix[ixm],
2109 							       x[ii], y[ii],
2110 							       x[(ii+1)%n], y[(ii+1)%n])) {
2111 						// Reverse things below
2112 //DEBUG printf("reverse below y(%d)=%f\n", iym, _ymatrix[iym]);
2113 						while (iym > -1) {
2114 							_legit_xy(ixm, iym) = _legit_xy(ixm, iym) == true
2115 								? false : true;
2116 							iym--;
2117 						}
2118 //DEBUG printf("i=%d; grid=\n", i); show_grid_maskCmd();
2119 						break;	// just in case
2120 					}
2121 				}
2122 			}
2123 		}
2124 	}
2125 	return true;
2126 }
2127 
2128 static          bool
width_rapidograph(char * s,double * w)2129 width_rapidograph(char *s, double *w)
2130 {
2131 // Named pen sizes, following the notation of Rapidograph(TM)
2132 // technical drawing pens.
2133 #define NUM_RAPIDOGRAPH 16
2134 	typedef struct {
2135 		const char *name;		// allow both 'X' and 'x' in names
2136 		float       width;		// in points
2137 	} r;
2138 	r r_table[NUM_RAPIDOGRAPH] = {
2139 		{"6x0", 0.369},
2140 		{"6X0", 0.369},
2141 		{"4x0", 0.510},
2142 		{"4X0", 0.510},
2143 		{"3x0", 0.709},
2144 		{"3X0", 0.709},
2145 		{"00",  0.850},
2146 		{"0",   0.992},
2147 		{"1",   1.417},
2148 		{"2",   1.701},
2149 		{"2.5", 1.984},
2150 		{"3",   2.268},
2151 		{"3.5", 2.835},
2152 		{"4",   3.402},
2153 		{"6",   3.969},
2154 		{"7",   5.669}
2155 	};
2156 	std::string ss(s);
2157 	un_double_quote(ss);
2158 	for (int i = 0; i < NUM_RAPIDOGRAPH; i++)
2159 		if (!strcmp(ss.c_str(), r_table[i].name)) {
2160 			*w = r_table[i].width;
2161 			return true;
2162 		}
2163 	err("Unknown rapidograph pen size `\\", s, "' requested.\n       Permitted sizes: 6x0, 4x0, 3x0, 00, 0, 1, 2, 2.5, 3, 3.5, 4, 6, 7", "\\");
2164 	return false;
2165 }
2166 
2167 bool
set_line_capCmd()2168 set_line_capCmd()
2169 {
2170 	if (_nword != 4) {
2171 		err("`set line cap .type.' has wrong number of words on command line");
2172 		return false;
2173 	}
2174 	double tmp;
2175 	if (!getdnum(_word[3], &tmp)) {
2176 		READ_WORD_ERROR(".type.");
2177 		return false;
2178 	}
2179 	int t = int(floor(0.5 + tmp));
2180 	if (t < 0 || t > 2) {
2181 		err("`set line cap .type.' only permits types 0, 1 and 2");
2182 		return false;
2183 	}
2184 	_griState.set_line_cap(t);
2185 	return true;
2186 }
2187 
2188 bool
set_line_joinCmd()2189 set_line_joinCmd()
2190 {
2191 	if (_nword != 4) {
2192 		err("`set line join .type.' has wrong number of words on command line");
2193 		return false;
2194 	}
2195 	double tmp;
2196 	if (!getdnum(_word[3], &tmp)) {
2197 		READ_WORD_ERROR(".type.");
2198 		return false;
2199 	}
2200 	int t = int(floor(0.5 + tmp));
2201 	if (t < 0 || t > 2) {
2202 		err("`set line join .type.' only permits types 0, 1 and 2");
2203 		return false;
2204 	}
2205 	_griState.set_line_join(t);
2206 	return true;
2207 }
2208 
2209 bool
set_line_widthCmd()2210 set_line_widthCmd()
2211 {
2212 	//show_words();
2213 	double          w;		// the width, in pt
2214 	unsigned int    skip = 0;
2215 	int             what = 0;	// -1=curve/rapido 0=curve 1=axis 2=symbol 3=all
2216 	if (word_is(3, "axis")) {
2217 		skip = 1;
2218 		what = 1;
2219 	} else if (word_is(3, "symbol")) {
2220 		skip = 1;
2221 		what = 2;
2222 	} else if (word_is(3, "all")) {
2223 		skip = 1;
2224 		what = 3;
2225 	} else if (word_is(3, "rapidograph")) {
2226 		what = -1;
2227 	} else {
2228 		if (_nword > 4) {
2229 			err("`set line width' found unexpected word `\\", _word[3], "'.", "\\");
2230 			return false;
2231 		}
2232 	}
2233 	// Check for 'set line width ...' with no width specification
2234 	if (_nword <= (skip + 3)) {
2235 		NUMBER_WORDS_ERROR;
2236 		return false;
2237 	}
2238 	// Take care of 'default', simple, and 'rapidograph' styles
2239 	if (word_is(3 + skip, "default")) {
2240 		Require(_nword == (4 + skip), NUMBER_WORDS_ERROR);
2241 		switch (what) {
2242 		case 0:
2243 			w = LINEWIDTH_DEFAULT;
2244 			break;
2245 		case 1:
2246 			w = LINEWIDTHAXIS_DEFAULT;
2247 			break;
2248 		case 2:
2249 			w = LINEWIDTHSYMBOL_DEFAULT;
2250 			break;
2251 		case 3:
2252 			w = LINEWIDTH_DEFAULT;
2253 			break;
2254 		default:
2255 			w = LINEWIDTHSYMBOL_DEFAULT;
2256 		}
2257 	} else if (_nword == (4 + skip) && !word_is(3, "rapidograph")) {
2258 		// Simple case
2259 		if (what == -1) {
2260 			if (!width_rapidograph(_word[3 + skip], &w)) {
2261 				err("`set line width' cannot understand rapidograph name");
2262 				return false;
2263 			}
2264 		} else {
2265 			if (!getdnum(_word[3 + skip], &w))
2266 				return false;
2267 		}
2268 	} else if (_nword == (5 + skip)) {
2269 		// Rapidograph style
2270 		if (word_is(3 + skip, "rapidograph")) {
2271 			if (!width_rapidograph(_word[4 + skip], &w)) {
2272 				err("`set line width' cannot understand rapidograph name");
2273 				return false;
2274 			}
2275 		} else {
2276 			err("`set line width' expecting word `rapidograph'");
2277 			return false;
2278 		}
2279 	} else {
2280 		if (word_is(_nword - 1, "rapidograph")) {
2281 			err("`set line width rapidograph' needs a pen-width name");
2282 		} else {
2283 			err("`set line width' cannot understand the width");
2284 		}
2285 		return false;
2286 	}
2287 	// check that w is not crazily small ... a common blunder.  See if less
2288 	// than 1 dot on a 600dpi printer.
2289 	if (w != 0.0) {
2290 		if (w / PT_PER_CM < 2.54 / 4800) {
2291 			char msg[200];
2292 			sprintf(msg,
2293 				"a line width of %g points is barely resolved on a 4800 dpi printer",
2294 				w);
2295 			warning(msg);
2296 		} else if (w / PT_PER_CM < 2.54 / 2400) {
2297 			char msg[200];
2298 			sprintf(msg,
2299 				"a line width of %g points is barely resolved on a 2400 dpi printer",
2300 				w);
2301 			warning(msg);
2302 		} else if (w / PT_PER_CM < 2.54 / 600) {
2303 			char msg[200];
2304 			sprintf(msg,
2305 				"a line width of %g points is barely resolved on a 600 dpi printer",
2306 				w);
2307 			warning(msg);
2308 		} else if (w / PT_PER_CM < 2.54 / 400) {
2309 			char msg[200];
2310 			sprintf(msg,
2311 				"a line width of %g points is barely resolved on a 400 dpi printer",
2312 				w);
2313 			warning(msg);
2314 		}
2315 	}
2316 	switch (what) {
2317 	case -1:
2318 	case 0:
2319 		_griState.set_linewidth_line(w);
2320 		PUT_VAR("..linewidth..", w);
2321 		break;
2322 	case 1:
2323 		_griState.set_linewidth_axis(w);
2324 		PUT_VAR("..linewidthaxis..", w);
2325 		break;
2326 	case 2:
2327 		_griState.set_linewidth_symbol(w);
2328 		PUT_VAR("..linewidthsymbol..", w);
2329 		break;
2330 	case 3:
2331 		_griState.set_linewidth_line(w);
2332 		PUT_VAR("..linewidth..", w);
2333 		_griState.set_linewidth_axis(w);
2334 		PUT_VAR("..linewidthaxis..", w);
2335 		_griState.set_linewidth_symbol(w);
2336 		PUT_VAR("..linewidthsymbol..", w);
2337 		break;
2338 	default:
2339 		_griState.set_linewidth_line(w);
2340 		PUT_VAR("..linewidthline..", w);
2341 	}
2342 	return true;
2343 }
2344 
2345 bool
set_missing_valueCmd()2346 set_missing_valueCmd()
2347 {
2348 	// `set missing value #|none'
2349 	double          tmp;
2350 	if (_nword == 4) {
2351 		if (!strcmp(_word[3], "none")) {
2352 			gr_set_missing_value_none();
2353 		} else if (getdnum(_word[3], &tmp)) {
2354 			gr_set_missing_value(tmp);
2355 			PUT_VAR("..missingvalue..", gr_currentmissingvalue());
2356 			{
2357 				char tmp[100];
2358 				sprintf(tmp, "%f", gr_currentmissingvalue());
2359 				put_syn("\\.missingvalue.", tmp, true);
2360 			}
2361 		} else {
2362 			// Actually, can not reach this code, with current error checking
2363 			// in getdnum().
2364 			demonstrate_command_usage();
2365 			READ_WORD_ERROR(".missing_value.");
2366 		}
2367 	} else {
2368 		demonstrate_command_usage();
2369 		NUMBER_WORDS_ERROR;
2370 		return false;
2371 	}
2372 	return true;
2373 }
2374 
2375 bool
set_page_sizeCmd()2376 set_page_sizeCmd()
2377 {
2378 	Require(_nword == 4, err("`set page size' requires 1 parameter"));
2379 	const char *s = _word[3];	// save typing
2380 	extern rectangle _page_size;
2381 	if (!strcmp(s, "letter")) {
2382 		_page_size.set(0.0, 0.0, 8.5, 11.0);
2383 		_page_size.scale(CM_PER_IN);
2384 	} else if (!strcmp(s, "legal")) {
2385 		_page_size.set(0.0, 0.0, 8.5, 14.0);
2386 		_page_size.scale(CM_PER_IN);
2387 	} else if (!strcmp(s, "folio")) {
2388 		_page_size.set(0.0, 0.0, 8.5, 13.0);
2389 		_page_size.scale(CM_PER_IN);
2390 	} else if (!strcmp(s, "tabloid")) {
2391 		_page_size.set(0.0, 0.0, 11.0, 17.0);
2392 		_page_size.scale(CM_PER_IN);
2393 	} else if (!strcmp(s, "A5")) {
2394 		_page_size.set(0.0, 0.0, 14.8, 21.0);
2395 	} else if (!strcmp(s, "A4")) {
2396 		_page_size.set(0.0, 0.0, 21.0, 29.7);
2397 	} else if (!strcmp(s, "A3")) {
2398 		_page_size.set(0.0, 0.0, 29.7, 42.0);
2399 	} else if (!strcmp(s, "A2")) {
2400 		_page_size.set(0.0, 0.0, 42.0, 59.4);
2401 	} else if (!strcmp(s, "A1")) {
2402 		_page_size.set(0.0, 0.0, 59.4, 84.1);
2403 	} else if (!strcmp(s, "A0")) {
2404 		_page_size.set(0.0, 0.0, 84.1, 118.9);
2405 	} else {
2406 		demonstrate_command_usage();
2407 		err("Unknown paper size `\\", s, "'.", "\\");
2408 		return false;
2409 	}
2410 	return true;
2411 }
2412 
2413 bool
set_pageCmd()2414 set_pageCmd()
2415 {
2416 	extern rectangle _page_size;
2417 	Require(_nword > 2, err("`set page' requires parameter(s)"));
2418 	double          mag, xcm, ycm;
2419 	if (!strcmp(_word[2], "portrait")) {
2420 		gr_setup_ps_portrait();
2421 	} else if (!strcmp(_word[2], "landscape")) {
2422 		if (!already_landscape) {
2423 		    if (_page_size.get_urx() == 0.0) {
2424 				warning("Defaulting to page size US-letter; you should really do `set page size' before `set page landscape'");
2425 				fprintf(gr_currentPSFILEpointer(),
2426 					"%.2f 72 mul 0 translate 90 rotate %% Landscape\n",
2427 					8.5);
2428 		    } else {
2429 				fprintf(gr_currentPSFILEpointer(),
2430 					"%.2f 72 mul 0 translate 90 rotate %% Landscape\n",
2431 					_page_size.get_urx() / CM_PER_IN);
2432 		    }
2433 			check_psfile();
2434 		}
2435 		already_landscape = true;
2436 		PUT_VAR("..landscape..", 1.0);
2437 		gr_setup_ps_landscape();
2438 	} else if (!strcmp(_word[2], "factor")) {
2439 		if (_nword != 4) {
2440 			err("Must specify .mag. in `set page factor .mag.'");
2441 			return false;
2442 		}
2443 		if (!getdnum(_word[3], &mag))
2444 			return false;
2445 		if (mag <= 0.0) {
2446 			err("Can't use negative enlargement factor");
2447 			return false;
2448 		}
2449 		gr_setscale(mag, mag);
2450 	} else if (!strcmp(_word[2], "translate")) {
2451 		if (_nword != 5) {
2452 			err("Must specify .xcm. and .ycm. in `set page translate .xcm. .ycm.'");
2453 			return false;
2454 		}
2455 		if (!getdnum(_word[3], &xcm))
2456 			return false;
2457 		if (!getdnum(_word[4], &ycm))
2458 			return false;
2459 		gr_settranslate(xcm, ycm);
2460 	} else {
2461 		err("Unknown `set page' parameter");
2462 		return false;
2463 	}
2464 	return true;
2465 }
2466 
2467 //`set path to "\path"|default for data|commands'
2468 bool
set_pathCmd()2469 set_pathCmd()
2470 {
2471 	if (_nword != 6) {
2472 		demonstrate_command_usage();
2473 		NUMBER_WORDS_ERROR;
2474 		return false;
2475 	}
2476 	if (strNE(_word[4], "for")) {
2477 		err("Fourth word must be `for', not `\\", _word[4], "' as given.", "\\");
2478 		return false;
2479 	}
2480 	char *which_path;
2481 	if (strEQ(_word[5], "data"))
2482 		which_path = (char *)"\\.path_data.";
2483 	else if (strEQ(_word[5], "commands"))
2484 		which_path = (char *)"\\.path_commands.";
2485 	else {
2486 		err("Sixth word must be `data' or `commands', not `\\", _word[5], "' as given.", "\\");
2487 		return false;
2488 	}
2489 	if (strEQ(_word[3], "default")) {
2490 		if (!put_syn(which_path, ".", true)) {
2491 			err("Internal error in setting path to default.");
2492 			return false;
2493 		}
2494 	} else {
2495 		std::string unquoted;
2496 		int ok = ExtractQuote(_word[3], unquoted);
2497 		if (ok) {
2498 			if (!put_syn(which_path, unquoted.c_str(), true)) {
2499 				err("`set path' cannot save `\\", _word[2], "' in synonym", which_path, "\\");
2500 				return false;
2501 			}
2502 		} else {
2503 			err("`set path' cannot understand path `\\", _word[2], "'.", "\\");
2504 			return false;
2505 		}
2506 	}
2507 	return true;
2508 }
2509 
2510 bool
set_postscript_filenameCmd()2511 set_postscript_filenameCmd()
2512 {
2513 	if (_nword != 4) {
2514 		demonstrate_command_usage();
2515 		NUMBER_WORDS_ERROR;
2516 		return false;
2517 	}
2518 	if (!gr_reopen_postscript(_word[3])) {
2519 		demonstrate_command_usage();
2520 		warning("Cannot open `\\", _word[3], "', so using old name.", "\\");
2521 	}
2522 	return true;
2523 }
2524 
2525 bool
set_symbol_sizeCmd()2526 set_symbol_sizeCmd()
2527 {
2528 	if (_nword < 3) {
2529 		err("`set symbol' what?");
2530 		return false;
2531 	}
2532 	if (!strcmp(_word[2], "size")) {
2533 		if (!strcmp(_word[3], "default")) {
2534 			tmp = SYMBOLSIZE_DEFAULT;
2535 			PUT_VAR("..symbolsize..", tmp);
2536 			gr_setsymbolsize_cm(SYMBOLSIZE_DEFAULT);
2537 			return true;
2538 		}
2539 		if (!getdnum(_word[3], &tmp))
2540 			return false;
2541 		if (tmp < 0.0 || tmp > 20.0) {
2542 			err("Ignoring bad symbol size <0 or >20 cm");
2543 			return false;
2544 		}
2545 		PUT_VAR("..symbolsize..", tmp);
2546 		gr_setsymbolsize_cm(tmp);
2547 	} else {
2548 		err("`set symbol' what?");
2549 		return false;
2550 	}
2551 	return true;
2552 }
2553 
2554 bool
set_tic_sizeCmd()2555 set_tic_sizeCmd()
2556 {
2557 	if (_nword < 3) {
2558 		err("`set tic' what?");
2559 		return false;
2560 	}
2561 	if (!strcmp(_word[2], "size")) {
2562 		if (_nword < 4) {
2563 			err("`set tic size' what?");
2564 			return false;
2565 		}
2566 		if (_nword > 4) {
2567 			err("Extra words in `set tic size' command");
2568 			return false;
2569 		}
2570 		if (!strcmp(_word[3], "default")) {
2571 			tmp = TICSIZE_DEFAULT;
2572 			PUT_VAR("..tic_size..", tmp);
2573 			return true;
2574 		}
2575 		if (!getdnum(_word[3], &tmp))
2576 			return false;
2577 		if (tmp < 0.0 || tmp > 20.0) {
2578 			err("Ignoring bad tic size <0 or >20 cm");
2579 			return false;
2580 		}
2581 		PUT_VAR("..tic_size..", tmp);
2582 	} else {
2583 		err("`set tic' what?");
2584 		return false;
2585 	}
2586 	return true;
2587 }
2588 
2589 bool
set_ticsCmd()2590 set_ticsCmd()
2591 {
2592 	if (_nword != 3) {
2593 		demonstrate_command_usage();
2594 		NUMBER_WORDS_ERROR;
2595 		return false;
2596 	}
2597 	if (!strcmp(_word[2], "in")) {
2598 		PUT_VAR("..tic_direction..", 1.0);
2599 	} else if (!strcmp(_word[2], "out")) {
2600 		PUT_VAR("..tic_direction..", 0.0);
2601 	} else {
2602 		demonstrate_command_usage();
2603 		err("Third word must be \"in\" or \"out\".");
2604 		return false;
2605 	}
2606 	return true;
2607 }
2608 
2609 bool
set_transparencyCmd()2610 set_transparencyCmd()
2611 {
2612 	if (_nword != 3) {
2613 		err("`set transpancy' to what value?");
2614 		return false;
2615 	}
2616 	double transparency;
2617 	Require(getdnum(_word[2], &transparency), READ_WORD_ERROR(".transparency."));
2618 	if (transparency < 0.0)
2619 		transparency = 0.0;
2620 	if (transparency > 1.0)
2621 		transparency = 1.0;
2622 	PUT_VAR("..transparency..", transparency);
2623 	_griState.set_transparency_line(transparency);
2624 	_griState.set_transparency_text(transparency);
2625 	return true;
2626 }
2627 
2628 bool
set_u_scaleCmd()2629 set_u_scaleCmd()
2630 {
2631 	double          xsize;
2632 	if (_nword < 3) {
2633 		demonstrate_command_usage();
2634 		err("`set u' ?WHAT?");
2635 		return false;
2636 	}
2637 	if (strcmp(_word[2], "scale")) {
2638 		demonstrate_command_usage();
2639 		err("`set u ?WHAT?' (try `set u scale ...'");
2640 		return false;
2641 	}
2642 	switch (_nword) {
2643 	case 4:
2644 		// `set u scale .cm_per_unit.'
2645 		if (!getdnum(_word[3], &tmp))
2646 			return false;
2647 		if (tmp != 0.0) {
2648 			_cm_per_u = tmp;
2649 			_uscale_exists = true;
2650 			return true;
2651 		} else {
2652 			err("`set u scale 0' illegal");
2653 			return false;
2654 		}
2655 	case 5:
2656 		// `set u scale as x'
2657 		if (strcmp(_word[3], "as") || strcmp(_word[4], "x")) {
2658 			err("Correct usage `set u scale as x'");
2659 			return false;
2660 		}
2661 		if (!_xscale_exists) {
2662 			err("First `set x axis' or `read columns x ...'");
2663 			return false;
2664 		}
2665 		if (_xtype != gr_axis_LINEAR) {
2666 			err("Can only `set u scale as x' if x is LINEAR");
2667 			return false;
2668 		}
2669 		if (!get_var("..xsize..", &xsize)) {
2670 			err("Can't remember ..xsize..");
2671 			return false;
2672 		}
2673 		_cm_per_u = xsize / (_xright - _xleft);
2674 		_uscale_exists = true;
2675 		return true;
2676 	default:
2677 		err("`set u' what?");
2678 		return false;
2679 	}
2680 }
2681 
2682 bool
set_v_scaleCmd()2683 set_v_scaleCmd()
2684 {
2685 	double          ysize;
2686 	if (_nword < 3) {
2687 		err("`set v' ?WHAT?");
2688 		return false;
2689 	}
2690 	if (strcmp(_word[2], "scale")) {
2691 		err("`set v ?WHAT?' (try `set v scale ...'");
2692 		return false;
2693 	}
2694 	switch (_nword) {
2695 	case 4:
2696 		// `set v scale .cm_per_unit.'
2697 		if (!getdnum(_word[3], &tmp))
2698 			return false;
2699 		if (tmp != 0.0) {
2700 			_cm_per_v = tmp;
2701 			_vscale_exists = true;
2702 			return true;
2703 		} else {
2704 			err("`set v scale 0' illegal");
2705 			return false;
2706 		}
2707 	case 5:
2708 		// `set v scale as y'
2709 		if (strcmp(_word[3], "as") || strcmp(_word[4], "y")) {
2710 			demonstrate_command_usage();
2711 			err("Correct usage `set v scale as y'");
2712 			return false;
2713 		}
2714 		if (!_yscale_exists) {
2715 			demonstrate_command_usage();
2716 			err("First `set y axis' or `read columns y ...'");
2717 			return false;
2718 		}
2719 		if (_ytype != gr_axis_LINEAR) {
2720 			demonstrate_command_usage();
2721 			err("Can only `set u scale as y' if y is LINEAR");
2722 			return false;
2723 		}
2724 		if (!get_var("..ysize..", &ysize)) {
2725 			demonstrate_command_usage();
2726 			err("Can't remember ..ysize..");
2727 			return false;
2728 		}
2729 		_cm_per_v = ysize / (_ytop - _ybottom);
2730 		_vscale_exists = true;
2731 		return true;
2732 	default:
2733 		err("`set v' what?");
2734 		return false;
2735 	}
2736 }
2737 
2738 bool
set_traceCmd()2739 set_traceCmd()
2740 {
2741 	switch (_nword) {
2742 	case 2:
2743 		PUT_VAR("..trace..", 1.0);
2744 		break;
2745 	case 3:
2746 		if (!strcmp(_word[2], "on")) {
2747 			PUT_VAR("..trace..", 1.0);
2748 		} else if (!strcmp(_word[2], "off")) {
2749 			PUT_VAR("..trace..", 0.0);
2750 		}
2751 		break;
2752 	default:
2753 		break;
2754 	}
2755 	return true;
2756 }
2757 
2758 bool
well_ordered(double min,double max,double inc)2759 well_ordered(double min, double max, double inc)
2760 {
2761 	if (min < max)
2762 		return ((inc > 0.0) ? true : false);
2763 	else
2764 		return ((inc < 0.0) ? true : false);
2765 }
2766 bool
inc_within_range(double min,double max,double inc)2767 inc_within_range(double min, double max, double inc)
2768 {
2769 	if (min < max)
2770 		return (min + inc < max);
2771 	else
2772 		return (min + inc > max);
2773 }
2774 
2775 bool
set_x_axisCmd()2776 set_x_axisCmd()
2777 {
2778 	_xatbottom = true;
2779 #if 1				// 2.9.x
2780 	if (word_is(3, "labels")) {
2781 		if (word_is(4, "automatic")) {
2782 			_x_labels.erase(_x_labels.begin(), _x_labels.end());
2783 			_x_label_positions.erase(_x_label_positions.begin(), _x_label_positions.end());
2784 			return true;
2785 		} else {
2786 			unsigned int start = 4;
2787 			if (word_is(start, "add")) {
2788 				start++;
2789 			} else {
2790 				_x_labels.erase(_x_labels.begin(), _x_labels.end());
2791 				_x_label_positions.erase(_x_label_positions.begin(), _x_label_positions.end());
2792 			}
2793 			for (unsigned int i = start; i < _nword; i++) {
2794 				double tmp;
2795 				if (!getdnum(_word[i], &tmp)) {
2796 					READ_WORD_ERROR(".pos.");
2797 					demonstrate_command_usage();
2798 					return false;
2799 				}
2800 				_x_label_positions.push_back(tmp);
2801 				if (i++ == _nword - 1) {
2802 					err("Missing label to be applied at position \\", _word[i-1], "\\");
2803 					demonstrate_command_usage();
2804 					return false;
2805 				}
2806 				std::string l = _word[i];
2807 				un_double_quote(l);
2808 				_x_labels.push_back(l);
2809 
2810 			}
2811 			return true;
2812 		}
2813 	}
2814 #endif // 2.9.x
2815         _x_gave_labelling = false;
2816         double labelling_value = 0.0;
2817 	if (_nword > 6 && (!strcmp(_word[_nword - 2], "labelling") || !strcmp(_word[_nword - 2], "labeling"))) {
2818                 if (_xtype == gr_axis_LOG) {
2819                         err("cannot use a 'labelling' value with a logarithmic axis");
2820                         return false;
2821                 }
2822                 if (!getdnum(_word[_nword - 1], &labelling_value)) {
2823                         READ_WORD_ERROR("labelling .labelling_value.");
2824                         return false;
2825                 }
2826                 _x_gave_labelling = true;
2827                 _nword -= 2;    // gobble last two words
2828         }
2829 	if (!strcmp(_word[_nword - 1], "bottom")) {
2830 		_xatbottom = true;
2831 		if (_nword == 4) {
2832 			_need_x_axis = true;
2833 			_need_y_axis = true;
2834 			return true;
2835 		}
2836 		_nword--;
2837 	} else if (!strcmp(_word[_nword - 1], "top")) {
2838 		_xatbottom = false;
2839 		if (_nword == 4) {
2840 			_need_x_axis = true;
2841 			_need_y_axis = true;
2842 			return true;
2843 		}
2844 		_nword--;
2845 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "increasing")) {
2846 		_xincreasing = true;
2847 		if (_xscale_exists && _xleft > _xright) {
2848 			swap(_xleft, _xright);
2849 			_xinc = -fabs(_xinc);
2850                         _x_labelling = _x_gave_labelling ? labelling_value : _xleft; // FIXME
2851 			PUT_VAR("..xleft..", _xleft);
2852 			PUT_VAR("..xright..", _xright);
2853 			PUT_VAR("..xinc..", _xinc);
2854 			PUT_VAR("..xlabelling..", _x_labelling);
2855 		}
2856 		return true;
2857 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "decreasing")) {
2858 		_xincreasing = false;
2859 		if (_xscale_exists && _xleft < _xright) {
2860 			swap(_xleft, _xright);
2861 			_xinc = fabs(_xinc);
2862                         _x_labelling = _x_gave_labelling ? labelling_value : _xleft;
2863 			PUT_VAR("..xleft..", _xleft);
2864 			PUT_VAR("..xright..", _xright);
2865 			PUT_VAR("..xinc..", _xinc);
2866                         PUT_VAR("..xlabelling..", _x_labelling);
2867 		}
2868 		return true;
2869 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "unknown")) {
2870 		_xscale_exists = false;
2871 		_need_x_axis = true;
2872 		_user_set_x_axis = false;
2873 		return true;
2874 	}
2875 	// ... specifying x axis
2876 	// 'set x axis .left. .right.'
2877 	if (_nword == 5) {
2878 		if (!getdnum(_word[3], &xleft) || !getdnum(_word[4], &xright)) {
2879 			READ_WORD_ERROR(".left. and .right.");
2880 			return false;
2881 		}
2882 		if (_xtype == gr_axis_LOG) {
2883 			Require(xleft > 0.0,
2884 				err("`set x axis .left. .right.' cannot have non-positive .left. value for logarithmic axis"));
2885 			Require(xright > 0.0,
2886 				err("`set x axis .left. .right.' cannot have non-positive .right. value for logarithmic axis"));
2887 		}
2888 		_xleft = xleft;
2889 		_xright = xright;
2890 		if (_xtype == gr_axis_LOG)
2891 			_xinc = 1.0;
2892 		else
2893 			_xinc = _xright - _xleft;
2894                 _x_labelling = _x_gave_labelling ? labelling_value : _xleft;
2895 		PUT_VAR("..xleft..", _xleft);
2896 		PUT_VAR("..xright..", _xright);
2897 		PUT_VAR("..xinc..", _xinc);
2898                 PUT_VAR("..xlabelling..", _x_labelling);
2899 		_xsubdiv = 1;
2900 		_xscale_exists = true;
2901 		_need_x_axis = true;
2902 		_need_y_axis = true;
2903 		_user_set_x_axis = true;
2904 		return true;
2905 	} else if (_nword == 6) { // 'set x axis .left. .right. .inc.'
2906 		if (!getdnum(_word[3], &xleft)
2907 		    || !getdnum(_word[4], &xright)
2908 		    || !getdnum(_word[5], &xinc)) {
2909 			// 'set x axis .left. .right. .incBig.'
2910 			READ_WORD_ERROR(".left. .right. .incBig.");
2911 			return false;
2912 		}
2913 		Require(well_ordered(xleft, xright, xinc),
2914 			err("`set x axis .left. .right. .incBig.' has .incBig. of wrong sign"));
2915 		SUGGEST(inc_within_range(xleft, xright, xinc),
2916 			warning("`set x axis .left. .right. .incBig.' has .incBig. that goes outside range"));
2917 		if (_xtype == gr_axis_LOG) {
2918 			Require(xleft > 0.0,
2919 				err("`set x axis .left. .right. .incBig.' cannot have non-positive .left. value for logarithmic axis"));
2920 			Require(xright > 0.0,
2921 				err("`set x axis .left. .right. .incBig.' cannot have non-positive .right. value for logarithmic axis"));
2922 			Require(xinc > 0.0,
2923 				err("`set x axis .left. .right. .incBig.' cannot have non-positive .incBig. value for logarithmic axis"));
2924 		}
2925 		_xleft = xleft;
2926 		_xright = xright;
2927 		if (_xtype == gr_axis_LOG) {
2928 			_xinc = xinc;
2929 			_xsubdiv = 1;
2930 		} else {
2931 			_xinc = xinc;
2932 			_xsubdiv = 1;
2933 		}
2934                 _x_labelling = _x_gave_labelling ? labelling_value : _xleft;
2935 		PUT_VAR("..xleft..", _xleft);
2936 		PUT_VAR("..xright..", _xright);
2937 		PUT_VAR("..xinc..", _xinc);
2938                 PUT_VAR("..xlabelling..", _x_labelling);
2939 		_xscale_exists = true;
2940 		_need_x_axis = true;
2941 		_need_y_axis = true;
2942 		_user_set_x_axis = true;
2943 		return true;
2944 	} else if (_nword == 7) { // 'set x axis .left. .right. .incBig. .incSml.'
2945 		if (!getdnum(_word[3], &xleft)
2946 		    || !getdnum(_word[4], &xright)
2947 		    || !getdnum(_word[5], &xinc)
2948 		    || !getdnum(_word[6], &tmp)) {
2949 			READ_WORD_ERROR(".left. .right. .incBig. .incSml.");
2950 			return false;
2951 		}
2952 		Require(well_ordered(xleft, xright, xinc),
2953 			err("`set x axis .left. .right. .incBig. .incSml.' has .incBig. of wrong sign"));
2954 		if (_xtype == gr_axis_LOG) {
2955 			Require(xleft > 0.0,
2956 				err("`set x axis .left. .right. .incBig. .incSml.' cannot have non-positive .left. value for logarithmic axis"));
2957 			Require(xright > 0.0,
2958 				err("`set x axis .left. .right. .incBig. .incSml.' cannot have non-positive .right. value for logarithmic axis"));
2959 			Require(xinc > 0.0,
2960 				err("`set x axis .left. .right. .incBig. .incSml.' cannot have non-positive .incBig. value for logarithmic axis"));
2961 		}
2962 		SUGGEST(inc_within_range(xleft, xright, xinc),
2963 			warning("`set x axis .left. .right. .incBig.' has .incBig. that goes outside range"));
2964 		_xleft = xleft;
2965 		_xright = xright;
2966 		if (_xtype == gr_axis_LOG) {
2967 			_xinc = xinc;
2968 			_xsubdiv = (tmp > 0) ? 1 : -1;
2969 		} else {
2970 			_xinc = xinc;
2971 			_xsubdiv = int(floor(0.5 + fabs((double) (xinc / tmp))));
2972 		}
2973                 _x_labelling = _x_gave_labelling ? labelling_value : _xleft;
2974 		PUT_VAR("..xleft..", _xleft);
2975 		PUT_VAR("..xright..", _xright);
2976 		PUT_VAR("..xinc..", _xinc);
2977                 PUT_VAR("..xlabelling..", _xleft);
2978 		_xscale_exists = true;
2979 		_need_x_axis = true;
2980 		_need_y_axis = true;
2981 		_user_set_x_axis = true;
2982 		return true;
2983 	} else if (_nword == 8) { // 'set x axis .left. .right. .inc. labelling .xlabelling.'
2984                 printf("HERE have 8 words\n");
2985 	} else {
2986 		err("`set x axis' may have only 2, 3 or 4 parameters");
2987 		return false;
2988 	}
2989 	_user_set_x_axis = true;
2990 	return true;
2991 }
2992 
2993 bool
set_x_formatCmd()2994 set_x_formatCmd()
2995 {
2996 	if (_nword < 4) {
2997 		err("Must specify a format for `set x format'");
2998 		return false;
2999 	}
3000 	if (!strcmp(_word[3], "off")) {
3001 		_xFmt.assign("");
3002 	} else if (!strcmp(_word[3], "default")){
3003 		_xFmt.assign(X_FMT_DEFAULT);
3004 	} else {
3005 		if (*_word[3] == '"') {
3006 			int len = strlen(_word[3]);
3007 			if (len <= 1) {
3008 				_xFmt.assign(X_FMT_DEFAULT);
3009 			} else {
3010 				if (*(_word[3] + len - 1) == '"')
3011 					_xFmt.assign(_word[3] + 1, len - 2);
3012 				else
3013 					_xFmt.assign(_word[3] + 1, len - 1);
3014 			}
3015 		} else {
3016 			_xFmt.assign(_word[3]);
3017 		}
3018 	}
3019 	return true;
3020 }
3021 
3022 bool
set_x_gridCmd()3023 set_x_gridCmd()
3024 {
3025 	double          x, xmin, xmax, xinc;
3026 	int             i, nx;
3027 	// get numbers
3028 	if (_nword != 6 && _nword != 7) {
3029 		demonstrate_command_usage();
3030 		NUMBER_WORDS_ERROR;
3031 		return false;
3032 	}
3033 	Require(getdnum(_word[3], &xmin), READ_WORD_ERROR(".xmin."));
3034 	Require(getdnum(_word[4], &xmax), READ_WORD_ERROR(".xmax."));
3035 	if (*_word[5] == '/') {
3036 		// kludge in case previous parsing didn't separate / from last number
3037 		if (_nword == 6) {
3038 			Require(getdnum(1 + _word[5], &xinc), err("Can't read /.numx."));
3039 		} else if (_nword == 7) {
3040 			Require(getdnum(_word[6], &xinc), err("Can't read /.numx."));
3041 		} else {
3042 			demonstrate_command_usage();
3043 			NUMBER_WORDS_ERROR;
3044 			return false;
3045 		}
3046 		Require(xinc >= 1.9, err("Bad /.x.; need >1"));
3047 		nx = (int) floor(0.5 + xinc); // redefined below, using xinc
3048 		xinc = (xmax - xmin) / (nx - 1);
3049 	} else {
3050 		Require(getdnum(_word[5], &xinc), READ_WORD_ERROR(".xinc."));
3051 	}
3052 	// check for stupidity
3053 	Require(xinc != 0.0, err("Can't have .xinc.=0"));
3054 	Require(xmin != xmax, err("Can't have .xmin. = .xmax."));
3055 	Require(well_ordered(xmin, xmax, xinc),
3056 		err("`set x grid .xmin. .xmax. .xinc.' has .xinc. of wrong sign"));
3057 	SUGGEST(inc_within_range(xmin, xmax, xinc),
3058 		warning("`set x grid .xmin. .xmax. .xinc.' has .xinc. that goes outside range"));
3059 	nx = int(floor(1.5 + fabs((double) ((xmax - xmin) / xinc))));
3060 	Require(nx > 0, err(".xinc. too big"));
3061 	// check against existing matrix
3062 	if (_grid_exists == true && nx != (int)_num_xmatrix_data) {
3063 		sprintf(_errorMsg, "# intervals %d disagrees with existing grid size %d",
3064 			nx, _num_xmatrix_data);
3065 		err(_errorMsg);
3066 		return false;
3067 	}
3068 	// get storage space
3069 	Require(allocate_xmatrix_storage(nx), err("Insufficient space for grid x data"));
3070 	// set up x grid
3071 	for (i = 0, x = xmin; i < nx; i++, x += xinc)
3072 		_xmatrix[i] = x;
3073 	// Use grid for axis scale, if the latter does not exist yet
3074 	if (!_xscale_exists) {
3075 		_xleft = xmin;
3076 		_xright = xmax;
3077 		_xinc = xinc;
3078 		_xscale_exists = true;
3079 		//printf("creating x scale %f to %f by %f\n",_xleft, _xright, _xinc);
3080 	}
3081 	// Override any existing scale
3082 	define_image_scales(_xmatrix[0], 0.0, _xmatrix[nx - 1], 0.0);
3083 	_xgrid_exists = true;
3084 	if (_xmatrix[1] > _xmatrix[0])
3085 		_xgrid_increasing = true;
3086 	else
3087 		_xgrid_increasing = false;
3088 	return true;
3089 }				// end set_x_gridCmd()
3090 
3091 bool
set_y_gridCmd()3092 set_y_gridCmd()
3093 {
3094 	double          y, ymin, ymax, yinc;
3095 	int             i, ny;
3096 	if (_nword != 6 && _nword != 7) {
3097 		demonstrate_command_usage();
3098 		NUMBER_WORDS_ERROR;
3099 		return false;
3100 	}
3101 	if (!getdnum(_word[3], &ymin)) {
3102 		demonstrate_command_usage();
3103 		READ_WORD_ERROR(".ymin.");
3104 		return false;
3105 	}
3106 	if (!getdnum(_word[4], &ymax)) {
3107 		demonstrate_command_usage();
3108 		READ_WORD_ERROR(".ymax.");
3109 		return false;
3110 	}
3111 	if (*_word[5] == '/') {
3112 		// kludge in case previous parsing didn't separate / from last number
3113 		if (_nword == 6) {
3114 			Require(getdnum(1 + _word[5], &yinc), err("Can't read /.numy."));
3115 		} else if (_nword == 7) {
3116 			Require(getdnum(_word[6], &yinc), err("Can't read /.numy."));
3117 		} else {
3118 			demonstrate_command_usage();
3119 			NUMBER_WORDS_ERROR;
3120 			return false;
3121 		}
3122 		Require(yinc >= 1.9, err("Bad /.y.; need >1"));
3123 		ny = int(floor(0.5 + yinc));
3124 		yinc = (ymax - ymin) / (yinc - 1);
3125 	} else {
3126 		if (!getdnum(_word[5], &yinc)) {
3127 			demonstrate_command_usage();
3128 			READ_WORD_ERROR(".yinc.");
3129 			return false;
3130 		}
3131 	}
3132 	// check for stupidity
3133 	Require(yinc != 0.0, err("Can't have .yinc.=0"));
3134 	Require(ymin != ymax, err("Can't have .ymin.=.ymax."));
3135 	Require(well_ordered(ymin, ymax, yinc),
3136 		err("`set y grid .ymin. .ymax. .yinc.' has .yinc. of wrong sign"));
3137 	SUGGEST(inc_within_range(ymin, ymax, yinc),
3138 		warning("`set y grid .ymin. .ymax. .yinc.' has .yinc. that goes outside range"));
3139 	ny = 1 + int(floor(0.5 + fabs((double) ((ymax - ymin) / yinc))));
3140 	Require(ny > 0, err(".yinc. too big"));
3141 	// check against existing matrix
3142 	if (_grid_exists == true && ny != int(_num_ymatrix_data)) {
3143 		demonstrate_command_usage();
3144 		sprintf(_errorMsg,
3145 			"# intervals %d disagrees with existing grid %d",
3146 			ny, _num_ymatrix_data);
3147 		err(_errorMsg);
3148 		return false;
3149 	}
3150 	// get storage space
3151 	Require(allocate_ymatrix_storage(ny), err("Insufficient space for grid y data."));
3152 	// set up y grid
3153 	for (i = 0, y = ymin; i < ny; i++, y += yinc)
3154 		_ymatrix[i] = y;
3155 	// Use grid for axis scale, if the latter does not exist yet
3156 	if (!_yscale_exists) {
3157 		_ybottom = ymin;
3158 		_ytop = ymax;
3159 		_yinc = yinc;
3160 		_yscale_exists = true;
3161 		//printf("creating y scale %f to %f by %f\n",_ybottom, _ytop, _yinc);
3162 	}
3163 	// Override any existing scale
3164 	define_image_scales(0.0, _ymatrix[0], 0.0, _ymatrix[ny - 1]);
3165 	_ygrid_exists = true;
3166 	if (_ymatrix[1] > _ymatrix[0])
3167 		_ygrid_increasing = true;
3168 	else
3169 		_ygrid_increasing = false;
3170 	return true;
3171 }				// end set_y_gridCmd()
3172 
3173 bool
set_x_marginCmd()3174 set_x_marginCmd()
3175 {
3176 	if (_nword == 4) {
3177 		if (!strcmp(_word[3], "default")) {
3178 			tmp = XMARGIN_DEFAULT;
3179 			PUT_VAR("..xmargin..", tmp);
3180 			_need_x_axis = true;
3181 			_need_y_axis = true;
3182 			return true;
3183 		}
3184 		if (!getdnum(_word[3], &tmp)) {
3185 			err("Can't understand `set x margin' parameter.");
3186 			return false;
3187 		}
3188 		PUT_VAR("..xmargin..", tmp);
3189 		_need_x_axis = true;
3190 		_need_y_axis = true;
3191 		return true;
3192 	} else if (_nword == 5) {
3193 		double          old = 0.0;
3194 		if (!strcmp(_word[3], "bigger")) {
3195 			if (!getdnum(_word[4], &tmp)) {
3196 				err("can't understand `set x margin bigger' parameter");
3197 				return false;
3198 			}
3199 			get_var("..xmargin..", &old);
3200 			tmp = old + tmp;
3201 			PUT_VAR("..xmargin..", tmp);
3202 			_need_x_axis = true;
3203 			_need_y_axis = true;
3204 			return true;
3205 		} else if (!strcmp(_word[3], "smaller")) {
3206 			if (!getdnum(_word[4], &tmp)) {
3207 				err("can't understand `set x margin smaller' parameter");
3208 				return false;
3209 			}
3210 			get_var("..xmargin..", &old);
3211 			tmp = old - tmp;
3212 			PUT_VAR("..xmargin..", tmp);
3213 			_need_x_axis = true;
3214 			_need_y_axis = true;
3215 			return true;
3216 		} else {
3217 			err("bad `set x margin' parameter\nvalid list: bigger/smaller");
3218 			return false;
3219 		}
3220 	} else {
3221 		err("Must specify margin in cm");
3222 		return false;
3223 	}
3224 }
3225 
3226 bool
set_x_nameCmd()3227 set_x_nameCmd()
3228 {
3229 	Require(_nword > 3, err("Must specify a name"));
3230 	if (word_is(3, "default")) {
3231 		_colX.setName("x");
3232 	} else {
3233 		std::string unquoted;
3234 		int status = ExtractQuote(_cmdLine, unquoted);
3235 		if (status == 0) {
3236 			err("`set x name' needs a double-quoted string");
3237 			return false;
3238 		}
3239 		if (status < 0) {
3240 			err("`set x name' found starting double-quote but no ending double-quote");
3241 			return false;
3242 		}
3243 		_colX.setName(unquoted.c_str());
3244 	}
3245 	return true;
3246 }
3247 
3248 // set_x_sizeCmd() -- set width of plot
3249 bool
set_x_sizeCmd()3250 set_x_sizeCmd()
3251 {
3252 	if (_nword != 4) {
3253 		err("Must specify axis length in cm");
3254 		return false;
3255 	}
3256 	if (!strcmp(_word[3], "default")) {
3257 		tmp = XSIZE_DEFAULT;
3258 		PUT_VAR("..xsize..", tmp);
3259 		_need_x_axis = true;
3260 		_need_y_axis = true;
3261 		return true;
3262 	}
3263 	Require(getdnum(_word[3], &tmp), READ_WORD_ERROR(".width_cm."));
3264 	Require(tmp >= 0.0, err("ignoring bad xsize <0"));
3265 	PUT_VAR("..xsize..", tmp);
3266 	_need_x_axis = true;
3267 	_need_y_axis = true;
3268 	return true;
3269 }
3270 
3271 bool
set_y_axisCmd()3272 set_y_axisCmd()
3273 {
3274 	_yatleft = true;
3275 #if 1				// 2.9.x
3276 	if (word_is(3, "labels")) {
3277 		if (word_is(4, "automatic")) {
3278 			_y_labels.erase(_y_labels.begin(), _y_labels.end());
3279 			_y_label_positions.erase(_y_label_positions.begin(), _y_label_positions.end());
3280 			return true;
3281 		} else {
3282 			unsigned int start = 4;
3283 			if (word_is(start, "add")) {
3284 				start++;
3285 			} else {
3286 				_y_labels.erase(_y_labels.begin(), _y_labels.end());
3287 				_y_label_positions.erase(_y_label_positions.begin(), _y_label_positions.end());
3288 			}
3289 			for (unsigned int i = start; i < _nword; i++) {
3290 				double tmp;
3291 				if (!getdnum(_word[i], &tmp)) {
3292 					READ_WORD_ERROR(".pos.");
3293 					demonstrate_command_usage();
3294 					return false;
3295 				}
3296 				_y_label_positions.push_back(tmp);
3297 				if (i++ == _nword - 1) {
3298 					err("Missing label to be applied at position \\", _word[i-1], "\\");
3299 					demonstrate_command_usage();
3300 					return false;
3301 				}
3302 				std::string l = _word[i];
3303 				un_double_quote(l);
3304 				_y_labels.push_back(l);
3305 			}
3306 			return true;
3307 		}
3308 	}
3309 #endif // 2.9.x
3310         _y_gave_labelling = false;
3311         double labelling_value = 0.0;
3312 	if (_nword > 6 && (!strcmp(_word[_nword - 2], "labelling") || !strcmp(_word[_nword - 2], "labeling"))) {
3313                 if (_ytype == gr_axis_LOG) {
3314                         err("cannot use a 'labelling' value with a logarithmic axis");
3315                         return false;
3316                 }
3317                 if (!getdnum(_word[_nword - 1], &labelling_value)) {
3318                         READ_WORD_ERROR("labelling .labelling_value.");
3319                         return false;
3320                 }
3321                 _y_gave_labelling = true;
3322                 _nword -= 2;    // gobble last two words
3323         }
3324 	if (!strcmp(_word[_nword - 1], "left")) {
3325 		_yatleft = true;
3326 		if (_nword == 4) {
3327 			_need_x_axis = true;
3328 			_need_y_axis = true;
3329 			return true;
3330 		}
3331 		_nword--;
3332 	} else if (!strcmp(_word[_nword - 1], "right")) {
3333 		_yatleft = false;
3334 		if (_nword == 4) {
3335 			_need_x_axis = true;
3336 			_need_y_axis = true;
3337 			return true;
3338 		}
3339 		_nword--;
3340 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "increasing")) {
3341 		_yincreasing = true;
3342 		if (_yscale_exists && _ybottom > _ytop) {
3343 			double tmp = _ybottom;
3344 			_ybottom = _ytop;
3345 			_ytop = tmp;
3346 			_yinc = -_yinc;
3347                         _y_labelling = _y_gave_labelling ? labelling_value : _ybottom;
3348 			PUT_VAR("..ybottom..", _ybottom);
3349 			PUT_VAR("..ytop..", _ytop);
3350 			PUT_VAR("..yinc..", _yinc);
3351 			PUT_VAR("..ylabelling..", _y_labelling);
3352 		}
3353 		return true;
3354 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "decreasing")) {
3355 		_yincreasing = false;
3356 		if (_yscale_exists && _ybottom < _ytop) {
3357 			double          tmp = _ybottom;
3358 			_ybottom = _ytop;
3359 			_ytop = tmp;
3360 			_yinc = -_yinc;
3361                         _y_labelling = _y_gave_labelling ? labelling_value : _ybottom;
3362 			PUT_VAR("..ybottom..", _ybottom);
3363 			PUT_VAR("..ytop..", _ytop);
3364 			PUT_VAR("..yinc..", _yinc);
3365 			PUT_VAR("..ylabelling..", _y_labelling);
3366 		}
3367 		return true;
3368 	} else if (_nword == 4 && !strcmp(_word[_nword - 1], "unknown")) {
3369 		_yscale_exists = false;
3370 		_need_y_axis = true;
3371 		_user_set_y_axis = false;
3372 		return true;
3373 	}
3374 	// set y axis name ...
3375 	if (_nword == 5 && word_is(3, "name")) {
3376 		set_y_axis_nameCmd();
3377 		return true;
3378 	}
3379 	if (_nword == 5 && word_is(3, "label")) {
3380 		//printf("YA YA YA %f %f %d\n",_version,_version_expected,gri_version_exceeds(2,8,99));
3381 		if (gri_version_exceeds(2, 8, 99)) {
3382 			if (_version_expected != 0 && _version_expected < 2.0899) {
3383 				warning("Using compatibility mode, interpreting `set y axis label'\n         as if it were the newly-named command  `set y axis name'.");
3384 				set_y_axis_nameCmd();
3385 				return true;
3386 			} else {
3387 				err("The `set y axis label' command is no longer available.\n       Please use `set y axis name' instead, or use the `expecting'\n       command with a version number lower than 2.9.0, to get\n       backwards compatability.");
3388 				return false;
3389 			}
3390 		} else {
3391 			set_y_axis_nameCmd();
3392 			return true;
3393 		}
3394 	}
3395 	// ... specifying y axis
3396 	if (_nword == 5) {
3397 		// set y axis .bottom. .top.
3398 		if (!getdnum(_word[3], &ybottom) || !getdnum(_word[4], &ytop)) {
3399 			err("can't understand parameters");
3400 			return false;
3401 		}
3402 		if (_ytype == gr_axis_LOG) {
3403 			Require(ybottom > 0.0,
3404 				err("`set y axis .bottom. .top.' cannot have non-positive .bottom. value for logarithmic axis"));
3405 			Require(ytop > 0.0,
3406 				err("`set y axis .bottom. .top.' cannot have non-positive .top. value for logarithmic axis"));
3407 		}
3408 		_ybottom = ybottom;
3409 		_ytop = ytop;
3410 		if (_ytype == gr_axis_LOG)
3411 			_yinc = 1.0;
3412 		else
3413 			_yinc = _ytop - _ybottom;
3414                 _y_labelling = _y_gave_labelling ? labelling_value : _ybottom;
3415 		PUT_VAR("..ybottom..", _ybottom);
3416 		PUT_VAR("..ytop..", _ytop);
3417 		PUT_VAR("..yinc..", _yinc);
3418                 PUT_VAR("..ylabelling..", _y_labelling);
3419 		_ysubdiv = 1;
3420 		_yscale_exists = true;
3421 		_need_x_axis = true;
3422 		_need_y_axis = true;
3423 		reset_top_of_plot();
3424 		_user_set_y_axis = true;
3425 		return true;
3426 	} else if (_nword == 6) {
3427 		// set y axis .bottom. .top. .incBig.
3428 		if (!getdnum(_word[3], &ybottom)
3429 		    || !getdnum(_word[4], &ytop)
3430 		    || !getdnum(_word[5], &yinc)) {
3431 			err("can't understand parameters");
3432 			return false;
3433 		}
3434 		Require(well_ordered(ybottom, ytop, yinc),
3435 			err("`set y axis .bottom. .top. .incBig.' has .incBig. of wrong sign"));
3436 		if (_ytype == gr_axis_LOG) {
3437 			Require(ybottom > 0.0,
3438 				err("`set y axis .bottom. .top. .incBig.' cannot have non-positive .bottom. value for logarithmic axis"));
3439 			Require(ytop > 0.0,
3440 				err("`set y axis .bottom. .top. .incBig.' cannot have non-positive .top. value for logarithmic axis"));
3441 			Require(yinc > 0.0,
3442 				err("`set y axis .bottom. .top. .incBig.' cannot have non-positive .incBig. value for logarithmic axis"));
3443 		}
3444 		SUGGEST(inc_within_range(ybottom, ytop, yinc),
3445 			warning("`set y axis .bottom. .top. .incBig.' has .incBig. that goes outside range"));
3446 		_ybottom = ybottom;
3447 		_ytop = ytop;
3448 		if (_ytype == gr_axis_LOG) {
3449 			_yinc = yinc;
3450 			_ysubdiv = 1;
3451 		} else {
3452 			_yinc = yinc;
3453 			_ysubdiv = 1;
3454 		}
3455                 _y_labelling = _y_gave_labelling ? labelling_value : _ybottom;
3456 		PUT_VAR("..ybottom..", _ybottom);
3457 		PUT_VAR("..ytop..", _ytop);
3458 		PUT_VAR("..yinc..", _yinc);
3459                 PUT_VAR("..ylabelling..", _y_labelling);
3460 		_yscale_exists = true;
3461 		_need_x_axis = true;
3462 		_need_y_axis = true;
3463 		reset_top_of_plot();
3464 		_user_set_y_axis = true;
3465 		return true;
3466 	} else if (_nword == 7) { // 'set y axis .bottom. .top. .incBig. .incSml.'
3467 		if (!getdnum(_word[3], &ybottom)
3468 		    || !getdnum(_word[4], &ytop)
3469 		    || !getdnum(_word[5], &yinc)
3470 		    || !getdnum(_word[6], &tmp)) {
3471 			err("can't understand parameters");
3472 			return false;
3473 		}
3474 		Require(well_ordered(ybottom, ytop, yinc),
3475 			err("`set y axis .bottom. .top. .incBig. .incSml.' has .incBig. of wrong sign"));
3476 		if (_ytype == gr_axis_LOG) {
3477 			Require(ybottom > 0.0,
3478 				err("`set y axis .bottom. .top. .incBig. .incSml.' cannot have non-positive .bottom. value for logarithmic axis"));
3479 			Require(ytop > 0.0,
3480 				err("`set y axis .bottom. .top. .incBig. .incSml.' cannot have non-positive .top. value for logarithmic axis"));
3481 			Require(yinc > 0.0,
3482 				err("`set y axis .bottom. .top. .incBig. .incSml.' cannot have non-positive .incBig. value for logarithmic axis"));
3483 		}
3484 		SUGGEST(inc_within_range(ybottom, ytop, yinc),
3485 			warning("`set y axis .bottom. .top. .incBig.' has .incBig. that goes outside range"));
3486 		_ybottom = ybottom;
3487 		_ytop = ytop;
3488 		if (_ytype == gr_axis_LOG) {
3489 			_yinc = yinc;
3490 			_ysubdiv = (tmp > 0) ? 1 : -1;
3491 		} else {
3492 			_yinc = yinc;
3493 			_ysubdiv = int(floor(0.5 + fabs((double) (yinc / tmp))));
3494 		}
3495                 _y_labelling = _y_gave_labelling ? labelling_value : _ybottom;
3496 		PUT_VAR("..ybottom..", _ybottom);
3497 		PUT_VAR("..ytop..", _ytop);
3498 		PUT_VAR("..yinc..", _yinc);
3499                 PUT_VAR("..ylaelled..", _y_labelling);
3500 		_yscale_exists = true;
3501 		_need_x_axis = true;
3502 		_need_y_axis = true;
3503 		reset_top_of_plot();
3504 		_user_set_y_axis = true;
3505 		return true;
3506 	} else {
3507                 //printf("_nword=%d\n",_nword); dandandan
3508                 err("`set y axis' may have only 2, 3 or 4 parameters");
3509 		return false;
3510 	}
3511 	_user_set_y_axis = true;
3512 	return true;
3513 }
3514 
3515 bool
set_y_axis_nameCmd()3516 set_y_axis_nameCmd()
3517 {
3518 	if (_nword == 5 && !strcmp(_word[3], "label")) { // Syntax prior to version 2.9.0
3519 		if (!strcmp(_word[4], "horizontal"))
3520 			gr_setyaxisstyle(1);
3521 		else if (!strcmp(_word[4], "vertical"))
3522 			gr_setyaxisstyle(0);
3523 		else {
3524 			err("`set y axis name' expecting 'horizontal' or 'vertical', but got `\\", _word[4], "'", "\\");
3525 			demonstrate_command_usage();
3526 			return false;
3527 		}
3528 	} else if (_nword == 5 && !strcmp(_word[3], "name")) { // From version 2.9.0 onwards
3529 		if (!strcmp(_word[4], "horizontal"))
3530 			gr_setyaxisstyle(1);
3531 		else if (!strcmp(_word[4], "vertical"))
3532 			gr_setyaxisstyle(0);
3533 		else {
3534 			err("`set y axis name' expecting 'horizontal' or 'vertical', but got `\\", _word[4], "'", "\\");
3535 			demonstrate_command_usage();
3536 			return false;
3537 		}
3538 	}
3539 	return true;
3540 }
3541 
3542 bool
set_y_formatCmd()3543 set_y_formatCmd()
3544 {
3545 	Require(_nword > 3, err("Must specify a format for `set y format'"));
3546 	if (!strcmp(_word[3], "off")) {
3547 		_yFmt.assign("");
3548 	} else if (!strcmp(_word[3], "default")){
3549 		_yFmt.assign(Y_FMT_DEFAULT);
3550 	} else {
3551 		if (*_word[3] == '"') {
3552 			int len = strlen(_word[3]);
3553 			if (len <= 1) {
3554 				_yFmt.assign(Y_FMT_DEFAULT);
3555 			} else {
3556 				if (*(_word[3] + len - 1) == '"')
3557 					_yFmt.assign(_word[3] + 1, len - 2);
3558 				else
3559 					_yFmt.assign(_word[3] + 1, len - 1);
3560 			}
3561 		} else {
3562 			_yFmt.assign(_word[3]);
3563 		}
3564 	}
3565 	return true;
3566 }
3567 
3568 bool
set_y_marginCmd()3569 set_y_marginCmd()
3570 {
3571 	if (_nword == 4) {
3572 		if (!strcmp(_word[3], "default")) {
3573 			tmp = YMARGIN_DEFAULT;
3574 			PUT_VAR("..ymargin..", tmp);
3575 			_need_x_axis = true;
3576 			_need_y_axis = true;
3577 			reset_top_of_plot();
3578 			return true;
3579 		}
3580 		Require(getdnum(_word[3], &tmp), err("can't understand `set y margin' parameter"));
3581 		PUT_VAR("..ymargin..", tmp);
3582 		_need_x_axis = true;
3583 		_need_y_axis = true;
3584 		reset_top_of_plot();
3585 		return true;
3586 	} else if (_nword == 5) {
3587 		double          old = 0.0;
3588 		if (!strcmp(_word[3], "bigger")) {
3589 			Require(getdnum(_word[4], &tmp),
3590 				err("can't understand `set y margin bigger' parameter"));
3591 			get_var("..ymargin..", &old);
3592 			tmp = old + tmp;
3593 			PUT_VAR("..ymargin..", tmp);
3594 			_need_x_axis = true;
3595 			_need_y_axis = true;
3596 			reset_top_of_plot();
3597 			return true;
3598 		} else if (!strcmp(_word[3], "smaller")) {
3599 			Require(getdnum(_word[4], &tmp),
3600 				err("can't understand `set y margin smaller' parameter"));
3601 			get_var("..ymargin..", &old);
3602 			tmp = old - tmp;
3603 			PUT_VAR("..ymargin..", tmp);
3604 			_need_x_axis = true;
3605 			_need_y_axis = true;
3606 			reset_top_of_plot();
3607 			return true;
3608 		} else {
3609 			err("bad `set y margin' parameter\nvalid list: bigger/smaller");
3610 			return false;
3611 		}
3612 	} else {
3613 		err("Must specify margin in cm");
3614 		return false;
3615 	}
3616 }
3617 
3618 bool
set_y_nameCmd()3619 set_y_nameCmd()
3620 {
3621 	Require(_nword > 3, err("Must specify a name"));
3622 	if (word_is(3, "default")) {
3623 		_colY.setName("y");
3624 	} else {
3625 		std::string unquoted;
3626 		int status = ExtractQuote(_cmdLine, unquoted);
3627 		if (status == 0) {
3628 			err("`set y name' needs a double-quoted string");
3629 			return false;
3630 		}
3631 		if (status < 0) {
3632 			err("`set y name' found starting double-quote but no ending double-quote");
3633 			return false;
3634 		}
3635 		_colY.setName(unquoted.c_str());
3636 	}
3637 	return true;
3638 }
3639 
3640 // set_y_sizeCmd() -- store height of plot
3641 bool
set_y_sizeCmd()3642 set_y_sizeCmd()
3643 {
3644 	Require(_nword == 4, err("Must specify axis length in cm"));
3645 	if (!strcmp(_word[3], "default")) {
3646 		tmp = YSIZE_DEFAULT;
3647 		PUT_VAR("..ysize..", tmp);
3648 		reset_top_of_plot();
3649 		_need_x_axis = true;
3650 		_need_y_axis = true;
3651 		return true;
3652 	}
3653 	Require(getdnum(_word[3], &tmp), READ_WORD_ERROR(".height_cm."));
3654 	Require (tmp >= 0.0, err("ignoring bad ysize <0"));
3655 	PUT_VAR("..ysize..", tmp);
3656 	reset_top_of_plot();
3657 	_need_x_axis = true;
3658 	_need_y_axis = true;
3659 	return true;
3660 }
3661 
3662 // Possible calls are as follows; code below may
3663 // be in a different order though!
3664 //  Type 1. \syn = word .n. of "string"
3665 //  Type 2. \syn = system ...
3666 //  Type 3. \syn = tmpname
3667 #if 0
3668 //  Type 4. \syn = &.a_var.
3669 //  Type 5. \syn = &\a_syn
3670 #endif
3671 //  Type 6. \syn = "string"
3672 bool
assign_synonym()3673 assign_synonym()
3674 {
3675 #if 0
3676 	printf("DEBUG %s:%d in assign_synonym.  words: ", __FILE__,__LINE__);
3677 	for (unsigned int iw = 0; iw < _nword; iw++)
3678 		printf("<%s> ", _word[iw]);
3679 	printf("\n");
3680 #endif
3681 	Require (_nword > 2, err("Can't understand command."));
3682 	if (!strncmp(_word[0], "\\@", 2)) {
3683 		err("The purported alias `\\", _word[0], "' doesn't name any known variable or synonym.", "\\");
3684 		return false;
3685 	}
3686 
3687 	// If assigning as e.g.
3688 	//     \.word1. = 10
3689 	//     \.word1. = "hi"
3690 	// then see if the calling-arg was a var/syn with an & to the left
3691 	if (!strncmp(_word[0], "\\.word", 6) && *(_word[0] + strlen(_word[0]) - 1) == '.') {
3692 		//printf("DEBUG %s:%d ASSIGNING to word[0] as '%s'\n",__FILE__,__LINE__,_word[0]);
3693 		std::string value;
3694 		if (!get_syn(_word[0], value, false)) {
3695 			err("Cannot access \\.word0. synonym");
3696 			return false;
3697 		}
3698 		//printf("DEBUG %s:%d value of '%s' is '%s'\n",__FILE__,__LINE__,_word[0],value.c_str());
3699 		std::string coded_name;
3700 		int coded_level = -1;
3701 		if (is_coded_string(value, coded_name, &coded_level)) {
3702 			//printf("DEBUG %s:%d '%s' was encoded `%s' at level %d\n",__FILE__,__LINE__, _word[0], coded_name.c_str(), coded_level);
3703 			if (coded_name.c_str()[0] == '.') {
3704 				int index = index_of_variable(coded_name.c_str(), coded_level);
3705 				//printf("DEBUG %s:%d A VAR ... index %d.  to assign '%s'\n",__FILE__,__LINE__,index,_word[2]);
3706 				if (index < 0) {
3707 					err("Cannot assign to non-existing variable `", coded_name.c_str(), "' as inferred from coded word `", value.c_str(), "'.", "\\");
3708 					return false;
3709 				}
3710 				double rhs;
3711 				if (!getdnum(_word[2], &rhs)) {
3712 					err("cannot assign `", _word[2], "' to variable `", coded_name.c_str(), "'.", "\\");
3713 					return false;
3714 				}
3715 				if (!strcmp(_word[1], "=")) {
3716 					variableStack[index].set_value(rhs);
3717 				} else {
3718 					double oldValue = variableStack[index].get_value();
3719 					if (strEQ(_word[1], "*="))
3720 						variableStack[index].set_value(oldValue * rhs);
3721 					else if (strEQ(_word[1], "/="))
3722 						variableStack[index].set_value(oldValue / rhs);
3723 					else if (strEQ(_word[1], "+="))
3724 						variableStack[index].set_value(oldValue +rhs);
3725 					else if (strEQ(_word[1], "-="))
3726 						variableStack[index].set_value(oldValue - rhs);
3727 					else if (strEQ(_word[1], "^="))
3728 						variableStack[index].set_value(pow(oldValue, rhs));
3729 					else if (strEQ(_word[1], "_=")) {
3730 						if (oldValue < 0.0)
3731 							variableStack[index].set_value(gr_currentmissingvalue());
3732 						else
3733 							variableStack[index].set_value(log(oldValue) / log(rhs));
3734 					} else {
3735 						err("`\\", _word[1], "' is not a known operator for variables", "'\\");
3736 						return false;
3737 					}
3738 				}
3739 			} else if (coded_name.c_str()[0] == '\\') {
3740 				int index = index_of_synonym(coded_name.c_str(), coded_level);
3741 				//printf("DEBUG %s:%d '%s' is syn index %d\n",__FILE__,__LINE__, coded_name.c_str(), index);
3742 				std::string unquoted;
3743 				int status = ExtractQuote(_word[2], unquoted);
3744 				if (status == 0) {
3745 					err("`\\synonym = \"value\" found no double-quoted string");
3746 					return false;
3747 				}
3748 				//printf("BEFORE trying to insert at position %d.\n",index);
3749 				//show_syn_stack();
3750 				synonymStack[index].set_value(unquoted.c_str());
3751 				//printf("AFTER.\n");
3752 				//show_syn_stack();
3753 			} else {
3754 				err("Internal error in decoding &\\.word?. for assignment");
3755 				return false;
3756 			}
3757 			return true;
3758 		}
3759 	}
3760 
3761 
3762 #if 0
3763 	// Check for e.g
3764 	// \syn = &.a_var.
3765 	// \syn = &\a_syn
3766 	if (_nword == 3 && *_word[2] == '&') {
3767 		const char *name = 1 + _word[2];
3768 		//printf("DEBUG %s:%d GOT A & and think name is <%s>\n",__FILE__,__LINE__,name);
3769 		char coded_pointer[200];	// BUG: should be big enough.  Jeeze!
3770 		if (is_var(name)) {
3771 			//printf("DEBUG: %s:%d & on a var named <%s>\n",__FILE__,__LINE__,name);
3772 			int the_index = index_of_variable(name);
3773 			sprintf(coded_pointer, "\\#v%d#", int(variablePointer.size()));
3774 			//printf("DEBUG %s:%d pushing back %d into position %d of variablePointer list\n",__FILE__,__LINE__,the_index,int(variablePointer.size()));
3775 			variablePointer.push_back(the_index);
3776 			Require(put_syn(_word[0], coded_pointer, true),
3777 				err("Cannot store synonym `\\", _word[0], "'", "\\"));
3778 		} else if (is_syn(name)) {
3779 			//printf("DEBUG: %s:%d & on a syn named <%s>\n",__FILE__,__LINE__,name);
3780 			int the_index = index_of_synonym(name);
3781 			//printf("DEBUG %s:%d pushing back %d into position %d of synonymPointer list\n",__FILE__,__LINE__,the_index,int(synonymPointer.size()));
3782 			sprintf(coded_pointer, "\\#s%d#", int(synonymPointer.size()));
3783 			synonymPointer.push_back(the_index);
3784 			Require(put_syn(_word[0], coded_pointer, true),
3785 				err("Cannot store synonym `\\", _word[0], "'", "\\"));
3786 			return true;
3787 		} else {
3788 			err("Cannot do '&' unless item to right is name of variable or synonym");
3789 			return false;
3790 		}
3791 		return true;
3792 	}
3793 #endif
3794 	// Following check should never be needed, but keep for possible future
3795 	// changes.
3796 	Require(is_syn(_word[0]), err("`\\", _word[0], "' must begin with `\\'", "\\"));
3797 	// `\synonym = word .n. of "string"'
3798 	if (_nword == 6
3799 	    && !strcmp(_word[1], "=")
3800 	    && !strcmp(_word[2], "word")
3801 	    && !strcmp(_word[4], "of")) {
3802 		double tmp;
3803 		if (!getdnum(_word[3], &tmp)) {
3804 			READ_WORD_ERROR(".n. in e.g. `\\syn = word .n. of \"string\"'");
3805 			return false;
3806 		}
3807 		int iwhich = int(floor(0.5 + tmp));
3808 		if (iwhich < 0) {
3809 			err("Cannot take a negatively-indexed word");
3810 			return false;
3811 		}
3812 		unsigned int which = (unsigned int)(iwhich);
3813 		std::string to_chop(_word[_nword - 1]);
3814 		if (to_chop[0] == '"')
3815 			to_chop.STRINGERASE(0, 1);
3816 		if (to_chop[to_chop.size() - 1] == '"') {
3817 			to_chop.STRINGERASE(to_chop.size() - 1, 1);
3818 		} else {
3819 			err("`\\syn = word N of \"string\" requires closing double-quote (\") sign");
3820 			return false;
3821 		}
3822 		char *to_chop_in_C = strdup(to_chop.c_str());
3823 		if (to_chop_in_C == NULL) {
3824 			err("Out of memory while trying to assign synonym as n-th word of string");
3825 			return false;
3826 		}
3827 		unsigned int max;
3828 		chop_into_words(to_chop_in_C, _Words2, &max, MAX_nword);
3829 		int i = strlen(_Words2[max - 1]);
3830 		if (i > 2 && *(_Words2[max - 1] + i - 1) == '"')
3831 			*(_Words2[max - 1] + i - 1) = '\0';
3832 		if (which > (max - 1)) {
3833 			err("The string \n`\\",
3834 			    _word[_nword - 1],
3835 			    "'\ndoes not have that many words.  NOTE: the first word is counted as 0.",
3836 			    "\\");
3837 			free(to_chop_in_C);
3838 			return false;
3839 		}
3840 		Require(put_syn(_word[0], _Words2[which], true),
3841 			err("Cannot store synonym `\\", _word[0], "'", "\\"));
3842 		free(to_chop_in_C);
3843 		return true;
3844 	} else if (!strcmp(_word[1], "=") && !strcmp(_word[2], "tmpname")) {
3845 		if (!put_syn(_word[0], tmp_file_name(), true))
3846 			gr_Error("Ran out of storage");
3847 		return true;
3848 	} else if (!strcmp(_word[1], "=") && !strcmp(_word[2], "system")) {
3849 		// `\synonym = system ...'
3850 #if !defined(HAVE_POPEN)
3851 		err("\
3852 This computer can't `\\synonym = system ...' since no popen() subroutine.");
3853 		return false;
3854 #else
3855 		FILE           *pipefile;
3856 		// Much of following code duplicated in sytemCmd(), so if any
3857 		// problems crop up, check there too.
3858 		char *            s = _cmdLine;
3859 		s += skip_space(s);	// skip any initial space
3860 		s += skip_nonspace(s);	// skip first word "\syn"
3861 		s += skip_space(s);	// skip space
3862 		s += skip_nonspace(s);	// skip "="
3863 		s += skip_space(s);	// skip space
3864 		s += skip_nonspace(s);	// skip "system"
3865 		s += skip_space(s);	// skip space
3866 		if (*s == '\0' || *s == '\n') {
3867 			err("`\\syn = system ...' needs a system command to do.");
3868 			return false;
3869 		}
3870 
3871 		// See if last word starts with "<<"; if so, then the stuff to be done
3872 		// appears on the lines following, ended by whatever word follows the
3873 		// "<<".
3874 		// ... compare doline.cc near line 510.
3875 		int i = strlen(s) - 2;
3876 		std::string read_until;
3877 		bool            using_read_until = false;
3878 		while (--i) {
3879 			if (!strncmp((s + i), "<<", 2)) {
3880 				bool            quoted_end_string = false;
3881 				int             spaces = 0;
3882 				while (isspace(*(s + i + 2 + spaces))) {
3883 					spaces++;
3884 				}
3885 				if (*(s + i + 2 + spaces) == '"') {
3886 					spaces++;
3887 					quoted_end_string = true;
3888 				}
3889 				read_until.assign(s + i + 2 + spaces);
3890 				using_read_until = true;
3891 				// trim junk from end of the 'read until' string
3892 				std::string::size_type cut_at;
3893 				if (quoted_end_string)
3894 					cut_at = read_until.find("\"");
3895 				else
3896 					cut_at = read_until.find(" ");
3897 				//printf("READING UNTIL '%s' ... i.e.\n", read_until.c_str());
3898 				if (cut_at != STRING_NPOS)
3899 					read_until.STRINGERASE(cut_at, read_until.size() - cut_at);
3900 				if (read_until.size() < 1) {
3901 					err("`system ... <<STOP_STRING' found no STOP_STRING");
3902 					return false;
3903 				}
3904 				//printf("reading until '%s'\n",read_until.c_str());
3905 				break;
3906 			}
3907 		}
3908 		static std::string cmd; // might save time in loops
3909 		cmd.assign(s);
3910 		if (using_read_until) {
3911 			// It is of the <<WORD form
3912 #if 1
3913 			cmd.append("\n");
3914 			extern std::vector<BlockSource> bsStack;
3915 			if (bsStack.size() == 0) {
3916 				if (((unsigned) superuser()) & FLAG_SYS)printf("DEBUG %s:%d GOBBLE from a file\n",__FILE__,__LINE__);
3917 				while (get_command_line()) {
3918 					if (((unsigned) superuser()) & FLAG_SYS)printf("DEBUG %s:%d cmd line is [%s]\n",__FILE__,__LINE__,_cmdLine);
3919 				        // Trim filename/fileline indicator
3920 					unsigned int l = strlen(_cmdLine);
3921 					for (unsigned int ii = 0; ii < l; ii++) {
3922 						if (_cmdLine[ii] == PASTE_CHAR) {
3923 							_cmdLine[ii] = '\0';
3924 							break;
3925 						}
3926 					}
3927 					if (!strncmp(_cmdLine + skip_space(_cmdLine), read_until.c_str(), read_until.size())) {
3928 						cmd.append(_cmdLine + skip_space(_cmdLine));
3929 						cmd.append("\n");
3930 						break;
3931 					}
3932 					cmd.append(_cmdLine);
3933 					cmd.append("\n");
3934 				}
3935 				std::string cmd_sub;
3936 				substitute_synonyms_cmdline(cmd.c_str(), cmd_sub, false);
3937 				cmd = cmd_sub;
3938 			} else {
3939 				extern unsigned int chars_read;
3940 				extern unsigned int offset_for_read;
3941 				extern bool get_line_in_block(const char *block, unsigned int *offset);
3942 				unsigned int offset = offset_for_read + chars_read;
3943 				if (((unsigned) superuser()) & FLAG_SYS)printf("DEBUG %s:%d GOBBLE from block source\n",__FILE__,__LINE__);
3944 				while (get_line_in_block(bsStack.back().get_start(), &offset)) {
3945 					if (((unsigned) superuser()) & FLAG_SYS)printf("DEBUG %s:%d cmd line is [%s]\n",__FILE__,__LINE__,_cmdLine);
3946 					bsStack.back().move_offset(strlen(_cmdLine) + 1);
3947 					chars_read += strlen(_cmdLine) + 1;
3948 					if (!strncmp(_cmdLine + skip_space(_cmdLine), read_until.c_str(), read_until.size())) {
3949 						cmd.append(_cmdLine + skip_space(_cmdLine));
3950 						cmd.append("\n");
3951 						break;
3952 					}
3953 					cmd.append(_cmdLine);
3954 					cmd.append("\n");
3955 				}
3956 				std::string cmd_sub;
3957 				substitute_synonyms_cmdline(cmd.c_str(), cmd_sub, false);
3958 				cmd = cmd_sub;
3959 			}
3960 			if (((unsigned) superuser()) & FLAG_SYS)printf("DEBUG %s:%d COMMAND START...\n%s\nDEBUG %s:%d ... COMMAND END\n",__FILE__,__LINE__,cmd.c_str(),__FILE__,__LINE__);
3961 #endif
3962 		} else {
3963 			// No, it is not of the <<WORD form
3964 			std::string::size_type loc = 0;
3965 			//printf("assigning synonym BEFORE [%s]\n",cmd.c_str());
3966 			while (STRING_NPOS != (loc = cmd.find("\\\\", loc))) {
3967 				cmd.STRINGERASE(loc, 2);
3968 				cmd.insert(loc, "\\");
3969 			}
3970 			//printf("AFTER [%s]\n",cmd.c_str());
3971 		}
3972 		clean_blanks_quotes(cmd);
3973 		cmd.append("\n");
3974 		if (((unsigned) superuser()) & FLAG_SYS) {
3975 			ShowStr("\n`\\synonym = system' sending the following command to the operating system:\n");
3976 			ShowStr(cmd.c_str());
3977 			ShowStr("\n");
3978 		}
3979 		pipefile = (FILE *) popen(cmd.c_str(), "r");
3980 		if (pipefile) {
3981 			std::string result;
3982 			GriString this_line;
3983 			//printf("START.\n");
3984 			do {
3985 				eof_status s = this_line.line_from_FILE(pipefile);
3986 				//printf("<%s> %d (%d=eof_after %d=eofbeforedata, %d=no_eof)\n",this_line.getValue(),s,eof_after_data,eof_before_data,no_eof);
3987 				if (s == eof_before_data)
3988 					break;
3989 				result.append(this_line.getValue());
3990 				//printf("NOW <%s>\n",result.c_str());
3991 			} while (1);
3992 			pclose(pipefile);
3993 			while (result[result.size() - 1] == '\n') {
3994 				//printf("ERASING newline at end ....\n");
3995 				result.STRINGERASE(result.size() - 1, 1);
3996 				//printf("<%s>\n",result.c_str());
3997 			}
3998 			//printf("final <%s>\n",result.c_str());
3999 			if (!put_syn(_word[0], result.c_str(), true)) OUT_OF_MEMORY;
4000 			return true;
4001 		} else {
4002 			err("`\\",
4003 			    _word[0],
4004 			    " = system ...' can't access system",
4005 			    "\\");
4006 			return false;
4007 		}
4008 #endif
4009 	} else {
4010                 // Type 6. \syn = "string"
4011 		std::string unquoted;
4012 		int status = ExtractQuote(_cmdLine, unquoted);
4013 		if (status == 0) {
4014 			err("`\\synonym = \"value\" found no double-quoted string");
4015 			return false;
4016 		}
4017 		if (status < 0) {
4018 			err("`\\synonym = \"value\" found starting double-quote but no ending double-quote");
4019 			return false;
4020 		}
4021 		//printf("%s:%d raw <%s> became <%s>\n",__FILE__,__LINE__,_cmdLine,unquoted.c_str());
4022 		if (!put_syn(_word[0], unquoted.c_str(), true)) OUT_OF_MEMORY;
4023 	}
4024 	return true;
4025 }
4026 
4027 // `set "\\syn" to "STRING"'
4028 // `set ".var." to  NUMBER'
4029 bool
setCmd()4030 setCmd()
4031 {
4032 	if (_nword != 4) {
4033 		NUMBER_WORDS_ERROR;
4034 		return false;
4035 	}
4036 	if (strNE(_word[2], "to")) {
4037 		demonstrate_command_usage();
4038 		err("Third word must be `to', not `\\", _word[2], "' as given.", "\\");
4039 		return false;
4040 	}
4041 	std::string name(_word[1]);
4042 	clean_blanks_quotes(name);
4043 	//printf("<%s>  ... <%s>>\n", _word[1], name.c_str());
4044 	if (is_var(name.c_str())) {
4045 		double value;
4046 		if (!getdnum(_word[3], &value)) {
4047 			demonstrate_command_usage();
4048 			err("Cannot interpret `\\", _word[3], "' as a numerical value.", "\\");
4049 			return false;
4050 		}
4051 		PUT_VAR(name.c_str(), value);
4052 	} else if (is_syn(name.c_str())) {
4053 		std::string value(_word[3]);
4054 		if (value.size() < 2
4055 		    || (value[0] != '"' || value[-1 + value.size()] != '"')) {
4056 			demonstrate_command_usage();
4057 			err("Need a double-quoted string to set the synonym to");
4058 			return false;
4059 		}
4060 		value.STRINGERASE(0, 1);
4061 		value.STRINGERASE(-1 + value.size(), 1);
4062 		put_syn(name.c_str(), value.c_str(), true);
4063 		//printf("Assigned '%s' to synonym named '%s'\n", value.c_str(), name.c_str());
4064 	} else {
4065 		demonstrate_command_usage();
4066 		err("Second word must be a variable name or a double-backslashed synonym name, in double quotes, not `\\", name.c_str(), "' as given.", "\\");
4067 		return false;
4068 	}
4069 	return true;
4070 }
4071