1 /* GNUPLOT - set.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 
34 /*
35  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
36  * Added user-specified bases for log scaling.
37  */
38 
39 #include "setshow.h"
40 
41 #include "alloc.h"
42 #include "axis.h"
43 #include "command.h"
44 #include "contour.h"
45 #include "datafile.h"
46 #include "datablock.h"
47 #include "fit.h"
48 #include "gp_hist.h"
49 #include "gp_time.h"
50 #include "hidden3d.h"
51 #include "jitter.h"
52 #include "misc.h"
53 #include "plot.h"
54 #include "plot2d.h"
55 #include "plot3d.h"
56 #include "tables.h"
57 #include "tabulate.h"
58 #include "term_api.h"
59 #include "util.h"
60 #include "variable.h"
61 #include "pm3d.h"
62 #include "getcolor.h"
63 #include <ctype.h>
64 #ifdef HAVE_ICONV
65 #include <iconv.h>
66 #endif
67 #ifdef HAVE_LANGINFO_H
68 #include <langinfo.h>
69 #endif
70 
71 static palette_color_mode pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_NONE;
72 
73 static void set_angles __PROTO((void));
74 static void set_arrow __PROTO((void));
75 static int assign_arrow_tag __PROTO((void));
76 static void set_autoscale __PROTO((void));
77 static void set_bars __PROTO((void));
78 static void set_border __PROTO((void));
79 static void set_boxplot __PROTO((void));
80 static void set_boxwidth __PROTO((void));
81 static void set_clabel __PROTO((void));
82 static void set_clip __PROTO((void));
83 static void set_cntrparam __PROTO((void));
84 static void set_cntrlabel __PROTO((void));
85 static void set_contour __PROTO((void));
86 static void set_dashtype __PROTO((void));
87 static void set_dgrid3d __PROTO((void));
88 static void set_decimalsign __PROTO((void));
89 static void set_degreesign __PROTO((char *));
90 static void set_dummy __PROTO((void));
91 static void set_encoding __PROTO((void));
92 static void set_fit __PROTO((void));
93 static void set_grid __PROTO((void));
94 static void set_hidden3d __PROTO((void));
95 static void set_history __PROTO((void));
96 static void set_isosamples __PROTO((void));
97 static void set_key __PROTO((void));
98 static void set_label __PROTO((void));
99 static int assign_label_tag __PROTO((void));
100 static void set_loadpath __PROTO((void));
101 static void set_fontpath __PROTO((void));
102 static void set_locale __PROTO((void));
103 static void set_logscale __PROTO((void));
104 static void set_mapping __PROTO((void));
105 static void set_margin __PROTO((t_position *));
106 static void set_minus_sign __PROTO((void));
107 static void set_micro __PROTO((void));
108 static void set_missing __PROTO((void));
109 static void set_separator __PROTO((char **));
110 static void set_datafile_commentschars __PROTO((void));
111 static void set_monochrome __PROTO((void));
112 static void set_mouse __PROTO((void));
113 static void set_offsets __PROTO((void));
114 static void set_origin __PROTO((void));
115 static void set_output __PROTO((void));
116 static void set_parametric __PROTO((void));
117 static void set_pm3d __PROTO((void));
118 static void set_palette __PROTO((void));
119 static void set_colorbox __PROTO((void));
120 static void set_pointsize __PROTO((void));
121 static void set_pointintervalbox __PROTO((void));
122 static void set_polar __PROTO((void));
123 static void set_print __PROTO((void));
124 #ifdef EAM_OBJECTS
125 static void set_object __PROTO((void));
126 static void set_obj __PROTO((int, int));
127 #endif
128 static void set_psdir __PROTO((void));
129 static void set_rgbmax __PROTO((void));
130 static void set_samples __PROTO((void));
131 static void set_size __PROTO((void));
132 static void set_style __PROTO((void));
133 static void set_surface __PROTO((void));
134 static void set_table __PROTO((void));
135 static void set_terminal __PROTO((void));
136 static void set_termoptions __PROTO((void));
137 static void set_theta __PROTO((void));
138 static void set_tics __PROTO((void));
139 static void set_ticscale __PROTO((void));
140 static void set_timefmt __PROTO((void));
141 static void set_timestamp __PROTO((void));
142 static void set_view __PROTO((void));
143 static void set_zero __PROTO((void));
144 static void set_timedata __PROTO((struct axis *));
145 static void set_range __PROTO((struct axis *));
146 static void set_paxis __PROTO((void));
147 static void set_raxis __PROTO((void));
148 static void set_xyplane __PROTO((void));
149 static void set_ticslevel __PROTO((void));
150 static void set_zeroaxis __PROTO((AXIS_INDEX));
151 static void set_allzeroaxis __PROTO((void));
152 
153 
154 /******** Local functions ********/
155 
156 static void set_xyzlabel __PROTO((text_label * label));
157 static void load_tics __PROTO((struct axis * axis));
158 static void load_tic_user __PROTO((struct axis * axis));
159 static void load_tic_series __PROTO((struct axis * axis));
160 
161 static void set_linestyle __PROTO((struct linestyle_def **head, lp_class destination_class));
162 static void set_arrowstyle __PROTO((void));
163 static int assign_arrowstyle_tag __PROTO((void));
164 static int set_tic_prop __PROTO((struct axis *));
165 static void set_mttics __PROTO((struct axis *this_axis));
166 
167 static void check_palette_grayscale __PROTO((void));
168 static int set_palette_defined __PROTO((void));
169 static void set_palette_file __PROTO((void));
170 static void set_palette_function __PROTO((void));
171 static void parse_histogramstyle __PROTO((histogram_style *hs,
172 		t_histogram_type def_type, int def_gap));
173 static void set_style_parallel __PROTO((void));
174 static void parse_lighting_options __PROTO((void));
175 
176 static const char *encoding_micro __PROTO((void));
177 static const char *encoding_minus __PROTO((void));
178 
179 static const struct position default_position
180 	= {first_axes, first_axes, first_axes, 0., 0., 0.};
181 static const struct position default_offset
182 	= {character, character, character, 0., 0., 0.};
183 
184 static lp_style_type default_hypertext_point_style
185 	= {1, LT_BLACK, 4, DASHTYPE_SOLID, 0, 0, 1.0, PTSZ_DEFAULT, DEFAULT_P_CHAR, {TC_RGB, 0x000000, 0.0}, DEFAULT_DASHPATTERN};
186 
187 /******** The 'set' command ********/
188 void
set_command()189 set_command()
190 {
191     c_token++;
192 
193     /* Mild form of backwards compatibility */
194 	/* Allow "set no{foo}" rather than "unset foo" */
195     if (gp_input_line[token[c_token].start_index] == 'n' &&
196 	       gp_input_line[token[c_token].start_index+1] == 'o' &&
197 	       gp_input_line[token[c_token].start_index+2] != 'n') {
198 	if (interactive)
199 	    int_warn(c_token, "deprecated syntax, use \"unset\"");
200 	token[c_token].start_index += 2;
201 	token[c_token].length -= 2;
202 	c_token--;
203 	unset_command();
204     } else {
205 
206 	int save_token = c_token;
207 	set_iterator = check_for_iteration();
208 	if (empty_iteration(set_iterator)) {
209 	    /* Skip iteration [i=start:end] where start > end */
210 	    while (!END_OF_COMMAND) c_token++;
211 	    set_iterator = cleanup_iteration(set_iterator);
212 	    return;
213 	}
214 	if (forever_iteration(set_iterator)) {
215 	    set_iterator = cleanup_iteration(set_iterator);
216 	    int_error(save_token, "unbounded iteration");
217 	}
218 	save_token = c_token;
219 	ITERATE:
220 
221 	switch(lookup_table(&set_tbl[0],c_token)) {
222 	case S_ANGLES:
223 	    set_angles();
224 	    break;
225 	case S_ARROW:
226 	    set_arrow();
227 	    break;
228 	case S_AUTOSCALE:
229 	    set_autoscale();
230 	    break;
231 	case S_BARS:
232 	    set_bars();
233 	    break;
234 	case S_BORDER:
235 	    set_border();
236 	    break;
237 	case S_BOXWIDTH:
238 	    set_boxwidth();
239 	    break;
240 	case S_CLABEL:
241 	    set_clabel();
242 	    break;
243 	case S_CLIP:
244 	    set_clip();
245 	    break;
246 	case S_COLOR:
247 	    unset_monochrome();
248 	    c_token++;
249 	    break;
250 	case S_COLORSEQUENCE:
251 	    set_colorsequence(0);
252 	    break;
253 	case S_CNTRPARAM:
254 	    set_cntrparam();
255 	    break;
256 	case S_CNTRLABEL:
257 	    set_cntrlabel();
258 	    break;
259 	case S_CONTOUR:
260 	    set_contour();
261 	    break;
262 	case S_DASHTYPE:
263 	    set_dashtype();
264 	    break;
265 	case S_DGRID3D:
266 	    set_dgrid3d();
267 	    break;
268 	case S_DECIMALSIGN:
269 	    set_decimalsign();
270 	    break;
271 	case S_DUMMY:
272 	    set_dummy();
273 	    break;
274 	case S_ENCODING:
275 	    set_encoding();
276 	    break;
277 	case S_FIT:
278 	    set_fit();
279 	    break;
280 	case S_FONTPATH:
281 	    set_fontpath();
282 	    break;
283 	case S_FORMAT:
284 	    set_format();
285 	    break;
286 	case S_GRID:
287 	    set_grid();
288 	    break;
289 	case S_HIDDEN3D:
290 	    set_hidden3d();
291 	    break;
292 	case S_HISTORYSIZE:	/* Deprecated in favor of "set history size" */
293 	case S_HISTORY:
294 	    set_history();
295 	    break;
296 	case S_ISOSAMPLES:
297 	    set_isosamples();
298 	    break;
299 	case S_JITTER:
300 	    set_jitter();
301 	    break;
302 	case S_KEY:
303 	    set_key();
304 	    break;
305 	case S_LINESTYLE:
306 	    set_linestyle(&first_linestyle, LP_STYLE);
307 	    break;
308 	case S_LINETYPE:
309 	    if (equals(c_token+1,"cycle")) {
310 		c_token += 2;
311 		linetype_recycle_count = int_expression();
312 	    } else
313 		set_linestyle(&first_perm_linestyle, LP_TYPE);
314 	    break;
315 	case S_LABEL:
316 	    set_label();
317 	    break;
318 	case S_LINK:
319 	case S_NONLINEAR:
320 	    link_command();
321 	    break;
322 	case S_LOADPATH:
323 	    set_loadpath();
324 	    break;
325 	case S_LOCALE:
326 	    set_locale();
327 	    break;
328 	case S_LOGSCALE:
329 	    set_logscale();
330 	    break;
331 	case S_MACROS:
332 	    /* Aug 2013 - macros are always enabled */
333 	    c_token++;
334 	    break;
335 	case S_MAPPING:
336 	    set_mapping();
337 	    break;
338 	case S_MARGIN:
339 	    /* Jan 2015: CHANGE to order <left>,<right>,<bottom>,<top> */
340 	    set_margin(&lmargin);
341 	    if (!equals(c_token,","))
342 		break;
343 	    set_margin(&rmargin);
344 	    if (!equals(c_token,","))
345 		break;
346 	    set_margin(&bmargin);
347 	    if (!equals(c_token,","))
348 		break;
349 	    set_margin(&tmargin);
350 	    break;
351 	case S_BMARGIN:
352 	    set_margin(&bmargin);
353 	    break;
354 	case S_LMARGIN:
355 	    set_margin(&lmargin);
356 	    break;
357 	case S_RMARGIN:
358 	    set_margin(&rmargin);
359 	    break;
360 	case S_TMARGIN:
361 	    set_margin(&tmargin);
362 	    break;
363 	case S_MICRO:
364 	    set_micro();
365 	    break;
366 	case S_MINUS_SIGN:
367 	    set_minus_sign();
368 	    break;
369 	case S_DATAFILE:
370 	    if (almost_equals(++c_token,"miss$ing"))
371 		set_missing();
372 	    else if (almost_equals(c_token,"sep$arators"))
373 		set_separator(&df_separators);
374 	    else if (almost_equals(c_token,"com$mentschars"))
375 		set_datafile_commentschars();
376 	    else if (almost_equals(c_token,"bin$ary"))
377 		df_set_datafile_binary();
378 	    else if (almost_equals(c_token,"fort$ran")) {
379 		df_fortran_constants = TRUE;
380 		c_token++;
381 	    } else if (almost_equals(c_token,"nofort$ran")) {
382 		df_fortran_constants = FALSE;
383 		c_token++;
384 	    } else if (almost_equals(c_token,"fpe_trap")) {
385 		df_nofpe_trap = FALSE;
386 		c_token++;
387 	    } else if (almost_equals(c_token,"nofpe_trap")) {
388 		df_nofpe_trap = TRUE;
389 		c_token++;
390 	    } else
391 		int_error(c_token,"expecting datafile modifier");
392 	    break;
393 	case S_MOUSE:
394 	    set_mouse();
395 	    break;
396 	case S_MONOCHROME:
397 	    set_monochrome();
398 	    break;
399 	case S_MULTIPLOT:
400 	    term_start_multiplot();
401 	    break;
402 	case S_OFFSETS:
403 	    set_offsets();
404 	    break;
405 	case S_ORIGIN:
406 	    set_origin();
407 	    break;
408 	case SET_OUTPUT:
409 	    set_output();
410 	    break;
411 	case S_PARAMETRIC:
412 	    set_parametric();
413 	    break;
414 	case S_PM3D:
415 	    set_pm3d();
416 	    break;
417 	case S_PALETTE:
418 	    set_palette();
419 	    break;
420 	case S_COLORBOX:
421 	    set_colorbox();
422 	    break;
423 	case S_POINTINTERVALBOX:
424 	    set_pointintervalbox();
425 	    break;
426 	case S_POINTSIZE:
427 	    set_pointsize();
428 	    break;
429 	case S_POLAR:
430 	    set_polar();
431 	    break;
432 	case S_PRINT:
433 	    set_print();
434 	    break;
435 	case S_PSDIR:
436 	    set_psdir();
437 	    break;
438 #ifdef EAM_OBJECTS
439 	case S_OBJECT:
440 	    set_object();
441 	    break;
442 #endif
443 	case S_SAMPLES:
444 	    set_samples();
445 	    break;
446 	case S_RGBMAX:
447 	    set_rgbmax();
448 	    break;
449 	case S_SIZE:
450 	    set_size();
451 	    break;
452 	case S_STYLE:
453 	    set_style();
454 	    break;
455 	case S_SURFACE:
456 	    set_surface();
457 	    break;
458 	case S_TABLE:
459 	    set_table();
460 	    break;
461 	case S_TERMINAL:
462 	    set_terminal();
463 	    break;
464 	case S_TERMOPTIONS:
465 	    set_termoptions();
466 	    break;
467 	case S_THETA:
468 	    set_theta();
469 	    break;
470 	case S_TICS:
471 	    set_tics();
472 	    break;
473 	case S_TICSCALE:
474 	    set_ticscale();
475 	    break;
476 	case S_TIMEFMT:
477 	    set_timefmt();
478 	    break;
479 	case S_TIMESTAMP:
480 	    set_timestamp();
481 	    break;
482 	case S_TITLE:
483 	    set_xyzlabel(&title);
484 	    title.rotate = 0.0;
485 	    break;
486 	case S_VIEW:
487 	    set_view();
488 	    break;
489 	case S_ZERO:
490 	    set_zero();
491 	    break;
492 
493 	case S_MXTICS:
494 	case S_NOMXTICS:
495 	case S_XTICS:
496 	case S_NOXTICS:
497 	case S_XDTICS:
498 	case S_NOXDTICS:
499 	case S_XMTICS:
500 	case S_NOXMTICS:
501 	    set_tic_prop(&axis_array[FIRST_X_AXIS]);
502 	    break;
503 	case S_MYTICS:
504 	case S_NOMYTICS:
505 	case S_YTICS:
506 	case S_NOYTICS:
507 	case S_YDTICS:
508 	case S_NOYDTICS:
509 	case S_YMTICS:
510 	case S_NOYMTICS:
511 	    set_tic_prop(&axis_array[FIRST_Y_AXIS]);
512 	    break;
513 	case S_MX2TICS:
514 	case S_NOMX2TICS:
515 	case S_X2TICS:
516 	case S_NOX2TICS:
517 	case S_X2DTICS:
518 	case S_NOX2DTICS:
519 	case S_X2MTICS:
520 	case S_NOX2MTICS:
521 	    set_tic_prop(&axis_array[SECOND_X_AXIS]);
522 	    break;
523 	case S_MY2TICS:
524 	case S_NOMY2TICS:
525 	case S_Y2TICS:
526 	case S_NOY2TICS:
527 	case S_Y2DTICS:
528 	case S_NOY2DTICS:
529 	case S_Y2MTICS:
530 	case S_NOY2MTICS:
531 	    set_tic_prop(&axis_array[SECOND_Y_AXIS]);
532 	    break;
533 	case S_MZTICS:
534 	case S_NOMZTICS:
535 	case S_ZTICS:
536 	case S_NOZTICS:
537 	case S_ZDTICS:
538 	case S_NOZDTICS:
539 	case S_ZMTICS:
540 	case S_NOZMTICS:
541 	    set_tic_prop(&axis_array[FIRST_Z_AXIS]);
542 	    break;
543 	case S_MCBTICS:
544 	case S_NOMCBTICS:
545 	case S_CBTICS:
546 	case S_NOCBTICS:
547 	case S_CBDTICS:
548 	case S_NOCBDTICS:
549 	case S_CBMTICS:
550 	case S_NOCBMTICS:
551 	    set_tic_prop(&axis_array[COLOR_AXIS]);
552 	    break;
553 	case S_RTICS:
554 	case S_MRTICS:
555 	    set_tic_prop(&axis_array[POLAR_AXIS]);
556 	    break;
557 	case S_TTICS:
558 	    set_tic_prop(&THETA_AXIS);
559 	    break;
560 	case S_MTTICS:
561 	    set_mttics(&THETA_AXIS);
562 	    break;
563 	case S_XDATA:
564 	    set_timedata(&axis_array[FIRST_X_AXIS]);
565 	    axis_array[T_AXIS].datatype
566 	      = axis_array[U_AXIS].datatype
567 	      = axis_array[FIRST_X_AXIS].datatype;
568 	    break;
569 	case S_YDATA:
570 	    set_timedata(&axis_array[FIRST_Y_AXIS]);
571 	    axis_array[V_AXIS].datatype
572 	      = axis_array[FIRST_X_AXIS].datatype;
573 	    break;
574 	case S_ZDATA:
575 	    set_timedata(&axis_array[FIRST_Z_AXIS]);
576 	    break;
577 	case S_CBDATA:
578 	    set_timedata(&axis_array[COLOR_AXIS]);
579 	    break;
580 	case S_X2DATA:
581 	    set_timedata(&axis_array[SECOND_X_AXIS]);
582 	    break;
583 	case S_Y2DATA:
584 	    set_timedata(&axis_array[SECOND_Y_AXIS]);
585 	    break;
586 	case S_XLABEL:
587 	    set_xyzlabel(&axis_array[FIRST_X_AXIS].label);
588 	    break;
589 	case S_YLABEL:
590 	    set_xyzlabel(&axis_array[FIRST_Y_AXIS].label);
591 	    break;
592 	case S_ZLABEL:
593 	    set_xyzlabel(&axis_array[FIRST_Z_AXIS].label);
594 	    break;
595 	case S_CBLABEL:
596 	    set_xyzlabel(&axis_array[COLOR_AXIS].label);
597 	    break;
598 	case S_RLABEL:
599 	    set_xyzlabel(&axis_array[POLAR_AXIS].label);
600 	    break;
601 	case S_X2LABEL:
602 	    set_xyzlabel(&axis_array[SECOND_X_AXIS].label);
603 	    break;
604 	case S_Y2LABEL:
605 	    set_xyzlabel(&axis_array[SECOND_Y_AXIS].label);
606 	    break;
607 	case S_XRANGE:
608 	    set_range(&axis_array[FIRST_X_AXIS]);
609 	    break;
610 	case S_X2RANGE:
611 	    set_range(&axis_array[SECOND_X_AXIS]);
612 	    break;
613 	case S_YRANGE:
614 	    set_range(&axis_array[FIRST_Y_AXIS]);
615 	    break;
616 	case S_Y2RANGE:
617 	    set_range(&axis_array[SECOND_Y_AXIS]);
618 	    break;
619 	case S_ZRANGE:
620 	    set_range(&axis_array[FIRST_Z_AXIS]);
621 	    break;
622 	case S_CBRANGE:
623 	    set_range(&axis_array[COLOR_AXIS]);
624 	    break;
625 	case S_RRANGE:
626 	    set_range(&axis_array[POLAR_AXIS]);
627 	    if (polar)
628 		rrange_to_xy();
629 	    break;
630 	case S_TRANGE:
631 	    set_range(&axis_array[T_AXIS]);
632 	    break;
633 	case S_URANGE:
634 	    set_range(&axis_array[U_AXIS]);
635 	    break;
636 	case S_VRANGE:
637 	    set_range(&axis_array[V_AXIS]);
638 	    break;
639 	case S_PAXIS:
640 	    set_paxis();
641 	    break;
642 	case S_RAXIS:
643 	    set_raxis();
644 	    break;
645 	case S_XZEROAXIS:
646 	    set_zeroaxis(FIRST_X_AXIS);
647 	    break;
648 	case S_YZEROAXIS:
649 	    set_zeroaxis(FIRST_Y_AXIS);
650 	    break;
651 	case S_ZZEROAXIS:
652 	    set_zeroaxis(FIRST_Z_AXIS);
653 	    break;
654 	case S_X2ZEROAXIS:
655 	    set_zeroaxis(SECOND_X_AXIS);
656 	    break;
657 	case S_Y2ZEROAXIS:
658 	    set_zeroaxis(SECOND_Y_AXIS);
659 	    break;
660 	case S_ZEROAXIS:
661 	    set_allzeroaxis();
662 	    break;
663 	case S_XYPLANE:
664 	    set_xyplane();
665 	    break;
666 	case S_TICSLEVEL:
667 	    set_ticslevel();
668 	    break;
669 	default:
670 	    int_error(c_token, "unrecognized option - see 'help set'.");
671 	    break;
672 	}
673 
674     	if (next_iteration(set_iterator)) {
675 	    c_token = save_token;
676 	    goto ITERATE;
677 	}
678 
679     }
680 
681     update_gpval_variables(0);
682 
683     set_iterator = cleanup_iteration(set_iterator);
684 }
685 
686 
687 /* process 'set angles' command */
688 static void
set_angles()689 set_angles()
690 {
691     c_token++;
692     if (END_OF_COMMAND) {
693 	/* assuming same as defaults */
694 	ang2rad = 1;
695     } else if (almost_equals(c_token, "r$adians")) {
696 	c_token++;
697 	ang2rad = 1;
698     } else if (almost_equals(c_token, "d$egrees")) {
699 	c_token++;
700 	ang2rad = DEG2RAD;
701     } else
702 	int_error(c_token, "expecting 'radians' or 'degrees'");
703 
704     if (polar && axis_array[T_AXIS].set_autoscale) {
705 	/* set trange if in polar mode and no explicit range */
706 	axis_array[T_AXIS].set_min = 0;
707 	axis_array[T_AXIS].set_max = 2 * M_PI / ang2rad;
708     }
709 }
710 
711 
712 /* process a 'set arrow' command */
713 /* set arrow {tag} {from x,y} {to x,y} {{no}head} ... */
714 /* allow any order of options - pm 25.11.2001 */
715 static void
set_arrow()716 set_arrow()
717 {
718     struct arrow_def *this_arrow = NULL;
719     struct arrow_def *new_arrow = NULL;
720     struct arrow_def *prev_arrow = NULL;
721     TBOOLEAN duplication = FALSE;
722     TBOOLEAN set_start = FALSE;
723     TBOOLEAN set_end = FALSE;
724     int save_token;
725     int tag;
726 
727     c_token++;
728 
729     /* get tag */
730     if (almost_equals(c_token, "back$head") || equals(c_token, "front")
731 	    || equals(c_token, "from") || equals(c_token, "at")
732 	    || equals(c_token, "to") || equals(c_token, "rto")
733 	    || equals(c_token, "size")
734 	    || equals(c_token, "filled") || equals(c_token, "empty")
735 	    || equals(c_token, "as") || equals(c_token, "arrowstyle")
736 	    || almost_equals(c_token, "head$s") || equals(c_token, "nohead")
737 	    || almost_equals(c_token, "nobo$rder")) {
738 	tag = assign_arrow_tag();
739 
740     } else
741 	tag = int_expression();
742 
743     if (tag <= 0)
744 	int_error(c_token, "tag must be > 0");
745 
746     /* OK! add arrow */
747     if (first_arrow != NULL) {	/* skip to last arrow */
748 	for (this_arrow = first_arrow; this_arrow != NULL;
749 	     prev_arrow = this_arrow, this_arrow = this_arrow->next)
750 	    /* is this the arrow we want? */
751 	    if (tag <= this_arrow->tag)
752 		break;
753     }
754     if (this_arrow == NULL || tag != this_arrow->tag) {
755 	new_arrow = gp_alloc(sizeof(struct arrow_def), "arrow");
756 	if (prev_arrow == NULL)
757 	    first_arrow = new_arrow;
758 	else
759 	    prev_arrow->next = new_arrow;
760 	new_arrow->tag = tag;
761 	new_arrow->next = this_arrow;
762 	this_arrow = new_arrow;
763 
764 	this_arrow->start = default_position;
765 	this_arrow->end = default_position;
766 	this_arrow->angle = 0.0;
767 	this_arrow->type = arrow_end_undefined;
768 
769 	default_arrow_style(&(new_arrow->arrow_properties));
770     }
771 
772     while (!END_OF_COMMAND) {
773 
774 	/* get start position */
775 	if (equals(c_token, "from") || equals(c_token,"at")) {
776 	    if (set_start) { duplication = TRUE; break; }
777 	    c_token++;
778 	    if (END_OF_COMMAND)
779 		int_error(c_token, "start coordinates expected");
780 	    /* get coordinates */
781 	    get_position(&this_arrow->start);
782 	    set_start = TRUE;
783 	    continue;
784 	}
785 
786 	/* get end or relative end position */
787 	if (equals(c_token, "to") || equals(c_token,"rto")) {
788 	    if (set_end) { duplication = TRUE; break; }
789 	    if (equals(c_token,"rto"))
790 		this_arrow->type = arrow_end_relative;
791 	    else
792 		this_arrow->type = arrow_end_absolute;
793 	    c_token++;
794 	    if (END_OF_COMMAND)
795 		int_error(c_token, "end coordinates expected");
796 	    /* get coordinates */
797 	    get_position(&this_arrow->end);
798 	    set_end = TRUE;
799 	    continue;
800 	}
801 
802 	/* get end position specified as length + orientation angle */
803 	if (almost_equals(c_token, "len$gth")) {
804 	    if (set_end) { duplication = TRUE; break; }
805 	    this_arrow->type = arrow_end_oriented;
806 	    c_token++;
807 	    get_position_default(&this_arrow->end, first_axes, 1);
808 	    set_end = TRUE;
809 	    continue;
810 	}
811 	if (almost_equals(c_token,"ang$le")) {
812 	    c_token++;
813 	    this_arrow->angle = real_expression();
814 	    continue;
815 	}
816 
817 	/* Allow interspersed style commands */
818 	save_token = c_token;
819 	arrow_parse(&this_arrow->arrow_properties, TRUE);
820 	if (save_token != c_token)
821 	    continue;
822 
823 	if (!END_OF_COMMAND)
824 	    int_error(c_token, "wrong argument in set arrow");
825 
826     } /* while (!END_OF_COMMAND) */
827 
828     if (duplication)
829 	int_error(c_token, "duplicate or contradictory arguments");
830 
831 }
832 
833 
834 /* assign a new arrow tag
835  * arrows are kept sorted by tag number, so this is easy
836  * returns the lowest unassigned tag number
837  */
838 static int
assign_arrow_tag()839 assign_arrow_tag()
840 {
841     struct arrow_def *this_arrow;
842     int last = 0;		/* previous tag value */
843 
844     for (this_arrow = first_arrow; this_arrow != NULL;
845 	 this_arrow = this_arrow->next)
846 	if (this_arrow->tag == last + 1)
847 	    last++;
848 	else
849 	    break;
850 
851     return (last + 1);
852 }
853 
854 /* helper routine for 'set autoscale' on a single axis */
855 static TBOOLEAN
set_autoscale_axis(struct axis * this)856 set_autoscale_axis(struct axis *this)
857 {
858     char keyword[16];
859     char *name = (char *) &(axis_name(this->index)[0]);
860 
861     if (equals(c_token, name)) {
862 	this->set_autoscale = AUTOSCALE_BOTH;
863 	this->min_constraint = CONSTRAINT_NONE;
864 	this->max_constraint = CONSTRAINT_NONE;
865 	++c_token;
866 	return TRUE;
867     }
868     sprintf(keyword, "%smi$n", name);
869     if (almost_equals(c_token, keyword)) {
870 	this->set_autoscale |= AUTOSCALE_MIN;
871 	this->min_constraint = CONSTRAINT_NONE;
872 	++c_token;
873 	return TRUE;
874     }
875     sprintf(keyword, "%sma$x", name);
876     if (almost_equals(c_token, keyword)) {
877 	this->set_autoscale |= AUTOSCALE_MAX;
878 	this->max_constraint = CONSTRAINT_NONE;
879 	++c_token;
880 	return TRUE;
881     }
882     sprintf(keyword, "%sfix", name);
883     if (equals(c_token, keyword)) {
884 	this->set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX;
885 	++c_token;
886 	return TRUE;
887     }
888     sprintf(keyword, "%sfixmi$n", name);
889     if (almost_equals(c_token, keyword)) {
890 	this->set_autoscale |= AUTOSCALE_FIXMIN;
891 	++c_token;
892 	return TRUE;
893     }
894     sprintf(keyword, "%sfixma$x", name);
895     if (almost_equals(c_token, keyword)) {
896 	this->set_autoscale |= AUTOSCALE_FIXMAX;
897 	++c_token;
898 	return TRUE;
899     }
900 
901     return FALSE;
902 }
903 
904 /* process 'set autoscale' command */
905 static void
set_autoscale()906 set_autoscale()
907 {
908     int axis;
909 
910     c_token++;
911     if (END_OF_COMMAND) {
912 	for (axis=0; axis<AXIS_ARRAY_SIZE; axis++)
913 	    axis_array[axis].set_autoscale = AUTOSCALE_BOTH;
914 	for (axis=0; axis<num_parallel_axes; axis++)
915 	    parallel_axis[axis].set_autoscale = AUTOSCALE_BOTH;
916 	return;
917     } else if (equals(c_token, "xy") || equals(c_token, "yx")) {
918 	axis_array[FIRST_X_AXIS].set_autoscale =
919 	    axis_array[FIRST_Y_AXIS].set_autoscale =  AUTOSCALE_BOTH;
920 	axis_array[FIRST_X_AXIS].min_constraint =
921 	    axis_array[FIRST_X_AXIS].max_constraint =
922 	    axis_array[FIRST_Y_AXIS].min_constraint =
923 	    axis_array[FIRST_Y_AXIS].max_constraint = CONSTRAINT_NONE;
924 	c_token++;
925 	return;
926     } else if (equals(c_token, "fix") || almost_equals(c_token, "noext$end")) {
927 	for (axis=0; axis<AXIS_ARRAY_SIZE; axis++)
928 	    axis_array[axis].set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX;
929 	for (axis=0; axis<num_parallel_axes; axis++)
930 	    parallel_axis[axis].set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX;
931 	c_token++;
932 	return;
933     } else if (almost_equals(c_token, "ke$epfix")) {
934 	for (axis=0; axis<AXIS_ARRAY_SIZE; axis++)
935 	    axis_array[axis].set_autoscale |= AUTOSCALE_BOTH;
936 	for (axis=0; axis<num_parallel_axes; axis++)
937 	    parallel_axis[axis].set_autoscale |= AUTOSCALE_BOTH;
938 	c_token++;
939 	return;
940     }
941 
942     if (set_autoscale_axis(&axis_array[FIRST_X_AXIS])) return;
943     if (set_autoscale_axis(&axis_array[FIRST_Y_AXIS])) return;
944     if (set_autoscale_axis(&axis_array[FIRST_Z_AXIS])) return;
945     if (set_autoscale_axis(&axis_array[SECOND_X_AXIS])) return;
946     if (set_autoscale_axis(&axis_array[SECOND_Y_AXIS])) return;
947     if (set_autoscale_axis(&axis_array[COLOR_AXIS])) return;
948     if (set_autoscale_axis(&axis_array[POLAR_AXIS])) return;
949     /* FIXME: Do these commands make any sense? */
950     if (set_autoscale_axis(&axis_array[T_AXIS])) return;
951     if (set_autoscale_axis(&axis_array[U_AXIS])) return;
952     if (set_autoscale_axis(&axis_array[V_AXIS])) return;
953 
954     /* come here only if nothing found: */
955 	int_error(c_token, "Invalid range");
956 }
957 
958 
959 /* process 'set bars' command */
960 static void
set_bars()961 set_bars()
962 {
963     int save_token;
964 
965     c_token++;
966 
967     if (END_OF_COMMAND)
968 	reset_bars();
969 
970     while (!END_OF_COMMAND) {
971 
972 	if (equals(c_token,"default")) {
973 	    reset_bars();
974 	    ++c_token;
975 	    return;
976 	}
977 
978 	/* Jul 2015 - allow a separate line type for error bars */
979 	save_token = c_token;
980 	lp_parse(&bar_lp, LP_ADHOC, FALSE);
981 	if (c_token != save_token) {
982 	    bar_lp.flags = LP_ERRORBAR_SET;
983 	    continue;
984 	}
985 
986 	if (almost_equals(c_token,"s$mall")) {
987 	    bar_size = 0.0;
988 	    ++c_token;
989 	} else if (almost_equals(c_token,"l$arge")) {
990 	    bar_size = 1.0;
991 	    ++c_token;
992 	} else if (almost_equals(c_token,"full$width")) {
993 	    bar_size = -1.0;
994 	    ++c_token;
995 	} else if (equals(c_token,"front")) {
996 	    bar_layer = LAYER_FRONT;
997 	    ++c_token;
998 	} else if (equals(c_token,"back")) {
999 	    bar_layer = LAYER_BACK;
1000 	    ++c_token;
1001 	} else {
1002 	    bar_size = real_expression();
1003 	}
1004     }
1005 }
1006 
1007 
1008 /* process 'set border' command */
1009 static void
set_border()1010 set_border()
1011 {
1012     c_token++;
1013     if (END_OF_COMMAND){
1014 	draw_border = 31;
1015 	border_layer = LAYER_FRONT;
1016 	border_lp = default_border_lp;
1017     }
1018 
1019     while (!END_OF_COMMAND) {
1020 	if (equals(c_token,"front")) {
1021 	    border_layer = LAYER_FRONT;
1022 	    c_token++;
1023 	} else if (equals(c_token,"back")) {
1024 	    border_layer = LAYER_BACK;
1025 	    c_token++;
1026 	} else if (equals(c_token,"behind")) {
1027 	    border_layer = LAYER_BEHIND;
1028 	    c_token++;
1029 	} else if (equals(c_token,"polar")) {
1030 	    draw_border |= 0x1000;
1031 	    c_token++;
1032 	} else {
1033 	    int save_token = c_token;
1034 	    lp_parse(&border_lp, LP_ADHOC, FALSE);
1035 	    if (save_token != c_token)
1036 		continue;
1037 	    draw_border = int_expression();
1038 	}
1039     }
1040 
1041     /* This is the only place the user can change the border	*/
1042     /* so remember what he set.  If draw_border is later changed*/
1043     /* internally, we can still recover the user's preference.	*/
1044     user_border = draw_border;
1045 }
1046 
1047 
1048 /* process 'set style boxplot' command */
1049 static void
set_boxplot()1050 set_boxplot()
1051 {
1052     c_token++;
1053     if (END_OF_COMMAND) {
1054 	boxplot_style defstyle = DEFAULT_BOXPLOT_STYLE;
1055 	boxplot_opts = defstyle;
1056     }
1057     while (!END_OF_COMMAND) {
1058 	if (almost_equals(c_token, "noout$liers")) {
1059 	    boxplot_opts.outliers = FALSE;
1060 	    c_token++;
1061 	}
1062 	else if (almost_equals(c_token, "out$liers")) {
1063 	    boxplot_opts.outliers = TRUE;
1064 	    c_token++;
1065 	}
1066 	else if (almost_equals(c_token, "point$type") || equals (c_token, "pt")) {
1067 	    c_token++;
1068 	    boxplot_opts.pointtype = int_expression()-1;
1069 	}
1070 	else if (equals(c_token,"range")) {
1071 	    c_token++;
1072 	    boxplot_opts.limit_type = 0;
1073 	    boxplot_opts.limit_value = real_expression();
1074 	}
1075 	else if (almost_equals(c_token,"frac$tion")) {
1076 	    c_token++;
1077 	    boxplot_opts.limit_value = real_expression();
1078 	    if (boxplot_opts.limit_value < 0 || boxplot_opts.limit_value > 1)
1079 		int_error(c_token-1,"fraction must be less than 1");
1080 	    boxplot_opts.limit_type = 1;
1081 	}
1082 	else if (almost_equals(c_token,"candle$sticks")) {
1083 	    c_token++;
1084 	    boxplot_opts.plotstyle = CANDLESTICKS;
1085 	}
1086 	else if (almost_equals(c_token,"finance$bars")) {
1087 	    c_token++;
1088 	    boxplot_opts.plotstyle = FINANCEBARS;
1089 	}
1090 	else if (almost_equals(c_token,"sep$aration")) {
1091 	    c_token++;
1092 	    boxplot_opts.separation = real_expression();
1093 	    if (boxplot_opts.separation < 0)
1094 		int_error(c_token-1,"separation must be > 0");
1095 	}
1096 	else if (almost_equals(c_token,"lab$els")) {
1097 	    c_token++;
1098 	    if (equals(c_token, "off")) {
1099 		boxplot_opts.labels = BOXPLOT_FACTOR_LABELS_OFF;
1100 	    }
1101 	    else if (equals(c_token, "x")) {
1102 		boxplot_opts.labels = BOXPLOT_FACTOR_LABELS_X;
1103 	    }
1104 	    else if (equals(c_token, "x2")) {
1105 		boxplot_opts.labels = BOXPLOT_FACTOR_LABELS_X2;
1106 	    }
1107 	    else if (equals(c_token, "auto")) {
1108 		boxplot_opts.labels = BOXPLOT_FACTOR_LABELS_AUTO;
1109 	    }
1110 	    else
1111 		int_error(c_token-1,"expecting 'x', 'x2', 'auto' or 'off'");
1112 	    c_token++;
1113 	}
1114 	else if (almost_equals(c_token, "median$linewidth")) {
1115 	    c_token++;
1116 	    boxplot_opts.median_linewidth = real_expression();
1117 	}
1118 	else if (almost_equals(c_token, "so$rted")) {
1119 	    boxplot_opts.sort_factors = TRUE;
1120 	    c_token++;
1121 	}
1122 	else if (almost_equals(c_token, "un$sorted")) {
1123 	    boxplot_opts.sort_factors = FALSE;
1124 	    c_token++;
1125 	}
1126 	else
1127 	    int_error(c_token,"unrecognized option");
1128     }
1129 
1130 }
1131 
1132 
1133 /* process 'set boxwidth' command */
1134 static void
set_boxwidth()1135 set_boxwidth()
1136 {
1137     c_token++;
1138     if (END_OF_COMMAND) {
1139 	boxwidth = -1.0;
1140 	boxwidth_is_absolute = TRUE;
1141     } else {
1142 	boxwidth = real_expression();
1143     }
1144     if (END_OF_COMMAND)
1145 	return;
1146     else {
1147 	if (almost_equals(c_token, "a$bsolute"))
1148 	    boxwidth_is_absolute = TRUE;
1149 	else if (almost_equals(c_token, "r$elative"))
1150 	    boxwidth_is_absolute = FALSE;
1151 	else
1152 	    int_error(c_token, "expecting 'absolute' or 'relative' ");
1153     }
1154     c_token++;
1155 }
1156 
1157 /* process 'set clabel' command */
1158 static void
set_clabel()1159 set_clabel()
1160 {
1161     char *new_format;
1162 
1163     c_token++;
1164     clabel_onecolor = FALSE;
1165     if ((new_format = try_to_get_string())) {
1166 	safe_strncpy(contour_format, new_format, sizeof(contour_format));
1167 	free(new_format);
1168     }
1169 }
1170 
1171 
1172 /* process 'set clip' command */
1173 static void
set_clip()1174 set_clip()
1175 {
1176     c_token++;
1177     if (END_OF_COMMAND) {
1178 	/* assuming same as points */
1179 	clip_points = TRUE;
1180     } else if (almost_equals(c_token, "p$oints")) {
1181 	clip_points = TRUE;
1182 	c_token++;
1183     } else if (almost_equals(c_token, "o$ne")) {
1184 	clip_lines1 = TRUE;
1185 	c_token++;
1186     } else if (almost_equals(c_token, "t$wo")) {
1187 	clip_lines2 = TRUE;
1188 	c_token++;
1189     } else
1190 	int_error(c_token, "expecting 'points', 'one', or 'two'");
1191 }
1192 
1193 
1194 /* process 'set cntrparam' command */
1195 static void
set_cntrparam()1196 set_cntrparam()
1197 {
1198     c_token++;
1199     if (END_OF_COMMAND) {
1200 	/* assuming same as defaults */
1201 	contour_pts = DEFAULT_NUM_APPROX_PTS;
1202 	contour_kind = CONTOUR_KIND_LINEAR;
1203 	contour_order = DEFAULT_CONTOUR_ORDER;
1204 	contour_levels = DEFAULT_CONTOUR_LEVELS;
1205 	contour_levels_kind = LEVELS_AUTO;
1206 	contour_firstlinetype = 0;
1207 	return;
1208     }
1209 
1210     while (!END_OF_COMMAND) {
1211     if (almost_equals(c_token, "p$oints")) {
1212 	c_token++;
1213 	contour_pts = int_expression();
1214     } else if (almost_equals(c_token, "first$linetype")) {
1215 	c_token++;
1216 	contour_firstlinetype = int_expression();
1217     } else if (almost_equals(c_token, "sort$ed")) {
1218 	c_token++;
1219 	contour_sortlevels = TRUE;
1220     } else if (almost_equals(c_token, "unsort$ed")) {
1221 	c_token++;
1222 	contour_sortlevels = FALSE;
1223     } else if (almost_equals(c_token, "li$near")) {
1224 	c_token++;
1225 	contour_kind = CONTOUR_KIND_LINEAR;
1226     } else if (almost_equals(c_token, "c$ubicspline")) {
1227 	c_token++;
1228 	contour_kind = CONTOUR_KIND_CUBIC_SPL;
1229     } else if (almost_equals(c_token, "b$spline")) {
1230 	c_token++;
1231 	contour_kind = CONTOUR_KIND_BSPLINE;
1232     } else if (almost_equals(c_token, "le$vels")) {
1233 	c_token++;
1234 
1235 	if (!(set_iterator && set_iterator->iteration)) {
1236 	    free_dynarray(&dyn_contour_levels_list);
1237 	    init_dynarray(&dyn_contour_levels_list, sizeof(double), 5, 10);
1238 	}
1239 
1240 	/*  RKC: I have modified the next two:
1241 	 *   to use commas to separate list elements as in xtics
1242 	 *   so that incremental lists start,incr[,end]as in "
1243 	 */
1244 	if (almost_equals(c_token, "di$screte")) {
1245 	    contour_levels_kind = LEVELS_DISCRETE;
1246 	    c_token++;
1247 	    if (END_OF_COMMAND)
1248 		int_error(c_token, "expecting discrete level");
1249 	    else
1250 		*(double *)nextfrom_dynarray(&dyn_contour_levels_list) =
1251 		    real_expression();
1252 
1253 	    while(!END_OF_COMMAND) {
1254 		if (!equals(c_token, ","))
1255 		    int_error(c_token,
1256 			      "expecting comma to separate discrete levels");
1257 		c_token++;
1258 		*(double *)nextfrom_dynarray(&dyn_contour_levels_list) =
1259 		    real_expression();
1260 	    }
1261 	    contour_levels = dyn_contour_levels_list.end;
1262 	} else if (almost_equals(c_token, "in$cremental")) {
1263 	    int i = 0;  /* local counter */
1264 
1265 	    contour_levels_kind = LEVELS_INCREMENTAL;
1266 	    c_token++;
1267 	    contour_levels_list[i++] = real_expression();
1268 	    if (!equals(c_token, ","))
1269 		int_error(c_token,
1270 			  "expecting comma to separate start,incr levels");
1271 	    c_token++;
1272 	    if ((contour_levels_list[i++] = real_expression()) == 0)
1273 		int_error(c_token, "increment cannot be 0");
1274 	    if (!END_OF_COMMAND) {
1275 		if (!equals(c_token, ","))
1276 		    int_error(c_token,
1277 			      "expecting comma to separate incr,stop levels");
1278 		c_token++;
1279 		/* need to round up, since 10,10,50 is 5 levels, not four,
1280 		 * but 10,10,49 is four
1281 		 */
1282 		dyn_contour_levels_list.end = i;
1283 		contour_levels = (int) ( (real_expression()-contour_levels_list[0])/contour_levels_list[1] + 1.0);
1284 	    }
1285 	} else if (almost_equals(c_token, "au$to")) {
1286 	    contour_levels_kind = LEVELS_AUTO;
1287 	    c_token++;
1288 	    if (!END_OF_COMMAND)
1289 		contour_levels = int_expression();
1290 	} else {
1291 	    if (contour_levels_kind == LEVELS_DISCRETE)
1292 		int_error(c_token, "Levels type is discrete, ignoring new number of contour levels");
1293 	    contour_levels = int_expression();
1294 	}
1295     } else if (almost_equals(c_token, "o$rder")) {
1296 	int order;
1297 	c_token++;
1298 	order = int_expression();
1299 	if ( order < 2 || order > MAX_BSPLINE_ORDER )
1300 	    int_error(c_token, "bspline order must be in [2..10] range.");
1301 	contour_order = order;
1302     } else
1303 	int_error(c_token, "expecting 'linear', 'cubicspline', 'bspline', 'points', 'levels' or 'order'");
1304     }
1305 }
1306 
1307 /* process 'set cntrlabel' command */
1308 static void
set_cntrlabel()1309 set_cntrlabel()
1310 {
1311     c_token++;
1312     if (END_OF_COMMAND) {
1313 	strcpy(contour_format, "%8.3g");
1314 	clabel_onecolor = FALSE;
1315 	return;
1316     }
1317     while (!END_OF_COMMAND) {
1318 	if (almost_equals(c_token, "form$at")) {
1319 	    char *new;
1320 	    c_token++;
1321 	    if ((new = try_to_get_string()))
1322 		safe_strncpy(contour_format,new,sizeof(contour_format));
1323 	    free(new);
1324 	} else if (equals(c_token, "font")) {
1325 	    char *ctmp;
1326 	    c_token++;
1327 	    if ((ctmp = try_to_get_string())) {
1328 		free(clabel_font);
1329 		clabel_font = ctmp;
1330 	    }
1331 	} else if (almost_equals(c_token, "one$color")) {
1332 	    c_token++;
1333 	    clabel_onecolor = TRUE;
1334 	} else if (equals(c_token, "start")) {
1335 	    c_token++;
1336 	    clabel_start = int_expression();
1337 	    if (clabel_start <= 0)
1338 		clabel_start = 5;
1339 	} else if (almost_equals(c_token, "int$erval")) {
1340 	    c_token++;
1341 	    clabel_interval = int_expression();
1342 	} else {
1343 	    int_error(c_token, "unrecognized option");
1344 	}
1345     }
1346 }
1347 
1348 /* process 'set contour' command */
1349 static void
set_contour()1350 set_contour()
1351 {
1352     c_token++;
1353     if (END_OF_COMMAND)
1354 	/* assuming same as points */
1355 	draw_contour = CONTOUR_BASE;
1356     else {
1357 	if (almost_equals(c_token, "ba$se"))
1358 	    draw_contour = CONTOUR_BASE;
1359 	else if (almost_equals(c_token, "s$urface"))
1360 	    draw_contour = CONTOUR_SRF;
1361 	else if (almost_equals(c_token, "bo$th"))
1362 	    draw_contour = CONTOUR_BOTH;
1363 	else
1364 	    int_error(c_token, "expecting 'base', 'surface', or 'both'");
1365 	c_token++;
1366     }
1367 }
1368 
1369 /* process 'set colorsequence command */
1370 void
set_colorsequence(int option)1371 set_colorsequence(int option)
1372 {
1373     unsigned long default_colors[] = DEFAULT_COLOR_SEQUENCE;
1374     unsigned long podo_colors[] = PODO_COLOR_SEQUENCE;
1375 
1376     if (option == 0) {	/* Read option from command line */
1377 	if (equals(++c_token, "default"))
1378 	    option = 1;
1379 	else if (equals(c_token, "podo"))
1380 	    option = 2;
1381 	else if (equals(c_token, "classic"))
1382 	    option = 3;
1383 	else
1384 	    int_error(c_token, "unrecognized color set");
1385     }
1386 
1387     if (option == 1 || option == 2) {
1388 	int i;
1389 	char *command;
1390 	char *command_template = "set linetype %2d lc rgb 0x%06x";
1391 	unsigned long *colors = default_colors;
1392 	if (option == 2)
1393 	    colors = podo_colors;
1394 	linetype_recycle_count = 8;
1395 	for (i = 1; i <= 8; i++) {
1396 	    command = gp_alloc(strlen(command_template)+8, "dynamic command");
1397 	    sprintf(command, command_template, i, colors[i-1]);
1398 	    do_string_and_free(command);
1399 	}
1400 
1401     } else if (option == 3) {
1402 	struct linestyle_def *this;
1403 	for (this = first_perm_linestyle; this != NULL; this = this->next) {
1404 	    this->lp_properties.pm3d_color.type = TC_LT;
1405 	    this->lp_properties.pm3d_color.lt = this->tag-1;
1406 	}
1407 	linetype_recycle_count = 0;
1408 
1409     } else {
1410 	int_error(c_token, "Expecting 'classic' or 'default'");
1411     }
1412     c_token++;
1413 }
1414 
1415 /* process 'set dashtype' command */
1416 static void
set_dashtype()1417 set_dashtype()
1418 {
1419     struct custom_dashtype_def *this_dashtype = NULL;
1420     struct custom_dashtype_def *new_dashtype = NULL;
1421     struct custom_dashtype_def *prev_dashtype = NULL;
1422     int tag, is_new = FALSE;
1423 
1424     c_token++;
1425 
1426     /* get tag */
1427     if (END_OF_COMMAND || ((tag = int_expression()) <= 0))
1428 	int_error(c_token, "tag must be > zero");
1429 
1430     /* Check if dashtype is already defined */
1431     for (this_dashtype = first_custom_dashtype; this_dashtype != NULL;
1432 	 prev_dashtype = this_dashtype, this_dashtype = this_dashtype->next)
1433 	if (tag <= this_dashtype->tag)
1434 		break;
1435 
1436     if (this_dashtype == NULL || tag != this_dashtype->tag) {
1437 	struct t_dashtype loc_dt = DEFAULT_DASHPATTERN;
1438 	new_dashtype = gp_alloc(sizeof(struct custom_dashtype_def), "dashtype");
1439 	if (prev_dashtype != NULL)
1440 	    prev_dashtype->next = new_dashtype;	/* add it to end of list */
1441 	else
1442 	    first_custom_dashtype = new_dashtype;	/* make it start of list */
1443 	new_dashtype->tag = tag;
1444 	new_dashtype->d_type = DASHTYPE_SOLID;
1445 	new_dashtype->next = this_dashtype;
1446 	new_dashtype->dashtype = loc_dt;
1447 	this_dashtype = new_dashtype;
1448 	is_new = TRUE;
1449     }
1450 
1451     if (almost_equals(c_token, "def$ault")) {
1452 	delete_dashtype(prev_dashtype, this_dashtype);
1453 	is_new = FALSE;
1454 	c_token++;
1455     } else {
1456 	/* FIXME: Maybe this should reject return values > 0 because */
1457 	/* otherwise we have potentially recursive definitions.      */
1458 	this_dashtype->d_type = parse_dashtype(&this_dashtype->dashtype);
1459     }
1460 
1461     if (!END_OF_COMMAND) {
1462 	if (is_new)
1463 	    delete_dashtype(prev_dashtype, this_dashtype);
1464 	int_error(c_token,"Extraneous arguments to set dashtype");
1465     }
1466 }
1467 
1468 /*
1469  * Delete dashtype from linked list.
1470  */
1471 void
delete_dashtype(struct custom_dashtype_def * prev,struct custom_dashtype_def * this)1472 delete_dashtype(struct custom_dashtype_def *prev, struct custom_dashtype_def *this)
1473 {
1474     if (this != NULL) {		/* there really is something to delete */
1475 	if (this == first_custom_dashtype)
1476 	    first_custom_dashtype = this->next;
1477 	else
1478 	    prev->next = this->next;
1479 	free(this);
1480     }
1481 }
1482 
1483 /* process 'set dgrid3d' command */
1484 static void
set_dgrid3d()1485 set_dgrid3d()
1486 {
1487     int token_cnt = 0; /* Number of comma-separated values read in */
1488 
1489     int gridx     = dgrid3d_row_fineness;
1490     int gridy     = dgrid3d_col_fineness;
1491     int normval   = dgrid3d_norm_value;
1492     double scalex = dgrid3d_x_scale;
1493     double scaley = dgrid3d_y_scale;
1494 
1495     /* dgrid3d has two different syntax alternatives: classic and new.
1496        If there is a "mode" keyword, the syntax is new, otherwise it is classic.*/
1497     dgrid3d_mode  = DGRID3D_DEFAULT;
1498 
1499     dgrid3d_kdensity = FALSE;
1500 
1501     c_token++;
1502     while ( !(END_OF_COMMAND) ) {
1503 	int tmp_mode = lookup_table(&dgrid3d_mode_tbl[0],c_token);
1504 	if (tmp_mode != DGRID3D_OTHER) {
1505 	    dgrid3d_mode = tmp_mode;
1506 	    c_token++;
1507 	}
1508 
1509 	switch (tmp_mode) {
1510 	case DGRID3D_QNORM:
1511 				if (!(END_OF_COMMAND)) normval = int_expression();
1512 				break;
1513 	case DGRID3D_SPLINES:
1514 				break;
1515 	case DGRID3D_GAUSS:
1516 	case DGRID3D_CAUCHY:
1517 	case DGRID3D_EXP:
1518 	case DGRID3D_BOX:
1519 	case DGRID3D_HANN:
1520 				if (!(END_OF_COMMAND) && almost_equals( c_token, "kdens$ity2d" )) {
1521 					dgrid3d_kdensity = TRUE;
1522 					c_token++;
1523 				}
1524 				if (!(END_OF_COMMAND)) {
1525 					scalex = real_expression();
1526 					scaley = scalex;
1527 					if (equals(c_token, ",")) {
1528 						c_token++;
1529 						scaley = real_expression();
1530 					}
1531 				}
1532 				break;
1533 
1534 	default:		/* {rows}{,cols{,norm}}} */
1535 
1536 			if  ( equals( c_token, "," )) {
1537 				c_token++;
1538 				token_cnt++;
1539 			} else if (token_cnt == 0) {
1540 				gridx = int_expression();
1541 				gridy = gridx; /* gridy defaults to gridx, unless overridden below */
1542 			} else if (token_cnt == 1) {
1543 				gridy = int_expression();
1544 			} else if (token_cnt == 2) {
1545 				normval = int_expression();
1546 			} else
1547 				int_error(c_token,"Unrecognized keyword or unexpected value");
1548 			break;
1549 	}
1550 
1551     }
1552 
1553     /* we could warn here about floating point values being truncated... */
1554     if (gridx < 2 || gridx > 1000 || gridy < 2 || gridy > 1000)
1555 	int_error( NO_CARET,
1556 		   "Number of grid points must be in [2:1000] - not changed!");
1557 
1558     /* no mode token found: classic format */
1559     if (dgrid3d_mode == DGRID3D_DEFAULT)
1560 	dgrid3d_mode = DGRID3D_QNORM;
1561 
1562     if (scalex < 0.0 || scaley < 0.0)
1563 	int_error( NO_CARET,
1564 		   "Scale factors must be greater than zero - not changed!" );
1565 
1566     dgrid3d_row_fineness = gridx;
1567     dgrid3d_col_fineness = gridy;
1568     dgrid3d_norm_value = normval;
1569     dgrid3d_x_scale = scalex;
1570     dgrid3d_y_scale = scaley;
1571     dgrid3d = TRUE;
1572 }
1573 
1574 
1575 /* process 'set decimalsign' command */
1576 static void
set_decimalsign()1577 set_decimalsign()
1578 {
1579     c_token++;
1580 
1581     /* Clear current setting */
1582 	free(decimalsign);
1583 	decimalsign=NULL;
1584 
1585     if (END_OF_COMMAND) {
1586 	reset_numeric_locale();
1587 	free(numeric_locale);
1588 	numeric_locale = NULL;
1589 #ifdef HAVE_LOCALE_H
1590     } else if (equals(c_token,"locale")) {
1591 	char *newlocale = NULL;
1592 	c_token++;
1593 	newlocale = try_to_get_string();
1594 	if (!newlocale)
1595 	    newlocale = gp_strdup(setlocale(LC_NUMERIC,""));
1596 	if (!newlocale)
1597 	    newlocale = gp_strdup(getenv("LC_ALL"));
1598 	if (!newlocale)
1599 	    newlocale = gp_strdup(getenv("LC_NUMERIC"));
1600 	if (!newlocale)
1601 	    newlocale = gp_strdup(getenv("LANG"));
1602 	if (!setlocale(LC_NUMERIC, newlocale ? newlocale : ""))
1603 	    int_error(c_token-1, "Could not find requested locale");
1604 	decimalsign = gp_strdup(get_decimal_locale());
1605 	fprintf(stderr,"decimal_sign in locale is %s\n", decimalsign);
1606 	/* Save this locale for later use, but return to "C" for now */
1607 	free(numeric_locale);
1608 	numeric_locale = newlocale;
1609 	setlocale(LC_NUMERIC,"C");
1610 #endif
1611     } else if (!(decimalsign = try_to_get_string()))
1612 	int_error(c_token, "expecting string");
1613 }
1614 
1615 /* process 'set dummy' command */
1616 static void
set_dummy()1617 set_dummy()
1618 {
1619     int i;
1620     c_token++;
1621     for (i=0; i<MAX_NUM_VAR; i++) {
1622 	if (END_OF_COMMAND)
1623 	    return;
1624 	if (isalpha((unsigned char)gp_input_line[token[c_token].start_index]))
1625 	    copy_str(set_dummy_var[i],c_token++, MAX_ID_LEN);
1626 	if (equals(c_token,","))
1627 	    c_token++;
1628 	else
1629 	    break;
1630     }
1631     if (!END_OF_COMMAND)
1632 	int_error(c_token,"unrecognized syntax");
1633 }
1634 
1635 
1636 /* process 'set encoding' command */
1637 static void
set_encoding()1638 set_encoding()
1639 {
1640     char *l = NULL;
1641     c_token++;
1642 
1643     if (END_OF_COMMAND) {
1644 	encoding = S_ENC_DEFAULT;
1645 #ifdef HAVE_LOCALE_H
1646     } else if (equals(c_token, "locale")) {
1647 	enum set_encoding_id newenc = encoding_from_locale();
1648 
1649 	l = setlocale(LC_CTYPE, "");
1650 	if (newenc == S_ENC_DEFAULT)
1651 	    int_warn(NO_CARET, "Locale not supported by gnuplot: %s", l);
1652 	if (newenc == S_ENC_INVALID)
1653 	    int_warn(NO_CARET, "Error converting locale \"%s\" to codepage number", l);
1654 	else
1655 	    encoding = newenc;
1656 	c_token++;
1657 #endif
1658     } else {
1659 	int temp = lookup_table(&set_encoding_tbl[0],c_token);
1660 	char *senc;
1661 
1662 	/* allow string variables as parameter */
1663 	if ((temp == S_ENC_INVALID) && isstringvalue(c_token) && (senc = try_to_get_string())) {
1664 	    int i;
1665 	    for (i = 0; encoding_names[i] != NULL; i++)
1666 		if (strcmp(encoding_names[i], senc) == 0)
1667 		    temp = i;
1668 	    free(senc);
1669 	} else {
1670 	    c_token++;
1671 	}
1672 
1673 	if (temp == S_ENC_INVALID)
1674 	    int_error(c_token, "unrecognized encoding specification; see 'help encoding'.");
1675 	encoding = temp;
1676     }
1677 
1678     init_special_chars();
1679 }
1680 
1681 static void
set_degreesign(char * locale)1682 set_degreesign(char *locale)
1683 {
1684 #if defined(HAVE_ICONV) && !(defined _WIN32)
1685     char degree_utf8[3] = {'\302', '\260', '\0'};
1686     size_t lengthin = 3;
1687     size_t lengthout = 8;
1688     char *in = degree_utf8;
1689     char *out = degree_sign;
1690     iconv_t cd;
1691 
1692     if (locale) {
1693 	/* This should work even if gnuplot doesn't understand the encoding */
1694 #ifdef HAVE_LANGINFO_H
1695 	char *cencoding = nl_langinfo(CODESET);
1696 #else
1697 	char *cencoding = strchr(locale, '.');
1698 	if (cencoding) cencoding++; /* Step past the dot in, e.g., ja_JP.EUC-JP */
1699 #endif
1700 	if (cencoding) {
1701 	    if (strcmp(cencoding,"UTF-8") == 0)
1702 		strcpy(degree_sign,degree_utf8);
1703 	    else if ((cd = iconv_open(cencoding, "UTF-8")) == (iconv_t)(-1))
1704 		int_warn(NO_CARET, "iconv_open failed for %s",cencoding);
1705 	    else {
1706 		if (iconv(cd, &in, &lengthin, &out, &lengthout) == (size_t)(-1))
1707 		    int_warn(NO_CARET, "iconv failed to convert degree sign");
1708 		iconv_close(cd);
1709 	    }
1710 	}
1711 	return;
1712     }
1713 #else
1714     (void)locale; /* -Wunused argument */
1715 #endif
1716 
1717     /* These are the internally-known encodings */
1718     memset(degree_sign, 0, sizeof(degree_sign));
1719     switch (encoding) {
1720     case S_ENC_UTF8:	degree_sign[0] = '\302'; degree_sign[1] = '\260'; break;
1721     case S_ENC_KOI8_R:
1722     case S_ENC_KOI8_U:	degree_sign[0] = '\234'; break;
1723     case S_ENC_CP437:
1724     case S_ENC_CP850:
1725     case S_ENC_CP852:	degree_sign[0] = '\370'; break;
1726     case S_ENC_SJIS:	break;  /* should be 0x818B */
1727     case S_ENC_CP950:	break;  /* should be 0xA258 */
1728     /* default applies at least to:
1729        ISO8859-1, -2, -9, -15,
1730        CP1250, CP1251, CP1252, CP1254
1731      */
1732     default:		degree_sign[0] = '\260'; break;
1733     }
1734 }
1735 
1736 /* Encoding-specific character enabled by "set micro" */
1737 static const char *
encoding_micro()1738 encoding_micro()
1739 {
1740     static const char micro_utf8[4] = {0xC2, 0xB5, 0x0, 0x0};
1741     static const char micro_437[2] = {0xE6, 0x0};
1742     static const char micro_latin1[2] = {0xB5, 0x0};
1743     static const char micro_default[2] = {'u', 0x0};
1744     switch (encoding) {
1745 	case S_ENC_UTF8:	return micro_utf8;
1746 	case S_ENC_CP1250:
1747 	case S_ENC_CP1251:
1748 	case S_ENC_CP1252:
1749 	case S_ENC_CP1254:
1750 	case S_ENC_ISO8859_1:
1751 	case S_ENC_ISO8859_9:
1752 	case S_ENC_ISO8859_15:	return micro_latin1;
1753 	case S_ENC_CP437:
1754 	case S_ENC_CP850:	return micro_437;
1755 	default:		return micro_default;
1756     }
1757 }
1758 
1759 /* process 'set fit' command */
1760 /* Encoding-specific character enabled by "set minussign" */
1761 static const char *
encoding_minus()1762 encoding_minus()
1763 {
1764     static const char minus_utf8[4] = {0xE2, 0x88, 0x92, 0x0};
1765     static const char minus_1252[2] = {0x96, 0x0};
1766     /* NB: This SJIS character is correct, but produces bad spacing if used	*/
1767     /*     static const char minus_sjis[4] = {0x81, 0x7c, 0x0, 0x0};		*/
1768     switch (encoding) {
1769 	case S_ENC_UTF8:	return minus_utf8;
1770 	case S_ENC_CP1252:	return minus_1252;
1771 	case S_ENC_SJIS:
1772 	default:		return NULL;
1773     }
1774 }
1775 
1776 
1777 void
init_special_chars(void)1778 init_special_chars(void)
1779 {
1780     /* Set degree sign to match encoding */
1781     char * l = NULL;
1782 #ifdef HAVE_LOCALE_H
1783     l = setlocale(LC_CTYPE, "");
1784 #endif
1785     set_degreesign(l);
1786 
1787     /* Set minus sign to match encoding */
1788     minus_sign = encoding_minus();
1789 
1790     /* Set micro character to match encoding */
1791     micro = encoding_micro();
1792 }
1793 
1794 
1795 /* process 'set fit' command */
1796 static void
set_fit()1797 set_fit()
1798 {
1799     c_token++;
1800 
1801     while (!END_OF_COMMAND) {
1802 	if (almost_equals(c_token, "log$file")) {
1803 	    char *tmp;
1804 
1805 	    c_token++;
1806 	    fit_suppress_log = FALSE;
1807 	    if (END_OF_COMMAND) {
1808 		free(fitlogfile);
1809 		fitlogfile = NULL;
1810 	    } else if (equals(c_token, "default")) {
1811 		c_token++;
1812 		free(fitlogfile);
1813 		fitlogfile = NULL;
1814 	    } else if ((tmp = try_to_get_string()) != NULL) {
1815 		free(fitlogfile);
1816 		fitlogfile = tmp;
1817 	    } else {
1818 		int_error(c_token, "expecting string");
1819 	    }
1820 	} else if (almost_equals(c_token, "nolog$file")) {
1821 	    fit_suppress_log = TRUE;
1822 	    c_token++;
1823 	} else if (almost_equals(c_token, "err$orvariables")) {
1824 	    fit_errorvariables = TRUE;
1825 	    c_token++;
1826 	} else if (almost_equals(c_token, "noerr$orvariables")) {
1827 	    fit_errorvariables = FALSE;
1828 	    c_token++;
1829 	} else if (almost_equals(c_token, "cov$ariancevariables")) {
1830 	    fit_covarvariables = TRUE;
1831 	    c_token++;
1832 	} else if (almost_equals(c_token, "nocov$ariancevariables")) {
1833 	    fit_covarvariables = FALSE;
1834 	    c_token++;
1835 	} else if (almost_equals(c_token, "errors$caling")) {
1836 	    fit_errorscaling = TRUE;
1837 	    c_token++;
1838 	} else if (almost_equals(c_token, "noerrors$caling")) {
1839 	    fit_errorscaling = FALSE;
1840 	    c_token++;
1841 	} else if (equals(c_token, "quiet")) {
1842 	    fit_verbosity = QUIET;
1843 	    c_token++;
1844 	} else if (equals(c_token, "noquiet")) {
1845 	    fit_verbosity = BRIEF;
1846 	    c_token++;
1847 	} else if (equals(c_token, "results")) {
1848 	    fit_verbosity = RESULTS;
1849 	    c_token++;
1850 	} else if (equals(c_token, "brief")) {
1851 	    fit_verbosity = BRIEF;
1852 	    c_token++;
1853 	} else if (equals(c_token, "verbose")) {
1854 	    fit_verbosity = VERBOSE;
1855 	    c_token++;
1856 	} else if (equals(c_token, "prescale")) {
1857 	    fit_prescale = TRUE;
1858 	    c_token++;
1859 	} else if (equals(c_token, "noprescale")) {
1860 	    fit_prescale = FALSE;
1861 	    c_token++;
1862 	} else if (equals(c_token, "limit")) {
1863 	    /* preserve compatibility with FIT_LIMIT user variable */
1864 	    struct udvt_entry *v;
1865 	    double value;
1866 
1867 	    c_token++;
1868 	    if (equals(c_token, "default")) {
1869 		c_token++;
1870 		value = 0.;
1871 	    } else
1872 		value = real_expression();
1873 	    if ((value > 0.) && (value < 1.)) {
1874 		v = add_udv_by_name((char *)FITLIMIT);
1875 		Gcomplex(&v->udv_value, value, 0);
1876 	    } else {
1877 		del_udv_by_name((char *)FITLIMIT, FALSE);
1878 	    }
1879 	} else if (equals(c_token, "limit_abs")) {
1880 	    double value;
1881 	    c_token++;
1882 	    value = real_expression();
1883 	    epsilon_abs = (value > 0.) ? value : 0.;
1884 	} else if (equals(c_token, "maxiter")) {
1885 	    /* preserve compatibility with FIT_MAXITER user variable */
1886 	    struct udvt_entry *v;
1887 	    int maxiter;
1888 
1889 	    c_token++;
1890 	    if (equals(c_token, "default")) {
1891 		c_token++;
1892 		maxiter = 0;
1893 	    } else
1894 		maxiter = int_expression();
1895 	    if (maxiter > 0) {
1896 		v = add_udv_by_name((char *)FITMAXITER);
1897 		Ginteger(&v->udv_value, maxiter);
1898 	    } else {
1899 		del_udv_by_name((char *)FITMAXITER, FALSE);
1900 	    }
1901 	} else if (equals(c_token, "start_lambda")) {
1902 	    /* preserve compatibility with FIT_START_LAMBDA user variable */
1903 	    struct udvt_entry *v;
1904 	    double value;
1905 
1906 	    c_token++;
1907 	    if (equals(c_token, "default")) {
1908 		c_token++;
1909 		value = 0.;
1910 	    } else
1911 		value = real_expression();
1912 	    if (value > 0.) {
1913 		v = add_udv_by_name((char *)FITSTARTLAMBDA);
1914 		Gcomplex(&v->udv_value, value, 0);
1915 	    } else {
1916 		del_udv_by_name((char *)FITSTARTLAMBDA, FALSE);
1917 	    }
1918 	} else if (equals(c_token, "lambda_factor")) {
1919 	    /* preserve compatibility with FIT_LAMBDA_FACTOR user variable */
1920 	    struct udvt_entry *v;
1921 	    double value;
1922 
1923 	    c_token++;
1924 	    if (equals(c_token, "default")) {
1925 		c_token++;
1926 		value = 0.;
1927 	    } else
1928 		value = real_expression();
1929 	    if (value > 0.) {
1930 		v = add_udv_by_name((char *)FITLAMBDAFACTOR);
1931 		Gcomplex(&v->udv_value, value, 0);
1932 	    } else {
1933 		del_udv_by_name((char *)FITLAMBDAFACTOR, FALSE);
1934 	    }
1935 	} else if (equals(c_token, "script")) {
1936 	    char *tmp;
1937 
1938 	    c_token++;
1939 	    if (END_OF_COMMAND) {
1940 		free(fit_script);
1941 		fit_script = NULL;
1942 	    } else if (equals(c_token, "default")) {
1943 		c_token++;
1944 		free(fit_script);
1945 		fit_script = NULL;
1946 	    } else if ((tmp = try_to_get_string())) {
1947 		free(fit_script);
1948 		fit_script = tmp;
1949 	    } else {
1950 		int_error(c_token, "expecting string");
1951 	    }
1952 	} else if (equals(c_token, "wrap")) {
1953 	    c_token++;
1954 	    fit_wrap = int_expression();
1955 	    if (fit_wrap < 0) fit_wrap = 0;
1956 	} else if (equals(c_token, "nowrap")) {
1957 	    c_token++;
1958 	    fit_wrap = 0;
1959 	} else if (equals(c_token, "v4")) {
1960 	    c_token++;
1961 	    fit_v4compatible = TRUE;
1962 	} else if (equals(c_token, "v5")) {
1963 	    c_token++;
1964 	    fit_v4compatible = FALSE;
1965 	} else {
1966 	    int_error(c_token, "unrecognized option --- see `help set fit`");
1967 	}
1968     } /* while (!end) */
1969 }
1970 
1971 
1972 /* process 'set format' command */
1973 void
set_format()1974 set_format()
1975 {
1976     TBOOLEAN set_for_axis[AXIS_ARRAY_SIZE] = AXIS_ARRAY_INITIALIZER(FALSE);
1977     AXIS_INDEX axis;
1978     char *format;
1979     td_type tictype = DT_UNINITIALIZED;
1980 
1981     c_token++;
1982     if ((axis = lookup_table(axisname_tbl, c_token)) >= 0) {
1983 	set_for_axis[axis] = TRUE;
1984 	c_token++;
1985     } else if (equals(c_token,"xy") || equals(c_token,"yx")) {
1986 	set_for_axis[FIRST_X_AXIS] = set_for_axis[FIRST_Y_AXIS] = TRUE;
1987 	c_token++;
1988     } else {
1989 	/* Set all of them */
1990 	for (axis = 0; axis < AXIS_ARRAY_SIZE; axis++)
1991 	    set_for_axis[axis] = TRUE;
1992     }
1993 
1994     if (END_OF_COMMAND) {
1995 	for (axis = FIRST_AXES; axis <= POLAR_AXIS; axis++) {
1996 	    if (set_for_axis[axis]) {
1997 		free(axis_array[axis].formatstring);
1998 		axis_array[axis].formatstring = gp_strdup(DEF_FORMAT);
1999 		axis_array[axis].tictype = DT_NORMAL;
2000 	    }
2001 	}
2002 	return;
2003     }
2004 
2005     if (!(format = try_to_get_string()))
2006 	int_error(c_token, "expecting format string");
2007 
2008     if (almost_equals(c_token,"time$date")) {
2009 	tictype = DT_TIMEDATE;
2010 	c_token++;
2011     } else if (almost_equals(c_token,"geo$graphic")) {
2012 	tictype = DT_DMS;
2013 	c_token++;
2014     } else if (almost_equals(c_token,"num$eric")) {
2015 	tictype = DT_NORMAL;
2016 	c_token++;
2017     }
2018 
2019     for (axis = FIRST_AXES; axis <= POLAR_AXIS; axis++) {
2020 	if (set_for_axis[axis]) {
2021 	    free(axis_array[axis].formatstring);
2022 	    axis_array[axis].formatstring = gp_strdup(format);
2023 	    if (tictype != DT_UNINITIALIZED)
2024 		axis_array[axis].tictype = tictype;
2025 	}
2026     }
2027     free(format);
2028 }
2029 
2030 
2031 /* process 'set grid' command */
2032 
2033 static void
set_grid()2034 set_grid()
2035 {
2036     TBOOLEAN explicit_change = FALSE;
2037     c_token++;
2038 #define	GRID_MATCH(axis, string)				\
2039 	    if (almost_equals(c_token, string+2)) {		\
2040 		if (string[2] == 'm')				\
2041 		    axis_array[axis].gridminor = TRUE;		\
2042 		else						\
2043 		    axis_array[axis].gridmajor = TRUE;		\
2044 		explicit_change = TRUE;				\
2045 		++c_token;					\
2046 	    } else if (almost_equals(c_token, string)) {	\
2047 		if (string[2] == 'm')				\
2048 		    axis_array[axis].gridminor = FALSE;		\
2049 		else						\
2050 		    axis_array[axis].gridmajor = FALSE;		\
2051 		explicit_change = TRUE;				\
2052 		++c_token;					\
2053 	    }
2054     while (!END_OF_COMMAND) {
2055 	GRID_MATCH(FIRST_X_AXIS, "nox$tics")
2056 	else GRID_MATCH(FIRST_Y_AXIS, "noy$tics")
2057 	else GRID_MATCH(FIRST_Z_AXIS, "noz$tics")
2058 	else GRID_MATCH(SECOND_X_AXIS, "nox2$tics")
2059 	else GRID_MATCH(SECOND_Y_AXIS, "noy2$tics")
2060 	else GRID_MATCH(FIRST_X_AXIS, "nomx$tics")
2061 	else GRID_MATCH(FIRST_Y_AXIS, "nomy$tics")
2062 	else GRID_MATCH(FIRST_Z_AXIS, "nomz$tics")
2063 	else GRID_MATCH(SECOND_X_AXIS, "nomx2$tics")
2064 	else GRID_MATCH(SECOND_Y_AXIS, "nomy2$tics")
2065 	else GRID_MATCH(COLOR_AXIS, "nocb$tics")
2066 	else GRID_MATCH(COLOR_AXIS, "nomcb$tics")
2067 	else GRID_MATCH(POLAR_AXIS, "nor$tics")
2068 	else GRID_MATCH(POLAR_AXIS, "nomr$tics")
2069 	else if (almost_equals(c_token,"po$lar")) {
2070 	    /* Dec 2016 - zero or negative disables radial grid lines */
2071 	    axis_array[POLAR_AXIS].gridmajor = TRUE;	/* Enable both circles and radii */
2072 	    polar_grid_angle = 30*DEG2RAD;
2073 	    c_token++;
2074 	    if (might_be_numeric(c_token)) {
2075 		double ang = real_expression();
2076 		polar_grid_angle = (ang > 2.*M_PI) ? DEG2RAD*ang : ang2rad*ang;
2077 	    }
2078 	} else if (almost_equals(c_token,"nopo$lar")) {
2079 	    polar_grid_angle = 0; /* not polar grid */
2080 	    c_token++;
2081 	} else if (equals(c_token,"back")) {
2082 	    grid_layer = LAYER_BACK;
2083 	    c_token++;
2084 	} else if (equals(c_token,"front")) {
2085 	    grid_layer = LAYER_FRONT;
2086 	    c_token++;
2087 	} else if (almost_equals(c_token,"vert$ical")) {
2088 	    grid_vertical_lines = TRUE;
2089 	    c_token++;
2090 	} else if (almost_equals(c_token,"novert$ical")) {
2091 	    grid_vertical_lines = FALSE;
2092 	    c_token++;
2093 	} else if (almost_equals(c_token,"layerd$efault")
2094 		|| equals(c_token, "behind")) {
2095 	    grid_layer = LAYER_BEHIND;
2096 	    c_token++;
2097 	} else { /* only remaining possibility is a line type */
2098 	    int save_token = c_token;
2099 	    lp_parse(&grid_lp, LP_ADHOC, FALSE);
2100 	    if (equals(c_token,",")) {
2101 		c_token++;
2102 		lp_parse(&mgrid_lp, LP_ADHOC, FALSE);
2103 	    } else if (save_token != c_token)
2104 		mgrid_lp = grid_lp;
2105 	    if (save_token == c_token)
2106 		break;
2107 	}
2108     }
2109 
2110     if (!explicit_change && !some_grid_selected()) {
2111 	/* no axis specified, thus select default grid */
2112 	if (polar) {
2113 	    axis_array[POLAR_AXIS].gridmajor = TRUE;
2114 	    polar_grid_angle = 30.*DEG2RAD;
2115 	} else {
2116 	    axis_array[FIRST_X_AXIS].gridmajor = TRUE;
2117 	    axis_array[FIRST_Y_AXIS].gridmajor = TRUE;
2118 	}
2119     }
2120 }
2121 
2122 
2123 /* process 'set hidden3d' command */
2124 static void
set_hidden3d()2125 set_hidden3d()
2126 {
2127     c_token++;
2128     set_hidden3doptions();
2129     hidden3d = TRUE;
2130     SET_REFRESH_OK(E_REFRESH_NOT_OK,0);
2131 }
2132 
2133 
2134 static void
set_history()2135 set_history()
2136 {
2137     c_token++;
2138 
2139     while (!END_OF_COMMAND) {
2140 	if (equals(c_token, "quiet")) {
2141 	    c_token++;
2142 	    history_quiet = TRUE;
2143 	    continue;
2144 	} else if (almost_equals(c_token, "num$bers")) {
2145 	    c_token++;
2146 	    history_quiet = FALSE;
2147 	    continue;
2148 	} else if (equals(c_token, "full")) {
2149 	    c_token++;
2150 	    history_full = TRUE;
2151 	    continue;
2152 	} else if (equals(c_token, "trim")) {
2153 	    c_token++;
2154 	    history_full = FALSE;
2155 	    continue;
2156 	} else if (almost_equals(c_token, "def$ault")) {
2157 	    c_token++;
2158 	    history_quiet = FALSE;
2159 	    history_full = TRUE;
2160 	    gnuplot_history_size = HISTORY_SIZE;
2161 	    continue;
2162 	} else if (equals(c_token, "size")) {
2163 	    c_token++;
2164 	    /* fall through */
2165 	}
2166 	/* Catches both the deprecated "set historysize" and "set history size" */
2167 	gnuplot_history_size = int_expression();
2168 #ifndef GNUPLOT_HISTORY
2169 	int_warn(NO_CARET, "This copy of gnuplot was built without support for command history.");
2170 #endif
2171     }
2172 }
2173 
2174 
2175 /* process 'set isosamples' command */
2176 static void
set_isosamples()2177 set_isosamples()
2178 {
2179     int tsamp1, tsamp2;
2180 
2181     c_token++;
2182     tsamp1 = abs(int_expression());
2183     tsamp2 = tsamp1;
2184     if (!END_OF_COMMAND) {
2185 	if (!equals(c_token,","))
2186 	    int_error(c_token, "',' expected");
2187 	c_token++;
2188 	tsamp2 = abs(int_expression());
2189     }
2190     if (tsamp1 < 2 || tsamp2 < 2)
2191 	int_error(c_token, "sampling rate must be > 1; sampling unchanged");
2192     else {
2193 	struct curve_points *f_p = first_plot;
2194 	struct surface_points *f_3dp = first_3dplot;
2195 
2196 	first_plot = NULL;
2197 	first_3dplot = NULL;
2198 	cp_free(f_p);
2199 	sp_free(f_3dp);
2200 
2201 	iso_samples_1 = tsamp1;
2202 	iso_samples_2 = tsamp2;
2203     }
2204 }
2205 
2206 
2207 /* When plotting an external key, the margin and l/r/t/b/c are
2208    used to determine one of twelve possible positions.  They must
2209    be defined appropriately in the case where stack direction
2210    determines exact position. */
2211 static void
set_key_position_from_stack_direction(legend_key * key)2212 set_key_position_from_stack_direction(legend_key *key)
2213 {
2214     if (key->stack_dir == GPKEY_VERTICAL) {
2215 	switch(key->hpos) {
2216 	case LEFT:
2217 	    key->margin = GPKEY_LMARGIN;
2218 	    break;
2219 	case CENTRE:
2220 	    if (key->vpos == JUST_TOP)
2221 		key->margin = GPKEY_TMARGIN;
2222 	    else
2223 		key->margin = GPKEY_BMARGIN;
2224 	    break;
2225 	case RIGHT:
2226 	    key->margin = GPKEY_RMARGIN;
2227 	    break;
2228 	}
2229     } else {
2230 	switch(key->vpos) {
2231 	case JUST_TOP:
2232 	    key->margin = GPKEY_TMARGIN;
2233 	    break;
2234 	case JUST_CENTRE:
2235 	    if (key->hpos == LEFT)
2236 		key->margin = GPKEY_LMARGIN;
2237 	    else
2238 		key->margin = GPKEY_RMARGIN;
2239 	    break;
2240 	case JUST_BOT:
2241 	    key->margin = GPKEY_BMARGIN;
2242 	    break;
2243 	}
2244     }
2245 }
2246 
2247 
2248 /* process 'set key' command */
2249 static void
set_key()2250 set_key()
2251 {
2252     TBOOLEAN vpos_set = FALSE, hpos_set = FALSE, reg_set = FALSE, sdir_set = FALSE;
2253     char *vpos_warn = "Multiple vertical position settings";
2254     char *hpos_warn = "Multiple horizontal position settings";
2255     char *reg_warn = "Multiple location region settings";
2256     char *sdir_warn = "Multiple stack direction settings";
2257     legend_key *key = &keyT;
2258 
2259     /* Only for backward compatibility with deprecated "set keytitle foo" */
2260     if (almost_equals(c_token,"keyt$itle"))
2261 	goto S_KEYTITLE;
2262 
2263     c_token++;
2264     key->visible = TRUE;
2265 
2266     while (!END_OF_COMMAND) {
2267 	switch(lookup_table(&set_key_tbl[0],c_token)) {
2268 	case S_KEY_ON:
2269 	    key->visible = TRUE;
2270 	    break;
2271 	case S_KEY_OFF:
2272 	    key->visible = FALSE;
2273 	    break;
2274 	case S_KEY_DEFAULT:
2275 	    reset_key();
2276 	    break;
2277 	case S_KEY_TOP:
2278 	    if (vpos_set)
2279 		int_warn(c_token, vpos_warn);
2280 	    key->vpos = JUST_TOP;
2281 	    vpos_set = TRUE;
2282 	    break;
2283 	case S_KEY_BOTTOM:
2284 	    if (vpos_set)
2285 		int_warn(c_token, vpos_warn);
2286 	    key->vpos = JUST_BOT;
2287 	    vpos_set = TRUE;
2288 	    break;
2289 	case S_KEY_LEFT:
2290 	    if (hpos_set)
2291 		int_warn(c_token, hpos_warn);
2292 	    key->hpos = LEFT;
2293 	    hpos_set = TRUE;
2294 	    break;
2295 	case S_KEY_RIGHT:
2296 	    if (hpos_set)
2297 		int_warn(c_token, hpos_warn);
2298 	    key->hpos = RIGHT;
2299 	    hpos_set = TRUE;
2300 	    break;
2301 	case S_KEY_CENTER:
2302 	    if (!vpos_set) key->vpos = JUST_CENTRE;
2303 	    if (!hpos_set) key->hpos = CENTRE;
2304 	    if (vpos_set || hpos_set)
2305 		vpos_set = hpos_set = TRUE;
2306 	    break;
2307 	case S_KEY_VERTICAL:
2308 	    if (sdir_set)
2309 		int_warn(c_token, sdir_warn);
2310 	    key->stack_dir = GPKEY_VERTICAL;
2311 	    sdir_set = TRUE;
2312 	    break;
2313 	case S_KEY_HORIZONTAL:
2314 	    if (sdir_set)
2315 		int_warn(c_token, sdir_warn);
2316 	    key->stack_dir = GPKEY_HORIZONTAL;
2317 	    sdir_set = TRUE;
2318 	    break;
2319 	case S_KEY_OVER:
2320 	    if (reg_set)
2321 		int_warn(c_token, reg_warn);
2322 	    /* Fall through */
2323 	case S_KEY_ABOVE:
2324 	    if (!hpos_set)
2325 		key->hpos = CENTRE;
2326 	    if (!sdir_set)
2327 		key->stack_dir = GPKEY_HORIZONTAL;
2328 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2329 	    key->margin = GPKEY_TMARGIN;
2330 	    reg_set = TRUE;
2331 	    break;
2332 	case S_KEY_UNDER:
2333 	    if (reg_set)
2334 		int_warn(c_token, reg_warn);
2335 	    /* Fall through */
2336 	case S_KEY_BELOW:
2337 	    if (!hpos_set)
2338 		key->hpos = CENTRE;
2339 	    if (!sdir_set)
2340 		key->stack_dir = GPKEY_HORIZONTAL;
2341 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2342 	    key->margin = GPKEY_BMARGIN;
2343 	    reg_set = TRUE;
2344 	    break;
2345 	case S_KEY_INSIDE:
2346 	    if (reg_set)
2347 		int_warn(c_token, reg_warn);
2348 	    key->region = GPKEY_AUTO_INTERIOR_LRTBC;
2349 	    key->fixed = FALSE;
2350 	    reg_set = TRUE;
2351 	    break;
2352 	case S_KEY_OUTSIDE:
2353 	    if (reg_set)
2354 		int_warn(c_token, reg_warn);
2355 	    key->region = GPKEY_AUTO_EXTERIOR_LRTBC;
2356 	    reg_set = TRUE;
2357 	    break;
2358 	case S_KEY_FIXED:
2359 	    if (reg_set)
2360 		int_warn(c_token, reg_warn);
2361 	    key->region = GPKEY_AUTO_INTERIOR_LRTBC;
2362 	    key->fixed = TRUE;
2363 	    reg_set = TRUE;
2364 	    break;
2365 	case S_KEY_TMARGIN:
2366 	    if (reg_set)
2367 		int_warn(c_token, reg_warn);
2368 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2369 	    key->margin = GPKEY_TMARGIN;
2370 	    reg_set = TRUE;
2371 	    break;
2372 	case S_KEY_BMARGIN:
2373 	    if (reg_set)
2374 		int_warn(c_token, reg_warn);
2375 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2376 	    key->margin = GPKEY_BMARGIN;
2377 	    reg_set = TRUE;
2378 	    break;
2379 	case S_KEY_LMARGIN:
2380 	    if (reg_set)
2381 		int_warn(c_token, reg_warn);
2382 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2383 	    key->margin = GPKEY_LMARGIN;
2384 	    reg_set = TRUE;
2385 	    break;
2386 	case S_KEY_RMARGIN:
2387 	    if (reg_set)
2388 		int_warn(c_token, reg_warn);
2389 	    key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
2390 	    key->margin = GPKEY_RMARGIN;
2391 	    reg_set = TRUE;
2392 	    break;
2393 	case S_KEY_LLEFT:
2394 	    key->just = GPKEY_LEFT;
2395 	    break;
2396 	case S_KEY_RRIGHT:
2397 	    key->just = GPKEY_RIGHT;
2398 	    break;
2399 	case S_KEY_REVERSE:
2400 	    key->reverse = TRUE;
2401 	    break;
2402 	case S_KEY_NOREVERSE:
2403 	    key->reverse = FALSE;
2404 	    break;
2405 	case S_KEY_INVERT:
2406 	    key->invert = TRUE;
2407 	    break;
2408 	case S_KEY_NOINVERT:
2409 	    key->invert = FALSE;
2410 	    break;
2411 	case S_KEY_ENHANCED:
2412 	    key->enhanced = TRUE;
2413 	    break;
2414 	case S_KEY_NOENHANCED:
2415 	    key->enhanced = FALSE;
2416 	    break;
2417 	case S_KEY_BOX:
2418 	    c_token++;
2419 	    key->box.l_type = LT_BLACK;
2420 	    if (!END_OF_COMMAND) {
2421 		int old_token = c_token;
2422 		lp_parse(&key->box, LP_ADHOC, FALSE);
2423 		if (old_token == c_token && isanumber(c_token)) {
2424 		    key->box.l_type = int_expression() - 1;
2425 		    c_token++;
2426 		}
2427 	    }
2428 	    c_token--;  /* is incremented after loop */
2429 	    break;
2430 	case S_KEY_NOBOX:
2431 	    key->box.l_type = LT_NODRAW;
2432 	    break;
2433 	case S_KEY_SAMPLEN:
2434 	    c_token++;
2435 	    key->swidth = real_expression();
2436 	    c_token--; /* it is incremented after loop */
2437 	    break;
2438 	case S_KEY_SPACING:
2439 	    c_token++;
2440 	    key->vert_factor = real_expression();
2441 	    if (key->vert_factor < 0.0)
2442 		key->vert_factor = 0.0;
2443 	    c_token--; /* it is incremented after loop */
2444 	    break;
2445 	case S_KEY_WIDTH:
2446 	    c_token++;
2447 	    key->width_fix = real_expression();
2448 	    c_token--; /* it is incremented after loop */
2449 	    break;
2450 	case S_KEY_HEIGHT:
2451 	    c_token++;
2452 	    key->height_fix = real_expression();
2453 	    c_token--; /* it is incremented after loop */
2454 	    break;
2455 	case S_KEY_AUTOTITLES:
2456 	    if (almost_equals(++c_token, "col$umnheader"))
2457 		key->auto_titles = COLUMNHEAD_KEYTITLES;
2458 	    else {
2459 		key->auto_titles = FILENAME_KEYTITLES;
2460 		c_token--;
2461 	    }
2462 	    break;
2463 	case S_KEY_NOAUTOTITLES:
2464 	    key->auto_titles = NOAUTO_KEYTITLES;
2465 	    break;
2466 	case S_KEY_TITLE:
2467 	     S_KEYTITLE:
2468 	    key->title.pos = CENTRE;
2469 	    set_xyzlabel( &key->title );
2470 	    c_token--;
2471 	    break;
2472 	case S_KEY_NOTITLE:
2473 	    free(key->title.text);
2474 	    key->title.text = NULL;
2475 	    break;
2476 	case S_KEY_FONT:
2477 	    c_token++;
2478 	    /* Make sure they've specified a font */
2479 	    if (!isstringvalue(c_token))
2480 		int_error(c_token,"expected font");
2481 	    else {
2482 		char *tmp = try_to_get_string();
2483 		if (tmp) {
2484 		    free(key->font);
2485 		    key->font = tmp;
2486 		}
2487 		c_token--;
2488 	    }
2489 	    break;
2490 	case S_KEY_TEXTCOLOR:
2491 	    {
2492 	    struct t_colorspec lcolor = DEFAULT_COLORSPEC;
2493 	    parse_colorspec(&lcolor, TC_VARIABLE);
2494 	    /* Only for backwards compatibility */
2495 	    if (lcolor.type == TC_RGB && lcolor.value == -1.0)
2496 		lcolor.type = TC_VARIABLE;
2497 	    key->textcolor = lcolor;
2498 	    }
2499 	    c_token--;
2500 	    break;
2501 	case S_KEY_MAXCOLS:
2502 	    c_token++;
2503 	    if (END_OF_COMMAND || almost_equals(c_token, "a$utomatic"))
2504 		key->maxcols = 0;
2505 	    else
2506 		key->maxcols = int_expression();
2507 	    if (key->maxcols < 0)
2508 		key->maxcols = 0;
2509 	    c_token--; /* it is incremented after loop */
2510 	    break;
2511 	case S_KEY_MAXROWS:
2512 	    c_token++;
2513 	    if (END_OF_COMMAND || almost_equals(c_token, "a$utomatic"))
2514 		key->maxrows = 0;
2515 	    else
2516 		key->maxrows = int_expression();
2517 	    if (key->maxrows < 0)
2518 		key->maxrows = 0;
2519 	    c_token--; /* it is incremented after loop */
2520 	    break;
2521 
2522 	case S_KEY_FRONT:
2523 	    key->front = TRUE;
2524 	    break;
2525 	case S_KEY_NOFRONT:
2526 	    key->front = FALSE;
2527 	    break;
2528 
2529 	case S_KEY_MANUAL:
2530 	    c_token++;
2531 	    if (reg_set)
2532 		int_warn(c_token, reg_warn);
2533 	    get_position(&key->user_pos);
2534 	    key->region = GPKEY_USER_PLACEMENT;
2535 	    reg_set = TRUE;
2536 	    c_token--;  /* will be incremented again soon */
2537 	    break;
2538 
2539 	case S_KEY_INVALID:
2540 	default:
2541 	    int_error(c_token, "unknown key option");
2542 	    break;
2543 	}
2544 	c_token++;
2545     }
2546 
2547     if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
2548 	set_key_position_from_stack_direction(key);
2549     else if (key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
2550 	if (vpos_set && (key->margin == GPKEY_TMARGIN || key->margin == GPKEY_BMARGIN))
2551 	    int_warn(NO_CARET,
2552 		     "ignoring top/center/bottom; incompatible with tmargin/bmargin.");
2553 	else if (hpos_set && (key->margin == GPKEY_LMARGIN || key->margin == GPKEY_RMARGIN))
2554 	    int_warn(NO_CARET,
2555 		     "ignoring left/center/right; incompatible with lmargin/tmargin.");
2556     }
2557 }
2558 
2559 
2560 /* process 'set label' command */
2561 /* set label {tag} {"label_text"{,<value>{,...}}} {<label options>} */
2562 /* EAM Mar 2003 - option parsing broken out into separate routine */
2563 static void
set_label()2564 set_label()
2565 {
2566     struct text_label *this_label = NULL;
2567     struct text_label *new_label = NULL;
2568     struct text_label *prev_label = NULL;
2569     struct value a;
2570     int save_token;
2571     int tag = -1;
2572 
2573     c_token++;
2574     if (END_OF_COMMAND)
2575 	return;
2576 
2577     /* The first item must be either a tag or the label text */
2578     save_token = c_token;
2579     if (isletter(c_token) && type_udv(c_token) == 0) {
2580 	tag = assign_label_tag();
2581     } else {
2582 	const_express(&a);
2583 	if (a.type == STRING) {
2584 	    c_token = save_token;
2585 	    tag = assign_label_tag();
2586 	    gpfree_string(&a);
2587 	} else {
2588 	    tag = (int) real(&a);
2589 	}
2590     }
2591 
2592     if (tag <= 0)
2593 	int_error(c_token, "tag must be > zero");
2594 
2595     if (first_label != NULL) {	/* skip to last label */
2596 	for (this_label = first_label; this_label != NULL;
2597 	     prev_label = this_label, this_label = this_label->next)
2598 	    /* is this the label we want? */
2599 	    if (tag <= this_label->tag)
2600 		break;
2601     }
2602     /* Insert this label into the list if it is a new one */
2603     if (this_label == NULL || tag != this_label->tag) {
2604 	new_label = new_text_label(tag);
2605 	new_label->offset = default_offset;
2606 	if (prev_label == NULL)
2607 	    first_label = new_label;
2608 	else
2609 	    prev_label->next = new_label;
2610 	new_label->next = this_label;
2611 	this_label = new_label;
2612     }
2613 
2614     if (!END_OF_COMMAND) {
2615 	char* text;
2616 	parse_label_options(this_label, 0);
2617 	text = try_to_get_string();
2618 	if (text) {
2619 	    free(this_label->text);
2620 	    this_label->text = text;
2621 	}
2622 
2623     }
2624 
2625     /* Now parse the label format and style options */
2626     parse_label_options(this_label, 0);
2627 }
2628 
2629 
2630 /* assign a new label tag
2631  * labels are kept sorted by tag number, so this is easy
2632  * returns the lowest unassigned tag number
2633  */
2634 static int
assign_label_tag()2635 assign_label_tag()
2636 {
2637     struct text_label *this_label;
2638     int last = 0;		/* previous tag value */
2639 
2640     for (this_label = first_label; this_label != NULL;
2641 	 this_label = this_label->next)
2642 	if (this_label->tag == last + 1)
2643 	    last++;
2644 	else
2645 	    break;
2646 
2647     return (last + 1);
2648 }
2649 
2650 
2651 /* process 'set loadpath' command */
2652 static void
set_loadpath()2653 set_loadpath()
2654 {
2655     /* We pick up all loadpath elements here before passing
2656      * them on to set_var_loadpath()
2657      */
2658     char *collect = NULL;
2659 
2660     c_token++;
2661     if (END_OF_COMMAND) {
2662 	clear_loadpath();
2663     } else while (!END_OF_COMMAND) {
2664 	char *ss;
2665 	if ((ss = try_to_get_string())) {
2666 	    int len = (collect? strlen(collect) : 0);
2667 	    gp_expand_tilde(&ss);
2668 	    collect = gp_realloc(collect, len+1+strlen(ss)+1, "tmp loadpath");
2669 	    if (len != 0) {
2670 		strcpy(collect+len+1,ss);
2671 		*(collect+len) = PATHSEP;
2672 	    }
2673 	    else
2674 		strcpy(collect,ss);
2675 	    free(ss);
2676 	} else {
2677 	    int_error(c_token, "expected string");
2678 	}
2679     }
2680     if (collect) {
2681 	set_var_loadpath(collect);
2682 	free(collect);
2683     }
2684 }
2685 
2686 
2687 /* process 'set fontpath' command */
2688 static void
set_fontpath()2689 set_fontpath()
2690 {
2691     /* We pick up all fontpath elements here before passing
2692      * them on to set_var_fontpath()
2693      */
2694     char *collect = NULL;
2695 
2696     c_token++;
2697     if (END_OF_COMMAND) {
2698 	clear_fontpath();
2699     } else while (!END_OF_COMMAND) {
2700 	char *ss;
2701 	if ((ss = try_to_get_string())) {
2702 	    int len = (collect? strlen(collect) : 0);
2703 	    gp_expand_tilde(&ss);
2704 	    collect = gp_realloc(collect, len+1+strlen(ss)+1, "tmp fontpath");
2705 	    if (len != 0) {
2706 		strcpy(collect+len+1,ss);
2707 		*(collect+len) = PATHSEP;
2708 	    }
2709 	    else
2710 		strcpy(collect,ss);
2711 	    free(ss);
2712 	} else {
2713 	    int_error(c_token, "expected string");
2714 	}
2715     }
2716     if (collect) {
2717 	set_var_fontpath(collect);
2718 	free(collect);
2719     }
2720 }
2721 
2722 
2723 /* process 'set locale' command */
2724 static void
set_locale()2725 set_locale()
2726 {
2727     char *s;
2728 
2729     c_token++;
2730     if (END_OF_COMMAND) {
2731 	init_locale();
2732     } else if ((s = try_to_get_string())) {
2733 	set_var_locale(s);
2734 	free(s);
2735     } else
2736 	int_error(c_token, "expected string");
2737 }
2738 
2739 
2740 /* process 'set logscale' command */
2741 static void
set_logscale()2742 set_logscale()
2743 {
2744     TBOOLEAN set_for_axis[AXIS_ARRAY_SIZE] = AXIS_ARRAY_INITIALIZER(FALSE);
2745     int axis;
2746     double newbase = 10;
2747     c_token++;
2748 
2749     if (END_OF_COMMAND) {
2750 	for (axis = 0; axis < POLAR_AXIS; axis++)
2751 	    set_for_axis[axis] = TRUE;
2752     } else {
2753 	/* do reverse search because of "x", "x1", "x2" sequence in axisname_tbl */
2754 	int i = 0;
2755 	while (i < token[c_token].length) {
2756 	    axis = lookup_table_nth_reverse(axisname_tbl, NUMBER_OF_MAIN_VISIBLE_AXES,
2757 		       gp_input_line + token[c_token].start_index + i);
2758 	    if (axis < 0) {
2759 		token[c_token].start_index += i;
2760 		int_error(c_token, "invalid axis");
2761 	    }
2762 	    set_for_axis[axisname_tbl[axis].value] = TRUE;
2763 	    i += strlen(axisname_tbl[axis].key);
2764 	}
2765 	c_token++;
2766 
2767 	if (!END_OF_COMMAND) {
2768 	    newbase = fabs(real_expression());
2769 	    if (newbase <= 1.0)
2770 		int_error(c_token,
2771 			  "log base must be > 1.0; logscale unchanged");
2772 	}
2773     }
2774 
2775 #if defined(NONLINEAR_AXES) && (NONLINEAR_AXES > 0)
2776     for (axis = 0; axis < NUMBER_OF_MAIN_VISIBLE_AXES; axis++) {
2777 	if (set_for_axis[axis]) {
2778 	    static char command[128];
2779 	    char *dummy;
2780 	    if (!isalpha(axis_name(axis)[0]))
2781 		continue;
2782 	    switch (axis) {
2783 	    case FIRST_Y_AXIS:
2784 	    case SECOND_Y_AXIS:
2785 		dummy = "y"; break;
2786 	    case FIRST_Z_AXIS:
2787 	    case COLOR_AXIS:
2788 		dummy = "z"; break;
2789 	    case POLAR_AXIS:
2790 		dummy = "r"; break;
2791 	    default:
2792 		dummy = "x"; break;
2793 	    }
2794 
2795 	    /* Avoid a warning message triggered by default axis range [-10:10] */
2796 	    if (axis_array[axis].set_min <= 0 && axis_array[axis].set_max > 0)
2797 		axis_array[axis].set_min = 0.1;
2798 
2799 	    /* Also forgive negative axis limits if we are currently autoscaling */
2800 	    if ((axis_array[axis].set_autoscale != AUTOSCALE_NONE)
2801 	    &&  (axis_array[axis].set_min <= 0 || axis_array[axis].set_max <= 0)) {
2802 		axis_array[axis].set_min = 0.1;
2803 		axis_array[axis].set_max = 10.;
2804 	    }
2805 
2806 	    if (newbase == 10.) {
2807 		sprintf(command, "set nonlinear %s via log10(%s) inv 10**%s",
2808 			axis_name(axis), dummy, dummy);
2809 	    } else {
2810 		sprintf(command, "set nonlinear %s via log(%s)/log(%g) inv (%g)**%s",
2811 			axis_name(axis), dummy, newbase, newbase, dummy);
2812 	    }
2813 	    do_string(command);
2814 	    axis_array[axis].ticdef.logscaling = TRUE;
2815 	    axis_array[axis].base = newbase;
2816 	    axis_array[axis].log_base = log(newbase);
2817 	    axis_array[axis].linked_to_primary->base = newbase;
2818 	    axis_array[axis].linked_to_primary->log_base = log(newbase);
2819 
2820 	    /* do_string("set nonlinear") cleared the log flags */
2821 	    axis_array[axis].log = TRUE;
2822 	    axis_array[axis].linked_to_primary->log = TRUE;
2823 	}
2824     }
2825 
2826 #else
2827     for (axis = 0; axis < NUMBER_OF_MAIN_VISIBLE_AXES; axis++) {
2828 	if (set_for_axis[axis]) {
2829 	    axis_array[axis].log = TRUE;
2830 	    axis_array[axis].base = newbase;
2831 	    axis_array[axis].log_base = log(newbase);
2832 	    if ((axis == POLAR_AXIS) && polar)
2833 		rrange_to_xy();
2834 	}
2835     }
2836 
2837     /* Because the log scaling is applied during data input, a quick refresh */
2838     /* using existing stored data will not work if the log setting changes.  */
2839     SET_REFRESH_OK(E_REFRESH_NOT_OK, 0);
2840 #endif
2841 
2842 }
2843 
2844 /* process 'set mapping3d' command */
2845 static void
set_mapping()2846 set_mapping()
2847 {
2848     c_token++;
2849     if (END_OF_COMMAND)
2850 	/* assuming same as points */
2851 	mapping3d = MAP3D_CARTESIAN;
2852     else if (almost_equals(c_token, "ca$rtesian"))
2853 	mapping3d = MAP3D_CARTESIAN;
2854     else if (almost_equals(c_token, "s$pherical"))
2855 	mapping3d = MAP3D_SPHERICAL;
2856     else if (almost_equals(c_token, "cy$lindrical"))
2857 	mapping3d = MAP3D_CYLINDRICAL;
2858     else
2859 	int_error(c_token,
2860 		  "expecting 'cartesian', 'spherical', or 'cylindrical'");
2861     c_token++;
2862 }
2863 
2864 
2865 /* process 'set {blrt}margin' command */
2866 static void
set_margin(t_position * margin)2867 set_margin(t_position *margin)
2868 {
2869     margin->scalex = character;
2870     margin->x = -1;
2871     c_token++;
2872 
2873     if (END_OF_COMMAND)
2874 	return;
2875 
2876     if (equals(c_token,"at") && !almost_equals(++c_token,"sc$reen"))
2877 	int_error(c_token,"expecting 'screen <fraction>'");
2878     if (almost_equals(c_token,"sc$reen")) {
2879 	margin->scalex = screen;
2880 	c_token++;
2881     }
2882 
2883     margin->x = real_expression();
2884     if (margin->x < 0)
2885 	margin->x = -1;
2886 
2887     if (margin->scalex == screen) {
2888 	if (margin->x < 0)
2889 	    margin->x = 0;
2890 	if (margin->x > 1)
2891 	    margin->x = 1;
2892     }
2893 
2894 }
2895 
2896 /* process 'set micro' command */
2897 static void
set_micro()2898 set_micro()
2899 {
2900     c_token++;
2901     use_micro = TRUE;
2902 }
2903 
2904 /* process 'set minus_sign' command */
2905 static void
set_minus_sign()2906 set_minus_sign()
2907 {
2908     c_token++;
2909     use_minus_sign = TRUE;
2910 }
2911 
2912 static void
set_separator(char ** xx_separators)2913 set_separator(char **xx_separators)
2914 {
2915     c_token++;
2916     free(*xx_separators);
2917     *xx_separators = NULL;
2918 
2919     if (END_OF_COMMAND)
2920 	return;
2921 
2922     if (almost_equals(c_token, "white$space")) {
2923 	c_token++;
2924     } else if (equals(c_token, "comma")) {
2925 	*xx_separators = gp_strdup(",");
2926 	c_token++;
2927     } else if (equals(c_token, "tab") || equals(c_token, "\'\\t\'")) {
2928 	*xx_separators = gp_strdup("\t");
2929 	c_token++;
2930     } else if (!(*xx_separators = try_to_get_string())) {
2931 	int_error(c_token, "expected \"<separator_char>\"");
2932     }
2933 }
2934 
2935 static void
set_datafile_commentschars()2936 set_datafile_commentschars()
2937 {
2938     char *s;
2939 
2940     c_token++;
2941 
2942     if (END_OF_COMMAND) {
2943 	free(df_commentschars);
2944 	df_commentschars = gp_strdup(DEFAULT_COMMENTS_CHARS);
2945     } else if ((s = try_to_get_string())) {
2946 	free(df_commentschars);
2947 	df_commentschars = s;
2948     } else /* Leave it the way it was */
2949 	int_error(c_token, "expected string with comments chars");
2950 }
2951 
2952 /* process 'set datafile missing' command */
2953 static void
set_missing()2954 set_missing()
2955 {
2956     c_token++;
2957     free(missing_val);
2958     missing_val = NULL;
2959     if (END_OF_COMMAND)
2960 	return;
2961     if (equals(c_token,"NaN") || equals(c_token,"nan")) {
2962 	missing_val = strdup("NaN");
2963 	c_token++;
2964     } else if (!(missing_val = try_to_get_string()))
2965 	int_error(c_token, "expected missing-value string");
2966 }
2967 
2968 /* (version 5) 'set monochrome' command */
2969 static void
set_monochrome()2970 set_monochrome()
2971 {
2972     monochrome = TRUE;
2973     if (!END_OF_COMMAND)
2974 	c_token++;
2975 
2976     if (almost_equals(c_token, "def$ault")) {
2977 	c_token++;
2978 	while (first_mono_linestyle)
2979 	    delete_linestyle(&first_mono_linestyle, first_mono_linestyle, first_mono_linestyle);
2980     }
2981 
2982     init_monochrome();
2983 
2984     if (almost_equals(c_token, "linet$ype") || equals(c_token, "lt")) {
2985 	/* we can pass this off to the generic "set linetype" code */
2986 	if (equals(c_token+1,"cycle")) {
2987 	    c_token += 2;
2988 	    mono_recycle_count = int_expression();
2989 	} else
2990 	    set_linestyle(&first_mono_linestyle, LP_TYPE);
2991     }
2992 
2993     if (!END_OF_COMMAND)
2994 	int_error(c_token, "unrecognized option");
2995 }
2996 
2997 static void
set_mouse()2998 set_mouse()
2999 {
3000 #ifdef USE_MOUSE
3001     char *ctmp;
3002 
3003     c_token++;
3004     mouse_setting.on = 1;
3005 
3006     while (!END_OF_COMMAND) {
3007 	if (almost_equals(c_token, "do$ubleclick")) {
3008 	    ++c_token;
3009 	    mouse_setting.doubleclick = real_expression();
3010 	    if (mouse_setting.doubleclick < 0)
3011 		mouse_setting.doubleclick = 0;
3012 	} else if (almost_equals(c_token, "nodo$ubleclick")) {
3013 	    mouse_setting.doubleclick = 0; /* double click off */
3014 	    ++c_token;
3015 	} else if (almost_equals(c_token, "zoomco$ordinates")) {
3016 	    mouse_setting.annotate_zoom_box = 1;
3017 	    ++c_token;
3018 	} else if (almost_equals(c_token, "nozoomco$ordinates")) {
3019 	    mouse_setting.annotate_zoom_box = 0;
3020 	    ++c_token;
3021 	} else if (almost_equals(c_token, "po$lardistancedeg")) {
3022 	    mouse_setting.polardistance = 1;
3023 	    UpdateStatusline();
3024 	    ++c_token;
3025 	} else if (almost_equals(c_token, "polardistancet$an")) {
3026 	    mouse_setting.polardistance = 2;
3027 	    UpdateStatusline();
3028 	    ++c_token;
3029 	} else if (almost_equals(c_token, "nopo$lardistance")) {
3030 	    mouse_setting.polardistance = 0;
3031 	    UpdateStatusline();
3032 	    ++c_token;
3033 	} else if (almost_equals(c_token, "label$s")) {
3034 	    mouse_setting.label = 1;
3035 	    ++c_token;
3036 	    /* check if the optional argument "<label options>" is present */
3037 	    if (isstringvalue(c_token) && (ctmp = try_to_get_string())) {
3038 		free(mouse_setting.labelopts);
3039 		mouse_setting.labelopts = ctmp;
3040 	    }
3041 	} else if (almost_equals(c_token, "nola$bels")) {
3042 	    mouse_setting.label = 0;
3043 	    ++c_token;
3044 	} else if (almost_equals(c_token, "ve$rbose")) {
3045 	    mouse_setting.verbose = 1;
3046 	    ++c_token;
3047 	} else if (almost_equals(c_token, "nove$rbose")) {
3048 	    mouse_setting.verbose = 0;
3049 	    ++c_token;
3050 	} else if (almost_equals(c_token, "zoomju$mp")) {
3051 	    mouse_setting.warp_pointer = 1;
3052 	    ++c_token;
3053 	} else if (almost_equals(c_token, "nozoomju$mp")) {
3054 	    mouse_setting.warp_pointer = 0;
3055 	    ++c_token;
3056 	} else if (almost_equals(c_token, "fo$rmat")) {
3057 	    ++c_token;
3058 	    if (isstringvalue(c_token) && (ctmp = try_to_get_string())) {
3059 		if (mouse_setting.fmt != mouse_fmt_default)
3060 		    free(mouse_setting.fmt);
3061 		mouse_setting.fmt = ctmp;
3062 	    } else
3063 		mouse_setting.fmt = mouse_fmt_default;
3064 	} else if (almost_equals(c_token, "mo$useformat")) {
3065 	    ++c_token;
3066 	    if (isstringvalue(c_token) && (ctmp = try_to_get_string())) {
3067 		free(mouse_alt_string);
3068 		mouse_alt_string = ctmp;
3069 		if (!strlen(mouse_alt_string)) {
3070 		    free(mouse_alt_string);
3071 		    mouse_alt_string = NULL;
3072 		    if (MOUSE_COORDINATES_ALT == mouse_mode)
3073 			mouse_mode = MOUSE_COORDINATES_REAL;
3074 		} else {
3075 		    mouse_mode = MOUSE_COORDINATES_ALT;
3076 		}
3077 		c_token++;
3078 	    } else {
3079 		int itmp = int_expression();
3080 		if (itmp >= MOUSE_COORDINATES_REAL
3081 		    && itmp <= MOUSE_COORDINATES_ALT) {
3082 		    if (MOUSE_COORDINATES_ALT == itmp && !mouse_alt_string) {
3083 			fprintf(stderr,
3084 			    "please 'set mouse mouseformat <fmt>' first.\n");
3085 		    } else {
3086 			mouse_mode = itmp;
3087 		    }
3088 		} else {
3089 		    fprintf(stderr, "should be: %d <= mouseformat <= %d\n",
3090 			MOUSE_COORDINATES_REAL, MOUSE_COORDINATES_ALT);
3091 		}
3092 	    }
3093 	} else if (almost_equals(c_token, "noru$ler")) {
3094 	    c_token++;
3095 	    set_ruler(FALSE, -1, -1);
3096 	} else if (almost_equals(c_token, "ru$ler")) {
3097 	    c_token++;
3098     	    if (END_OF_COMMAND || !equals(c_token, "at")) {
3099 		set_ruler(TRUE, -1, -1);
3100 	    } else { /* set mouse ruler at ... */
3101 		struct position where;
3102 		int x, y;
3103 		c_token++;
3104 		if (END_OF_COMMAND)
3105 		    int_error(c_token, "expecting ruler coordinates");
3106 		get_position(&where);
3107 		map_position(&where, &x, &y, "ruler at");
3108 		set_ruler(TRUE, (int)x, (int)y);
3109 	    }
3110 	} else if (almost_equals(c_token, "zoomfac$tors")) {
3111 	    double x = 1.0, y = 1.0;
3112 	    c_token++;
3113 	    if (!END_OF_COMMAND) {
3114 		x = real_expression();
3115 		if (equals(c_token,",")) {
3116 		    c_token++;
3117 		    y = real_expression();
3118 		}
3119 	    }
3120 	    mouse_setting.xmzoom_factor = x;
3121 	    mouse_setting.ymzoom_factor = y;
3122 	} else {
3123 	    if (!END_OF_COMMAND)
3124     		int_error(c_token, "wrong option");
3125 	    break;
3126 	}
3127     }
3128 #ifdef OS2
3129     PM_update_menu_items();
3130 #endif
3131 #else /* USE_MOUSE */
3132     c_token++;
3133     int_warn(NO_CARET, "this copy of gnuplot has no mouse support");
3134 #endif /* USE_MOUSE */
3135 }
3136 
3137 /* process 'set offsets' command */
3138 static void
set_offsets()3139 set_offsets()
3140 {
3141     c_token++;
3142     if (END_OF_COMMAND) {
3143 	loff.x = roff.x = toff.y = boff.y = 0.0;
3144 	return;
3145     }
3146 
3147     loff.scalex = first_axes;
3148     if (almost_equals(c_token,"gr$aph")) {
3149 	loff.scalex = graph;
3150 	c_token++;
3151     }
3152     loff.x = real_expression();
3153     if (!equals(c_token, ","))
3154 	return;
3155 
3156     roff.scalex = first_axes;
3157     if (almost_equals(++c_token,"gr$aph")) {
3158 	roff.scalex = graph;
3159 	c_token++;
3160     }
3161     roff.x = real_expression();
3162     if (!equals(c_token, ","))
3163 	return;
3164 
3165     toff.scaley = first_axes;
3166     if (almost_equals(++c_token,"gr$aph")) {
3167 	toff.scaley = graph;
3168 	c_token++;
3169     }
3170     toff.y = real_expression();
3171     if (!equals(c_token, ","))
3172 	return;
3173 
3174     boff.scaley = first_axes;
3175     if (almost_equals(++c_token,"gr$aph")) {
3176 	boff.scaley = graph;
3177 	c_token++;
3178     }
3179     boff.y = real_expression();
3180 }
3181 
3182 
3183 /* process 'set origin' command */
3184 static void
set_origin()3185 set_origin()
3186 {
3187     c_token++;
3188     if (END_OF_COMMAND) {
3189 	xoffset = 0.0;
3190 	yoffset = 0.0;
3191     } else {
3192 	xoffset = real_expression();
3193 	if (!equals(c_token,","))
3194 	    int_error(c_token, "',' expected");
3195 	c_token++;
3196 	yoffset = real_expression();
3197     }
3198 }
3199 
3200 
3201 /* process 'set output' command */
3202 static void
set_output()3203 set_output()
3204 {
3205     char *testfile;
3206 
3207     c_token++;
3208     if (multiplot)
3209 	int_error(c_token, "you can't change the output in multiplot mode");
3210 
3211     if (END_OF_COMMAND) {	/* no file specified */
3212 	term_set_output(NULL);
3213 	if (outstr) {
3214 	    free(outstr);
3215 	    outstr = NULL; /* means STDOUT */
3216 	}
3217     } else if ((testfile = try_to_get_string())) {
3218 	gp_expand_tilde(&testfile);
3219 	term_set_output(testfile);
3220 	if (testfile != outstr) {
3221 	    if (testfile)
3222 		free(testfile);
3223 	    testfile = outstr;
3224 	}
3225 	/* if we get here then it worked, and outstr now = testfile */
3226     } else
3227 	int_error(c_token, "expecting filename");
3228 
3229     /* Invalidate previous palette */
3230     invalidate_palette();
3231 
3232 }
3233 
3234 
3235 /* process 'set print' command */
3236 static void
set_print()3237 set_print()
3238 {
3239     TBOOLEAN append_p = FALSE;
3240     char *testfile = NULL;
3241 
3242     c_token++;
3243     if (END_OF_COMMAND) {	/* no file specified */
3244 	print_set_output(NULL, FALSE, append_p);
3245     } else if (equals(c_token, "$") && isletter(c_token + 1)) { /* datablock */
3246 	/* NB: has to come first because try_to_get_string will choke on the datablock name */
3247 	char * datablock_name = strdup(parse_datablock_name());
3248 	if (!END_OF_COMMAND) {
3249 	    if (equals(c_token, "append")) {
3250 		append_p = TRUE;
3251 		c_token++;
3252 	    } else {
3253 		int_error(c_token, "expecting keyword \'append\'");
3254 	    }
3255 	}
3256 	print_set_output(datablock_name, TRUE, append_p);
3257     } else if ((testfile = try_to_get_string())) {  /* file name */
3258 	gp_expand_tilde(&testfile);
3259 	if (!END_OF_COMMAND) {
3260 	    if (equals(c_token, "append")) {
3261 		append_p = TRUE;
3262 		c_token++;
3263 	    } else {
3264 		int_error(c_token, "expecting keyword \'append\'");
3265 	    }
3266 	}
3267 	print_set_output(testfile, FALSE, append_p);
3268     } else
3269 	int_error(c_token, "expecting filename or datablock");
3270 }
3271 
3272 /* process 'set psdir' command */
3273 static void
set_psdir()3274 set_psdir()
3275 {
3276     c_token++;
3277     if (END_OF_COMMAND) {	/* no file specified */
3278 	free(PS_psdir);
3279 	PS_psdir = NULL;
3280     } else if ((PS_psdir = try_to_get_string())) {
3281 	gp_expand_tilde(&PS_psdir);
3282     } else
3283 	int_error(c_token, "expecting filename");
3284 }
3285 
3286 /* process 'set parametric' command */
3287 static void
set_parametric()3288 set_parametric()
3289 {
3290     c_token++;
3291 
3292     if (!parametric) {
3293 	parametric = TRUE;
3294 	if (!polar) { /* already done for polar */
3295 	    strcpy (set_dummy_var[0], "t");
3296 	    strcpy (set_dummy_var[1], "y");
3297 	    if (interactive)
3298 		(void) fprintf(stderr,"\n\tdummy variable is t for curves, u/v for surfaces\n");
3299 	}
3300     }
3301 }
3302 
3303 
3304 /* is resetting palette enabled?
3305  * note: reset_palette() is disabled within 'test palette'
3306  */
3307 int enable_reset_palette = 1;
3308 
3309 /* default settings for palette */
3310 void
reset_palette()3311 reset_palette()
3312 {
3313     if (!enable_reset_palette) return;
3314     free(sm_palette.gradient);
3315     free(sm_palette.color);
3316     free_at(sm_palette.Afunc.at);
3317     free_at(sm_palette.Bfunc.at);
3318     free_at(sm_palette.Cfunc.at);
3319     init_color();
3320     pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_NONE;
3321 }
3322 
3323 
3324 
3325 /* Process 'set palette defined' gradient specification */
3326 /* Syntax
3327  *   set palette defined   -->  use default palette
3328  *   set palette defined ( <pos1> <colorspec1>, ... , <posN> <colorspecN> )
3329  *     <posX>  gray value, automatically rescaled to [0, 1]
3330  *     <colorspecX>   :=  { "<color_name>" | "<X-style-color>" |  <r> <g> <b> }
3331  *        <color_name>     predefined colors (see below)
3332  *        <X-style-color>  "#rrggbb" with 2char hex values for red, green, blue
3333  *        <r> <g> <b>      three values in [0, 1] for red, green and blue
3334  *   return 1 if named colors where used, 0 otherwise
3335  */
3336 static int
set_palette_defined()3337 set_palette_defined()
3338 {
3339     double p=0, r=0, g=0, b=0;
3340     int num, named_colors=0;
3341     int actual_size=8;
3342 
3343     /* Invalidate previous gradient */
3344     invalidate_palette();
3345 
3346     free( sm_palette.gradient );
3347     sm_palette.gradient = gp_alloc( actual_size*sizeof(gradient_struct), "pm3d gradient" );
3348     sm_palette.smallest_gradient_interval = 1;
3349 
3350     if (END_OF_COMMAND) {
3351 	/* lets use some default gradient */
3352 	double pal[][4] = { {0.0, 0.05, 0.05, 0.2}, {0.1, 0, 0, 1},
3353 			    {0.25, 0.7, 0.85, 0.9}, {0.4, 0, 0.75, 0},
3354 			    {0.5, 1, 1, 0}, {0.7, 1, 0, 0},
3355 			    {0.9, 0.6, 0.6, 0.6}, {1.0, 0.95, 0.95, 0.95} };
3356 	int i;
3357 	for (i=0; i<8; i++) {
3358 	    sm_palette.gradient[i].pos = pal[i][0];
3359 	    sm_palette.gradient[i].col.r = pal[i][1];
3360 	    sm_palette.gradient[i].col.g = pal[i][2];
3361 	    sm_palette.gradient[i].col.b = pal[i][3];
3362 	}
3363 	sm_palette.gradient_num = 8;
3364 	sm_palette.cmodel = C_MODEL_RGB;
3365 	sm_palette.smallest_gradient_interval = 0.1;  /* From pal[][] */
3366 	c_token--; /* Caller will increment! */
3367 	return 0;
3368     }
3369 
3370     if ( !equals(c_token,"(") )
3371 	int_error( c_token, "expected ( to start gradient definition" );
3372 
3373     ++c_token;
3374     num = -1;
3375 
3376     while (!END_OF_COMMAND) {
3377 	char *col_str;
3378 	p = real_expression();
3379 	col_str = try_to_get_string();
3380 	if (col_str) {
3381 	    /* either color name or X-style rgb value "#rrggbb" */
3382 	    if (col_str[0] == '#' || col_str[0] == '0') {
3383 		/* X-style specifier */
3384 		int rr,gg,bb;
3385 		if ((sscanf( col_str, "#%2x%2x%2x", &rr, &gg, &bb ) != 3 )
3386 		&&  (sscanf( col_str, "0x%2x%2x%2x", &rr, &gg, &bb ) != 3 ))
3387 		    int_error( c_token-1,
3388 			       "Unknown color specifier. Use '#RRGGBB' of '0xRRGGBB'." );
3389 		r = (double)(rr)/255.;
3390 		g = (double)(gg)/255.;
3391 		b = (double)(bb)/255.;
3392 	    }
3393 	    else { /* some predefined names */
3394 		/* Maybe we could scan the X11 rgb.txt file to look up color
3395 		 * names?  Or at least move these definitions to some file
3396 		 * which is included somehow during compilation instead
3397 		 * hardcoding them. */
3398 		/* Can't use lookupt_table() as it works for tokens only,
3399 		   so we'll do it manually */
3400 		const struct gen_table *tbl = pm3d_color_names_tbl;
3401 		while (tbl->key) {
3402 		    if (!strcmp(col_str, tbl->key)) {
3403 			r = (double)((tbl->value >> 16 ) & 255) / 255.;
3404 			g = (double)((tbl->value >> 8 ) & 255) / 255.;
3405 			b = (double)(tbl->value & 255) / 255.;
3406 			break;
3407 		    }
3408 		    tbl++;
3409 		}
3410 		if (!tbl->key)
3411 		    int_error( c_token-1, "Unknown color name." );
3412 		named_colors = 1;
3413 	    }
3414 	    free(col_str);
3415 	} else {
3416 	    /* numerical rgb, hsv, xyz, ... values  [0,1] */
3417 	    r = real_expression();
3418 	    if (r<0 || r>1 )  int_error(c_token-1,"Value out of range [0,1].");
3419 	    g = real_expression();
3420 	    if (g<0 || g>1 )  int_error(c_token-1,"Value out of range [0,1].");
3421 	    b = real_expression();
3422 	    if (b<0 || b>1 )  int_error(c_token-1,"Value out of range [0,1].");
3423 	}
3424 	++num;
3425 
3426 	if ( num >= actual_size ) {
3427 	    /* get more space for the gradient */
3428 	    actual_size += 10;
3429 	    sm_palette.gradient = gp_realloc( sm_palette.gradient,
3430 			  actual_size*sizeof(gradient_struct),
3431 			  "pm3d gradient" );
3432 	}
3433 	sm_palette.gradient[num].pos = p;
3434 	sm_palette.gradient[num].col.r = r;
3435 	sm_palette.gradient[num].col.g = g;
3436 	sm_palette.gradient[num].col.b = b;
3437 	if (equals(c_token,")") ) break;
3438 	if ( !equals(c_token,",") )
3439 	    int_error( c_token, "expected comma" );
3440 	++c_token;
3441 
3442     }
3443 
3444     sm_palette.gradient_num = num + 1;
3445     check_palette_grayscale();
3446 
3447     return named_colors;
3448 }
3449 
3450 
3451 /*  process 'set palette file' command
3452  *  load a palette from file, honor datafile modifiers
3453  */
3454 static void
set_palette_file()3455 set_palette_file()
3456 {
3457     int specs;
3458     double v[4];
3459     int i, j, actual_size;
3460     char *file_name;
3461 
3462     ++c_token;
3463 
3464     /* get filename */
3465     if (!(file_name = try_to_get_string()))
3466 	int_error(c_token, "missing filename");
3467 
3468     df_set_plot_mode(MODE_QUERY);	/* Needed only for binary datafiles */
3469     specs = df_open(file_name, 4, NULL);
3470     free(file_name);
3471 
3472     if (specs > 0 && specs < 3)
3473 	int_error( c_token, "Less than 3 using specs for palette");
3474 
3475     if (sm_palette.gradient) {
3476 	free( sm_palette.gradient );
3477 	sm_palette.gradient = 0;
3478     }
3479     actual_size = 10;
3480     sm_palette.gradient =
3481       gp_alloc( actual_size*sizeof(gradient_struct), "gradient" );
3482 
3483     i = 0;
3484 
3485     /* values are simply clipped to [0,1] without notice */
3486     while ((j = df_readline(v, 4)) != DF_EOF) {
3487 	if (i >= actual_size) {
3488 	  actual_size += 10;
3489 	  sm_palette.gradient = (gradient_struct*)
3490 	    gp_realloc( sm_palette.gradient,
3491 			actual_size*sizeof(gradient_struct),
3492 			"pm3d gradient" );
3493 	}
3494 	switch (j) {
3495 	    case 3:
3496 		sm_palette.gradient[i].col.r = clip_to_01(v[0]);
3497 		sm_palette.gradient[i].col.g = clip_to_01(v[1]);
3498 		sm_palette.gradient[i].col.b = clip_to_01(v[2]);
3499 		sm_palette.gradient[i].pos = i ;
3500 		break;
3501 	    case 4:
3502 		sm_palette.gradient[i].col.r = clip_to_01(v[1]);
3503 		sm_palette.gradient[i].col.g = clip_to_01(v[2]);
3504 		sm_palette.gradient[i].col.b = clip_to_01(v[3]);
3505 		sm_palette.gradient[i].pos = v[0];
3506 		break;
3507 	    default:
3508 		df_close();
3509 		int_error(c_token, "Bad data on line %d", df_line_number);
3510 		break;
3511 	}
3512 	++i;
3513     }
3514     df_close();
3515     if (i==0)
3516 	int_error( c_token, "No valid palette found" );
3517 
3518     sm_palette.gradient_num = i;
3519     check_palette_grayscale();
3520 
3521 }
3522 
3523 
3524 /* Process a 'set palette function' command.
3525  *  Three functions with fixed dummy variable gray are registered which
3526  *  map gray to the different color components.
3527  *  If ALLOW_DUMMY_VAR_FOR_GRAY is set:
3528  *    A different dummy variable may proceed the formulae in quotes.
3529  *    This syntax is different from the usual '[u=<start>:<end>]', but
3530  *    as <start> and <end> are fixed to 0 and 1 you would have to type
3531  *    always '[u=]' which looks strange, especially as just '[u]'
3532  *    wouldn't work.
3533  *  If unset:  dummy variable is fixed to 'gray'.
3534  */
3535 static void
set_palette_function()3536 set_palette_function()
3537 {
3538     int start_token;
3539     char saved_dummy_var[MAX_ID_LEN+1];
3540 
3541     ++c_token;
3542     strncpy( saved_dummy_var, c_dummy_var[0], MAX_ID_LEN );
3543 
3544     /* set dummy variable */
3545 #ifdef ALLOW_DUMMY_VAR_FOR_GRAY
3546     if (isstring(c_token)) {
3547 	quote_str( c_dummy_var[0], c_token, MAX_ID_LEN );
3548 	++c_token;
3549     }
3550     else
3551 #endif /* ALLOW_DUMMY_VAR_FOR_GRAY */
3552     strncpy( c_dummy_var[0], "gray", MAX_ID_LEN );
3553 
3554 
3555     /* Afunc */
3556     start_token = c_token;
3557     if (sm_palette.Afunc.at) {
3558 	free_at( sm_palette.Afunc.at );
3559 	sm_palette.Afunc.at = NULL;
3560     }
3561     dummy_func = &sm_palette.Afunc;
3562     sm_palette.Afunc.at = perm_at();
3563     if (! sm_palette.Afunc.at)
3564 	int_error(start_token, "not enough memory for function");
3565     m_capture(&(sm_palette.Afunc.definition), start_token, c_token-1);
3566     dummy_func = NULL;
3567     if (!equals(c_token,","))
3568 	int_error(c_token,"expected comma" );
3569     ++c_token;
3570 
3571     /* Bfunc */
3572     start_token = c_token;
3573     if (sm_palette.Bfunc.at) {
3574 	free_at( sm_palette.Bfunc.at );
3575 	sm_palette.Bfunc.at = NULL;
3576     }
3577     dummy_func = &sm_palette.Bfunc;
3578     sm_palette.Bfunc.at = perm_at();
3579     if (! sm_palette.Bfunc.at)
3580 	int_error(start_token, "not enough memory for function");
3581     m_capture(&(sm_palette.Bfunc.definition), start_token, c_token-1);
3582     dummy_func = NULL;
3583     if (!equals(c_token,","))
3584 	int_error(c_token,"expected comma" );
3585     ++c_token;
3586 
3587     /* Cfunc */
3588     start_token = c_token;
3589     if (sm_palette.Cfunc.at) {
3590 	free_at( sm_palette.Cfunc.at );
3591 	sm_palette.Cfunc.at = NULL;
3592     }
3593     dummy_func = &sm_palette.Cfunc;
3594     sm_palette.Cfunc.at = perm_at();
3595     if (! sm_palette.Cfunc.at)
3596 	int_error(start_token, "not enough memory for function");
3597     m_capture(&(sm_palette.Cfunc.definition), start_token, c_token-1);
3598     dummy_func = NULL;
3599 
3600     strncpy( c_dummy_var[0], saved_dummy_var, MAX_ID_LEN );
3601 }
3602 
3603 
3604 /*
3605  *  Normalize gray scale of gradient to fill [0,1] and
3606  *  complain if gray values are not strictly increasing.
3607  *  Maybe automatic sorting of the gray values could be a
3608  *  feature.
3609  */
3610 static void
check_palette_grayscale()3611 check_palette_grayscale()
3612 {
3613     int i;
3614     double off, f;
3615     gradient_struct *gradient = sm_palette.gradient;
3616 
3617     /* check if gray values are sorted */
3618     for (i=0; i<sm_palette.gradient_num-1; ++i ) {
3619 	if (gradient[i].pos > gradient[i+1].pos) {
3620 	    int_error( c_token, "Gray scale not sorted in gradient." );
3621 	}
3622     }
3623 
3624     /* fit gray axis into [0:1]:  subtract offset and rescale */
3625     off = gradient[0].pos;
3626     f = 1.0 / ( gradient[sm_palette.gradient_num-1].pos-off );
3627     for (i=1; i<sm_palette.gradient_num-1; ++i ) {
3628 	gradient[i].pos = f*(gradient[i].pos-off);
3629     }
3630 
3631     /* paranoia on the first and last entries */
3632     gradient[0].pos = 0.0;
3633     gradient[sm_palette.gradient_num-1].pos = 1.0;
3634 
3635     /* save smallest interval */
3636     sm_palette.smallest_gradient_interval = 1.0;
3637     for (i=1; i<sm_palette.gradient_num-1; ++i ) {
3638 	if (((gradient[i].pos - gradient[i-1].pos) > 0)
3639 	&&  (sm_palette.smallest_gradient_interval > (gradient[i].pos - gradient[i-1].pos)))
3640 	     sm_palette.smallest_gradient_interval = (gradient[i].pos - gradient[i-1].pos);
3641     }
3642 }
3643 
3644 #define CHECK_TRANSFORM  do {				  \
3645     if (transform_defined)				  \
3646 	int_error(c_token, "inconsistent palette options" ); \
3647     transform_defined = 1;				  \
3648 }  while(0)
3649 
3650 /* Process 'set palette' command */
3651 static void
set_palette()3652 set_palette()
3653 {
3654     int transform_defined = 0;
3655     int named_color = 0;
3656 
3657     c_token++;
3658 
3659     if (END_OF_COMMAND) /* reset to default settings */
3660 	reset_palette();
3661     else { /* go through all options of 'set palette' */
3662 	for ( ; !END_OF_COMMAND; c_token++ ) {
3663 	    switch (lookup_table(&set_palette_tbl[0],c_token)) {
3664 	    /* positive and negative picture */
3665 	    case S_PALETTE_POSITIVE: /* "pos$itive" */
3666 		sm_palette.positive = SMPAL_POSITIVE;
3667 		continue;
3668 	    case S_PALETTE_NEGATIVE: /* "neg$ative" */
3669 		sm_palette.positive = SMPAL_NEGATIVE;
3670 		continue;
3671 	    /* Now the options that determine the palette of smooth colours */
3672 	    /* gray or rgb-coloured */
3673 	    case S_PALETTE_GRAY: /* "gray" */
3674 		sm_palette.colorMode = SMPAL_COLOR_MODE_GRAY;
3675 		continue;
3676 	    case S_PALETTE_GAMMA: /* "gamma" */
3677 		++c_token;
3678 		sm_palette.gamma = real_expression();
3679 		--c_token;
3680 		continue;
3681 	    case S_PALETTE_COLOR: /* "col$or" */
3682 		if (pm3d_last_set_palette_mode != SMPAL_COLOR_MODE_NONE) {
3683 		    sm_palette.colorMode = pm3d_last_set_palette_mode;
3684 		} else {
3685 		    sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
3686 		}
3687 		continue;
3688 	    /* rgb color mapping formulae: rgb$formulae r,g,b (3 integers) */
3689 	    case S_PALETTE_RGBFORMULAE: { /* "rgb$formulae" */
3690 		int i;
3691 		char * formerr = "color formula out of range (use `show palette rgbformulae' to display the range)";
3692 
3693 		CHECK_TRANSFORM;
3694 		c_token++;
3695 		i = int_expression();
3696 		if (abs(i) >= sm_palette.colorFormulae)
3697 		    int_error(c_token, formerr);
3698 		sm_palette.formulaR = i;
3699 		if (!equals(c_token--,","))
3700 		    continue;
3701 		c_token += 2;
3702 		i = int_expression();
3703 		if (abs(i) >= sm_palette.colorFormulae)
3704 		    int_error(c_token, formerr);
3705 		sm_palette.formulaG = i;
3706 		if (!equals(c_token--,","))
3707 		    continue;
3708 		c_token += 2;
3709 		i = int_expression();
3710 		if (abs(i) >= sm_palette.colorFormulae)
3711 		    int_error(c_token, formerr);
3712 		sm_palette.formulaB = i;
3713 		c_token--;
3714 		sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
3715 		pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_RGB;
3716 		continue;
3717 	    } /* rgbformulae */
3718 	    /* rgb color mapping based on the "cubehelix" scheme proposed by */
3719 	    /* D A Green (2011)  http://arxiv.org/abs/1108.5083		     */
3720 	    case S_PALETTE_CUBEHELIX: { /* cubehelix */
3721 		TBOOLEAN done = FALSE;
3722 		CHECK_TRANSFORM;
3723 		sm_palette.colorMode = SMPAL_COLOR_MODE_CUBEHELIX;
3724 		sm_palette.cubehelix_start = 0.5;
3725 		sm_palette.cubehelix_cycles = -1.5;
3726 		sm_palette.cubehelix_saturation = 1.0;
3727 		c_token++;
3728 		do {
3729 		    if (equals(c_token,"start")) {
3730 			c_token++;
3731 			sm_palette.cubehelix_start = real_expression();
3732 		    }
3733 		    else if (almost_equals(c_token,"cyc$les")) {
3734 			c_token++;
3735 			sm_palette.cubehelix_cycles = real_expression();
3736 		    }
3737 		    else if (almost_equals(c_token, "sat$uration")) {
3738 			c_token++;
3739 			sm_palette.cubehelix_saturation = real_expression();
3740 		    }
3741 		    else
3742 			done = TRUE;
3743 		} while (!done);
3744 		--c_token;
3745 		continue;
3746 	    } /* cubehelix */
3747 	    case S_PALETTE_DEFINED: { /* "def$ine" */
3748 		CHECK_TRANSFORM;
3749 		++c_token;
3750 		named_color = set_palette_defined();
3751 		sm_palette.colorMode = SMPAL_COLOR_MODE_GRADIENT;
3752 		pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_GRADIENT;
3753 		continue;
3754 	    }
3755 	    case S_PALETTE_FILE: { /* "file" */
3756 		CHECK_TRANSFORM;
3757 		set_palette_file();
3758 		sm_palette.colorMode = SMPAL_COLOR_MODE_GRADIENT;
3759 		pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_GRADIENT;
3760 		--c_token;
3761 		continue;
3762 	    }
3763 	    case S_PALETTE_FUNCTIONS: { /* "func$tions" */
3764 		CHECK_TRANSFORM;
3765 		set_palette_function();
3766 		sm_palette.colorMode = SMPAL_COLOR_MODE_FUNCTIONS;
3767 		pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_FUNCTIONS;
3768 		--c_token;
3769 		continue;
3770 	    }
3771 	    case S_PALETTE_MODEL: { /* "mo$del" */
3772 		int model;
3773 		++c_token;
3774 		if (END_OF_COMMAND)
3775 		    int_error( c_token, "expected color model" );
3776 		model = lookup_table(&color_model_tbl[0],c_token);
3777 		if (model == -1)
3778 		    int_error(c_token,"unknown color model");
3779 		if (model == C_MODEL_XYZ)
3780 		    int_warn(c_token,"CIE/XYZ not supported");
3781 		sm_palette.cmodel = model;
3782 		continue;
3783 	    }
3784 	    /* ps_allcF: write all rgb formulae into PS file? */
3785 	    case S_PALETTE_NOPS_ALLCF: /* "nops_allcF" */
3786 		sm_palette.ps_allcF = FALSE;
3787 		continue;
3788 	    case S_PALETTE_PS_ALLCF: /* "ps_allcF" */
3789 		sm_palette.ps_allcF = TRUE;
3790 		continue;
3791 	    /* max colors used */
3792 	    case S_PALETTE_MAXCOLORS: { /* "maxc$olors" */
3793 		int i;
3794 
3795 		c_token++;
3796 		i = int_expression();
3797 		if (i<0 || i==1)
3798 		    int_warn(c_token,"maxcolors must be > 1");
3799 		else
3800 		    sm_palette.use_maxcolors = i;
3801 		--c_token;
3802 		continue;
3803 	    }
3804 	    } /* switch over palette lookup table */
3805 	    int_error(c_token,"invalid palette option");
3806 	} /* end of while !end of command over palette options */
3807     } /* else(arguments found) */
3808 
3809     if (named_color && sm_palette.cmodel != C_MODEL_RGB && interactive)
3810 	int_warn(NO_CARET,
3811 		 "Named colors will produce strange results if not in color mode RGB." );
3812 
3813     /* Invalidate previous palette */
3814     invalidate_palette();
3815 }
3816 
3817 #undef CHECK_TRANSFORM
3818 
3819 /* process 'set colorbox' command */
3820 static void
set_colorbox()3821 set_colorbox()
3822 {
3823     c_token++;
3824 
3825     if (END_OF_COMMAND) /* reset to default position */
3826 	color_box.where = SMCOLOR_BOX_DEFAULT;
3827     else { /* go through all options of 'set colorbox' */
3828 	for ( ; !END_OF_COMMAND; c_token++ ) {
3829 	    switch (lookup_table(&set_colorbox_tbl[0],c_token)) {
3830 	    /* vertical or horizontal color gradient */
3831 	    case S_COLORBOX_VERTICAL: /* "v$ertical" */
3832 		color_box.rotation = 'v';
3833 		continue;
3834 	    case S_COLORBOX_HORIZONTAL: /* "h$orizontal" */
3835 		color_box.rotation = 'h';
3836 		continue;
3837 	    /* color box where: default position */
3838 	    case S_COLORBOX_DEFAULT: /* "def$ault" */
3839 		color_box.where = SMCOLOR_BOX_DEFAULT;
3840 		continue;
3841 	    /* color box where: position by user */
3842 	    case S_COLORBOX_USER: /* "u$ser" */
3843 		color_box.where = SMCOLOR_BOX_USER;
3844 		continue;
3845 	    /* color box layer: front or back */
3846 	    case S_COLORBOX_FRONT: /* "fr$ont" */
3847 		color_box.layer = LAYER_FRONT;
3848 		continue;
3849 	    case S_COLORBOX_BACK: /* "ba$ck" */
3850 		color_box.layer = LAYER_BACK;
3851 		continue;
3852 	    /* border of the color box */
3853 	    case S_COLORBOX_BORDER: /* "bo$rder" */
3854 
3855 		color_box.border = 1;
3856 		c_token++;
3857 
3858 		if (!END_OF_COMMAND) {
3859 		    /* expecting a border line type */
3860 		    color_box.border_lt_tag = int_expression();
3861 		    if (color_box.border_lt_tag <= 0) {
3862 			color_box.border_lt_tag = 0;
3863 			int_error(c_token, "tag must be strictly positive (see `help set style line')");
3864 		    }
3865 		    --c_token;
3866 		}
3867 		continue;
3868 	    case S_COLORBOX_BDEFAULT: /* "bd$efault" */
3869 		color_box.border_lt_tag = -1; /* use default border */
3870 		continue;
3871 	    case S_COLORBOX_NOBORDER: /* "nobo$rder" */
3872 		color_box.border = 0;
3873 		continue;
3874 	    /* colorbox origin */
3875 	    case S_COLORBOX_ORIGIN: /* "o$rigin" */
3876 		c_token++;
3877 		if (END_OF_COMMAND) {
3878 		    int_error(c_token, "expecting screen value [0 - 1]");
3879 		} else {
3880 		    /* FIXME: should be 2 but old save files may have 3 */
3881 		    get_position_default(&color_box.origin, screen, 3);
3882 		}
3883 		c_token--;
3884 		continue;
3885 	    /* colorbox size */
3886 	    case S_COLORBOX_SIZE: /* "s$ize" */
3887 		c_token++;
3888 		if (END_OF_COMMAND) {
3889 		    int_error(c_token, "expecting screen value [0 - 1]");
3890 		} else {
3891 		    /* FIXME: should be 2 but old save files may have 3 */
3892 		    get_position_default(&color_box.size, screen, 3);
3893 		}
3894 		c_token--;
3895 		continue;
3896 	    case S_COLORBOX_INVERT: /* Flip direction of color gradient + cbaxis */
3897 		c_token++;
3898 		color_box.invert = TRUE;
3899 		continue;
3900 	    case S_COLORBOX_NOINVERT: /* Flip direction of color gradient + cbaxis */
3901 		c_token++;
3902 		color_box.invert = FALSE;
3903 		continue;
3904 	    } /* switch over colorbox lookup table */
3905 	    int_error(c_token,"invalid colorbox option");
3906 	} /* end of while !end of command over colorbox options */
3907     if (color_box.where == SMCOLOR_BOX_NO) /* default: draw at default position */
3908 	color_box.where = SMCOLOR_BOX_DEFAULT;
3909     }
3910 }
3911 
3912 
3913 /* process 'set pm3d' command */
3914 static void
set_pm3d()3915 set_pm3d()
3916 {
3917     int c_token0 = ++c_token;
3918 
3919     if (END_OF_COMMAND) { /* assume default settings */
3920 	pm3d_reset(); /* sets pm3d.implicit to PM3D_EXPLICIT and pm3d.where to "s" */
3921 	pm3d.implicit = PM3D_IMPLICIT; /* for historical reasons */
3922     }
3923     else { /* go through all options of 'set pm3d' */
3924 	for ( ; !END_OF_COMMAND; c_token++ ) {
3925 	    switch (lookup_table(&set_pm3d_tbl[0],c_token)) {
3926 	    /* where to plot */
3927 	    case S_PM3D_AT: /* "at" */
3928 		c_token++;
3929 		if (get_pm3d_at_option(&pm3d.where[0]))
3930 		    return; /* error */
3931 		c_token--;
3932 #if 1
3933 		if (c_token == c_token0+1)
3934 		    /* for historical reasons: if "at" is the first option of pm3d,
3935 		     * like "set pm3d at X other_opts;", then implicit is switched on */
3936 		    pm3d.implicit = PM3D_IMPLICIT;
3937 #endif
3938 		continue;
3939 	    case S_PM3D_INTERPOLATE: /* "interpolate" */
3940 		c_token++;
3941 		if (END_OF_COMMAND) {
3942 		    int_error(c_token, "expecting step values i,j");
3943 		} else {
3944 		    pm3d.interp_i = int_expression();
3945 		    if (!equals(c_token,","))
3946 			int_error(c_token, "',' expected");
3947 		    c_token++;
3948 		    pm3d.interp_j = int_expression();
3949 		    c_token--;
3950 		}
3951 		continue;
3952 	    /* forward and backward drawing direction */
3953 	    case S_PM3D_SCANSFORWARD: /* "scansfor$ward" */
3954 		pm3d.direction = PM3D_SCANS_FORWARD;
3955 		continue;
3956 	    case S_PM3D_SCANSBACKWARD: /* "scansback$ward" */
3957 		pm3d.direction = PM3D_SCANS_BACKWARD;
3958 		continue;
3959 	    case S_PM3D_SCANS_AUTOMATIC: /* "scansauto$matic" */
3960 		pm3d.direction = PM3D_SCANS_AUTOMATIC;
3961 		continue;
3962 	    case S_PM3D_DEPTH: /* "dep$thorder" */
3963 		pm3d.direction = PM3D_DEPTH;
3964 		if (equals(c_token+1, "base")) {
3965 		    pm3d.base_sort = TRUE;
3966 		    c_token++;
3967 		} else {
3968 		    pm3d.base_sort = FALSE;
3969 		}
3970 		continue;
3971 	    /* flush scans: left, right or center */
3972 	    case S_PM3D_FLUSH:  /* "fl$ush" */
3973 		c_token++;
3974 		if (almost_equals(c_token, "b$egin"))
3975 		    pm3d.flush = PM3D_FLUSH_BEGIN;
3976 		else if (almost_equals(c_token, "c$enter"))
3977 		    pm3d.flush = PM3D_FLUSH_CENTER;
3978 		else if (almost_equals(c_token, "e$nd"))
3979 		    pm3d.flush = PM3D_FLUSH_END;
3980 		else
3981 		    int_error(c_token,"expecting flush 'begin', 'center' or 'end'");
3982 		continue;
3983 	    /* clipping method */
3984 	    case S_PM3D_CLIP_1IN: /* "clip1$in" */
3985 		pm3d.clip = PM3D_CLIP_1IN;
3986 		continue;
3987 	    case S_PM3D_CLIP_4IN: /* "clip4$in" */
3988 		pm3d.clip = PM3D_CLIP_4IN;
3989 		continue;
3990 	    case S_PM3D_CLIPCB:
3991 		pm3d.no_clipcb = FALSE;
3992 		continue;
3993 	    case S_PM3D_NOCLIPCB:
3994 		pm3d.no_clipcb = TRUE;
3995 		continue;
3996 	    /* setup everything for plotting a map */
3997 	    case S_PM3D_MAP: /* "map" */
3998 		pm3d.where[0] = 'b'; pm3d.where[1] = 0; /* set pm3d at b */
3999 		data_style = PM3DSURFACE;
4000 		func_style = PM3DSURFACE;
4001 		splot_map = TRUE;
4002 		continue;
4003 	    /* flushing triangles */
4004 	    case S_PM3D_FTRIANGLES: /* "ftr$iangles" */
4005 		pm3d.ftriangles = 1;
4006 		continue;
4007 	    case S_PM3D_NOFTRIANGLES: /* "noftr$iangles" */
4008 		pm3d.ftriangles = 0;
4009 		continue;
4010 	    /* deprecated pm3d "hidden3d" option, now used for borders */
4011 	    case S_PM3D_HIDDEN:
4012 		if (isanumber(c_token+1)) {
4013 		    c_token++;
4014 		    load_linetype(&pm3d.border, int_expression());
4015 		    c_token--;
4016 		    continue;
4017 		}
4018 		/* fall through */
4019 	    case S_PM3D_BORDER: /* border {linespec} */
4020 		c_token++;
4021 		pm3d.border = default_pm3d_border;
4022 		lp_parse(&pm3d.border, LP_ADHOC, FALSE);
4023 		c_token--;
4024 		continue;
4025 	    case S_PM3D_NOHIDDEN:
4026 	    case S_PM3D_NOBORDER:
4027 		pm3d.border.l_type = LT_NODRAW;
4028 		continue;
4029 	    case S_PM3D_SOLID: /* "so$lid" */
4030 	    case S_PM3D_NOTRANSPARENT: /* "notr$ansparent" */
4031 	    case S_PM3D_NOSOLID: /* "noso$lid" */
4032 	    case S_PM3D_TRANSPARENT: /* "tr$ansparent" */
4033 		if (interactive)
4034 		    int_warn(c_token, "Deprecated syntax --- ignored");
4035 	    case S_PM3D_IMPLICIT: /* "i$mplicit" */
4036 	    case S_PM3D_NOEXPLICIT: /* "noe$xplicit" */
4037 		pm3d.implicit = PM3D_IMPLICIT;
4038 		continue;
4039 	    case S_PM3D_NOIMPLICIT: /* "noi$mplicit" */
4040 	    case S_PM3D_EXPLICIT: /* "e$xplicit" */
4041 		pm3d.implicit = PM3D_EXPLICIT;
4042 		continue;
4043 
4044 	    case S_PM3D_WHICH_CORNER: /* "corners2color" */
4045 		c_token++;
4046 		if (equals(c_token, "mean"))
4047 		    pm3d.which_corner_color = PM3D_WHICHCORNER_MEAN;
4048 		else if (equals(c_token, "geomean"))
4049 		    pm3d.which_corner_color = PM3D_WHICHCORNER_GEOMEAN;
4050 		else if (equals(c_token, "harmean"))
4051 			pm3d.which_corner_color = PM3D_WHICHCORNER_HARMEAN;
4052 		else if (equals(c_token, "median"))
4053 		    pm3d.which_corner_color = PM3D_WHICHCORNER_MEDIAN;
4054 		else if (equals(c_token, "min"))
4055 		    pm3d.which_corner_color = PM3D_WHICHCORNER_MIN;
4056 		else if (equals(c_token, "max"))
4057 		    pm3d.which_corner_color = PM3D_WHICHCORNER_MAX;
4058 		else if (equals(c_token, "rms"))
4059 			pm3d.which_corner_color = PM3D_WHICHCORNER_RMS;
4060 		else if (equals(c_token, "c1"))
4061 		    pm3d.which_corner_color = PM3D_WHICHCORNER_C1;
4062 		else if (equals(c_token, "c2"))
4063 		    pm3d.which_corner_color = PM3D_WHICHCORNER_C2;
4064 		else if (equals(c_token, "c3"))
4065 		    pm3d.which_corner_color = PM3D_WHICHCORNER_C3;
4066 		else if (equals(c_token, "c4"))
4067 		    pm3d.which_corner_color = PM3D_WHICHCORNER_C4;
4068 		else
4069 		    int_error(c_token,"expecting 'mean', 'geomean', 'harmean', 'median', 'min', 'max', 'c1', 'c2', 'c3' or 'c4'");
4070 		continue;
4071 
4072 	    case S_PM3D_NOLIGHTING_MODEL:
4073 		pm3d_shade.strength = 0.0;
4074 		continue;
4075 
4076 	    case S_PM3D_LIGHTING_MODEL:
4077 		parse_lighting_options();
4078 		continue;
4079 
4080 	    } /* switch over pm3d lookup table */
4081 	    int_error(c_token,"invalid pm3d option");
4082 	} /* end of while !end of command over pm3d options */
4083 	if (PM3D_SCANS_AUTOMATIC == pm3d.direction
4084 	    && PM3D_FLUSH_BEGIN != pm3d.flush) {
4085 	    pm3d.direction = PM3D_SCANS_FORWARD;
4086 	    /* FIXME: Why isn't this combination supported? */
4087 	    FPRINTF((stderr, "pm3d: `scansautomatic' and `flush %s' are incompatible\n",
4088 		PM3D_FLUSH_END == pm3d.flush ? "end": "center"));
4089 	}
4090     }
4091 }
4092 
4093 
4094 /* process 'set pointintervalbox' command */
4095 static void
set_pointintervalbox()4096 set_pointintervalbox()
4097 {
4098     c_token++;
4099     if (END_OF_COMMAND)
4100 	pointintervalbox = 1.0;
4101     else
4102 	pointintervalbox = real_expression();
4103     if (pointintervalbox <= 0)
4104 	pointintervalbox = 1.0;
4105 }
4106 
4107 /* process 'set pointsize' command */
4108 static void
set_pointsize()4109 set_pointsize()
4110 {
4111     c_token++;
4112     if (END_OF_COMMAND)
4113 	pointsize = 1.0;
4114     else
4115 	pointsize = real_expression();
4116     if (pointsize <= 0)
4117 	pointsize = 1.0;
4118 }
4119 
4120 
4121 /* process 'set polar' command */
4122 static void
set_polar()4123 set_polar()
4124 {
4125     c_token++;
4126 
4127     if (polar)
4128 	return;
4129 
4130     polar = TRUE;
4131     raxis = TRUE;
4132 
4133     if (!parametric) {
4134 	if (interactive)
4135 	    (void) fprintf(stderr,"\n\tdummy variable is t for curves\n");
4136 	strcpy (set_dummy_var[0], "t");
4137     }
4138     if (axis_array[T_AXIS].set_autoscale) {
4139 	/* only if user has not set a range manually */
4140 	axis_array[T_AXIS].set_min = 0.0;
4141 	/* 360 if degrees, 2pi if radians */
4142 	axis_array[T_AXIS].set_max = 2 * M_PI / ang2rad;
4143     }
4144     if (axis_array[POLAR_AXIS].set_autoscale != AUTOSCALE_BOTH)
4145 	rrange_to_xy();
4146 }
4147 
4148 #ifdef EAM_OBJECTS
4149 /*
4150  * Process command     'set object <tag> {rectangle|ellipse|circle|polygon}'
4151  * set object {tag} rectangle {from <bottom_left> {to|rto} <top_right>}
4152  *                     {{at|center} <xcen>,<ycen> size <w>,<h>}
4153  *                     {fc|fillcolor <colorspec>} {lw|linewidth <lw>}
4154  *                     {fs <fillstyle>} {front|back|behind}
4155  *                     {default}
4156  * EAM Jan 2005
4157  */
4158 
4159 static void
set_object()4160 set_object()
4161 {
4162     int tag;
4163 
4164     /* The next token must either be a tag or the object type */
4165     c_token++;
4166     if (almost_equals(c_token, "rect$angle") || almost_equals(c_token, "ell$ipse")
4167     ||  almost_equals(c_token, "circ$le") || almost_equals(c_token, "poly$gon"))
4168 	tag = -1; /* We'll figure out what it really is later */
4169     else {
4170 	tag = int_expression();
4171 	if (tag <= 0)
4172 	    int_error(c_token, "tag must be > zero");
4173     }
4174 
4175     if (almost_equals(c_token, "rect$angle")) {
4176 	set_obj(tag, OBJ_RECTANGLE);
4177 
4178     } else if (almost_equals(c_token, "ell$ipse")) {
4179 	set_obj(tag, OBJ_ELLIPSE);
4180 
4181     } else if (almost_equals(c_token, "circ$le")) {
4182 	set_obj(tag, OBJ_CIRCLE);
4183 
4184     } else if (almost_equals(c_token, "poly$gon")) {
4185 	set_obj(tag, OBJ_POLYGON);
4186 
4187     } else if (tag > 0) {
4188 	/* Look for existing object with this tag */
4189 	t_object *this_object = first_object;
4190 	for (; this_object != NULL; this_object = this_object->next)
4191 	     if (tag == this_object->tag)
4192 		break;
4193 	if (this_object && tag == this_object->tag) {
4194 	    c_token--;
4195 	    set_obj(tag, this_object->object_type);
4196 	} else
4197 	    int_error(c_token, "unknown object");
4198 
4199     } else
4200 	int_error(c_token, "unrecognized object type");
4201 
4202 }
4203 
4204 static t_object *
new_object(int tag,int object_type,t_object * new)4205 new_object(int tag, int object_type, t_object *new)
4206 {
4207     t_object def_rect = DEFAULT_RECTANGLE_STYLE;
4208     t_object def_ellipse = DEFAULT_ELLIPSE_STYLE;
4209     t_object def_circle = DEFAULT_CIRCLE_STYLE;
4210     t_object def_polygon = DEFAULT_POLYGON_STYLE;
4211 
4212     if (!new)
4213 	new = gp_alloc(sizeof(struct object), "object");
4214     else if (new->object_type == OBJ_POLYGON)
4215 	free(new->o.polygon.vertex);
4216 
4217     if (object_type == OBJ_RECTANGLE) {
4218 	*new = def_rect;
4219 	new->lp_properties.l_type = LT_DEFAULT; /* Use default rectangle color */
4220 	new->fillstyle.fillstyle = FS_DEFAULT;  /* and default fill style */
4221     } else if (object_type == OBJ_ELLIPSE)
4222 	*new = def_ellipse;
4223     else if (object_type == OBJ_CIRCLE)
4224 	*new = def_circle;
4225     else if (object_type == OBJ_POLYGON)
4226 	*new = def_polygon;
4227     else
4228 	int_error(NO_CARET,"object initialization failure");
4229 
4230     new->tag = tag;
4231     new->object_type = object_type;
4232 
4233     return new;
4234 }
4235 
4236 static void
set_obj(int tag,int obj_type)4237 set_obj(int tag, int obj_type)
4238 {
4239     t_rectangle *this_rect = NULL;
4240     t_ellipse *this_ellipse = NULL;
4241     t_circle *this_circle = NULL;
4242     t_polygon *this_polygon = NULL;
4243     t_object *this_object = NULL;
4244     t_object *new_obj = NULL;
4245     t_object *prev_object = NULL;
4246     TBOOLEAN got_fill = FALSE;
4247     TBOOLEAN got_lt = FALSE;
4248     TBOOLEAN got_fc = FALSE;
4249     TBOOLEAN got_corners = FALSE;
4250     TBOOLEAN got_center = FALSE;
4251     TBOOLEAN got_origin = FALSE;
4252 
4253     c_token++;
4254 
4255     /* We are setting the default, not any particular rectangle */
4256     if (tag < -1) {
4257 	c_token--;
4258 	if (obj_type == OBJ_RECTANGLE) {
4259 	    this_object = &default_rectangle;
4260 	    this_rect = &this_object->o.rectangle;
4261 	} else
4262 	    int_error(c_token, "Unknown object type");
4263 
4264     } else {
4265 	/* Look for existing object with this tag */
4266 	for (this_object = first_object; this_object != NULL;
4267 	     prev_object = this_object, this_object = this_object->next)
4268 	     /* is this the one we want? */
4269 	     if (0 < tag  &&  tag <= this_object->tag)
4270 		break;
4271 
4272 	/* Insert this rect into the list if it is a new one */
4273 	if (this_object == NULL || tag != this_object->tag) {
4274 	    if (tag == -1)
4275 		tag = (prev_object) ? prev_object->tag+1 : 1;
4276 	    new_obj = new_object(tag, obj_type, NULL);
4277 	    if (prev_object == NULL)
4278 		first_object = new_obj;
4279 	    else
4280 		prev_object->next = new_obj;
4281 	    new_obj->next = this_object;
4282 	    this_object = new_obj;
4283 	    /* V5 CHANGE: Apply default rectangle style now rather than later */
4284 	    if (obj_type == OBJ_RECTANGLE) {
4285 		this_object->fillstyle = default_rectangle.fillstyle;
4286 		this_object->lp_properties = default_rectangle.lp_properties;
4287 	    }
4288 	}
4289 
4290 	/* Over-write old object if the type has changed */
4291 	else if (this_object->object_type != obj_type) {
4292 	    t_object *save_link = this_object->next;
4293 	    new_obj = new_object(tag, obj_type, this_object);
4294 	    this_object->next = save_link;
4295 	}
4296 
4297 	this_rect = &this_object->o.rectangle;
4298 	this_ellipse = &this_object->o.ellipse;
4299 	this_circle = &this_object->o.circle;
4300 	this_polygon = &this_object->o.polygon;
4301 
4302     }
4303 
4304     while (!END_OF_COMMAND) {
4305 	int save_token = c_token;
4306 
4307 	switch (obj_type) {
4308 	case OBJ_RECTANGLE:
4309 		if (equals(c_token,"from")) {
4310 		    /* Read in the bottom left and upper right corners */
4311 		    c_token++;
4312 		    get_position(&this_rect->bl);
4313 		    if (equals(c_token,"to")) {
4314 			c_token++;
4315 			get_position(&this_rect->tr);
4316 		    } else if (equals(c_token,"rto")) {
4317 			c_token++;
4318 			get_position_default(&this_rect->tr, this_rect->bl.scalex, 2);
4319 			if (this_rect->bl.scalex != this_rect->tr.scalex
4320 			||  this_rect->bl.scaley != this_rect->tr.scaley)
4321 			    int_error(c_token,"relative coordinates must match in type");
4322 			this_rect->tr.x += this_rect->bl.x;
4323 			this_rect->tr.y += this_rect->bl.y;
4324 		    } else
4325 			int_error(c_token,"Expecting to or rto");
4326 		    got_corners = TRUE;
4327 		    this_rect->type = 0;
4328 		    continue;
4329 
4330 		} else if (equals(c_token,"at") || almost_equals(c_token,"cen$ter")) {
4331 		    /* Read in the center position */
4332 		    c_token++;
4333 		    get_position(&this_rect->center);
4334 		    got_center = TRUE;
4335 		    this_rect->type = 1;
4336 		    continue;
4337 
4338 		} else if (equals(c_token,"size")) {
4339 		    /* Read in the width and height */
4340 		    c_token++;
4341 		    get_position(&this_rect->extent);
4342 		    got_center = TRUE;
4343 		    this_rect->type = 1;
4344 		    continue;
4345 		}
4346 		break;
4347 
4348 	case OBJ_CIRCLE:
4349 		if (equals(c_token,"at") || almost_equals(c_token,"cen$ter")) {
4350 		    /* Read in the center position */
4351 		    c_token++;
4352 		    get_position(&this_circle->center);
4353 		    continue;
4354 
4355 		} else if (equals(c_token,"size") || equals(c_token,"radius")) {
4356 		    /* Read in the radius */
4357 		    c_token++;
4358 		    get_position(&this_circle->extent);
4359 		    continue;
4360 
4361 		} else if (equals(c_token, "arc")) {
4362 		    /* Start and end angle for arc */
4363 		    if (equals(++c_token,"[")) {
4364 			double arc;
4365 			c_token++;
4366 			arc = real_expression();
4367 			if (fabs(arc) > 1000.)
4368 			    int_error(c_token-1,"Angle out of range");
4369 			else
4370 			    this_circle->arc_begin = arc;
4371 			if (equals(c_token++, ":")) {
4372 			    arc = real_expression();
4373 			    if (fabs(arc) > 1000.)
4374 				int_error(c_token-1,"Angle out of range");
4375 			    else
4376 				this_circle->arc_end = arc;
4377 			    if (equals(c_token++,"]"))
4378 				continue;
4379 			}
4380 		    }
4381 		    int_error(--c_token, "Expecting arc [<begin>:<end>]");
4382 		} else if (equals(c_token, "wedge")) {
4383 		    c_token++;
4384 		    this_circle->wedge = TRUE;
4385 		    continue;
4386 		} else if (equals(c_token, "nowedge")) {
4387 		    c_token++;
4388 		    this_circle->wedge = FALSE;
4389 		    continue;
4390 		}
4391 		break;
4392 
4393 	case OBJ_ELLIPSE:
4394 		if (equals(c_token,"at") || almost_equals(c_token,"cen$ter")) {
4395 		    /* Read in the center position */
4396 		    c_token++;
4397 		    get_position(&this_ellipse->center);
4398 		    continue;
4399 
4400 		} else if (equals(c_token,"size")) {
4401 		    /* Read in the width and height */
4402 		    c_token++;
4403 		    get_position(&this_ellipse->extent);
4404 		    continue;
4405 
4406 		} else if (almost_equals(c_token,"ang$le")) {
4407 		    c_token++;
4408 		    this_ellipse->orientation = real_expression();
4409 		    continue;
4410 
4411 		} else if (almost_equals(c_token,"unit$s")) {
4412 		    c_token++;
4413 		    if (equals(c_token,"xy") || END_OF_COMMAND) {
4414 			this_ellipse->type = ELLIPSEAXES_XY;
4415 		    } else if (equals(c_token,"xx")) {
4416 			this_ellipse->type = ELLIPSEAXES_XX;
4417 		    } else if (equals(c_token,"yy")) {
4418 			this_ellipse->type = ELLIPSEAXES_YY;
4419 		    } else {
4420 			int_error(c_token, "expecting 'xy', 'xx' or 'yy'" );
4421 		    }
4422 		    c_token++;
4423 		    continue;
4424 
4425 		}
4426 		break;
4427 
4428 	case OBJ_POLYGON:
4429 		if (equals(c_token,"from")) {
4430 		    c_token++;
4431 		    this_polygon->vertex = gp_realloc(this_polygon->vertex,
4432 					sizeof(struct position),
4433 					"polygon vertex");
4434 		    get_position(&this_polygon->vertex[0]);
4435 		    this_polygon->type = 1;
4436 		    got_origin = TRUE;
4437 		    continue;
4438 		}
4439 		if (!got_corners && (equals(c_token,"to") || equals(c_token,"rto"))) {
4440 		    while (equals(c_token,"to") || equals(c_token,"rto")) {
4441 			if (!got_origin)
4442 			    goto polygon_error;
4443 			this_polygon->vertex = gp_realloc(this_polygon->vertex,
4444 					    (this_polygon->type+1) * sizeof(struct position),
4445 					    "polygon vertex");
4446 			if (equals(c_token++,"to")) {
4447 			    get_position(&this_polygon->vertex[this_polygon->type]);
4448 			} else /* "rto" */ {
4449 			    int v = this_polygon->type;
4450 			    get_position_default(&this_polygon->vertex[v],
4451 						  this_polygon->vertex->scalex, 2);
4452 			    if (this_polygon->vertex[v].scalex != this_polygon->vertex[v-1].scalex
4453 			    ||  this_polygon->vertex[v].scaley != this_polygon->vertex[v-1].scaley)
4454 				int_error(c_token,"relative coordinates must match in type");
4455 			    this_polygon->vertex[v].x += this_polygon->vertex[v-1].x;
4456 			    this_polygon->vertex[v].y += this_polygon->vertex[v-1].y;
4457 			}
4458 			this_polygon->type++;
4459 			got_corners = TRUE;
4460 		    }
4461 		    if (got_corners && memcmp(&this_polygon->vertex[this_polygon->type-1],
4462 					      &this_polygon->vertex[0],sizeof(struct position))) {
4463 			fprintf(stderr,"Polygon is not closed - adding extra vertex\n");
4464 			this_polygon->vertex = gp_realloc(this_polygon->vertex,
4465 					    (this_polygon->type+1) * sizeof(struct position),
4466 					    "polygon vertex");
4467 			this_polygon->vertex[this_polygon->type] = this_polygon->vertex[0];
4468 			this_polygon->type++;
4469 		    }
4470 		    continue;
4471 		}
4472 		break;
4473 		polygon_error:
4474 			free(this_polygon->vertex);
4475 			this_polygon->vertex = NULL;
4476 			this_polygon->type = 0;
4477 			int_error(c_token, "Unrecognized polygon syntax");
4478 		/* End of polygon options */
4479 
4480 	default:
4481 		int_error(c_token, "unrecognized object type");
4482 	} /* End of object-specific options */
4483 
4484 	/* The rest of the options apply to any type of object */
4485 
4486 	if (equals(c_token,"front")) {
4487 	    this_object->layer = LAYER_FRONT;
4488 	    c_token++;
4489 	    continue;
4490 	} else if (equals(c_token,"back")) {
4491 	    this_object->layer = LAYER_BACK;
4492 	    c_token++;
4493 	    continue;
4494 	} else if (equals(c_token,"behind")) {
4495 	    this_object->layer = LAYER_BEHIND;
4496 	    c_token++;
4497 	    continue;
4498 	} else if (almost_equals(c_token,"def$ault")) {
4499 	    if (tag < 0) {
4500 		int_error(c_token,
4501 		    "Invalid command - did you mean 'unset style rectangle'?");
4502 	    } else {
4503 		this_object->lp_properties.l_type = LT_DEFAULT;
4504 		this_object->fillstyle.fillstyle = FS_DEFAULT;
4505 	    }
4506 	    got_fill = got_lt = TRUE;
4507 	    c_token++;
4508 	    continue;
4509 	} else if (equals(c_token, "clip")) {
4510 	    this_object->clip = OBJ_CLIP;
4511 	    c_token++;
4512 	    continue;
4513 	} else if (equals(c_token, "noclip")) {
4514 	    this_object->clip = OBJ_NOCLIP;
4515 	    c_token++;
4516 	    continue;
4517 	}
4518 
4519 	/* Now parse the style options; default to whatever the global style is  */
4520 	if (!got_fill) {
4521 	    fill_style_type *default_style;
4522 	    if (this_object->object_type == OBJ_RECTANGLE)
4523 		default_style = &default_rectangle.fillstyle;
4524 	    else
4525 		default_style = &default_fillstyle;
4526 
4527 	    if (new_obj)
4528 		parse_fillstyle(&this_object->fillstyle, default_style->fillstyle,
4529 			default_style->filldensity, default_style->fillpattern,
4530 			default_style->border_color);
4531 	    else
4532 		parse_fillstyle(&this_object->fillstyle, this_object->fillstyle.fillstyle,
4533 			this_object->fillstyle.filldensity, this_object->fillstyle.fillpattern,
4534 			this_object->fillstyle.border_color);
4535 	    if (c_token != save_token) {
4536 		got_fill = TRUE;
4537 		continue;
4538 	    }
4539 	}
4540 
4541 	/* Parse the colorspec */
4542 	if (!got_fc) {
4543 	    if (equals(c_token,"fc") || almost_equals(c_token,"fillc$olor")) {
4544 		this_object->lp_properties.l_type = LT_BLACK; /* Anything but LT_DEFAULT */
4545 		parse_colorspec(&this_object->lp_properties.pm3d_color, TC_FRAC);
4546 		if (this_object->lp_properties.pm3d_color.type == TC_DEFAULT)
4547 		    this_object->lp_properties.l_type = LT_DEFAULT;
4548 	    }
4549 
4550 	    if (c_token != save_token) {
4551 		got_fc = TRUE;
4552 		continue;
4553 	    }
4554 	}
4555 
4556 	/* Line properties (will be used for the object border if the fillstyle has one. */
4557 	/* LP_NOFILL means don't eat fillcolor here since at is set separately with "fc". */
4558 	if (!got_lt) {
4559 	    lp_style_type lptmp = this_object->lp_properties;
4560 	    lp_parse(&lptmp, LP_NOFILL, FALSE);
4561 	    if (c_token != save_token) {
4562 		this_object->lp_properties.l_width = lptmp.l_width;
4563 		this_object->lp_properties.d_type = lptmp.d_type;
4564 		this_object->lp_properties.custom_dash_pattern = lptmp.custom_dash_pattern;
4565 		got_lt = TRUE;
4566 		continue;
4567 	    }
4568 	}
4569 
4570 	int_error(c_token, "Unrecognized or duplicate option");
4571     }
4572 
4573     if (got_center && got_corners)
4574 	int_error(NO_CARET,"Inconsistent options");
4575 
4576 }
4577 #endif
4578 
4579 static void
set_rgbmax()4580 set_rgbmax()
4581 {
4582     c_token++;
4583     if (END_OF_COMMAND)
4584 	rgbmax = 255;
4585     else
4586 	rgbmax = real_expression();
4587     if (rgbmax <= 0)
4588 	rgbmax = 255;
4589 }
4590 
4591 /* process 'set samples' command */
4592 static void
set_samples()4593 set_samples()
4594 {
4595     int tsamp1, tsamp2;
4596 
4597     c_token++;
4598     tsamp1 = abs(int_expression());
4599     tsamp2 = tsamp1;
4600     if (!END_OF_COMMAND) {
4601 	if (!equals(c_token,","))
4602 	    int_error(c_token, "',' expected");
4603 	c_token++;
4604 	tsamp2 = abs(int_expression());
4605     }
4606     if (tsamp1 < 2 || tsamp2 < 2)
4607 	int_error(c_token, "sampling rate must be > 1; sampling unchanged");
4608     else {
4609 	struct surface_points *f_3dp = first_3dplot;
4610 
4611 	first_3dplot = NULL;
4612 	sp_free(f_3dp);
4613 
4614 	samples_1 = tsamp1;
4615 	samples_2 = tsamp2;
4616     }
4617 }
4618 
4619 
4620 /* process 'set size' command */
4621 static void
set_size()4622 set_size()
4623 {
4624     c_token++;
4625     if (END_OF_COMMAND) {
4626 	xsize = 1.0;
4627 	ysize = 1.0;
4628     } else {
4629 	if (almost_equals(c_token, "sq$uare")) {
4630 	    aspect_ratio = 1.0;
4631 	    ++c_token;
4632 	} else if (almost_equals(c_token,"ra$tio")) {
4633 	    ++c_token;
4634 	    aspect_ratio = real_expression();
4635 	} else if (almost_equals(c_token, "nora$tio") || almost_equals(c_token, "nosq$uare")) {
4636 	    aspect_ratio = 0.0;
4637 	    ++c_token;
4638 	}
4639 
4640 	if (!END_OF_COMMAND) {
4641 	    xsize = real_expression();
4642 	    if (equals(c_token,",")) {
4643 		c_token++;
4644 		ysize = real_expression();
4645 	    } else {
4646 		ysize = xsize;
4647 	    }
4648 	}
4649     }
4650     if (xsize <= 0 || ysize <=0) {
4651 	xsize = ysize = 1.0;
4652 	int_error(NO_CARET,"Illegal value for size");
4653     }
4654 }
4655 
4656 
4657 /* process 'set style' command */
4658 static void
set_style()4659 set_style()
4660 {
4661     c_token++;
4662 
4663     switch(lookup_table(&show_style_tbl[0],c_token)){
4664     case SHOW_STYLE_DATA:
4665 	data_style = get_style();
4666 	if (data_style == FILLEDCURVES) {
4667 	    get_filledcurves_style_options(&filledcurves_opts_data);
4668 	    if (!filledcurves_opts_data.opt_given) /* default value */
4669 		filledcurves_opts_data.closeto = FILLEDCURVES_CLOSED;
4670 	}
4671 	break;
4672     case SHOW_STYLE_FUNCTION:
4673 	{
4674 	    enum PLOT_STYLE temp_style = get_style();
4675 
4676 	    if ((temp_style & PLOT_STYLE_HAS_ERRORBAR)
4677 	    ||  (temp_style == LABELPOINTS) || (temp_style == HISTOGRAMS)
4678 	    ||  (temp_style == IMAGE) || (temp_style == RGBIMAGE) || (temp_style == RGBA_IMAGE)
4679 	    ||  (temp_style == PARALLELPLOT))
4680 		int_error(c_token, "style not usable for function plots, left unchanged");
4681 	    else
4682 		func_style = temp_style;
4683 	    if (func_style == FILLEDCURVES) {
4684 		get_filledcurves_style_options(&filledcurves_opts_func);
4685 		if (!filledcurves_opts_func.opt_given) /* default value */
4686 		    filledcurves_opts_func.closeto = FILLEDCURVES_CLOSED;
4687 	    }
4688 	    break;
4689 	}
4690     case SHOW_STYLE_LINE:
4691 	set_linestyle(&first_linestyle, LP_STYLE);
4692 	break;
4693     case SHOW_STYLE_FILLING:
4694 	parse_fillstyle( &default_fillstyle,
4695 			default_fillstyle.fillstyle,
4696 			default_fillstyle.filldensity,
4697 			default_fillstyle.fillpattern,
4698 			default_fillstyle.border_color);
4699 	break;
4700     case SHOW_STYLE_ARROW:
4701 	set_arrowstyle();
4702 	break;
4703 #ifdef EAM_OBJECTS
4704     case SHOW_STYLE_RECTANGLE:
4705 	c_token++;
4706 	set_obj(-2, OBJ_RECTANGLE);
4707 	break;
4708     case SHOW_STYLE_CIRCLE:
4709 	c_token++;
4710 	while (!END_OF_COMMAND) {
4711 	    if (almost_equals(c_token,"r$adius")) {
4712 		c_token++;
4713 		get_position(&default_circle.o.circle.extent);
4714 	    } else if (almost_equals(c_token, "wedge$s")) {
4715 		c_token++;
4716 		default_circle.o.circle.wedge = TRUE;
4717 	    } else if (almost_equals(c_token, "nowedge$s")) {
4718 		c_token++;
4719 		default_circle.o.circle.wedge = FALSE;
4720 	    } else if (equals(c_token, "clip")) {
4721 		c_token++;
4722 		default_circle.clip = OBJ_CLIP;
4723 	    } else if (equals(c_token, "noclip")) {
4724 		c_token++;
4725 		default_circle.clip = OBJ_NOCLIP;
4726 	    } else
4727 		int_error(c_token, "unrecognized style option" );
4728 	}
4729 	break;
4730     case SHOW_STYLE_ELLIPSE:
4731 	c_token++;
4732 	while (!END_OF_COMMAND) {
4733 	    if (equals(c_token,"size")) {
4734 		c_token++;
4735 		get_position(&default_ellipse.o.ellipse.extent);
4736 		c_token--;
4737 	    } else if (almost_equals(c_token,"ang$le")) {
4738 		c_token++;
4739 		if (might_be_numeric(c_token)) {
4740 		    default_ellipse.o.ellipse.orientation = real_expression();
4741 		    c_token--;
4742 		}
4743 	    } else if (almost_equals(c_token,"unit$s")) {
4744 		c_token++;
4745 		if (equals(c_token,"xy") || END_OF_COMMAND) {
4746 		    default_ellipse.o.ellipse.type = ELLIPSEAXES_XY;
4747 		} else if (equals(c_token,"xx")) {
4748 		    default_ellipse.o.ellipse.type = ELLIPSEAXES_XX;
4749 		} else if (equals(c_token,"yy")) {
4750 		    default_ellipse.o.ellipse.type = ELLIPSEAXES_YY;
4751 		} else {
4752 		    int_error(c_token, "expecting 'xy', 'xx' or 'yy'" );
4753 		}
4754 	    } else if (equals(c_token, "clip")) {
4755 		c_token++;
4756 		default_ellipse.clip = OBJ_CLIP;
4757 	    } else if (equals(c_token, "noclip")) {
4758 		c_token++;
4759 		default_ellipse.clip = OBJ_NOCLIP;
4760 	    } else
4761 		int_error(c_token, "expecting 'units {xy|xx|yy}', 'angle <number>' or 'size <position>'" );
4762 
4763 	    c_token++;
4764 	}
4765 	break;
4766 #endif
4767     case SHOW_STYLE_HISTOGRAM:
4768 	parse_histogramstyle(&histogram_opts,HT_CLUSTERED,histogram_opts.gap);
4769 	break;
4770 #ifdef EAM_BOXED_TEXT
4771     case SHOW_STYLE_TEXTBOX:
4772 	c_token++;
4773 	while (!END_OF_COMMAND) {
4774 	    if (almost_equals(c_token,"op$aque")) {
4775 		textbox_opts.opaque = TRUE;
4776 		c_token++;
4777 	    } else if (almost_equals(c_token,"trans$parent")) {
4778 		textbox_opts.opaque = FALSE;
4779 		c_token++;
4780 	    } else if (almost_equals(c_token,"mar$gins")) {
4781 		struct value a;
4782 		c_token++;
4783 		if (END_OF_COMMAND) {
4784 		    textbox_opts.xmargin = 1.;
4785 		    textbox_opts.ymargin = 1.;
4786 		    break;
4787 		}
4788 		textbox_opts.xmargin = real(const_express(&a));
4789 		if (textbox_opts.xmargin < 0)
4790 		    textbox_opts.xmargin = 0;
4791 		if (!equals(c_token++,",") || END_OF_COMMAND)
4792 		    break;
4793 		textbox_opts.ymargin = real(const_express(&a));
4794 		if (textbox_opts.ymargin < 0)
4795 		    textbox_opts.ymargin = 0;
4796 	    } else if (almost_equals(c_token,"fillc$olor") || equals(c_token,"fc")) {
4797 		parse_colorspec(&textbox_opts.fillcolor, TC_RGB);
4798 	    } else if (almost_equals(c_token,"nobo$rder")) {
4799 		c_token++;
4800 		textbox_opts.noborder = TRUE;
4801 		textbox_opts.border_color.type = TC_LT;
4802 		textbox_opts.border_color.lt = LT_NODRAW;
4803 	    } else if (almost_equals(c_token,"bo$rdercolor")) {
4804 		c_token++;
4805 		textbox_opts.noborder = FALSE;
4806 		textbox_opts.border_color.type = TC_LT;
4807 		textbox_opts.border_color.lt = LT_BLACK;
4808 		if (END_OF_COMMAND)
4809 		    continue;
4810 		if (equals(c_token,"lt"))
4811 		    c_token--;
4812 		parse_colorspec(&textbox_opts.border_color, TC_RGB);
4813 	    } else if (almost_equals(c_token,"linew$idth") || equals(c_token,"lw")) {
4814 		c_token++;
4815 		textbox_opts.linewidth = real_expression();
4816 		if (textbox_opts.linewidth < 0)
4817 		    textbox_opts.linewidth = 1.0;
4818 	    } else
4819 		int_error(c_token,"unrecognized option");
4820 	}
4821 	break;
4822 #endif
4823     case SHOW_STYLE_INCREMENT:
4824 #if TRUE || defined(BACKWARDS_COMPATIBLE)
4825 	c_token++;
4826 	if (END_OF_COMMAND || almost_equals(c_token,"def$ault"))
4827 	    prefer_line_styles = FALSE;
4828 	else if (almost_equals(c_token,"u$serstyles"))
4829 	    prefer_line_styles = TRUE;
4830 	else
4831 	    int_error(c_token,"unrecognized option");
4832 	c_token++;
4833 #endif
4834 	break;
4835     case SHOW_STYLE_BOXPLOT:
4836 	set_boxplot();
4837 	break;
4838     case SHOW_STYLE_PARALLEL:
4839 	set_style_parallel();
4840 	break;
4841     default:
4842 	int_error(c_token, "unrecognized option - see 'help set style'");
4843     }
4844 }
4845 
4846 
4847 /* process 'set surface' command */
4848 static void
set_surface()4849 set_surface()
4850 {
4851     c_token++;
4852     draw_surface = TRUE;
4853     implicit_surface = TRUE;
4854     if (!END_OF_COMMAND) {
4855 	if (equals(c_token, "implicit"))
4856 	    ;
4857 	else if (equals(c_token, "explicit"))
4858 	    implicit_surface = FALSE;
4859 	c_token++;
4860     }
4861 }
4862 
4863 
4864 /* process 'set table' command */
4865 static void
set_table()4866 set_table()
4867 {
4868     char *tablefile;
4869     int filename_token = ++c_token;
4870     TBOOLEAN append = FALSE;
4871 
4872     if (table_outfile) {
4873 	fclose(table_outfile);
4874 	table_outfile = NULL;
4875     }
4876     table_var = NULL;
4877 
4878     if (equals(c_token, "$") && isletter(c_token + 1)) { /* datablock */
4879 	/* NB: has to come first because try_to_get_string will choke on the datablock name */
4880 	table_var = add_udv_by_name(parse_datablock_name());
4881 	if (table_var == NULL)
4882 	    int_error(c_token, "Error allocating datablock");
4883 	if (equals(c_token, "append")) {
4884 	    c_token++;
4885 	    append = TRUE;
4886 	}
4887 	if (!append || table_var->udv_value.type != DATABLOCK) {
4888 	    gpfree_datablock(&table_var->udv_value);
4889 	    gpfree_string(&table_var->udv_value);
4890 	    table_var->udv_value.type = DATABLOCK;
4891 	    table_var->udv_value.v.data_array = NULL;
4892 	}
4893 
4894     } else if ((tablefile = try_to_get_string())) {  /* file name */
4895 	/* 'set table "foo"' creates a new output file */
4896 	/* 'set table "foo" append' writes to the end of an existing output file */
4897 	gp_expand_tilde(&tablefile);
4898 	if (equals(c_token, "append")) {
4899 	    c_token++;
4900 	    append = TRUE;
4901 	}
4902 	if (!(table_outfile = fopen(tablefile, (append ? "a" : "w"))))
4903 	   os_error(filename_token, "cannot open table output file");
4904 	free(tablefile);
4905     }
4906 
4907     if (almost_equals(c_token,"sep$arator")) {
4908 	set_separator(&table_sep);
4909     }
4910 
4911     table_mode = TRUE;
4912 }
4913 
4914 
4915 /* process 'set terminal' comamnd */
4916 static void
set_terminal()4917 set_terminal()
4918 {
4919     c_token++;
4920 
4921     if (multiplot)
4922 	int_error(c_token, "You can't change the terminal in multiplot mode");
4923 
4924     if (END_OF_COMMAND) {
4925 	list_terms();
4926 	screen_ok = FALSE;
4927 	return;
4928     }
4929 
4930     /* `set term push' */
4931     if (equals(c_token,"push")) {
4932 	push_terminal(interactive);
4933 	c_token++;
4934 	return;
4935     } /* set term push */
4936 
4937 #ifdef USE_MOUSE
4938     event_reset((void *)1);   /* cancel zoombox etc. */
4939 #endif
4940     term_reset();
4941 
4942     /* `set term pop' */
4943     if (equals(c_token,"pop")) {
4944 	pop_terminal();
4945 	c_token++;
4946 	return;
4947     } /* set term pop */
4948 
4949     /* `set term <normal terminal>' */
4950     /* NB: if set_term() exits via int_error() then term will not be changed */
4951     term = set_term();
4952 
4953     /* get optional mode parameters
4954      * not all drivers reset the option string before
4955      * strcat-ing to it, so we reset it for them
4956      */
4957     *term_options = 0;
4958     term->options();
4959     if (interactive && *term_options)
4960 	fprintf(stderr,"Options are '%s'\n",term_options);
4961     if ((term->flags & TERM_MONOCHROME))
4962 	init_monochrome();
4963 }
4964 
4965 
4966 /*
4967  * Accept a single terminal option to apply to the current terminal if possible.
4968  * If the current terminal cannot support this option, we silently ignore it.
4969  * Only reasonably common terminal options are supported.
4970  *
4971  * If necessary, the code in term->options() can detect that it was called
4972  * from here because in this case almost_equals(c_token-1, "termopt$ion");
4973  */
4974 
4975 static void
set_termoptions()4976 set_termoptions()
4977 {
4978     TBOOLEAN ok_to_call_terminal = FALSE;
4979     int save_end_of_line = num_tokens;
4980     c_token++;
4981 
4982     if (END_OF_COMMAND || !term)
4983 	return;
4984 
4985     if (almost_equals(c_token,"enh$anced")
4986 	   ||  almost_equals(c_token,"noenh$anced")) {
4987 	num_tokens = GPMIN(num_tokens,c_token+1);
4988 	if (term->enhanced_open)
4989 	    ok_to_call_terminal = TRUE;
4990 	else
4991 	    c_token++;
4992     } else if (equals(c_token,"font") ||  equals(c_token,"fname")) {
4993 	num_tokens = GPMIN(num_tokens,c_token+2);
4994 	ok_to_call_terminal = TRUE;
4995     } else if (equals(c_token,"fontscale")) {
4996 	num_tokens = GPMIN(num_tokens,c_token+2);
4997 	if (term->flags & TERM_FONTSCALE)
4998 	    ok_to_call_terminal = TRUE;
4999 	else {
5000 	    c_token++;
5001 	    real_expression();   /* Silently ignore the request */
5002 	}
5003     } else if (equals(c_token,"pointscale") || equals(c_token,"ps")) {
5004 	num_tokens = GPMIN(num_tokens,c_token+2);
5005 	if (term->flags & TERM_POINTSCALE)
5006 	    ok_to_call_terminal = TRUE;
5007 	else {
5008 	    c_token++;
5009 	    real_expression();   /* Silently ignore the request */
5010 	}
5011     } else if (equals(c_token,"lw") || almost_equals(c_token,"linew$idth")) {
5012 	num_tokens = GPMIN(num_tokens,c_token+2);
5013 	if (term->flags & TERM_LINEWIDTH)
5014 	    ok_to_call_terminal = TRUE;
5015 	else {
5016 	    c_token++;
5017 	    real_expression();   /* Silently ignore the request */
5018 	}
5019     } else if (almost_equals(c_token,"dash$ed") || equals(c_token,"solid")) {
5020 	/* Silently ignore the request */
5021 	num_tokens = GPMIN(num_tokens,++c_token);
5022     } else if (almost_equals(c_token,"dashl$ength") || equals(c_token,"dl")) {
5023 	num_tokens = GPMIN(num_tokens,c_token+2);
5024 	if (term->flags & TERM_CAN_DASH)
5025 	    ok_to_call_terminal = TRUE;
5026 	else
5027 	    c_token+=2;
5028     } else if (!strcmp(term->name,"gif") && equals(c_token,"delay") && num_tokens==4) {
5029 	ok_to_call_terminal = TRUE;
5030     } else {
5031 	int_error(c_token,"This option cannot be changed using 'set termoption'");
5032     }
5033     if (ok_to_call_terminal) {
5034 	*term_options = 0;
5035 	(term->options)();
5036     }
5037     num_tokens = save_end_of_line;
5038 }
5039 
5040 /* Various properties of the theta axis in polar mode */
5041 static void
set_theta()5042 set_theta()
5043 {
5044     c_token++;
5045     while (!END_OF_COMMAND) {
5046 	if (almost_equals(c_token, "r$ight"))
5047 	    theta_origin = 0.0;
5048 	else if (almost_equals(c_token, "t$op"))
5049 	    theta_origin = 90.0;
5050 	else if (almost_equals(c_token, "l$eft"))
5051 	    theta_origin = 180.0;
5052 	else if (almost_equals(c_token, "b$ottom"))
5053 	    theta_origin = -90.;
5054 	else if (equals(c_token, "clockwise") || equals(c_token, "cw"))
5055 	    theta_direction = -1;
5056 	else if (equals(c_token, "counterclockwise") || equals(c_token, "ccw"))
5057 	    theta_direction = 1;
5058 	else
5059 	    int_error(c_token,"unrecognized option");
5060 	c_token++;
5061     }
5062 }
5063 
5064 /* process 'set tics' command */
5065 static void
set_tics()5066 set_tics()
5067 {
5068     unsigned int i = 0;
5069     TBOOLEAN axisset = FALSE;
5070     TBOOLEAN mirror_opt = FALSE; /* set to true if (no)mirror option specified) */
5071 
5072     ++c_token;
5073 
5074     if (END_OF_COMMAND) {
5075 	for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5076 	    axis_array[i].tic_in = TRUE;
5077     }
5078 
5079     while (!END_OF_COMMAND) {
5080 	if (almost_equals(c_token, "ax$is")) {
5081 	    axisset = TRUE;
5082 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5083 		axis_array[i].ticmode &= ~TICS_ON_BORDER;
5084 		axis_array[i].ticmode |= TICS_ON_AXIS;
5085 	    }
5086 	    ++c_token;
5087 	} else if (almost_equals(c_token, "bo$rder")) {
5088 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5089 		axis_array[i].ticmode &= ~TICS_ON_AXIS;
5090 		axis_array[i].ticmode |= TICS_ON_BORDER;
5091 	    }
5092 	    ++c_token;
5093 	} else if (almost_equals(c_token, "mi$rror")) {
5094 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5095 		axis_array[i].ticmode |= TICS_MIRROR;
5096     	    mirror_opt = TRUE;
5097 	    ++c_token;
5098 	} else if (almost_equals(c_token, "nomi$rror")) {
5099 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5100 		axis_array[i].ticmode &= ~TICS_MIRROR;
5101 	    mirror_opt = TRUE;
5102 	    ++c_token;
5103 	} else if (almost_equals(c_token,"in$wards")) {
5104 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5105 		axis_array[i].tic_in = TRUE;
5106 	    ++c_token;
5107 	} else if (almost_equals(c_token,"out$wards")) {
5108 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5109 		axis_array[i].tic_in = FALSE;
5110 	    ++c_token;
5111 	} else if (almost_equals(c_token, "sc$ale")) {
5112 	    set_ticscale();
5113 	} else if (almost_equals(c_token, "ro$tate")) {
5114 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5115 		axis_array[i].tic_rotate = TEXT_VERTICAL;
5116 	    }
5117 	    ++c_token;
5118 	    if (equals(c_token, "by")) {
5119 		int langle;
5120 		++c_token;
5121 		langle = int_expression();
5122 		for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5123 		    axis_array[i].tic_rotate = langle;
5124 	    }
5125 	} else if (almost_equals(c_token, "noro$tate")) {
5126 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5127 		axis_array[i].tic_rotate = 0;
5128 	    ++c_token;
5129 	} else if (almost_equals(c_token, "l$eft")) {
5130 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5131 		axis_array[i].tic_pos = LEFT;
5132 		axis_array[i].manual_justify = TRUE;
5133 	    }
5134 	    c_token++;
5135 	} else if (almost_equals(c_token, "c$entre")
5136 		|| almost_equals(c_token, "c$enter")) {
5137 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5138 		axis_array[i].tic_pos = CENTRE;
5139 		axis_array[i].manual_justify = TRUE;
5140 	    }
5141 	    c_token++;
5142 	} else if (almost_equals(c_token, "ri$ght")) {
5143 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5144 		axis_array[i].tic_pos = RIGHT;
5145 		axis_array[i].manual_justify = TRUE;
5146 	    }
5147 	    c_token++;
5148 	} else if (almost_equals(c_token, "autoj$ustify")) {
5149 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5150 		axis_array[i].manual_justify = FALSE;
5151 	    c_token++;
5152 	} else if (almost_equals(c_token, "off$set")) {
5153 	    struct position lpos;
5154 	    ++c_token;
5155 	    get_position_default(&lpos, character, 3);
5156 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5157 		axis_array[i].ticdef.offset = lpos;
5158 	} else if (almost_equals(c_token, "nooff$set")) {
5159 	    ++c_token;
5160 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5161 		axis_array[i].ticdef.offset = default_offset;
5162 	} else if (almost_equals(c_token, "format")) {
5163 	    set_format();
5164 	} else if (almost_equals(c_token, "enh$anced")) {
5165 	    ++c_token;
5166 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5167 		axis_array[i].ticdef.enhanced = TRUE;
5168 	} else if (almost_equals(c_token, "noenh$anced")) {
5169 	    ++c_token;
5170 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5171 		axis_array[i].ticdef.enhanced = FALSE;
5172 	} else if (almost_equals(c_token, "f$ont")) {
5173 	    ++c_token;
5174 	    /* Make sure they've specified a font */
5175 	    if (!isstringvalue(c_token))
5176 		int_error(c_token,"expected font");
5177 	    else {
5178 		char *lfont = try_to_get_string();
5179 		for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5180 		    free(axis_array[i].ticdef.font);
5181 		    axis_array[i].ticdef.font = gp_strdup(lfont);
5182 		}
5183 		free(lfont);
5184 	    }
5185 	} else if (equals(c_token,"tc") ||
5186 		   almost_equals(c_token,"text$color")) {
5187 	    struct t_colorspec lcolor;
5188 	    parse_colorspec(&lcolor, TC_FRAC);
5189 	    for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
5190 		axis_array[i].ticdef.textcolor = lcolor;
5191 	} else if (equals(c_token,"front")) {
5192 	    grid_tics_in_front = TRUE;
5193 	    ++c_token;
5194 	} else if (equals(c_token,"back")) {
5195 	    grid_tics_in_front = FALSE;
5196 	    ++c_token;
5197 	} else if (!END_OF_COMMAND) {
5198 	    int_error(c_token, "extraneous arguments in set tics");
5199 	}
5200     }
5201 
5202     /* if tics are off and not set by axis, reset to default (border) */
5203     for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5204 	if (((axis_array[i].ticmode & TICS_MASK) == NO_TICS) && (!axisset)) {
5205 	    if ((i == SECOND_X_AXIS) || (i == SECOND_Y_AXIS))
5206 		continue; /* don't switch on secondary axes by default */
5207 	    axis_array[i].ticmode = TICS_ON_BORDER;
5208 	    if ((mirror_opt == FALSE) && ((i == FIRST_X_AXIS) || (i == FIRST_Y_AXIS) || (i == COLOR_AXIS))) {
5209 		axis_array[i].ticmode |= TICS_MIRROR;
5210 	    }
5211 	}
5212     }
5213 }
5214 
5215 
5216 /* process 'set ticscale' command */
5217 static void
set_ticscale()5218 set_ticscale()
5219 {
5220     int i, ticlevel;
5221 
5222     ++c_token;
5223     if (almost_equals(c_token, "def$ault")) {
5224 	++c_token;
5225 	for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5226 	    axis_array[i].ticscale = 1.0;
5227 	    axis_array[i].miniticscale = 0.5;
5228 	}
5229 	ticscale[0] = 1.0;
5230 	ticscale[1] = 0.5;
5231 	for (ticlevel = 2; ticlevel < MAX_TICLEVEL; ticlevel++)
5232 	    ticscale[ticlevel] = 1.0;
5233     } else {
5234 	double lticscale, lminiticscale;
5235 	lticscale = real_expression();
5236 	if (equals(c_token, ",")) {
5237 	    ++c_token;
5238 	    lminiticscale = real_expression();
5239 	} else {
5240 	    lminiticscale = 0.5 * lticscale;
5241 	}
5242 	for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
5243 	    axis_array[i].ticscale = lticscale;
5244 	    axis_array[i].miniticscale = lminiticscale;
5245 	}
5246 	ticlevel = 2;
5247 	while (equals(c_token, ",")) {
5248 	    ++c_token;
5249 	    ticscale[ticlevel++] = real_expression();
5250 	    if (ticlevel >= MAX_TICLEVEL)
5251 		break;
5252 	}
5253     }
5254 }
5255 
5256 
5257 /* process 'set ticslevel' command */
5258 /* is datatype 'time' relevant here ? */
5259 static void
set_ticslevel()5260 set_ticslevel()
5261 {
5262     c_token++;
5263     xyplane.z = real_expression();
5264     xyplane.absolute = FALSE;
5265 }
5266 
5267 
5268 /* process 'set xyplane' command */
5269 /* is datatype 'time' relevant here ? */
5270 static void
set_xyplane()5271 set_xyplane()
5272 {
5273     if (equals(++c_token, "at")) {
5274 	c_token++;
5275 	xyplane.z = real_expression();
5276 	xyplane.absolute = TRUE;
5277 	return;
5278     } else if (!almost_equals(c_token,"rel$ative")) {
5279 	c_token--;
5280 	/* int_warn(NO_CARET, "deprecated syntax"); */
5281     }
5282     set_ticslevel();
5283 }
5284 
5285 
5286 /* Process 'set timefmt' command */
5287 /* HBB 20000507: changed this to a per-axis setting. I.e. you can now
5288  * have separate timefmt parse strings, different axes */
5289 /* V5 Oct 2014: But that was never documented, and makes little sense since
5290  * the input format is a property of the data file, not the graph axis.
5291  * Revert to a single global default timefmt as documented.
5292  * If the default is not sufficient, use timecolumn(N,"format") on input.
5293  * Use "set {axis}tics format" to control the output format.
5294  */
5295 static void
set_timefmt()5296 set_timefmt()
5297 {
5298     char *ctmp;
5299     c_token++;
5300 
5301     if ((ctmp = try_to_get_string())) {
5302 	free(timefmt);
5303 	timefmt = ctmp;
5304     } else {
5305 	free(timefmt);
5306 	timefmt = gp_strdup(TIMEFMT);
5307     }
5308 }
5309 
5310 
5311 /* process 'set timestamp' command */
5312 static void
set_timestamp()5313 set_timestamp()
5314 {
5315     TBOOLEAN got_format = FALSE;
5316     char *new;
5317 
5318     c_token++;
5319 
5320     while (!END_OF_COMMAND) {
5321 
5322 	if (almost_equals(c_token,"t$op")) {
5323 	    timelabel_bottom = FALSE;
5324 	    c_token++;
5325 	    continue;
5326 	} else if (almost_equals(c_token, "b$ottom")) {
5327 	    timelabel_bottom = TRUE;
5328 	    c_token++;
5329 	    continue;
5330 	}
5331 
5332 	if (almost_equals(c_token,"r$otate")) {
5333 	    timelabel.rotate = TEXT_VERTICAL;
5334 	    c_token++;
5335 	    continue;
5336 	} else if (almost_equals(c_token, "n$orotate")) {
5337 	    timelabel.rotate = 0;
5338 	    c_token++;
5339 	    continue;
5340 	}
5341 
5342 	if (almost_equals(c_token,"off$set")) {
5343 	    c_token++;
5344 	    get_position_default(&(timelabel.offset), character, 3);
5345 	    continue;
5346 	}
5347 
5348 	if (equals(c_token,"font")) {
5349 	    c_token++;
5350 	    new = try_to_get_string();
5351 	    free(timelabel.font);
5352 	    timelabel.font = new;
5353 	    continue;
5354 	}
5355 
5356 	if (equals(c_token,"tc") || almost_equals(c_token,"text$color")) {
5357 	    parse_colorspec(&(timelabel.textcolor), TC_VARIABLE);
5358 	    continue;
5359 	}
5360 
5361 	if (!got_format && ((new = try_to_get_string()))) {
5362 	    /* we have a format string */
5363 	    free(timelabel.text);
5364 	    timelabel.text = new;
5365 	    got_format = TRUE;
5366 	    continue;
5367 	}
5368 
5369 	int_error(c_token,"unrecognized option");
5370 
5371     }
5372 
5373     if (!(timelabel.text))
5374 	timelabel.text = gp_strdup(DEFAULT_TIMESTAMP_FORMAT);
5375     if (timelabel.rotate && !timelabel_bottom)
5376 	timelabel.pos = RIGHT;
5377     else
5378 	timelabel.pos = LEFT;
5379 }
5380 
5381 
5382 /* process 'set view' command */
5383 static void
set_view()5384 set_view()
5385 {
5386     int i;
5387     TBOOLEAN was_comma = TRUE;
5388     static const char errmsg1[] = "rot_%c must be in [0:%d] degrees range; view unchanged";
5389     static const char errmsg2[] = "%sscale must be > 0; view unchanged";
5390     double local_vals[4];
5391 
5392     c_token++;
5393     if (equals(c_token,"map")) {
5394 	splot_map = TRUE;
5395 	mapview_scale = 1.0;
5396 	c_token++;
5397 	if (equals(c_token,"scale")) {
5398 	    c_token++;
5399 	    mapview_scale = real_expression();
5400 	}
5401 	if (aspect_ratio_3D != 0) {
5402 	    aspect_ratio = -1;
5403 	    aspect_ratio_3D = 0;
5404 	}
5405 	return;
5406     };
5407 
5408     if (splot_map == TRUE)
5409 	splot_map = FALSE; /* default is no map */
5410 
5411     if (almost_equals(c_token,"equal$_axes")) {
5412 	c_token++;
5413 	if (END_OF_COMMAND || equals(c_token,"xy")) {
5414 	    aspect_ratio_3D = 2;
5415 	    c_token++;
5416 	} else if (equals(c_token,"xyz")) {
5417 	    aspect_ratio_3D = 3;
5418 	    c_token++;
5419 	}
5420 	return;
5421     } else if (almost_equals(c_token,"noequal$_axes")) {
5422 	aspect_ratio_3D = 0;
5423 	c_token++;
5424 	return;
5425     }
5426 
5427     if (equals(c_token,"azimuth")) {
5428 	c_token++;
5429 	azimuth = real_expression();
5430 	return;
5431     }
5432 
5433     local_vals[0] = surface_rot_x;
5434     local_vals[1] = surface_rot_z;
5435     local_vals[2] = surface_scale;
5436     local_vals[3] = surface_zscale;
5437     for (i = 0; i < 4 && !(END_OF_COMMAND);) {
5438 	if (equals(c_token,",")) {
5439 	    if (was_comma) i++;
5440 	    was_comma = TRUE;
5441 	    c_token++;
5442 	} else {
5443 	    if (!was_comma)
5444 		int_error(c_token, "',' expected");
5445 	    local_vals[i] = real_expression();
5446 	    i++;
5447 	    was_comma = FALSE;
5448 	}
5449     }
5450 
5451     if (local_vals[0] < 0 || local_vals[0] > 360)
5452 	int_error(c_token, errmsg1, 'x', 360);
5453     if (local_vals[1] < 0 || local_vals[1] > 360)
5454 	int_error(c_token, errmsg1, 'z', 360);
5455     if (local_vals[2] < 1e-6)
5456 	int_error(c_token, errmsg2, "");
5457     if (local_vals[3] < 1e-6)
5458 	int_error(c_token, errmsg2, "z");
5459 
5460     surface_rot_x = local_vals[0];
5461     surface_rot_z = local_vals[1];
5462     surface_scale = local_vals[2];
5463     surface_zscale = local_vals[3];
5464     surface_lscale = log(surface_scale);
5465 }
5466 
5467 
5468 /* process 'set zero' command */
5469 static void
set_zero()5470 set_zero()
5471 {
5472     struct value a;
5473     c_token++;
5474     zero = magnitude(const_express(&a));
5475 }
5476 
5477 
5478 /* process 'set {x|y|z|x2|y2}data' command */
5479 static void
set_timedata(struct axis * this_axis)5480 set_timedata(struct axis *this_axis)
5481 {
5482     c_token++;
5483     this_axis->datatype = DT_NORMAL;
5484     if (almost_equals(c_token,"t$ime")) {
5485 	this_axis->datatype = DT_TIMEDATE;
5486 	c_token++;
5487     } else if (almost_equals(c_token,"geo$graphic")) {
5488 	this_axis->datatype = DT_DMS;
5489 	c_token++;
5490     }
5491     /* FIXME: this provides approximate backwards compatibility */
5492     /*        but may be more trouble to explain than it's worth */
5493     this_axis->tictype = this_axis->datatype;
5494 }
5495 
5496 
5497 static void
set_range(struct axis * this_axis)5498 set_range(struct axis *this_axis)
5499 {
5500     c_token++;
5501 
5502     if (almost_equals(c_token,"re$store")) {
5503 	c_token++;
5504 	this_axis->set_min = this_axis->writeback_min;
5505 	this_axis->set_max = this_axis->writeback_max;
5506 	this_axis->set_autoscale = AUTOSCALE_NONE;
5507     } else {
5508 	if (!equals(c_token,"["))
5509 	    int_error(c_token, "expecting '[' or 'restore'");
5510 	c_token++;
5511 	this_axis->set_autoscale =
5512 	    load_range(this_axis,
5513 		       &this_axis->set_min, &this_axis->set_max,
5514 		       this_axis->set_autoscale);
5515 	if (!equals(c_token,"]"))
5516 	    int_error(c_token, "expecting ']'");
5517 	c_token++;
5518 	while (!END_OF_COMMAND) {
5519 	    if (almost_equals(c_token, "rev$erse")) {
5520 		++c_token;
5521 		this_axis->range_flags |= RANGE_IS_REVERSED;
5522 	    } else if (almost_equals(c_token, "norev$erse")) {
5523 		++c_token;
5524 		this_axis->range_flags &= ~RANGE_IS_REVERSED;
5525 	    } else if (almost_equals(c_token, "wr$iteback")) {
5526 		++c_token;
5527 		this_axis->range_flags |= RANGE_WRITEBACK;
5528 	    } else if (almost_equals(c_token, "nowri$teback")) {
5529 		++c_token;
5530 		this_axis->range_flags &= ~RANGE_WRITEBACK;
5531 	    } else if (almost_equals(c_token, "ext$end")) {
5532 		++c_token;
5533 		this_axis->set_autoscale &= ~(AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX);
5534 	    } else if (almost_equals(c_token, "noext$end")) {
5535 		++c_token;
5536 		this_axis->set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX;
5537 	    } else
5538 		int_error(c_token,"unrecognized option");
5539 	}
5540     }
5541 
5542     /* If this is one end of a linked axis pair, replicate the new range to the	*/
5543     /* linked axis, possibly via a mapping function. 				*/
5544     if (this_axis->linked_to_secondary)
5545 	clone_linked_axes(this_axis, this_axis->linked_to_secondary);
5546     else if (this_axis->linked_to_primary)
5547 	clone_linked_axes(this_axis, this_axis->linked_to_primary);
5548 }
5549 
5550 /*
5551  * set paxis <axis> {range <range-options> | tics <tic-options> }
5552  */
5553 static void
set_paxis()5554 set_paxis()
5555 {
5556     int p;
5557     c_token++;
5558     p = int_expression();
5559 
5560     if (p <= 0 || p > MAX_PARALLEL_AXES)
5561 	int_error(c_token-1, "illegal paxis");
5562     if (p > num_parallel_axes)
5563 	extend_parallel_axis(p);
5564 
5565     if (equals(c_token, "range"))
5566 	set_range( &parallel_axis[p-1] );
5567     else if (almost_equals(c_token, "tic$s"))
5568 	set_tic_prop( &parallel_axis[p-1] );
5569     else
5570 	int_error(c_token, "expecting 'range' or 'tics'");
5571 }
5572 
5573 static void
set_raxis()5574 set_raxis()
5575 {
5576     raxis = TRUE;
5577     c_token++;
5578 }
5579 
5580 /* process 'set {xyz}zeroaxis' command */
5581 static void
set_zeroaxis(AXIS_INDEX axis)5582 set_zeroaxis(AXIS_INDEX axis)
5583 {
5584     c_token++;
5585     if (axis_array[axis].zeroaxis != (void *)(&default_axis_zeroaxis))
5586 	free(axis_array[axis].zeroaxis);
5587     if (END_OF_COMMAND)
5588 	axis_array[axis].zeroaxis = (void *)(&default_axis_zeroaxis);
5589     else {
5590 	/* Some non-default style for the zeroaxis */
5591 	axis_array[axis].zeroaxis = gp_alloc(sizeof(lp_style_type), "zeroaxis");
5592 	*(axis_array[axis].zeroaxis) = default_axis_zeroaxis;
5593 	lp_parse(axis_array[axis].zeroaxis, LP_ADHOC, FALSE);
5594     }
5595 }
5596 
5597 /* process 'set zeroaxis' command */
5598 static void
set_allzeroaxis()5599 set_allzeroaxis()
5600 {
5601     int save_token = c_token;
5602     set_zeroaxis(FIRST_X_AXIS);
5603     c_token = save_token;
5604     set_zeroaxis(FIRST_Y_AXIS);
5605     c_token = save_token;
5606     set_zeroaxis(FIRST_Z_AXIS);
5607 }
5608 
5609 /* Implements 'set tics' 'set xtics' 'set ytics' etc */
5610 static int
set_tic_prop(struct axis * this_axis)5611 set_tic_prop(struct axis *this_axis)
5612 {
5613     int match = 0;		/* flag, set by matching a tic command */
5614     char nocmd[12];		/* fill w/ "no"+axis_name+suffix */
5615     char *cmdptr = NULL, *sfxptr = NULL;
5616     AXIS_INDEX axis = this_axis->index;
5617 
5618     if (axis < NUMBER_OF_MAIN_VISIBLE_AXES) {
5619 	(void) strcpy(nocmd, "no");
5620 	cmdptr = &nocmd[2];
5621 	(void) strcpy(cmdptr, axis_name(axis));
5622 	sfxptr = &nocmd[strlen(nocmd)];
5623 	(void) strcpy(sfxptr, "t$ics");	/* STRING */
5624     }
5625     if (axis == THETA_AXIS.index)
5626 	cmdptr = "ttics";
5627 
5628     if (almost_equals(c_token, cmdptr) || axis >= PARALLEL_AXES) {
5629 	TBOOLEAN axisset = FALSE;
5630 	TBOOLEAN mirror_opt = FALSE; /* set to true if (no)mirror option specified) */
5631 	this_axis->ticdef.def.mix = FALSE;
5632 	match = 1;
5633 	++c_token;
5634 	do {
5635 	    if (almost_equals(c_token, "ax$is")) {
5636 		axisset = TRUE;
5637 		this_axis->ticmode &= ~TICS_ON_BORDER;
5638 		this_axis->ticmode |= TICS_ON_AXIS;
5639 		++c_token;
5640 	    } else if (almost_equals(c_token, "bo$rder")) {
5641 		this_axis->ticmode &= ~TICS_ON_AXIS;
5642 		this_axis->ticmode |= TICS_ON_BORDER;
5643 		++c_token;
5644 	    } else if (almost_equals(c_token, "mi$rror")) {
5645 		this_axis->ticmode |= TICS_MIRROR;
5646 		mirror_opt = TRUE;
5647 		++c_token;
5648 	    } else if (almost_equals(c_token, "nomi$rror")) {
5649 		this_axis->ticmode &= ~TICS_MIRROR;
5650 		mirror_opt = TRUE;
5651 		++c_token;
5652 	    } else if (almost_equals(c_token, "in$wards")) {
5653 		this_axis->tic_in = TRUE;
5654 		++c_token;
5655 	    } else if (almost_equals(c_token, "out$wards")) {
5656 		this_axis->tic_in = FALSE;
5657 		++c_token;
5658 	    } else if (almost_equals(c_token, "sc$ale")) {
5659 		++c_token;
5660 		if (almost_equals(c_token, "def$ault")) {
5661 		    this_axis->ticscale = 1.0;
5662 		    this_axis->miniticscale = 0.5;
5663 		    ++c_token;
5664 		} else {
5665 		    this_axis->ticscale = real_expression();
5666 		    if (equals(c_token, ",")) {
5667 			++c_token;
5668 			this_axis->miniticscale = real_expression();
5669 		    } else
5670 			this_axis->miniticscale = 0.5 * this_axis->ticscale;
5671 		}
5672 	    } else if (almost_equals(c_token, "ro$tate")) {
5673 		this_axis->tic_rotate = TEXT_VERTICAL;
5674 		++c_token;
5675 		if (equals(c_token, "by")) {
5676 		    c_token++;
5677 		    this_axis->tic_rotate = int_expression();
5678 		}
5679 	    } else if (almost_equals(c_token, "noro$tate")) {
5680 		this_axis->tic_rotate = 0;
5681 		++c_token;
5682 	    } else if (almost_equals(c_token, "off$set")) {
5683 		++c_token;
5684 		get_position_default(&this_axis->ticdef.offset,
5685 				     character, 3);
5686 	    } else if (almost_equals(c_token, "nooff$set")) {
5687 		++c_token;
5688 		this_axis->ticdef.offset = default_offset;
5689 	    } else if (almost_equals(c_token, "l$eft")) {
5690 		this_axis->tic_pos = LEFT;
5691 		this_axis->manual_justify = TRUE;
5692 		c_token++;
5693 	    } else if (almost_equals(c_token, "c$entre")
5694 		       || almost_equals(c_token, "c$enter")) {
5695 		this_axis->tic_pos = CENTRE;
5696 		this_axis->manual_justify = TRUE;
5697 		c_token++;
5698 	    } else if (almost_equals(c_token, "ri$ght")) {
5699 		this_axis->tic_pos = RIGHT;
5700 		this_axis->manual_justify = TRUE;
5701 		c_token++;
5702 	    } else if (almost_equals(c_token, "autoj$ustify")) {
5703 		this_axis->manual_justify = FALSE;
5704 		c_token++;
5705 	    } else if (almost_equals(c_token,"range$limited")) {
5706 		this_axis->ticdef.rangelimited = TRUE;
5707 		++c_token;
5708 	    } else if (almost_equals(c_token,"norange$limited")) {
5709 		this_axis->ticdef.rangelimited = FALSE;
5710 		++c_token;
5711 	    } else if (almost_equals(c_token, "f$ont")) {
5712 		++c_token;
5713 		/* Make sure they've specified a font */
5714 		if (!isstringvalue(c_token))
5715 		    int_error(c_token,"expected font");
5716 		else {
5717 		    free(this_axis->ticdef.font);
5718 		    this_axis->ticdef.font = NULL;
5719 		    this_axis->ticdef.font = try_to_get_string();
5720 		}
5721 
5722 	    /* The geographic/timedate/numeric options are new in version 5 */
5723 	    } else if (almost_equals(c_token,"geo$graphic")) {
5724 		++c_token;
5725 		this_axis->tictype = DT_DMS;
5726 	    } else if (almost_equals(c_token,"time$date")) {
5727 		++c_token;
5728 		this_axis->tictype = DT_TIMEDATE;
5729 	    } else if (almost_equals(c_token,"numeric")) {
5730 		++c_token;
5731 		this_axis->tictype = DT_NORMAL;
5732 
5733 	    } else if (equals(c_token,"format")) {
5734 		char *format;
5735 		++c_token;
5736 		if (END_OF_COMMAND)
5737 		    format = gp_strdup(DEF_FORMAT);
5738 		else if (!((format = try_to_get_string())))
5739 		    int_error(c_token,"expected format");
5740 		free(this_axis->formatstring);
5741 		this_axis->formatstring  = format;
5742 	    } else if (almost_equals(c_token, "enh$anced")) {
5743 		++c_token;
5744 		this_axis->ticdef.enhanced = TRUE;
5745 	    } else if (almost_equals(c_token, "noenh$anced")) {
5746 		++c_token;
5747 		this_axis->ticdef.enhanced = FALSE;
5748 	    } else if (equals(c_token,"tc") ||
5749 		       almost_equals(c_token,"text$color")) {
5750 		parse_colorspec(&this_axis->ticdef.textcolor,
5751 				axis == FIRST_Z_AXIS ? TC_Z : TC_FRAC);
5752 	    } else if (almost_equals(c_token, "au$tofreq")) {
5753 		/* auto tic interval */
5754 		++c_token;
5755 		if (!this_axis->ticdef.def.mix) {
5756 		    free_marklist(this_axis->ticdef.def.user);
5757 		    this_axis->ticdef.def.user = NULL;
5758 		}
5759 		this_axis->ticdef.type = TIC_COMPUTED;
5760 #ifdef NONLINEAR_AXES
5761 	    } else if (almost_equals(c_token, "log$scale")) {
5762 		++c_token;
5763 		this_axis->ticdef.logscaling = TRUE;
5764 	    } else if (almost_equals(c_token, "nolog$scale")) {
5765 		++c_token;
5766 		this_axis->ticdef.logscaling = FALSE;
5767 #endif
5768 	    } else if (equals(c_token,"add")) {
5769 		++c_token;
5770 		this_axis->ticdef.def.mix = TRUE;
5771 	    } else if (!END_OF_COMMAND) {
5772 		load_tics(this_axis);
5773 	    }
5774 	} while (!END_OF_COMMAND);
5775 
5776 	/* if tics are off and not set by axis, reset to default (border) */
5777 	if (((this_axis->ticmode & TICS_MASK) == NO_TICS) && (!axisset)) {
5778 	    if (axis >= PARALLEL_AXES)
5779 		this_axis->ticmode |= TICS_ON_AXIS;
5780 	    else
5781 		this_axis->ticmode |= TICS_ON_BORDER;
5782 	    if ((mirror_opt == FALSE) && ((axis == FIRST_X_AXIS) || (axis == FIRST_Y_AXIS) || (axis == COLOR_AXIS))) {
5783 		this_axis->ticmode |= TICS_MIRROR;
5784 	    }
5785 	}
5786 
5787     }
5788 
5789     /* The remaining command options cannot work for parametric or parallel axes */
5790     if (axis >= NUMBER_OF_MAIN_VISIBLE_AXES)
5791 	return match;
5792 
5793     if (almost_equals(c_token, nocmd)) {	/* NOSTRING */
5794 	this_axis->ticmode &= ~TICS_MASK;
5795 	c_token++;
5796 	match = 1;
5797     }
5798 
5799 /* other options */
5800 
5801     (void) strcpy(sfxptr, "m$tics");	/* MONTH */
5802     if (almost_equals(c_token, cmdptr)) {
5803 	if (!this_axis->ticdef.def.mix) {
5804 	    free_marklist(this_axis->ticdef.def.user);
5805 	    this_axis->ticdef.def.user = NULL;
5806 	}
5807 	this_axis->ticdef.type = TIC_MONTH;
5808 	++c_token;
5809 	match = 1;
5810     }
5811     if (almost_equals(c_token, nocmd)) {	/* NOMONTH */
5812 	this_axis->ticdef.type = TIC_COMPUTED;
5813 	++c_token;
5814 	match = 1;
5815     }
5816     (void) strcpy(sfxptr, "d$tics");	/* DAYS */
5817     if (almost_equals(c_token, cmdptr)) {
5818 	match = 1;
5819 	if (!this_axis->ticdef.def.mix) {
5820 	    free_marklist(this_axis->ticdef.def.user);
5821 	    this_axis->ticdef.def.user = NULL;
5822 	}
5823 	this_axis->ticdef.type = TIC_DAY;
5824 	++c_token;
5825     }
5826     if (almost_equals(c_token, nocmd)) {	/* NODAYS */
5827 	this_axis->ticdef.type = TIC_COMPUTED;
5828 	++c_token;
5829 	match = 1;
5830     }
5831     *cmdptr = 'm';
5832     (void) strcpy(cmdptr + 1, axis_name(axis));
5833     (void) strcat(cmdptr, "t$ics");	/* MINISTRING */
5834 
5835     if (almost_equals(c_token, cmdptr)) {
5836 	c_token++;
5837 	match = 1;
5838 	if (END_OF_COMMAND) {
5839 	    this_axis->minitics = MINI_AUTO;
5840 	} else if (almost_equals(c_token, "def$ault")) {
5841 	    this_axis->minitics = MINI_DEFAULT;
5842 	    ++c_token;
5843 	} else {
5844 	    int freq = int_expression();
5845 	    if (freq > 0 && freq < 101) {
5846 		this_axis->mtic_freq = freq;
5847 		this_axis->minitics = MINI_USER;
5848 	    } else {
5849 		this_axis->minitics = MINI_DEFAULT;
5850 		int_warn(c_token-1,"Expecting number of intervals");
5851 	    }
5852 	}
5853     }
5854     if (almost_equals(c_token, nocmd)) {	/* NOMINI */
5855 	this_axis->minitics = MINI_OFF;
5856 	c_token++;
5857 	match = 1;
5858     }
5859     return (match);
5860 }
5861 
5862 /*
5863  * minor tics around perimeter of polar grid circle (theta).
5864  * This version works like other axes (parameter is # of subintervals)
5865  * but it might be more reasonable to simply take increment in degress.
5866  */
5867 static void
set_mttics(struct axis * this_axis)5868 set_mttics(struct axis *this_axis)
5869 {
5870     c_token++;
5871 
5872     if (END_OF_COMMAND) {
5873 	this_axis->minitics = MINI_AUTO;
5874 	++c_token;
5875     } else {
5876 	int freq = int_expression();
5877 	if (freq > 0 && freq < 361) {
5878 	    this_axis->mtic_freq = freq;
5879 	    this_axis->minitics = MINI_USER;
5880 	} else {
5881 	    this_axis->minitics = MINI_AUTO;
5882 	    int_warn(c_token-1,"Expecting number of intervals");
5883 	}
5884     }
5885 }
5886 
5887 /* process a 'set {x/y/z}label command */
5888 /* set {x/y/z}label {label_text} {offset {x}{,y}} {<fontspec>} {<textcolor>} */
5889 static void
set_xyzlabel(text_label * label)5890 set_xyzlabel(text_label *label)
5891 {
5892     char *text = NULL;
5893 
5894     c_token++;
5895     if (END_OF_COMMAND) {	/* no label specified */
5896 	free(label->text);
5897 	label->text = NULL;
5898 	return;
5899     }
5900 
5901     parse_label_options(label, 0);
5902 
5903     if (!END_OF_COMMAND) {
5904 	text = try_to_get_string();
5905 	if (text) {
5906 	    free(label->text);
5907 	    label->text = text;
5908 	}
5909     }
5910 
5911     parse_label_options(label, 0);
5912 
5913 }
5914 
5915 
5916 /*
5917  * Change or insert a new linestyle in a list of line styles.
5918  * Supports the old 'set linestyle' command (backwards-compatible)
5919  * and the new "set style line" and "set linetype" commands.
5920  * destination_class is either LP_STYLE or LP_TYPE.
5921  */
5922 static void
set_linestyle(struct linestyle_def ** head,lp_class destination_class)5923 set_linestyle(struct linestyle_def **head, lp_class destination_class)
5924 {
5925     struct linestyle_def *this_linestyle = NULL;
5926     struct linestyle_def *new_linestyle = NULL;
5927     struct linestyle_def *prev_linestyle = NULL;
5928     int tag;
5929 
5930     c_token++;
5931 
5932     /* get tag */
5933     if (END_OF_COMMAND || ((tag = int_expression()) <= 0))
5934 	int_error(c_token, "tag must be > zero");
5935 
5936     /* Check if linestyle is already defined */
5937     for (this_linestyle = *head; this_linestyle != NULL;
5938 	 prev_linestyle = this_linestyle, this_linestyle = this_linestyle->next)
5939 	if (tag <= this_linestyle->tag)
5940 		break;
5941 
5942     if (this_linestyle == NULL || tag != this_linestyle->tag) {
5943 	/* Default style is based on linetype with the same tag id */
5944 	struct lp_style_type loc_lp = DEFAULT_LP_STYLE_TYPE;
5945 	loc_lp.l_type = tag - 1;
5946 	loc_lp.p_type = tag - 1;
5947 	loc_lp.d_type = DASHTYPE_SOLID;
5948 	loc_lp.pm3d_color.type = TC_LT;
5949 	loc_lp.pm3d_color.lt = tag - 1;
5950 
5951 	new_linestyle = gp_alloc(sizeof(struct linestyle_def), "linestyle");
5952 	if (prev_linestyle != NULL)
5953 	    prev_linestyle->next = new_linestyle;	/* add it to end of list */
5954 	else
5955 	    *head = new_linestyle;	/* make it start of list */
5956 	new_linestyle->tag = tag;
5957 	new_linestyle->next = this_linestyle;
5958 	new_linestyle->lp_properties = loc_lp;
5959 	this_linestyle = new_linestyle;
5960     }
5961 
5962     if (almost_equals(c_token, "def$ault")) {
5963 	delete_linestyle(head, prev_linestyle, this_linestyle);
5964 	c_token++;
5965     } else
5966 	/* pick up a line spec; dont allow ls, do allow point type */
5967 	lp_parse(&this_linestyle->lp_properties, destination_class, TRUE);
5968 
5969     if (!END_OF_COMMAND)
5970 	int_error(c_token,"Extraneous arguments to set %s",
5971 		head == &first_perm_linestyle ? "linetype" : "style line");
5972 }
5973 
5974 /*
5975  * Delete linestyle from linked list.
5976  * Called with pointers to the head of the list,
5977  * to the previous linestyle (not strictly necessary),
5978  * and to the linestyle to delete.
5979  */
5980 void
delete_linestyle(struct linestyle_def ** head,struct linestyle_def * prev,struct linestyle_def * this)5981 delete_linestyle(struct linestyle_def **head, struct linestyle_def *prev, struct linestyle_def *this)
5982 {
5983     if (this != NULL) {		/* there really is something to delete */
5984 	if (this == *head)
5985 	    *head = this->next;
5986 	else
5987 	    prev->next = this->next;
5988 	free(this);
5989     }
5990 }
5991 
5992 
5993 /* ======================================================== */
5994 /* process a 'set arrowstyle' command */
5995 /* set style arrow {tag} {nohead|head|backhead|heads} {size l,a{,b}} {{no}filled} {linestyle...} {layer n}*/
5996 static void
set_arrowstyle()5997 set_arrowstyle()
5998 {
5999     struct arrowstyle_def *this_arrowstyle = NULL;
6000     struct arrowstyle_def *new_arrowstyle = NULL;
6001     struct arrowstyle_def *prev_arrowstyle = NULL;
6002     struct arrow_style_type loc_arrow;
6003     int tag;
6004 
6005     default_arrow_style(&loc_arrow);
6006 
6007     c_token++;
6008 
6009     /* get tag */
6010     if (!END_OF_COMMAND) {
6011 	/* must be a tag expression! */
6012 	tag = int_expression();
6013 	if (tag <= 0)
6014 	    int_error(c_token, "tag must be > zero");
6015     } else
6016 	tag = assign_arrowstyle_tag();	/* default next tag */
6017 
6018     /* search for arrowstyle */
6019     if (first_arrowstyle != NULL) {	/* skip to last arrowstyle */
6020 	for (this_arrowstyle = first_arrowstyle; this_arrowstyle != NULL;
6021 	     prev_arrowstyle = this_arrowstyle,
6022 	     this_arrowstyle = this_arrowstyle->next)
6023 	    /* is this the arrowstyle we want? */
6024 	    if (tag <= this_arrowstyle->tag)
6025 		break;
6026     }
6027 
6028     if (this_arrowstyle == NULL || tag != this_arrowstyle->tag) {
6029 	/* adding the arrowstyle */
6030 	new_arrowstyle = (struct arrowstyle_def *)
6031 	    gp_alloc(sizeof(struct arrowstyle_def), "arrowstyle");
6032 	default_arrow_style(&(new_arrowstyle->arrow_properties));
6033 	if (prev_arrowstyle != NULL)
6034 	    prev_arrowstyle->next = new_arrowstyle;	/* add it to end of list */
6035 	else
6036 	    first_arrowstyle = new_arrowstyle;	/* make it start of list */
6037 	new_arrowstyle->arrow_properties.tag = tag;
6038 	new_arrowstyle->tag = tag;
6039 	new_arrowstyle->next = this_arrowstyle;
6040 	this_arrowstyle = new_arrowstyle;
6041     }
6042 
6043     if (END_OF_COMMAND)
6044 	this_arrowstyle->arrow_properties = loc_arrow;
6045     else if (almost_equals(c_token, "def$ault")) {
6046 	this_arrowstyle->arrow_properties = loc_arrow;
6047 	c_token++;
6048     } else
6049 	/* pick up a arrow spec : dont allow arrowstyle */
6050 	arrow_parse(&this_arrowstyle->arrow_properties, FALSE);
6051 
6052     if (!END_OF_COMMAND)
6053 	int_error(c_token, "extraneous or out-of-order arguments in set arrowstyle");
6054 
6055 }
6056 
6057 /* assign a new arrowstyle tag
6058  * arrowstyles are kept sorted by tag number, so this is easy
6059  * returns the lowest unassigned tag number
6060  */
6061 static int
assign_arrowstyle_tag()6062 assign_arrowstyle_tag()
6063 {
6064     struct arrowstyle_def *this;
6065     int last = 0;		/* previous tag value */
6066 
6067     for (this = first_arrowstyle; this != NULL; this = this->next)
6068 	if (this->tag == last + 1)
6069 	    last++;
6070 	else
6071 	    break;
6072 
6073     return (last + 1);
6074 }
6075 
6076 /* For set [xy]tics... command */
6077 static void
load_tics(struct axis * this_axis)6078 load_tics(struct axis *this_axis)
6079 {
6080     if (equals(c_token, "(")) {	/* set : TIC_USER */
6081 	c_token++;
6082 	load_tic_user(this_axis);
6083     } else {			/* series : TIC_SERIES */
6084 	load_tic_series(this_axis);
6085     }
6086 }
6087 
6088 /* load TIC_USER definition */
6089 /* (tic[,tic]...)
6090  * where tic is ["string"] value [level]
6091  * Left paren is already scanned off before entry.
6092  */
6093 static void
load_tic_user(struct axis * this_axis)6094 load_tic_user(struct axis *this_axis)
6095 {
6096     char *ticlabel;
6097     double ticposition;
6098 
6099     /* Free any old tic labels */
6100     if (!this_axis->ticdef.def.mix && !(set_iterator && set_iterator->iteration)) {
6101 	free_marklist(this_axis->ticdef.def.user);
6102 	this_axis->ticdef.def.user = NULL;
6103     }
6104 
6105     /* Mark this axis as user-generated ticmarks only, unless the */
6106     /* mix flag indicates that both user- and auto- tics are OK.  */
6107     if (!this_axis->ticdef.def.mix)
6108 	this_axis->ticdef.type = TIC_USER;
6109 
6110     while (!END_OF_COMMAND && !equals(c_token,")")) {
6111 	int ticlevel=0;
6112 	int save_token;
6113 	/* syntax is  (  {'format'} value {level} {, ...} )
6114 	 * but for timedata, the value itself is a string, which
6115 	 * complicates things somewhat
6116 	 */
6117 
6118 	/* has a string with it? */
6119 	save_token = c_token;
6120 	ticlabel = try_to_get_string();
6121 	if (ticlabel && this_axis->datatype == DT_TIMEDATE
6122 	    && (equals(c_token,",") || equals(c_token,")"))) {
6123 	    c_token = save_token;
6124 	    free(ticlabel);
6125 	    ticlabel = NULL;
6126 	}
6127 
6128 	/* in any case get the value */
6129 	ticposition = get_num_or_time(this_axis);
6130 
6131 	if (!END_OF_COMMAND &&
6132 	    !equals(c_token, ",") &&
6133 	    !equals(c_token, ")")) {
6134 	  ticlevel = int_expression(); /* tic level */
6135 	}
6136 
6137 	/* add to list */
6138 	add_tic_user(this_axis, ticlabel, ticposition, ticlevel);
6139 	free(ticlabel);
6140 
6141 	/* expect "," or ")" here */
6142 	if (!END_OF_COMMAND && equals(c_token, ","))
6143 	    c_token++;		/* loop again */
6144 	else
6145 	    break;		/* hopefully ")" */
6146     }
6147 
6148     if (END_OF_COMMAND || !equals(c_token, ")")) {
6149 	free_marklist(this_axis->ticdef.def.user);
6150 	this_axis->ticdef.def.user = NULL;
6151 	int_error(c_token, "expecting right parenthesis )");
6152     }
6153     c_token++;
6154 }
6155 
6156 void
free_marklist(struct ticmark * list)6157 free_marklist(struct ticmark *list)
6158 {
6159     while (list != NULL) {
6160 	struct ticmark *freeable = list;
6161 	list = list->next;
6162 	if (freeable->label != NULL)
6163 	    free(freeable->label);
6164 	free(freeable);
6165     }
6166 }
6167 
6168 /* Remove tic labels that were read from a datafile during a previous plot
6169  * via the 'using xtics(n)' mechanism.  These have tick level < 0.
6170  */
6171 struct ticmark *
prune_dataticks(struct ticmark * list)6172 prune_dataticks(struct ticmark *list)
6173 {
6174     struct ticmark a = {0.0,NULL,0,NULL};
6175     struct ticmark *b = &a;
6176     struct ticmark *tmp;
6177 
6178     while (list) {
6179 	if (list->level < 0) {
6180 	    free(list->label);
6181 	    tmp = list->next;
6182 	    free(list);
6183 	    list = tmp;
6184 	} else {
6185 	    b->next = list;
6186 	    b = list;
6187 	    list = list->next;
6188 	}
6189     }
6190     b->next = NULL;
6191     return a.next;
6192 }
6193 
6194 /* load TIC_SERIES definition */
6195 /* [start,]incr[,end] */
6196 static void
load_tic_series(struct axis * this_axis)6197 load_tic_series(struct axis *this_axis)
6198 {
6199     double start, incr, end;
6200     int incr_token;
6201     struct ticdef *tdef = &(this_axis->ticdef);
6202 
6203     start = get_num_or_time(this_axis);
6204 
6205     if (!equals(c_token, ",")) {
6206 	/* only step specified */
6207 	incr_token = c_token;
6208 	incr = start;
6209 	start = -VERYLARGE;
6210 	end = VERYLARGE;
6211     } else {
6212 	c_token++;
6213 	incr_token = c_token;
6214 	incr = get_num_or_time(this_axis);
6215 
6216 	if (!equals(c_token, ",")) {
6217 	    /* only step and increment specified */
6218 	    end = VERYLARGE;
6219 	} else {
6220 	    c_token++;
6221 	    end = get_num_or_time(this_axis);
6222 	}
6223     }
6224 
6225     if (start < end && incr <= 0)
6226 	int_error(incr_token, "increment must be positive");
6227     if (start > end && incr >= 0)
6228 	int_error(incr_token, "increment must be negative");
6229     if (start > end) {
6230 	/* put in order */
6231 	double numtics = floor((end * (1 + SIGNIF) - start) / incr);
6232 
6233 	end = start;
6234 	start = end + numtics * incr;
6235 	incr = -incr;
6236     }
6237 
6238     if (!tdef->def.mix) { /* remove old list */
6239 	free_marklist(tdef->def.user);
6240 	tdef->def.user = NULL;
6241     }
6242     tdef->type = TIC_SERIES;
6243     tdef->def.series.start = start;
6244     tdef->def.series.incr = incr;
6245     tdef->def.series.end = end;
6246 }
6247 
6248 /*
6249  * new_text_label() allocates and initializes a text_label structure.
6250  * This routine is also used by the plot and splot with labels commands.
6251  */
6252 struct text_label *
new_text_label(int tag)6253 new_text_label(int tag)
6254 {
6255     struct text_label *new;
6256 
6257     new = gp_alloc( sizeof(struct text_label), "text_label");
6258     memset(new, 0, sizeof(struct text_label));
6259     new->tag = tag;
6260     new->place = default_position;
6261     new->pos = LEFT;
6262     new->textcolor.type = TC_DEFAULT;
6263     new->lp_properties.p_type = 1;
6264     new->offset = default_offset;
6265 
6266     return(new);
6267 }
6268 
6269 /*
6270  * Parse the sub-options for label style and placement.
6271  * This is called from set_label, and from plot2d and plot3d
6272  * to handle options for 'plot with labels'
6273  * Note: ndim = 2 means we are inside a plot command,
6274  *       ndim = 3 means we are inside an splot command
6275  *       ndim = 0 in a set command
6276  */
6277 void
parse_label_options(struct text_label * this_label,int ndim)6278 parse_label_options( struct text_label *this_label, int ndim)
6279 {
6280     struct position pos;
6281     char *font = NULL;
6282     enum JUSTIFY just = LEFT;
6283     int rotate = 0;
6284     TBOOLEAN set_position = FALSE, set_just = FALSE, set_point = FALSE,
6285 	set_rot = FALSE, set_font = FALSE, set_offset = FALSE,
6286 	set_layer = FALSE, set_textcolor = FALSE, set_hypertext = FALSE;
6287     int layer = LAYER_BACK;
6288     TBOOLEAN axis_label = (this_label->tag <= NONROTATING_LABEL_TAG);
6289     TBOOLEAN hypertext = FALSE;
6290     struct position offset = default_offset;
6291     t_colorspec textcolor = {TC_DEFAULT,0,0.0};
6292     struct lp_style_type loc_lp = DEFAULT_LP_STYLE_TYPE;
6293     loc_lp.flags = LP_NOT_INITIALIZED;
6294 
6295    /* Now parse the label format and style options */
6296     while (!END_OF_COMMAND) {
6297 	/* get position */
6298 	if ((ndim == 0) && !set_position && equals(c_token, "at") && !axis_label) {
6299 	    c_token++;
6300 	    get_position(&pos);
6301 	    set_position = TRUE;
6302 	    continue;
6303 	}
6304 
6305 	/* get justification */
6306 	if (! set_just) {
6307 	    if (almost_equals(c_token, "l$eft")) {
6308 		just = LEFT;
6309 		c_token++;
6310 		set_just = TRUE;
6311 		continue;
6312 	    } else if (almost_equals(c_token, "c$entre")
6313 		       || almost_equals(c_token, "c$enter")) {
6314 		just = CENTRE;
6315 		c_token++;
6316 		set_just = TRUE;
6317 		continue;
6318 	    } else if (almost_equals(c_token, "r$ight")) {
6319 		just = RIGHT;
6320 		c_token++;
6321 		set_just = TRUE;
6322 		continue;
6323 	    }
6324 	}
6325 
6326 	/* get rotation (added by RCC) */
6327 	if (almost_equals(c_token, "rot$ate")) {
6328 	    c_token++;
6329 	    set_rot = TRUE;
6330 	    rotate = this_label->rotate;
6331 	    if (equals(c_token, "by")) {
6332 		c_token++;
6333 		rotate = int_expression();
6334 		if (this_label->tag == ROTATE_IN_3D_LABEL_TAG)
6335 		    this_label->tag = NONROTATING_LABEL_TAG;
6336 	    } else if (almost_equals(c_token,"para$llel")) {
6337 		if (this_label->tag >= 0)
6338 		    int_error(c_token,"invalid option");
6339 		c_token++;
6340 		this_label->tag = ROTATE_IN_3D_LABEL_TAG;
6341 	    } else if (almost_equals(c_token,"var$iable")) {
6342 		if (ndim == 2)	/* only in 2D plot with labels */
6343 		    this_label->tag = VARIABLE_ROTATE_LABEL_TAG;
6344 		else
6345 		    set_rot = FALSE;
6346 		c_token++;
6347 	    } else
6348 		rotate = TEXT_VERTICAL;
6349 	    continue;
6350 	} else if (almost_equals(c_token, "norot$ate")) {
6351 	    rotate = 0;
6352 	    c_token++;
6353 	    set_rot = TRUE;
6354 	    if (this_label->tag == ROTATE_IN_3D_LABEL_TAG)
6355 		this_label->tag = NONROTATING_LABEL_TAG;
6356 	    continue;
6357 	}
6358 
6359 	/* get font (added by DJL) */
6360 	if (! set_font && equals(c_token, "font")) {
6361 	    c_token++;
6362 	    if ((font = try_to_get_string())) {
6363 		set_font = TRUE;
6364 		continue;
6365 	    } else
6366 		int_error(c_token, "'fontname,fontsize' expected");
6367 	}
6368 
6369 	/* Flag this as hypertext rather than a normal label */
6370 	if (!set_hypertext && almost_equals(c_token,"hyper$text")) {
6371 	    c_token++;
6372 	    hypertext = TRUE;
6373 	    set_hypertext = TRUE;
6374 	    if (!set_point)
6375 		loc_lp = default_hypertext_point_style;
6376 	    continue;
6377 	} else if (!set_hypertext && almost_equals(c_token,"nohyper$text")) {
6378 	    c_token++;
6379 	    hypertext = FALSE;
6380 	    set_hypertext = TRUE;
6381 	    continue;
6382 	}
6383 
6384 	/* get front/back (added by JDP) */
6385 	if ((ndim == 0) && !set_layer && !axis_label) {
6386 	    if (equals(c_token, "back")) {
6387 		layer = LAYER_BACK;
6388 		c_token++;
6389 		set_layer = TRUE;
6390 		continue;
6391 	    } else if (equals(c_token, "front")) {
6392 		layer = LAYER_FRONT;
6393 		c_token++;
6394 		set_layer = TRUE;
6395 		continue;
6396 	    }
6397 	}
6398 
6399 #ifdef EAM_BOXED_TEXT
6400 	if (equals(c_token, "boxed")) {
6401 	    this_label->boxed = 1;
6402 	    c_token++;
6403 	    continue;
6404 	} else if (equals(c_token, "noboxed")) {
6405 	    this_label->boxed = 0;
6406 	    c_token++;
6407 	    continue;
6408 	}
6409 #endif
6410 
6411 	if (!axis_label && (loc_lp.flags == LP_NOT_INITIALIZED || set_hypertext)) {
6412 	    if (almost_equals(c_token, "po$int")) {
6413 		int stored_token = ++c_token;
6414 		struct lp_style_type tmp_lp;
6415 		loc_lp.flags = LP_SHOW_POINTS;
6416 		tmp_lp = loc_lp;
6417 		lp_parse(&tmp_lp, LP_ADHOC, TRUE);
6418 		if (stored_token != c_token)
6419 		    loc_lp = tmp_lp;
6420 		set_point = TRUE;
6421 		continue;
6422 	    } else if (almost_equals(c_token, "nopo$int")) {
6423 		loc_lp.flags = 0;
6424 		c_token++;
6425 		continue;
6426 	    }
6427 	}
6428 
6429 	if (! set_offset && almost_equals(c_token, "of$fset")) {
6430 	    c_token++;
6431 	    get_position_default(&offset, character, ndim);
6432 	    set_offset = TRUE;
6433 	    continue;
6434 	}
6435 
6436 	if ((equals(c_token,"tc") || almost_equals(c_token,"text$color"))
6437 	    && ! set_textcolor ) {
6438 	    parse_colorspec( &textcolor, TC_VARIABLE );
6439 	    set_textcolor = TRUE;
6440 	    continue;
6441 	}
6442 
6443 	if (almost_equals(c_token,"noenh$anced")) {
6444 	    this_label->noenhanced = TRUE;
6445 	    c_token++;
6446 	    continue;
6447 	} else if (almost_equals(c_token,"enh$anced")) {
6448 	    this_label->noenhanced = FALSE;
6449 	    c_token++;
6450 	    continue;
6451 	}
6452 
6453 	/* Coming here means that none of the previous 'if's struck
6454 	 * its "continue" statement, i.e.  whatever is in the command
6455 	 * line is forbidden by the 'set label' command syntax.
6456 	 * On the other hand, 'plot with labels' may have additional stuff coming up.
6457 	 */
6458 	break;
6459 
6460     } /* while(!END_OF_COMMAND) */
6461 
6462     /* HBB 20011120: this chunk moved here, behind the while()
6463      * loop. Only after all options have been parsed it's safe to
6464      * overwrite the position if none has been specified. */
6465     if (!set_position)
6466 	pos = default_position;
6467 
6468     /* OK! copy the requested options into the label */
6469 	if (set_position)
6470 	    this_label->place = pos;
6471 	if (set_just)
6472 	    this_label->pos = just;
6473 	if (set_rot)
6474 	    this_label->rotate = rotate;
6475 	if (set_layer)
6476 	    this_label->layer = layer;
6477 	if (set_font) {
6478 	    free(this_label->font);
6479 	    this_label->font = font;
6480 	}
6481 	if (set_textcolor)
6482 	    this_label->textcolor = textcolor;
6483 	if ((loc_lp.flags & LP_NOT_INITIALIZED) == 0)
6484 	    this_label->lp_properties = loc_lp;
6485 	if (set_offset)
6486 	    this_label->offset = offset;
6487 	if (set_hypertext)
6488 	    this_label->hypertext = hypertext;
6489 
6490     /* Make sure the z coord and the z-coloring agree */
6491     if (this_label->textcolor.type == TC_Z)
6492 	this_label->textcolor.value = this_label->place.z;
6493     if (this_label->lp_properties.pm3d_color.type == TC_Z)
6494 	this_label->lp_properties.pm3d_color.value = this_label->place.z;
6495 }
6496 
6497 
6498 /* <histogramstyle> = {clustered {gap <n>} | rowstacked | columnstacked */
6499 /*                     errorbars {gap <n>} {linewidth <lw>}}            */
6500 /*                    {title <title_options>}                           */
6501 static void
parse_histogramstyle(histogram_style * hs,t_histogram_type def_type,int def_gap)6502 parse_histogramstyle( histogram_style *hs,
6503 		t_histogram_type def_type,
6504 		int def_gap)
6505 {
6506     text_label title_specs = EMPTY_LABELSTRUCT;
6507 
6508     /* Set defaults */
6509     hs->type  = def_type;
6510     hs->gap   = def_gap;
6511 
6512     if (END_OF_COMMAND)
6513 	return;
6514     if (!equals(c_token,"hs") && !almost_equals(c_token,"hist$ogram"))
6515 	return;
6516     c_token++;
6517 
6518     while (!END_OF_COMMAND) {
6519 	if (almost_equals(c_token, "clust$ered")) {
6520 	    hs->type = HT_CLUSTERED;
6521 	    c_token++;
6522 	} else if (almost_equals(c_token, "error$bars")) {
6523 	    hs->type = HT_ERRORBARS;
6524 	    c_token++;
6525 	} else if (almost_equals(c_token, "rows$tacked")) {
6526 	    hs->type = HT_STACKED_IN_LAYERS;
6527 	    c_token++;
6528 	} else if (almost_equals(c_token, "columns$tacked")) {
6529 	    hs->type = HT_STACKED_IN_TOWERS;
6530 	    c_token++;
6531 	} else if (equals(c_token, "gap")) {
6532 	    if (isanumber(++c_token))
6533 		hs->gap = int_expression();
6534 	    else
6535 		int_error(c_token,"expected gap value");
6536 	} else if (almost_equals(c_token, "ti$tle")) {
6537 	    title_specs.offset = hs->title.offset;
6538 	    set_xyzlabel(&title_specs);
6539 	    free(title_specs.text);
6540 	    title_specs.text = NULL;
6541 	    if (hs->title.font) {
6542 		free(hs->title.font);
6543 		hs->title.font = NULL;
6544 	    }
6545 	    hs->title = title_specs;
6546 	} else if ((equals(c_token,"lw") || almost_equals(c_token,"linew$idth"))
6547 		  && (hs->type == HT_ERRORBARS)) {
6548 	    c_token++;
6549 	    hs->bar_lw = real_expression();
6550 	    if (hs->bar_lw <= 0)
6551 		hs->bar_lw = 1;
6552 	} else
6553 	    /* We hit something unexpected */
6554 	    break;
6555     }
6556 }
6557 
6558 /*
6559  * set pm3d lighting {primary <fraction>} {specular <fraction>}
6560  */
6561 static void
parse_lighting_options()6562 parse_lighting_options()
6563 {
6564     c_token++;
6565 
6566     /* TODO: Add separate "set" commands for these */
6567     pm3d_shade.ambient = 1.0;
6568     pm3d_shade.Phong = 5.0;	/* Phong exponent */
6569     pm3d_shade.rot_x = 45;	/* illumination angle */
6570     pm3d_shade.rot_z = -45;	/* illumination angle */
6571     pm3d_shade.fixed = TRUE;	/* TRUE means the light does not rotate */
6572 
6573     /* This is what you get from simply "set pm3d lighting" */
6574     pm3d_shade.strength = 0.5;	/* contribution of primary light source */
6575     pm3d_shade.spec = 0.2;	/* contribution of specular highlights */
6576 
6577     while (!END_OF_COMMAND) {
6578 	if (almost_equals(c_token,"primary")) {
6579 	    c_token++;
6580 	    pm3d_shade.strength = real_expression();
6581 	    pm3d_shade.strength = clip_to_01(pm3d_shade.strength);
6582 	    continue;
6583 	}
6584 
6585 	if (almost_equals(c_token,"spec$ular")) {
6586 	    c_token++;
6587 	    pm3d_shade.spec = real_expression();
6588 	    pm3d_shade.spec = clip_to_01(pm3d_shade.spec);
6589 	    continue;
6590 	}
6591 
6592 	break;
6593     }
6594 
6595     c_token--;
6596 }
6597 
6598 /* process 'set style parallelaxis' command */
6599 static void
set_style_parallel()6600 set_style_parallel()
6601 {
6602     c_token++;
6603     while (!END_OF_COMMAND) {
6604 	int save_token = c_token;
6605 	lp_parse( &parallel_axis_style.lp_properties,  LP_ADHOC, FALSE );
6606 	if (save_token != c_token)
6607 	    continue;
6608 	if (equals(c_token, "front"))
6609 	    parallel_axis_style.layer = LAYER_FRONT;
6610 	else if (equals(c_token, "back"))
6611 	    parallel_axis_style.layer = LAYER_BACK;
6612 	else
6613 	    int_error(c_token, "unrecognized option");
6614 	c_token++;
6615     }
6616 }
6617 
6618 /* Utility routine to propagate rrange into corresponding x and y ranges */
6619 void
rrange_to_xy()6620 rrange_to_xy()
6621 {
6622     double min;
6623 
6624     /* An inverted R axis makes no sense in most cases.
6625      * One reasonable use is to project altitude/azimuth spherical coordinates
6626      * so that the zenith (azimuth = 90) is in the center and the horizon
6627      * (azimuth = 0) is at the perimeter.
6628      */
6629     if (R_AXIS.set_min > R_AXIS.set_max) {
6630 	if (nonlinear(&R_AXIS))
6631 	    int_error(NO_CARET, "cannot invert nonlinear R axis");
6632 	inverted_raxis = TRUE;
6633     } else {
6634 	inverted_raxis = FALSE;
6635     }
6636 
6637     if (R_AXIS.set_autoscale & AUTOSCALE_MIN)
6638 	min = 0;
6639     else
6640 	min = R_AXIS.set_min;
6641     if (R_AXIS.set_autoscale & AUTOSCALE_MAX) {
6642 	X_AXIS.set_autoscale = AUTOSCALE_BOTH;
6643 	Y_AXIS.set_autoscale = AUTOSCALE_BOTH;
6644     } else {
6645 	X_AXIS.set_autoscale = AUTOSCALE_NONE;
6646 	Y_AXIS.set_autoscale = AUTOSCALE_NONE;
6647 	if (nonlinear(&R_AXIS))
6648 	    X_AXIS.set_max = eval_link_function(R_AXIS.linked_to_primary, R_AXIS.set_max)
6649 			   - eval_link_function(R_AXIS.linked_to_primary, min);
6650 	else if (R_AXIS.log)
6651 	    /* NB: Can't get here if "set log" is implemented as nonlinear */
6652 	    X_AXIS.set_max =  AXIS_DO_LOG(POLAR_AXIS, R_AXIS.set_max)
6653 			    - AXIS_DO_LOG(POLAR_AXIS, min);
6654 	else
6655 	    X_AXIS.set_max = fabs(R_AXIS.set_max - min);
6656 
6657 	Y_AXIS.set_max = X_AXIS.set_max;
6658 	Y_AXIS.set_min = X_AXIS.set_min = -X_AXIS.set_max;
6659     }
6660 }
6661 
6662