Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / set.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: set.c,v 1.236.2.20 2009/09/01 16:38:55 sfeam Exp $"); }
3 #endif
4
5 /* GNUPLOT - set.c */
6
7 /*[
8  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
9  *
10  * Permission to use, copy, and distribute this software and its
11  * documentation for any purpose with or without fee is hereby granted,
12  * provided that the above copyright notice appear in all copies and
13  * that both that copyright notice and this permission notice appear
14  * in supporting documentation.
15  *
16  * Permission to modify the software is granted, but not the right to
17  * distribute the complete modified source code.  Modifications are to
18  * be distributed as patches to the released version.  Permission to
19  * distribute binaries produced by compiling modified sources is granted,
20  * provided you
21  *   1. distribute the corresponding source modifications from the
22  *    released version in the form of a patch file along with the binaries,
23  *   2. add special version identification to distinguish your version
24  *    in addition to the base release version number,
25  *   3. provide your name and address as the primary contact for the
26  *    support of your modified version, and
27  *   4. retain our contact information in regard to use of the base
28  *    software.
29  * Permission to distribute the released version of the source code along
30  * with corresponding source modifications in the form of a patch file is
31  * granted with same provisions 2 through 4 for binary distributions.
32  *
33  * This software is provided "as is" without express or implied warranty
34  * to the extent permitted by applicable law.
35 ]*/
36
37
38 /*
39  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
40  * Added user-specified bases for log scaling.
41  */
42
43 #include "setshow.h"
44
45 #include "alloc.h"
46 #include "axis.h"
47 #include "command.h"
48 #include "contour.h"
49 #include "datafile.h"
50 #include "fit.h"
51 #include "gadgets.h"
52 #include "gp_hist.h"
53 #include "gp_time.h"
54 #include "hidden3d.h"
55 #include "misc.h"
56 /* #include "parse.h" */
57 #include "plot.h"
58 #include "plot2d.h"
59 #include "plot3d.h"
60 #include "tables.h"
61 #include "term_api.h"
62 #include "util.h"
63 #include "variable.h"
64 #include "pm3d.h"
65 #include "getcolor.h"
66 #include <ctype.h>
67
68 static palette_color_mode pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_NONE;
69
70 static void set_angles __PROTO((void));
71 static void set_arrow __PROTO((void));
72 static int assign_arrow_tag __PROTO((void));
73 static void set_autoscale __PROTO((void));
74 static void set_bars __PROTO((void));
75 static void set_border __PROTO((void));
76 static void set_boxwidth __PROTO((void));
77 static void set_clabel __PROTO((void));
78 static void set_clip __PROTO((void));
79 static void set_cntrparam __PROTO((void));
80 static void set_contour __PROTO((void));
81 static void set_dgrid3d __PROTO((void));
82 static void set_decimalsign __PROTO((void));
83 static void set_dummy __PROTO((void));
84 static void set_encoding __PROTO((void));
85 static void set_fit __PROTO((void));
86 static void set_format __PROTO((void));
87 static void set_grid __PROTO((void));
88 static void set_hidden3d __PROTO((void));
89 #ifdef GNUPLOT_HISTORY
90 static void set_historysize __PROTO((void));
91 #endif
92 static void set_isosamples __PROTO((void));
93 static void set_key __PROTO((void));
94 static void set_keytitle __PROTO((void));
95 static void set_label __PROTO((void));
96 static int assign_label_tag __PROTO((void));
97 static void set_loadpath __PROTO((void));
98 static void set_fontpath __PROTO((void));
99 static void set_locale __PROTO((void));
100 static void set_logscale __PROTO((void));
101 #ifdef GP_MACROS
102 static void set_macros __PROTO((void));
103 #endif
104 static void set_mapping __PROTO((void));
105 static void set_margin __PROTO((t_position *));
106 static void set_missing __PROTO((void));
107 static void set_separator __PROTO((void));
108 static void set_datafile_commentschars __PROTO((void));
109 #ifdef USE_MOUSE
110 static void set_mouse __PROTO((void));
111 #endif
112 static void set_offsets __PROTO((void));
113 static void set_origin __PROTO((void));
114 static void set_output __PROTO((void));
115 static void set_parametric __PROTO((void));
116 static void set_pm3d __PROTO((void));
117 static void set_palette __PROTO((void));
118 static void set_colorbox __PROTO((void));
119 static void set_pointsize __PROTO((void));
120 static void set_polar __PROTO((void));
121 static void set_print __PROTO((void));
122 #ifdef EAM_OBJECTS
123 static void set_object __PROTO((void));
124 static void set_rectangle __PROTO((int));
125 #endif
126 static void set_samples __PROTO((void));
127 static void set_size __PROTO((void));
128 static void set_style __PROTO((void));
129 static void set_surface __PROTO((void));
130 static void set_table __PROTO((void));
131 static void set_terminal __PROTO((void));
132 static void set_termoptions __PROTO((void));
133 static void set_tics __PROTO((void));
134 static void set_ticscale __PROTO((void));
135 static void set_ticslevel __PROTO((void));
136 static void set_timefmt __PROTO((void));
137 static void set_timestamp __PROTO((void));
138 static void set_view __PROTO((void));
139 static void set_zero __PROTO((void));
140 static void set_timedata __PROTO((AXIS_INDEX));
141 static void set_range __PROTO((AXIS_INDEX));
142 static void set_zeroaxis __PROTO((AXIS_INDEX));
143 static void set_allzeroaxis __PROTO((void));
144
145
146 /******** Local functions ********/
147
148 static void set_xyzlabel __PROTO((text_label * label));
149 static void load_tics __PROTO((AXIS_INDEX axis));
150 static void load_tic_user __PROTO((AXIS_INDEX axis));
151 static void load_tic_series __PROTO((AXIS_INDEX axis));
152 static void load_offsets __PROTO((double *a, double *b, double *c, double *d));
153
154 static void set_linestyle __PROTO((void));
155 static int assign_linestyle_tag __PROTO((void));
156 static void set_arrowstyle __PROTO((void));
157 static int assign_arrowstyle_tag __PROTO((void));
158 static int looks_like_numeric __PROTO((char *));
159 static int set_tic_prop __PROTO((AXIS_INDEX));
160 static char *fill_numbers_into_string __PROTO((char *pattern));
161
162 static void check_palette_grayscale __PROTO((void));
163 static int set_palette_defined __PROTO((void));
164 static void set_palette_file __PROTO((void));
165 static void set_palette_function __PROTO((void));
166 #ifdef EAM_HISTOGRAMS
167 static void parse_histogramstyle __PROTO((histogram_style *hs, 
168                 t_histogram_type def_type, int def_gap));
169 #endif
170
171 static struct position default_position
172         = {first_axes, first_axes, first_axes, 0., 0., 0.};
173
174 /* Backwards compatibility ... */
175 static void set_nolinestyle __PROTO((void));
176
177 /******** The 'set' command ********/
178 void
179 set_command()
180 {
181     static char GPFAR setmess[] =
182     "valid set options:  [] = choose one, {} means optional\n\n\
183 \t'angles', 'arrow', 'autoscale', 'bars', 'border', 'boxwidth',\n\
184 \t'clabel', 'clip', 'cntrparam', 'colorbox', 'contour', 'decimalsign',\n\
185 \t'dgrid3d', 'dummy', 'encoding', 'fit', 'format', 'grid',\n\
186 \t'hidden3d', 'historysize', 'isosamples', 'key', 'label', 'locale',\n\
187 \t'logscale', 'macros', '[blrt]margin', 'mapping', 'mouse', 'multiplot',\n\
188 \t'offsets', 'origin', 'output', 'palette', 'parametric', 'pm3d',\n\
189 \t'pointsize', 'polar', 'print', '[rtuv]range', 'samples', 'size',\n\
190 \t'style', 'surface', 'terminal', 'tics', 'ticscale', 'ticslevel',\n\
191 \t'timestamp', 'timefmt', 'title', 'view', 'xyplane', '[xyz]{2}data',\n\
192 \t'[xyz]{2}label', '[xyz]{2}range', '{no}{m}[xyz]{2}tics',\n\
193 \t'[xyz]{2}[md]tics', '{[xyz]{2}}zeroaxis', 'zero'";
194
195     c_token++;
196
197 #ifdef BACKWARDS_COMPATIBLE
198
199     /* retain backwards compatibility to the old syntax for now
200      * Oh, such ugliness ...
201      */
202
203     if (almost_equals(c_token,"da$ta")) {
204         if (interactive)
205             int_warn(c_token, "deprecated syntax, use \"set style data\"");
206         if (!almost_equals(++c_token,"s$tyle"))
207             int_error(c_token,"expecting keyword 'style'");
208         else
209             data_style = get_style();
210     } else if (almost_equals(c_token,"fu$nction")) {
211         if (interactive)
212             int_warn(c_token, "deprecated syntax, use \"set style function\"");
213         if (!almost_equals(++c_token,"s$tyle"))
214             int_error(c_token,"expecting keyword 'style'");
215         else {
216             enum PLOT_STYLE temp_style = get_style();
217
218             if (temp_style & PLOT_STYLE_HAS_ERRORBAR)
219                 int_error(c_token, "style not usable for function plots, left unchanged");
220 #ifdef EAM_HISTOGRAMS
221             else if (temp_style == HISTOGRAMS)
222                 int_error(c_token, "style not usable for function plots, left unchanged");
223 #endif
224             else
225                 func_style = temp_style;
226         }
227     } else if (almost_equals(c_token,"li$nestyle") || equals(c_token, "ls" )) {
228         if (interactive)
229             int_warn(c_token, "deprecated syntax, use \"set style line\"");
230         c_token++;
231         set_linestyle();
232     } else if (almost_equals(c_token,"noli$nestyle") || equals(c_token, "nols" )) {
233         c_token++;
234         set_nolinestyle();
235     } else if (gp_input_line[token[c_token].start_index] == 'n' &&
236                gp_input_line[token[c_token].start_index+1] == 'o') {
237         if (interactive)
238             int_warn(c_token, "deprecated syntax, use \"unset\"");
239         token[c_token].start_index += 2;
240         token[c_token].length -= 2;
241         c_token--;
242         unset_command();
243     } else if (almost_equals(c_token,"miss$ing")) {
244         if (interactive)
245             int_warn(c_token, "deprecated syntax, use \"set datafile missing\"");
246         set_missing();
247     } else {
248
249 #endif /* BACKWARDS_COMPATIBLE */
250
251         switch(lookup_table(&set_tbl[0],c_token)) {
252         case S_ANGLES:
253             set_angles();
254             break;
255         case S_ARROW:
256             set_arrow();
257             break;
258         case S_AUTOSCALE:
259             set_autoscale();
260             break;
261         case S_BARS:
262             set_bars();
263             break;
264         case S_BORDER:
265             set_border();
266             break;
267         case S_BOXWIDTH:
268             set_boxwidth();
269             break;
270         case S_CLABEL:
271             set_clabel();
272             break;
273         case S_CLIP:
274             set_clip();
275             break;
276         case S_CNTRPARAM:
277             set_cntrparam();
278             break;
279         case S_CONTOUR:
280             set_contour();
281             break;
282         case S_DGRID3D:
283             set_dgrid3d();
284             break;
285         case S_DECIMALSIGN:
286             set_decimalsign();
287             break;
288         case S_DUMMY:
289             set_dummy();
290             break;
291         case S_ENCODING:
292             set_encoding();
293             break;
294         case S_FIT:
295             set_fit();
296             break;
297         case S_FONTPATH:
298             set_fontpath();
299             break;
300         case S_FORMAT:
301             set_format();
302             break;
303         case S_GRID:
304             set_grid();
305             break;
306         case S_HIDDEN3D:
307             set_hidden3d();
308             break;
309         case S_HISTORYSIZE:
310 #ifdef GNUPLOT_HISTORY
311             set_historysize();
312 #else
313             int_error(c_token, "Command 'set historysize' requires history support.");
314 #endif
315             break;
316         case S_ISOSAMPLES:
317             set_isosamples();
318             break;
319         case S_KEY:
320             set_key();
321             break;
322         case S_KEYTITLE:
323             set_keytitle();
324             break;
325         case S_LABEL:
326             set_label();
327             break;
328         case S_LOADPATH:
329             set_loadpath();
330             break;
331         case S_LOCALE:
332             set_locale();
333             break;
334         case S_LOGSCALE:
335             set_logscale();
336             break;
337 #ifdef GP_MACROS
338         case S_MACROS:
339             set_macros();
340             break;
341 #endif
342         case S_MAPPING:
343             set_mapping();
344             break;
345         case S_BMARGIN:
346             set_margin(&bmargin);
347             break;
348         case S_LMARGIN:
349             set_margin(&lmargin);
350             break;
351         case S_RMARGIN:
352             set_margin(&rmargin);
353             break;
354         case S_TMARGIN:
355             set_margin(&tmargin);
356             break;
357         case S_DATAFILE:
358             if (almost_equals(++c_token,"miss$ing"))
359                 set_missing();
360             else if (almost_equals(c_token,"sep$arator"))
361                 set_separator();
362             else if (almost_equals(c_token,"com$mentschars"))
363                 set_datafile_commentschars();
364 #ifdef BINARY_DATA_FILE
365             else if (almost_equals(c_token,"bin$ary"))
366                 df_set_datafile_binary();
367 #endif
368             else if (almost_equals(c_token,"fort$ran")) {
369                 df_fortran_constants = TRUE;
370                 c_token++;
371             } else if (almost_equals(c_token,"nofort$ran")) {
372                 df_fortran_constants = FALSE;
373                 c_token++;
374             } else
375                 int_error(c_token,"expecting datafile modifier");
376             break;
377 #ifdef USE_MOUSE
378         case S_MOUSE:
379             set_mouse();
380             break;
381 #endif
382         case S_MULTIPLOT:
383             term_start_multiplot();
384             break;
385         case S_OFFSETS:
386             set_offsets();
387             break;
388         case S_ORIGIN:
389             set_origin();
390             break;
391         case SET_OUTPUT:
392             set_output();
393             break;
394         case S_PARAMETRIC:
395             set_parametric();
396             break;
397         case S_PM3D:
398             set_pm3d();
399             break;
400         case S_PALETTE:
401             set_palette();
402             break;
403         case S_COLORBOX:
404             set_colorbox();
405             break;
406         case S_POINTSIZE:
407             set_pointsize();
408             break;
409         case S_POLAR:
410             set_polar();
411             break;
412         case S_PRINT:
413             set_print();
414             break;
415 #ifdef EAM_OBJECTS
416         case S_OBJECT:
417             set_object();
418             break;
419 #endif
420         case S_SAMPLES:
421             set_samples();
422             break;
423         case S_SIZE:
424             set_size();
425             break;
426         case S_STYLE:
427             set_style();
428             break;
429         case S_SURFACE:
430             set_surface();
431             break;
432         case S_TABLE:
433             set_table();
434             break;
435         case S_TERMINAL:
436             set_terminal();
437             break;
438         case S_TERMOPTIONS:
439             set_termoptions();
440             break;
441         case S_TICS:
442             set_tics();
443             break;
444         case S_TICSCALE:
445             set_ticscale();
446             break;
447         case S_TICSLEVEL:
448             set_ticslevel();
449             break;
450         case S_TIMEFMT:
451             set_timefmt();
452             break;
453         case S_TIMESTAMP:
454             set_timestamp();
455             break;
456         case S_TITLE:
457             set_xyzlabel(&title);
458             break;
459         case S_VIEW:
460             set_view();
461             break;
462         case S_ZERO:
463             set_zero();
464             break;
465 /* FIXME */
466         case S_MXTICS:
467         case S_NOMXTICS:
468         case S_XTICS:
469         case S_NOXTICS:
470         case S_XDTICS:
471         case S_NOXDTICS:
472         case S_XMTICS:
473         case S_NOXMTICS:
474             set_tic_prop(FIRST_X_AXIS);
475             break;
476         case S_MYTICS:
477         case S_NOMYTICS:
478         case S_YTICS:
479         case S_NOYTICS:
480         case S_YDTICS:
481         case S_NOYDTICS:
482         case S_YMTICS:
483         case S_NOYMTICS:
484             set_tic_prop(FIRST_Y_AXIS);
485             break;
486         case S_MX2TICS:
487         case S_NOMX2TICS:
488         case S_X2TICS:
489         case S_NOX2TICS:
490         case S_X2DTICS:
491         case S_NOX2DTICS:
492         case S_X2MTICS:
493         case S_NOX2MTICS:
494             set_tic_prop(SECOND_X_AXIS);
495             break;
496         case S_MY2TICS:
497         case S_NOMY2TICS:
498         case S_Y2TICS:
499         case S_NOY2TICS:
500         case S_Y2DTICS:
501         case S_NOY2DTICS:
502         case S_Y2MTICS:
503         case S_NOY2MTICS:
504             set_tic_prop(SECOND_Y_AXIS);
505             break;
506         case S_MZTICS:
507         case S_NOMZTICS:
508         case S_ZTICS:
509         case S_NOZTICS:
510         case S_ZDTICS:
511         case S_NOZDTICS:
512         case S_ZMTICS:
513         case S_NOZMTICS:
514             set_tic_prop(FIRST_Z_AXIS);
515             break;
516         case S_MCBTICS:
517         case S_NOMCBTICS:
518         case S_CBTICS:
519         case S_NOCBTICS:
520         case S_CBDTICS:
521         case S_NOCBDTICS:
522         case S_CBMTICS:
523         case S_NOCBMTICS:
524             set_tic_prop(COLOR_AXIS);
525             break;
526         case S_XDATA:
527             set_timedata(FIRST_X_AXIS);
528             /* HBB 20000506: the old cod this this, too, although it
529              * serves no useful purpose, AFAICS */
530             /* HBB 20010201: Changed implementation to fix bug
531              * (c_token advanced too far) */
532             axis_array[T_AXIS].is_timedata
533               = axis_array[U_AXIS].is_timedata
534               = axis_array[FIRST_X_AXIS].is_timedata;
535             break;
536         case S_YDATA:
537             set_timedata(FIRST_Y_AXIS);
538             /* dito */
539             axis_array[V_AXIS].is_timedata
540               = axis_array[FIRST_X_AXIS].is_timedata;
541             break;
542         case S_ZDATA:
543             set_timedata(FIRST_Z_AXIS);
544             break;
545         case S_CBDATA:
546             set_timedata(COLOR_AXIS);
547             break;
548         case S_X2DATA:
549             set_timedata(SECOND_X_AXIS);
550             break;
551         case S_Y2DATA:
552             set_timedata(SECOND_Y_AXIS);
553             break;
554         case S_XLABEL:
555             set_xyzlabel(&axis_array[FIRST_X_AXIS].label);
556             break;
557         case S_YLABEL:
558             set_xyzlabel(&axis_array[FIRST_Y_AXIS].label);
559             break;
560         case S_ZLABEL:
561             set_xyzlabel(&axis_array[FIRST_Z_AXIS].label);
562             break;
563         case S_CBLABEL:
564             set_xyzlabel(&axis_array[COLOR_AXIS].label);
565             break;
566         case S_X2LABEL:
567             set_xyzlabel(&axis_array[SECOND_X_AXIS].label);
568             break;
569         case S_Y2LABEL:
570             set_xyzlabel(&axis_array[SECOND_Y_AXIS].label);
571             break;
572         case S_XRANGE:
573             set_range(FIRST_X_AXIS);
574             break;
575         case S_X2RANGE:
576             set_range(SECOND_X_AXIS);
577             break;
578         case S_YRANGE:
579             set_range(FIRST_Y_AXIS);
580             break;
581         case S_Y2RANGE:
582             set_range(SECOND_Y_AXIS);
583             break;
584         case S_ZRANGE:
585             set_range(FIRST_Z_AXIS);
586             break;
587         case S_CBRANGE:
588             set_range(COLOR_AXIS);
589             break;
590         case S_RRANGE:
591             set_range(R_AXIS);
592             break;
593         case S_TRANGE:
594             set_range(T_AXIS);
595             break;
596         case S_URANGE:
597             set_range(U_AXIS);
598             break;
599         case S_VRANGE:
600             set_range(V_AXIS);
601             break;
602         case S_XZEROAXIS:
603             set_zeroaxis(FIRST_X_AXIS);
604             break;
605         case S_YZEROAXIS:
606             set_zeroaxis(FIRST_Y_AXIS);
607             break;
608         case S_ZZEROAXIS:
609             set_zeroaxis(FIRST_Z_AXIS);
610             break;
611         case S_X2ZEROAXIS:
612             set_zeroaxis(SECOND_X_AXIS);
613             break;
614         case S_Y2ZEROAXIS:
615             set_zeroaxis(SECOND_Y_AXIS);
616             break;
617         case S_ZEROAXIS:
618             set_allzeroaxis();
619             break;
620         default:
621             int_error(c_token, setmess);
622             break;
623         }
624
625 #ifdef BACKWARDS_COMPATIBLE
626     }
627 #endif
628     update_gpval_variables(0); /* update GPVAL_ inner variables */
629
630 }
631
632
633 /* process 'set angles' command */
634 static void
635 set_angles()
636 {
637     c_token++;
638     if (END_OF_COMMAND) {
639         /* assuming same as defaults */
640         ang2rad = 1;
641     } else if (almost_equals(c_token, "r$adians")) {
642         c_token++;
643         ang2rad = 1;
644     } else if (almost_equals(c_token, "d$egrees")) {
645         c_token++;
646         ang2rad = DEG2RAD;
647     } else
648         int_error(c_token, "expecting 'radians' or 'degrees'");
649
650     if (polar && axis_array[T_AXIS].set_autoscale) {
651         /* set trange if in polar mode and no explicit range */
652         axis_array[T_AXIS].set_min = 0;
653         axis_array[T_AXIS].set_max = 2 * M_PI / ang2rad;
654     }
655 }
656
657
658 /* process a 'set arrow' command */
659 /* set arrow {tag} {from x,y} {to x,y} {{no}head} ... */
660 /* allow any order of options - pm 25.11.2001 */
661 static void
662 set_arrow()
663 {
664     struct arrow_def *this_arrow = NULL;
665     struct arrow_def *new_arrow = NULL;
666     struct arrow_def *prev_arrow = NULL;
667     TBOOLEAN duplication = FALSE;
668     TBOOLEAN set_start = FALSE;
669     TBOOLEAN set_end = FALSE;
670     struct value a;
671     int save_token;
672     int tag;
673     
674     c_token++;
675
676     /* get tag */
677     if (almost_equals(c_token, "back$head") || equals(c_token, "front")
678             || equals(c_token, "from") || equals(c_token, "size")
679             || equals(c_token, "to") || equals(c_token, "rto")
680             || equals(c_token, "filled") || equals(c_token, "empty")
681             || equals(c_token, "as") || equals(c_token, "arrowstyle")
682             || almost_equals(c_token, "head$s") || equals(c_token, "nohead")) {
683         tag = assign_arrow_tag();
684
685     } else
686         tag = (int) real(const_express(&a));
687
688     if (tag <= 0)
689         int_error(c_token, "tag must be > 0");
690
691     /* OK! add arrow */
692     if (first_arrow != NULL) {  /* skip to last arrow */
693         for (this_arrow = first_arrow; this_arrow != NULL;
694              prev_arrow = this_arrow, this_arrow = this_arrow->next)
695             /* is this the arrow we want? */
696             if (tag <= this_arrow->tag)
697                 break;
698     }
699     if (this_arrow == NULL || tag != this_arrow->tag) {
700         new_arrow = gp_alloc(sizeof(struct arrow_def), "arrow");
701         if (prev_arrow == NULL)
702             first_arrow = new_arrow;
703         else
704             prev_arrow->next = new_arrow;
705         new_arrow->tag = tag;
706         new_arrow->next = this_arrow;
707         this_arrow = new_arrow;
708
709         this_arrow->start = default_position;
710         this_arrow->end = default_position;
711
712         default_arrow_style(&(new_arrow->arrow_properties));
713     }
714
715     while (!END_OF_COMMAND) {
716
717         /* get start position */
718         if (equals(c_token, "from")) {
719             if (set_start) { duplication = TRUE; break; }
720             c_token++;
721             if (END_OF_COMMAND)
722                 int_error(c_token, "start coordinates expected");
723             /* get coordinates */
724             get_position(&this_arrow->start);
725             set_start = TRUE;
726             continue;
727         }
728
729         /* get end or relative end position */
730         if (equals(c_token, "to") || equals(c_token,"rto")) {
731             if (set_end) { duplication = TRUE; break; }
732             this_arrow->relative = (equals(c_token,"rto")) ? TRUE : FALSE;
733             c_token++;
734             if (END_OF_COMMAND)
735                 int_error(c_token, "end coordinates expected");
736             /* get coordinates */
737             get_position(&this_arrow->end);
738             set_end = TRUE;
739             continue;
740         }
741
742         /* Allow interspersed style commands */
743         save_token = c_token;
744         arrow_parse(&this_arrow->arrow_properties, TRUE);
745         if (save_token != c_token)
746             continue;
747
748         if (!END_OF_COMMAND)
749             int_error(c_token, "wrong argument in set arrow");
750
751     } /* while (!END_OF_COMMAND) */
752
753     if (duplication)
754         int_error(c_token, "duplicate or contradictory arguments");
755
756 }
757
758
759 /* assign a new arrow tag
760  * arrows are kept sorted by tag number, so this is easy
761  * returns the lowest unassigned tag number
762  */
763 static int
764 assign_arrow_tag()
765 {
766     struct arrow_def *this_arrow;
767     int last = 0;               /* previous tag value */
768
769     for (this_arrow = first_arrow; this_arrow != NULL;
770          this_arrow = this_arrow->next)
771         if (this_arrow->tag == last + 1)
772             last++;
773         else
774             break;
775
776     return (last + 1);
777 }
778
779
780 /* process 'set autoscale' command */
781 static void
782 set_autoscale()
783 {
784     char min_string[20], max_string[20];
785
786     c_token++;
787     if (END_OF_COMMAND) {
788         INIT_AXIS_ARRAY(set_autoscale , AUTOSCALE_BOTH);
789         return;
790     } else if (equals(c_token, "xy") || equals(c_token, "yx")) {
791         axis_array[FIRST_X_AXIS].set_autoscale =
792             axis_array[FIRST_Y_AXIS].set_autoscale =  AUTOSCALE_BOTH;
793         c_token++;
794         return;
795     } else if (equals(c_token, "fix")) {
796         int a = 0;
797         while (a < AXIS_ARRAY_SIZE) {
798             axis_array[a].set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX;
799             a++;
800         }
801         c_token++;
802         return;
803     } else if (almost_equals(c_token, "ke$epfix")) {
804         int a = 0;
805         while (a < AXIS_ARRAY_SIZE)
806             axis_array[a++].set_autoscale |= AUTOSCALE_BOTH;
807         c_token++;
808         return;
809     }
810
811     /* save on replication with a macro */
812 #define PROCESS_AUTO_LETTER(axis)                                       \
813     do {                                                                \
814         AXIS *this = axis_array + axis;                                 \
815                                                                         \
816         if (equals(c_token, axis_defaults[axis].name)) {                \
817             this->set_autoscale = AUTOSCALE_BOTH;                       \
818             ++c_token;                                                  \
819             return;                                                     \
820         }                                                               \
821         sprintf(min_string, "%smi$n", axis_defaults[axis].name);        \
822         if (almost_equals(c_token, min_string)) {                       \
823             this->set_autoscale |= AUTOSCALE_MIN;                       \
824             ++c_token;                                                  \
825             return;                                                     \
826         }                                                               \
827         sprintf(max_string, "%sma$x", axis_defaults[axis].name);        \
828         if (almost_equals(c_token, max_string)) {                       \
829             this->set_autoscale |= AUTOSCALE_MAX;                       \
830             ++c_token;                                                  \
831             return;                                                     \
832         }                                                               \
833         sprintf(min_string, "%sfix", axis_defaults[axis].name);         \
834         if (equals(c_token, min_string)) {                              \
835             this->set_autoscale |= AUTOSCALE_FIXMIN | AUTOSCALE_FIXMAX; \
836             ++c_token;                                                  \
837             return;                                                     \
838         }                                                               \
839         sprintf(min_string, "%sfixmi$n", axis_defaults[axis].name);     \
840         if (almost_equals(c_token, min_string)) {                       \
841             this->set_autoscale |= AUTOSCALE_FIXMIN;                    \
842             ++c_token;                                                  \
843             return;                                                     \
844         }                                                               \
845         sprintf(max_string, "%sfixma$x", axis_defaults[axis].name);     \
846         if (almost_equals(c_token, max_string)) {                       \
847             this->set_autoscale |= AUTOSCALE_FIXMAX;                    \
848             ++c_token;                                                  \
849             return;                                                     \
850         }                                                               \
851     } while(0)
852
853     PROCESS_AUTO_LETTER(R_AXIS);
854     PROCESS_AUTO_LETTER(T_AXIS);
855     PROCESS_AUTO_LETTER(U_AXIS);
856     PROCESS_AUTO_LETTER(V_AXIS);
857     PROCESS_AUTO_LETTER(FIRST_X_AXIS);
858     PROCESS_AUTO_LETTER(FIRST_Y_AXIS);
859     PROCESS_AUTO_LETTER(FIRST_Z_AXIS);
860     PROCESS_AUTO_LETTER(SECOND_X_AXIS);
861     PROCESS_AUTO_LETTER(SECOND_Y_AXIS);
862     PROCESS_AUTO_LETTER(COLOR_AXIS);
863     /* came here only if nothing found: */
864         int_error(c_token, "Invalid range");
865 }
866
867
868 /* process 'set bars' command */
869 static void
870 set_bars()
871 {
872     struct value a;
873
874     c_token++;
875     if(END_OF_COMMAND) {
876         bar_size = 1.0;
877     } else if(almost_equals(c_token,"s$mall")) {
878         bar_size = 0.0;
879         ++c_token;
880     } else if(almost_equals(c_token,"l$arge")) {
881         bar_size = 1.0;
882         ++c_token;
883     } else if(almost_equals(c_token,"full$width")) {
884         bar_size = -1.0;
885         ++c_token;
886     } else {
887         bar_size = real(const_express(&a));
888     }
889 }
890
891
892 /* process 'set border' command */
893 static void
894 set_border()
895 {
896     struct value a;
897
898     c_token++;
899     if(END_OF_COMMAND){
900         draw_border = 31;
901         border_layer = 1;
902         border_lp = default_border_lp;
903     }
904     
905     while (!END_OF_COMMAND) {
906         if (equals(c_token,"front")) {
907             border_layer = 1;
908             c_token++;
909         } else if (equals(c_token,"back")) {
910             border_layer = 0;
911             c_token++;
912         } else {
913             int save_token = c_token;
914             lp_parse(&border_lp, TRUE, FALSE);
915             if (save_token != c_token)
916                 continue;
917             draw_border = (int)real(const_express(&a));
918         }
919     }
920 }
921
922
923 /* process 'set boxwidth' command */
924 static void
925 set_boxwidth()
926 {
927     struct value a;
928
929     c_token++;
930     if (END_OF_COMMAND) {
931         boxwidth = -1.0;
932         boxwidth_is_absolute = TRUE;
933     } else {
934         boxwidth = real(const_express(&a));
935     }
936     if (END_OF_COMMAND)
937         return;
938     else {
939         if (almost_equals(c_token, "a$bsolute"))
940             boxwidth_is_absolute = TRUE;
941         else if (almost_equals(c_token, "r$elative"))
942             boxwidth_is_absolute = FALSE;
943         else
944             int_error(c_token, "expecting 'absolute' or 'relative' ");
945     }
946     c_token++;
947 }
948
949 /* process 'set clabel' command */
950 static void
951 set_clabel()
952 {
953     char *new_format;
954
955     c_token++;
956     label_contours = TRUE;
957     if ((new_format = try_to_get_string())) {
958         strncpy(contour_format, new_format, sizeof(contour_format));
959         free(new_format);
960     }
961 }
962
963
964 /* process 'set clip' command */
965 static void
966 set_clip()
967 {
968     c_token++;
969     if (END_OF_COMMAND)
970         /* assuming same as points */
971         clip_points = TRUE;
972     else if (almost_equals(c_token, "p$oints"))
973         clip_points = TRUE;
974     else if (almost_equals(c_token, "o$ne"))
975         clip_lines1 = TRUE;
976     else if (almost_equals(c_token, "t$wo"))
977         clip_lines2 = TRUE;
978     else
979         int_error(c_token, "expecting 'points', 'one', or 'two'");
980     c_token++;
981 }
982
983
984 /* process 'set cntrparam' command */
985 static void
986 set_cntrparam()
987 {
988     struct value a;
989
990     c_token++;
991     if (END_OF_COMMAND) {
992         /* assuming same as defaults */
993         contour_pts = DEFAULT_NUM_APPROX_PTS;
994         contour_kind = CONTOUR_KIND_LINEAR;
995         contour_order = DEFAULT_CONTOUR_ORDER;
996         contour_levels = DEFAULT_CONTOUR_LEVELS;
997         contour_levels_kind = LEVELS_AUTO;
998     } else if (almost_equals(c_token, "p$oints")) {
999         c_token++;
1000         contour_pts = (int) real(const_express(&a));
1001     } else if (almost_equals(c_token, "li$near")) {
1002         c_token++;
1003         contour_kind = CONTOUR_KIND_LINEAR;
1004     } else if (almost_equals(c_token, "c$ubicspline")) {
1005         c_token++;
1006         contour_kind = CONTOUR_KIND_CUBIC_SPL;
1007     } else if (almost_equals(c_token, "b$spline")) {
1008         c_token++;
1009         contour_kind = CONTOUR_KIND_BSPLINE;
1010     } else if (almost_equals(c_token, "le$vels")) {
1011         c_token++;
1012
1013         free_dynarray(&dyn_contour_levels_list);
1014         init_dynarray(&dyn_contour_levels_list, sizeof(double), 5, 10);
1015
1016         /*  RKC: I have modified the next two:
1017          *   to use commas to separate list elements as in xtics
1018          *   so that incremental lists start,incr[,end]as in "
1019          */
1020         if (almost_equals(c_token, "di$screte")) {
1021             contour_levels_kind = LEVELS_DISCRETE;
1022             c_token++;
1023             if(END_OF_COMMAND)
1024                 int_error(c_token, "expecting discrete level");
1025             else
1026                 *(double *)nextfrom_dynarray(&dyn_contour_levels_list) =
1027                     real(const_express(&a));
1028
1029             while(!END_OF_COMMAND) {
1030                 if (!equals(c_token, ","))
1031                     int_error(c_token,
1032                               "expecting comma to separate discrete levels");
1033                 c_token++;
1034                 *(double *)nextfrom_dynarray(&dyn_contour_levels_list) =
1035                     real(const_express(&a));
1036             }
1037             contour_levels = dyn_contour_levels_list.end;
1038         } else if (almost_equals(c_token, "in$cremental")) {
1039             int i = 0;  /* local counter */
1040
1041             contour_levels_kind = LEVELS_INCREMENTAL;
1042             c_token++;
1043             contour_levels_list[i++] = real(const_express(&a));
1044             if (!equals(c_token, ","))
1045                 int_error(c_token,
1046                           "expecting comma to separate start,incr levels");
1047             c_token++;
1048             if((contour_levels_list[i++] = real(const_express(&a))) == 0)
1049                 int_error(c_token, "increment cannot be 0");
1050             if(!END_OF_COMMAND) {
1051                 if (!equals(c_token, ","))
1052                     int_error(c_token,
1053                               "expecting comma to separate incr,stop levels");
1054                 c_token++;
1055                 /* need to round up, since 10,10,50 is 5 levels, not four,
1056                  * but 10,10,49 is four
1057                  */
1058                 dyn_contour_levels_list.end = i;
1059                 contour_levels = (int) ( (real(const_express(&a))-contour_levels_list[0])/contour_levels_list[1] + 1.0);
1060             }
1061         } else if (almost_equals(c_token, "au$to")) {
1062             contour_levels_kind = LEVELS_AUTO;
1063             c_token++;
1064             if(!END_OF_COMMAND)
1065                 contour_levels = (int) real(const_express(&a));
1066         } else {
1067             if(contour_levels_kind == LEVELS_DISCRETE)
1068                 int_error(c_token, "Levels type is discrete, ignoring new number of contour levels");
1069             contour_levels = (int) real(const_express(&a));
1070         }
1071     } else if (almost_equals(c_token, "o$rder")) {
1072         int order;
1073         c_token++;
1074         order = (int) real(const_express(&a));
1075         if ( order < 2 || order > MAX_BSPLINE_ORDER )
1076             int_error(c_token, "bspline order must be in [2..10] range.");
1077         contour_order = order;
1078     } else
1079         int_error(c_token, "expecting 'linear', 'cubicspline', 'bspline', 'points', 'levels' or 'order'");
1080 }
1081
1082
1083 /* process 'set contour' command */
1084 static void
1085 set_contour()
1086 {
1087     c_token++;
1088     if (END_OF_COMMAND)
1089         /* assuming same as points */
1090         draw_contour = CONTOUR_BASE;
1091     else {
1092         if (almost_equals(c_token, "ba$se"))
1093             draw_contour = CONTOUR_BASE;
1094         else if (almost_equals(c_token, "s$urface"))
1095             draw_contour = CONTOUR_SRF;
1096         else if (almost_equals(c_token, "bo$th"))
1097             draw_contour = CONTOUR_BOTH;
1098         else
1099             int_error(c_token, "expecting 'base', 'surface', or 'both'");
1100         c_token++;
1101     }
1102 }
1103
1104
1105 /* process 'set dgrid3d' command */
1106 static void
1107 set_dgrid3d()
1108 {
1109     struct value a;
1110     int local_vals[3];
1111     int i;
1112     TBOOLEAN was_comma = TRUE;
1113
1114     c_token++;
1115     local_vals[0] = dgrid3d_row_fineness;
1116     local_vals[1] = dgrid3d_col_fineness;
1117     local_vals[2] = dgrid3d_norm_value;
1118
1119     for (i = 0; i < 3 && !(END_OF_COMMAND);) {
1120         if (equals(c_token,",")) {
1121             if (was_comma) i++;
1122             was_comma = TRUE;
1123             c_token++;
1124         } else {
1125             if (!was_comma)
1126                 int_error(c_token, "',' expected");
1127             local_vals[i] = real(const_express(&a));
1128             i++;
1129             was_comma = FALSE;
1130         }
1131     }
1132
1133     if (local_vals[0] < 2 || local_vals[0] > 1000)
1134         int_error(c_token, "Row size must be in [2:1000] range; size unchanged");
1135     if (local_vals[1] < 2 || local_vals[1] > 1000)
1136         int_error(c_token, "Col size must be in [2:1000] range; size unchanged");
1137     if (local_vals[2] < 1 || local_vals[2] > 100)
1138         int_error(c_token, "Norm must be in [1:100] range; norm unchanged");
1139
1140     dgrid3d_row_fineness = local_vals[0];
1141     dgrid3d_col_fineness = local_vals[1];
1142     dgrid3d_norm_value = local_vals[2];
1143     dgrid3d = TRUE;
1144 }
1145
1146
1147 /* process 'set decimalsign' command */
1148 static void
1149 set_decimalsign()
1150 {
1151     c_token++;
1152
1153     /* Clear current setting */
1154         free(decimalsign);
1155         decimalsign=NULL;
1156
1157     if (END_OF_COMMAND) {
1158 #ifdef HAVE_LOCALE_H
1159         setlocale(LC_NUMERIC,"C");
1160     } else if (equals(c_token,"locale")) {
1161         char *newlocale = NULL;
1162         c_token++;
1163         newlocale = try_to_get_string();
1164         if (!newlocale)
1165             newlocale = gp_strdup(getenv("LC_ALL"));
1166         if (!newlocale)
1167             newlocale = gp_strdup(getenv("LC_NUMERIC"));
1168         if (!newlocale)
1169             newlocale = gp_strdup(getenv("LANG"));
1170         if (!setlocale(LC_NUMERIC, newlocale ? newlocale : ""))
1171             int_error(c_token-1, "Could not find requested locale");
1172         decimalsign = gp_strdup(localeconv()->decimal_point);
1173         fprintf(stderr,"decimal_sign in locale is %s\n", decimalsign);
1174         free(newlocale);
1175 #endif
1176     } else if (!(decimalsign = try_to_get_string()))
1177         int_error(c_token, "expecting string");
1178 }
1179
1180 /* process 'set dummy' command */
1181 static void
1182 set_dummy()
1183 {
1184     c_token++;
1185     if (END_OF_COMMAND)
1186         int_error(c_token, "expecting dummy variable name");
1187     else {
1188         if (!equals(c_token,","))
1189             copy_str(set_dummy_var[0],c_token++, MAX_ID_LEN);
1190         if (!END_OF_COMMAND && equals(c_token,",")) {
1191             c_token++;
1192             if (END_OF_COMMAND)
1193                 int_error(c_token, "expecting second dummy variable name");
1194             copy_str(set_dummy_var[1],c_token++, MAX_ID_LEN);
1195         }
1196     }
1197 }
1198
1199
1200 /* process 'set encoding' command */
1201 static void
1202 set_encoding()
1203 {
1204     int temp;
1205
1206     c_token++;
1207
1208     if(END_OF_COMMAND)
1209         temp = S_ENC_DEFAULT;
1210     else {
1211         temp = lookup_table(&set_encoding_tbl[0],c_token);
1212
1213         if (temp == S_ENC_INVALID)
1214             int_error(c_token, "expecting one of 'default', 'iso_8859_1', 'iso_8859_2', 'iso_8859_15', 'cp437', 'cp850', 'cp852', 'koi8r' or 'koi8u'");
1215         c_token++;
1216     }
1217     encoding = temp;
1218 }
1219
1220
1221 /* process 'set fit' command */
1222 static void
1223 set_fit()
1224 {
1225     c_token++;
1226
1227     while (!END_OF_COMMAND) {
1228         if (almost_equals(c_token, "log$file")) {
1229             c_token++;
1230             if (END_OF_COMMAND) {
1231                 if (fitlogfile != NULL)
1232                     free(fitlogfile);
1233                 fitlogfile=NULL;
1234             } else if (!(fitlogfile = try_to_get_string()))
1235                 int_error(c_token, "expecting string");
1236 #if GP_FIT_ERRVARS
1237         } else if (almost_equals(c_token, "err$orvariables")) {
1238             fit_errorvariables = TRUE;
1239             c_token++;
1240         } else if (almost_equals(c_token, "noerr$orvariables")) {
1241             fit_errorvariables = FALSE;
1242             c_token++;
1243 #endif /* GP_FIT_ERRVARS */
1244         } else {
1245             int_error(c_token,
1246                       "unknown --- expected 'logfile' or [no]errorvariables");
1247         }
1248     } /* while (!end) */
1249 }
1250
1251
1252 /* process 'set format' command */
1253 static void
1254 set_format()
1255 {
1256     TBOOLEAN set_for_axis[AXIS_ARRAY_SIZE] = AXIS_ARRAY_INITIALIZER(FALSE);
1257     int axis;
1258
1259     c_token++;
1260     if ((axis = lookup_table(axisname_tbl, c_token)) >= 0) {
1261         set_for_axis[axis] = TRUE;
1262         c_token++;
1263     } else if (equals(c_token,"xy") || equals(c_token,"yx")) {
1264         set_for_axis[FIRST_X_AXIS]
1265             = set_for_axis[FIRST_Y_AXIS]
1266             = TRUE;
1267         c_token++;
1268     } else {
1269         /* Assume he wants all */
1270         for (axis = 0; axis < AXIS_ARRAY_SIZE; axis++)
1271             set_for_axis[axis] = TRUE;
1272     }
1273
1274     if (END_OF_COMMAND) {
1275         SET_DEFFORMAT(FIRST_X_AXIS , set_for_axis);
1276         SET_DEFFORMAT(FIRST_Y_AXIS , set_for_axis);
1277         SET_DEFFORMAT(FIRST_Z_AXIS , set_for_axis);
1278         SET_DEFFORMAT(SECOND_X_AXIS, set_for_axis);
1279         SET_DEFFORMAT(SECOND_Y_AXIS, set_for_axis);
1280         SET_DEFFORMAT(COLOR_AXIS   , set_for_axis);
1281     } else {
1282         char *format = try_to_get_string();
1283         if (!format)
1284             int_error(c_token, "expecting format string");
1285         else {
1286
1287 #define SET_FORMATSTRING(axis)                                                  \
1288             if (set_for_axis[axis]) {                                           \
1289                 strncpy(axis_array[axis].formatstring, format, MAX_ID_LEN);     \
1290                 axis_array[axis].format_is_numeric = looks_like_numeric(format);\
1291             }
1292             SET_FORMATSTRING(FIRST_X_AXIS);
1293             SET_FORMATSTRING(FIRST_Y_AXIS);
1294             SET_FORMATSTRING(FIRST_Z_AXIS);
1295             SET_FORMATSTRING(SECOND_X_AXIS);
1296             SET_FORMATSTRING(SECOND_Y_AXIS);
1297             SET_FORMATSTRING(COLOR_AXIS);
1298 #undef SET_FORMATSTRING
1299
1300             free(format);
1301         }
1302     }
1303 }
1304
1305
1306 /* process 'set grid' command */
1307
1308 static void
1309 set_grid()
1310 {
1311     TBOOLEAN explicit_change = FALSE;
1312     c_token++;
1313 #define GRID_MATCH(axis, string)                                \
1314             if (almost_equals(c_token, string+2)) {             \
1315                 if (string[2] == 'm')                           \
1316                     axis_array[axis].gridminor = TRUE;          \
1317                 else                                            \
1318                     axis_array[axis].gridmajor = TRUE;          \
1319                 explicit_change = TRUE;                         \
1320                 ++c_token;                                      \
1321             } else if (almost_equals(c_token, string)) {        \
1322                 if (string[2] == 'm')                           \
1323                     axis_array[axis].gridminor = FALSE;         \
1324                 else                                            \
1325                     axis_array[axis].gridmajor = FALSE;         \
1326                 explicit_change = TRUE;                         \
1327                 ++c_token;                                      \
1328             }
1329     while (!END_OF_COMMAND) {
1330         GRID_MATCH(FIRST_X_AXIS, "nox$tics")
1331         else GRID_MATCH(FIRST_Y_AXIS, "noy$tics")
1332         else GRID_MATCH(FIRST_Z_AXIS, "noz$tics")
1333         else GRID_MATCH(SECOND_X_AXIS, "nox2$tics")
1334         else GRID_MATCH(SECOND_Y_AXIS, "noy2$tics")
1335         else GRID_MATCH(FIRST_X_AXIS, "nomx$tics")
1336         else GRID_MATCH(FIRST_Y_AXIS, "nomy$tics")
1337         else GRID_MATCH(FIRST_Z_AXIS, "nomz$tics")
1338         else GRID_MATCH(SECOND_X_AXIS, "nomx2$tics")
1339         else GRID_MATCH(SECOND_Y_AXIS, "nomy2$tics")
1340         else GRID_MATCH(COLOR_AXIS, "nocb$tics")
1341         else GRID_MATCH(COLOR_AXIS, "nomcb$tics")
1342         else if (almost_equals(c_token,"po$lar")) {
1343             if (!some_grid_selected()) {
1344                 /* grid_selection = GRID_X; */
1345                 axis_array[FIRST_X_AXIS].gridmajor = TRUE;
1346             }
1347             c_token++;
1348             if (END_OF_COMMAND) {
1349                 polar_grid_angle = 30*DEG2RAD;
1350             } else {
1351                 /* get radial interval */
1352                 struct value a;
1353                 polar_grid_angle = ang2rad*real(const_express(&a));
1354             }
1355         } else if (almost_equals(c_token,"nopo$lar")) {
1356             polar_grid_angle = 0; /* not polar grid */
1357             c_token++;
1358         } else if (equals(c_token,"back")) {
1359             grid_layer = 0;
1360             c_token++;
1361         } else if (equals(c_token,"front")) {
1362             grid_layer = 1;
1363             c_token++;
1364         } else if (almost_equals(c_token,"layerd$efault")) {
1365             grid_layer = -1;
1366             c_token++;
1367         } else
1368             break; /* might be a linetype */
1369     }
1370
1371     if (!END_OF_COMMAND) {
1372         struct value a;
1373         int old_token = c_token;
1374
1375         lp_parse(&grid_lp, TRUE, FALSE);
1376         if (c_token == old_token) { /* nothing parseable found... */
1377             grid_lp.l_type = real(const_express(&a)) - 1;
1378         }
1379
1380         /* probably just  set grid <linetype> */
1381
1382         if (END_OF_COMMAND) {
1383             memcpy(&mgrid_lp,&grid_lp,sizeof(struct lp_style_type));
1384         } else {
1385             if (equals(c_token,","))
1386                 c_token++;
1387             old_token = c_token;
1388             lp_parse(&mgrid_lp, TRUE, FALSE);
1389             if (c_token == old_token) {
1390                 mgrid_lp.l_type = real(const_express(&a)) -1;
1391             }
1392         }
1393     }
1394
1395     if (!explicit_change && !some_grid_selected()) {
1396         /* no axis specified, thus select default grid */
1397         axis_array[FIRST_X_AXIS].gridmajor = TRUE;
1398         axis_array[FIRST_Y_AXIS].gridmajor = TRUE;
1399     }
1400 }
1401
1402
1403 /* process 'set hidden3d' command */
1404 static void
1405 set_hidden3d()
1406 {
1407     c_token++;
1408 #ifdef LITE
1409     printf(" Hidden Line Removal Not Supported in LITE version\n");
1410 #else
1411     /* HBB 970618: new parsing engine for hidden3d options */
1412     set_hidden3doptions();
1413     hidden3d = TRUE;
1414 #endif
1415 }
1416
1417
1418 #ifdef GNUPLOT_HISTORY
1419 /* process 'set historysize' command */
1420 static void
1421 set_historysize()
1422 {
1423     struct value a;
1424
1425     c_token++;
1426
1427     gnuplot_history_size = (int) real(const_express(&a));
1428     if (gnuplot_history_size < 0) {
1429         gnuplot_history_size = 0;
1430     }
1431 }
1432 #endif
1433
1434
1435 /* process 'set isosamples' command */
1436 static void
1437 set_isosamples()
1438 {
1439     int tsamp1, tsamp2;
1440     struct value a;
1441
1442     c_token++;
1443     tsamp1 = (int)magnitude(const_express(&a));
1444     tsamp2 = tsamp1;
1445     if (!END_OF_COMMAND) {
1446         if (!equals(c_token,","))
1447             int_error(c_token, "',' expected");
1448         c_token++;
1449         tsamp2 = (int)magnitude(const_express(&a));
1450     }
1451     if (tsamp1 < 2 || tsamp2 < 2)
1452         int_error(c_token, "sampling rate must be > 1; sampling unchanged");
1453     else {
1454         struct curve_points *f_p = first_plot;
1455         struct surface_points *f_3dp = first_3dplot;
1456
1457         first_plot = NULL;
1458         first_3dplot = NULL;
1459         cp_free(f_p);
1460         sp_free(f_3dp);
1461
1462         iso_samples_1 = tsamp1;
1463         iso_samples_2 = tsamp2;
1464     }
1465 }
1466
1467
1468 /* When plotting an external key, the margin and l/r/t/b/c are
1469    used to determine one of twelve possible positions.  They must
1470    be defined appropriately in the case where stack direction
1471    determines exact position. */
1472 static void
1473 set_key_position_from_stack_direction(legend_key *key)
1474 {
1475     if (key->stack_dir == GPKEY_VERTICAL) {
1476         switch(key->hpos) {
1477         case LEFT:
1478             key->margin = GPKEY_LMARGIN;
1479             break;
1480         case CENTRE:
1481             if (key->vpos == JUST_TOP)
1482                 key->margin = GPKEY_TMARGIN;
1483             else
1484                 key->margin = GPKEY_BMARGIN;
1485             break;
1486         case RIGHT:
1487             key->margin = GPKEY_RMARGIN;
1488             break;
1489         }
1490     } else {
1491         switch(key->vpos) {
1492         case JUST_TOP:
1493             key->margin = GPKEY_TMARGIN;
1494             break;
1495         case JUST_CENTRE:
1496             if (key->hpos == LEFT)
1497                 key->margin = GPKEY_LMARGIN;
1498             else
1499                 key->margin = GPKEY_RMARGIN;
1500             break;
1501         case JUST_BOT:
1502             key->margin = GPKEY_BMARGIN;
1503             break;
1504         }
1505     }
1506 }
1507
1508
1509 /* process 'set key' command */
1510 static void
1511 set_key()
1512 {
1513     TBOOLEAN vpos_set = FALSE, hpos_set = FALSE, reg_set = FALSE, sdir_set = FALSE;
1514     char *vpos_warn = "Multiple vertical position settings";
1515     char *hpos_warn = "Multiple horizontal position settings";
1516     char *reg_warn = "Multiple location region settings";
1517     char *sdir_warn = "Multiple stack direction settings";
1518     struct value a;
1519     legend_key *key = &keyT;
1520
1521     c_token++;
1522     key->visible = TRUE;
1523
1524 #ifdef BACKWARDS_COMPATIBLE
1525     if (END_OF_COMMAND) {
1526         reset_key();
1527         key->title[0] = '\0';
1528         if (interactive)
1529             int_warn(c_token, "deprecated syntax, use \"set key default\"");
1530     }
1531 #endif
1532
1533     while (!END_OF_COMMAND) {
1534         switch(lookup_table(&set_key_tbl[0],c_token)) {
1535         case S_KEY_ON:
1536             key->visible = TRUE;
1537             break;
1538         case S_KEY_OFF:
1539             key->visible = FALSE;
1540             break;
1541         case S_KEY_DEFAULT:
1542             reset_key();
1543             key->title[0] = '\0';
1544             break;
1545         case S_KEY_TOP:
1546             if (vpos_set)
1547                 int_warn(c_token, vpos_warn);
1548             key->vpos = JUST_TOP;
1549             vpos_set = TRUE;
1550             break;
1551         case S_KEY_BOTTOM:
1552             if (vpos_set)
1553                 int_warn(c_token, vpos_warn);
1554             key->vpos = JUST_BOT;
1555             vpos_set = TRUE;
1556             break;
1557         case S_KEY_LEFT:
1558             if (hpos_set)
1559                 int_warn(c_token, hpos_warn);
1560             key->hpos = LEFT;
1561             hpos_set = TRUE;
1562             break;
1563         case S_KEY_RIGHT:
1564             if (hpos_set)
1565                 int_warn(c_token, hpos_warn);
1566             key->hpos = RIGHT;
1567             hpos_set = TRUE;
1568             break;
1569         case S_KEY_CENTER:
1570             if (!vpos_set) key->vpos = JUST_CENTRE;
1571             if (!hpos_set) key->hpos = CENTRE;
1572             if (vpos_set || hpos_set)
1573                 vpos_set = hpos_set = TRUE;
1574             break;
1575         case S_KEY_VERTICAL:
1576             if (sdir_set)
1577                 int_warn(c_token, sdir_warn);
1578             key->stack_dir = GPKEY_VERTICAL;
1579             sdir_set = TRUE;
1580             break;
1581         case S_KEY_HORIZONTAL:
1582             if (sdir_set)
1583                 int_warn(c_token, sdir_warn);
1584             key->stack_dir = GPKEY_HORIZONTAL;
1585             sdir_set = TRUE;
1586             break;
1587         case S_KEY_OVER:
1588             if (reg_set)
1589                 int_warn(c_token, reg_warn);
1590             /* Fall through */
1591         case S_KEY_ABOVE:
1592             if (!hpos_set)
1593                 key->hpos = CENTRE;
1594             if (!sdir_set)
1595                 key->stack_dir = GPKEY_HORIZONTAL;
1596             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1597             key->margin = GPKEY_TMARGIN;
1598             reg_set = TRUE;
1599             break;
1600         case S_KEY_UNDER:
1601             if (reg_set)
1602                 int_warn(c_token, reg_warn);
1603             /* Fall through */
1604         case S_KEY_BELOW:
1605             if (!hpos_set)
1606                 key->hpos = CENTRE;
1607             if (!sdir_set)
1608                 key->stack_dir = GPKEY_HORIZONTAL;
1609             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1610             key->margin = GPKEY_BMARGIN;
1611             reg_set = TRUE;
1612             break;
1613         case S_KEY_INSIDE:
1614             if (reg_set)
1615                 int_warn(c_token, reg_warn);
1616             key->region = GPKEY_AUTO_INTERIOR_LRTBC;
1617             reg_set = TRUE;
1618             break;
1619         case S_KEY_OUTSIDE:
1620 #ifdef BACKWARDS_COMPATIBLE
1621             if (!hpos_set)
1622                 key->hpos = RIGHT;
1623             if (!sdir_set)
1624                 key->stack_dir = GPKEY_VERTICAL;
1625 #endif
1626             if (reg_set)
1627                 int_warn(c_token, reg_warn);
1628             key->region = GPKEY_AUTO_EXTERIOR_LRTBC;
1629             reg_set = TRUE;
1630             break;
1631         case S_KEY_TMARGIN:
1632             if (reg_set)
1633                 int_warn(c_token, reg_warn);
1634             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1635             key->margin = GPKEY_TMARGIN;
1636             reg_set = TRUE;
1637             break;
1638         case S_KEY_BMARGIN:
1639             if (reg_set)
1640                 int_warn(c_token, reg_warn);
1641             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1642             key->margin = GPKEY_BMARGIN;
1643             reg_set = TRUE;
1644             break;
1645         case S_KEY_LMARGIN:
1646             if (reg_set)
1647                 int_warn(c_token, reg_warn);
1648             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1649             key->margin = GPKEY_LMARGIN;
1650             reg_set = TRUE;
1651             break;
1652         case S_KEY_RMARGIN:
1653             if (reg_set)
1654                 int_warn(c_token, reg_warn);
1655             key->region = GPKEY_AUTO_EXTERIOR_MARGIN;
1656             key->margin = GPKEY_RMARGIN;
1657             reg_set = TRUE;
1658             break;
1659         case S_KEY_LLEFT:
1660             key->just = GPKEY_LEFT;
1661             break;
1662         case S_KEY_RRIGHT:
1663             key->just = GPKEY_RIGHT;
1664             break;
1665         case S_KEY_REVERSE:
1666             key->reverse = TRUE;
1667             break;
1668         case S_KEY_NOREVERSE:
1669             key->reverse = FALSE;
1670             break;
1671         case S_KEY_INVERT:
1672             key->invert = TRUE;
1673             break;
1674         case S_KEY_NOINVERT:
1675             key->invert = FALSE;
1676             break;
1677         case S_KEY_ENHANCED:
1678             key->enhanced = TRUE;
1679             break;
1680         case S_KEY_NOENHANCED:
1681             key->enhanced = FALSE;
1682             break;
1683         case S_KEY_BOX:
1684             c_token++;
1685             key->box.l_type = LT_BLACK;
1686             if (!END_OF_COMMAND) {
1687                 int old_token = c_token;
1688                 lp_parse(&key->box, TRUE, FALSE);
1689                 if (old_token == c_token && isanumber(c_token)) {
1690                     key->box.l_type = real(const_express(&a)) - 1;
1691                     c_token++;
1692                 }
1693             }
1694             c_token--;  /* is incremented after loop */
1695             break;
1696         case S_KEY_NOBOX:
1697             key->box.l_type = LT_NODRAW;
1698             break;
1699         case S_KEY_SAMPLEN:
1700             c_token++;
1701             key->swidth = real(const_express(&a));
1702             c_token--; /* it is incremented after loop */
1703             break;
1704         case S_KEY_SPACING:
1705             c_token++;
1706             key->vert_factor = real(const_express(&a));
1707             if (key->vert_factor < 0.0)
1708                 key->vert_factor = 0.0;
1709             c_token--; /* it is incremented after loop */
1710             break;
1711         case S_KEY_WIDTH:
1712             c_token++;
1713             key->width_fix = real(const_express(&a));
1714             c_token--; /* it is incremented after loop */
1715             break;
1716         case S_KEY_HEIGHT:
1717             c_token++;
1718             key->height_fix = real(const_express(&a));
1719             c_token--; /* it is incremented after loop */
1720             break;
1721         case S_KEY_AUTOTITLES:
1722             if (almost_equals(++c_token, "col$umnheader"))
1723                 key->auto_titles = COLUMNHEAD_KEYTITLES;
1724             else {
1725                 key->auto_titles = FILENAME_KEYTITLES;
1726                 c_token--;
1727             }
1728             break;
1729         case S_KEY_NOAUTOTITLES:
1730             key->auto_titles = NOAUTO_KEYTITLES;
1731             break;
1732         case S_KEY_TITLE:
1733             {
1734                 char *s;
1735                 c_token++;
1736                 if ((s = try_to_get_string())) {
1737                     strncpy(key->title,s,sizeof(key->title));
1738                     free(s);
1739                 } else
1740                     key->title[0] = '\0';
1741                 c_token--;
1742             }
1743             break;
1744         case S_KEY_MANUAL:
1745             c_token++;
1746 #ifdef BACKWARDS_COMPATIBLE
1747         case S_KEY_INVALID:
1748         default:
1749 #endif
1750             if (reg_set)
1751                 int_warn(c_token, reg_warn);
1752             get_position(&key->user_pos);
1753             key->region = GPKEY_USER_PLACEMENT;
1754             reg_set = TRUE;
1755             c_token--;  /* will be incremented again soon */
1756             break;
1757 #ifndef BACKWARDS_COMPATIBLE
1758         case S_KEY_INVALID:
1759         default:
1760             int_error(c_token, "unknown key option");
1761             break;
1762 #endif
1763         }
1764         c_token++;
1765     }
1766
1767     if (key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
1768         set_key_position_from_stack_direction(key);
1769     else if (key->region == GPKEY_AUTO_EXTERIOR_MARGIN) {
1770         if (vpos_set && (key->margin == GPKEY_TMARGIN || key->margin == GPKEY_BMARGIN))
1771             int_warn(NO_CARET,
1772                      "ignoring top/center/bottom; incompatible with tmargin/bmargin.");
1773         else if (hpos_set && (key->margin == GPKEY_LMARGIN || key->margin == GPKEY_RMARGIN))
1774             int_warn(NO_CARET,
1775                      "ignoring left/center/right; incompatible with lmargin/tmargin.");
1776     }
1777 }
1778
1779
1780 /* process 'set keytitle' command */
1781 static void
1782 set_keytitle()
1783 {
1784     legend_key *key = &keyT;
1785
1786     c_token++;
1787     if (END_OF_COMMAND) {       /* set to default */
1788         key->title[0] = NUL;
1789     } else {
1790         char *s;
1791         if ((s = try_to_get_string())) {
1792             strncpy(key->title,s,sizeof(key->title));
1793             free(s);
1794         }
1795     }
1796 }
1797
1798 /* process 'set label' command */
1799 /* set label {tag} {"label_text"{,<value>{,...}}} {<label options>} */
1800 /* EAM Mar 2003 - option parsing broken out into separate routine */
1801 static void
1802 set_label()
1803 {
1804     struct text_label *this_label = NULL;
1805     struct text_label *new_label = NULL;
1806     struct text_label *prev_label = NULL;
1807     struct value a;
1808     int tag;
1809
1810     c_token++;
1811     
1812     /* get tag */
1813     if (!END_OF_COMMAND
1814         /* FIXME - Are these tests really still needed? */
1815         && !isstringvalue(c_token)
1816         && !equals(c_token, "at")
1817         && !equals(c_token, "left")
1818         && !equals(c_token, "center")
1819         && !equals(c_token, "centre")
1820         && !equals(c_token, "right")
1821         && !equals(c_token, "front")
1822         && !equals(c_token, "back")
1823         && !almost_equals(c_token, "rot$ate")
1824         && !almost_equals(c_token, "norot$ate")
1825         && !equals(c_token, "lt")
1826         && !almost_equals(c_token, "linet$ype")
1827         && !equals(c_token, "pt")
1828         && !almost_equals(c_token, "pointt$ype")
1829         && !equals(c_token, "tc")
1830         && !almost_equals(c_token, "text$color")
1831         && !equals(c_token, "font")) {
1832
1833         /* must be an expression, but is it a tag or is it the label itself? */
1834 #ifdef GP_STRING_VARS
1835         int save_token = c_token;
1836         const_express(&a);
1837         if (a.type == STRING) {
1838             c_token = save_token;
1839             tag = assign_label_tag();
1840             gpfree_string(&a);
1841         } else
1842             tag = (int) real(&a);
1843 #else
1844         tag = (int) real(const_express(&a));
1845 #endif
1846
1847     } else
1848         tag = assign_label_tag();       /* default next tag */
1849
1850     if (tag <= 0)
1851         int_error(c_token, "tag must be > zero");
1852
1853     if (first_label != NULL) {  /* skip to last label */
1854         for (this_label = first_label; this_label != NULL;
1855              prev_label = this_label, this_label = this_label->next)
1856             /* is this the label we want? */
1857             if (tag <= this_label->tag)
1858                 break;
1859     }
1860     /* Insert this label into the list if it is a new one */
1861     if (this_label == NULL || tag != this_label->tag) {
1862         struct position default_offset = { character, character, character, 
1863                                            0., 0., 0. };
1864         new_label = new_text_label(tag);
1865         new_label->offset = default_offset;
1866         if (prev_label == NULL)
1867             first_label = new_label;
1868         else
1869             prev_label->next = new_label;
1870         new_label->next = this_label;
1871         this_label = new_label;
1872     }
1873
1874 #ifdef GP_STRING_VARS
1875     if (!END_OF_COMMAND) {
1876         char* text;
1877         parse_label_options( this_label );
1878         text = try_to_get_string();
1879         if (text) {
1880             free(this_label->text);
1881             this_label->text = text;
1882         }
1883 #else
1884     /* get text from string */
1885     if (!END_OF_COMMAND && isstring(c_token)) {
1886         char *text = gp_alloc (token_len(c_token), "text_label->text");
1887         quote_str(text, c_token, token_len(c_token));
1888         c_token++;
1889         this_label->text = text;
1890 #endif
1891         /* HBB 20001021: new functionality. If next token is a ','
1892          * treat it as a numeric expression whose value is to be
1893          * sprintf()ed into the label string (which contains an
1894          * appropriate %f format string) */
1895         /* EAM Oct 2004 - this is superseded by general string variable
1896          * handling, but left in for backward compatibility */
1897         if (!END_OF_COMMAND && equals(c_token, ","))
1898             this_label->text = fill_numbers_into_string(this_label->text);
1899     }
1900
1901     /* Now parse the label format and style options */
1902     parse_label_options( this_label );
1903 }
1904
1905
1906 /* assign a new label tag
1907  * labels are kept sorted by tag number, so this is easy
1908  * returns the lowest unassigned tag number
1909  */
1910 static int
1911 assign_label_tag()
1912 {
1913     struct text_label *this_label;
1914     int last = 0;               /* previous tag value */
1915
1916     for (this_label = first_label; this_label != NULL;
1917          this_label = this_label->next)
1918         if (this_label->tag == last + 1)
1919             last++;
1920         else
1921             break;
1922
1923     return (last + 1);
1924 }
1925
1926
1927 /* process 'set loadpath' command */
1928 static void
1929 set_loadpath()
1930 {
1931     /* We pick up all loadpath elements here before passing
1932      * them on to set_var_loadpath()
1933      */
1934     char *collect = NULL;
1935
1936     c_token++;
1937     if (END_OF_COMMAND) {
1938         clear_loadpath();
1939     } else while (!END_OF_COMMAND) {
1940         if (isstring(c_token)) {
1941             int len;
1942             char *ss = gp_alloc(token_len(c_token), "tmp storage");
1943             len = (collect? strlen(collect) : 0);
1944             quote_str(ss,c_token,token_len(c_token));
1945             collect = gp_realloc(collect, len+1+strlen(ss)+1, "tmp loadpath");
1946             if (len != 0) {
1947                 strcpy(collect+len+1,ss);
1948                 *(collect+len) = PATHSEP;
1949             }
1950             else
1951                 strcpy(collect,ss);
1952             free(ss);
1953             ++c_token;
1954         } else {
1955             int_error(c_token, "expected string");
1956         }
1957     }
1958     if (collect) {
1959         set_var_loadpath(collect);
1960         free(collect);
1961     }
1962 }
1963
1964
1965 /* process 'set fontpath' command */
1966 static void
1967 set_fontpath()
1968 {
1969     /* We pick up all fontpath elements here before passing
1970      * them on to set_var_fontpath()
1971      */
1972     char *collect = NULL;
1973
1974     c_token++;
1975     if (END_OF_COMMAND) {
1976         clear_fontpath();
1977     } else while (!END_OF_COMMAND) {
1978         if (isstring(c_token)) {
1979             int len;
1980             char *ss = gp_alloc(token_len(c_token), "tmp storage");
1981             len = (collect? strlen(collect) : 0);
1982             quote_str(ss,c_token,token_len(c_token));
1983             collect = gp_realloc(collect, len+1+strlen(ss)+1, "tmp fontpath");
1984             if (len != 0) {
1985                 strcpy(collect+len+1,ss);
1986                 *(collect+len) = PATHSEP;
1987             }
1988             else
1989                 strcpy(collect,ss);
1990             free(ss);
1991             ++c_token;
1992         } else {
1993             int_error(c_token, "expected string");
1994         }
1995     }
1996     if (collect) {
1997         set_var_fontpath(collect);
1998         free(collect);
1999     }
2000 }
2001
2002
2003 /* process 'set locale' command */
2004 static void
2005 set_locale()
2006 {
2007     char *s;
2008
2009     c_token++;
2010     if (END_OF_COMMAND) {
2011         init_locale();
2012     } else if ((s = try_to_get_string())) {
2013         set_var_locale(s);
2014         free(s);
2015     } else
2016         int_error(c_token, "expected string");
2017 }
2018
2019
2020 /* process 'set logscale' command */
2021 static void
2022 set_logscale()
2023 {
2024     c_token++;
2025     if (END_OF_COMMAND) {
2026         INIT_AXIS_ARRAY(log,TRUE);
2027         INIT_AXIS_ARRAY(base, 10.0);
2028     } else {
2029         TBOOLEAN set_for_axis[AXIS_ARRAY_SIZE] = AXIS_ARRAY_INITIALIZER(FALSE);
2030         int axis;
2031         double newbase = 10;
2032
2033         /* do reverse search because of "x", "x1", "x2" sequence in axisname_tbl */
2034         int i = 0;
2035         while (i < token[c_token].length) {
2036             axis = lookup_table_nth_reverse(axisname_tbl, AXIS_ARRAY_SIZE,
2037                        gp_input_line + token[c_token].start_index + i);
2038             if (axis < 0) {
2039                 token[c_token].start_index += i;
2040                 int_error(c_token, "unknown axis");
2041             }
2042             set_for_axis[axisname_tbl[axis].value] = TRUE;
2043             i += strlen(axisname_tbl[axis].key);
2044         }
2045         c_token++;
2046
2047         if (!END_OF_COMMAND) {
2048             struct value a;
2049             newbase = magnitude(const_express(&a));
2050             if (newbase < 1.1)
2051                 int_error(c_token,
2052                           "log base must be >= 1.1; logscale unchanged");
2053         }
2054
2055         for (axis = 0; axis < AXIS_ARRAY_SIZE; axis++)
2056             if (set_for_axis[axis]) {
2057                 axis_array[axis].log = TRUE;
2058                 axis_array[axis].base = newbase;
2059         }
2060     }
2061 }
2062
2063 #ifdef GP_MACROS
2064 static void
2065 set_macros()
2066 {
2067    c_token++;
2068    expand_macros = TRUE;
2069 }
2070 #endif
2071
2072 /* process 'set mapping3d' command */
2073 static void
2074 set_mapping()
2075 {
2076     c_token++;
2077     if (END_OF_COMMAND)
2078         /* assuming same as points */
2079         mapping3d = MAP3D_CARTESIAN;
2080     else if (almost_equals(c_token, "ca$rtesian"))
2081         mapping3d = MAP3D_CARTESIAN;
2082     else if (almost_equals(c_token, "s$pherical"))
2083         mapping3d = MAP3D_SPHERICAL;
2084     else if (almost_equals(c_token, "cy$lindrical"))
2085         mapping3d = MAP3D_CYLINDRICAL;
2086     else
2087         int_error(c_token,
2088                   "expecting 'cartesian', 'spherical', or 'cylindrical'");
2089         c_token++;
2090 }
2091
2092
2093 /* process 'set {blrt}margin' command */
2094 static void
2095 set_margin(t_position *margin)
2096 {
2097     struct value a;
2098
2099     margin->scalex = character;
2100     margin->x = -1;
2101     c_token++;
2102
2103     if (END_OF_COMMAND)
2104         return;
2105
2106     if (equals(c_token,"at") && !almost_equals(++c_token,"sc$reen"))
2107         int_error(c_token,"expecting 'screen <fraction>'");
2108     if (almost_equals(c_token,"sc$reen")) {
2109         margin->scalex = screen;
2110         c_token++;
2111     }
2112
2113     margin->x = real(const_express(&a));
2114     if (margin->x < 0)
2115         margin->x = -1;
2116 }
2117
2118 static void
2119 set_separator()
2120 {
2121     c_token++;
2122     if (END_OF_COMMAND) {
2123         df_separator = '\0';
2124         return;
2125     }
2126     if (almost_equals(c_token, "white$space"))
2127         df_separator = '\0';
2128     else if (!isstring(c_token))
2129         int_error(c_token, "expected \"<separator_char>\"");
2130     else if (equals(c_token, "\"\\t\"") || equals(c_token, "\'\\t\'"))
2131         df_separator = '\t';
2132     else if (gp_input_line[token[c_token].start_index]
2133              != gp_input_line[token[c_token].start_index + 2])
2134         int_error(c_token, "extra chars after <separation_char>");
2135     else
2136         df_separator = gp_input_line[token[c_token].start_index + 1];
2137     c_token++;
2138 }
2139
2140 static void
2141 set_datafile_commentschars()
2142 {
2143     char *s;
2144
2145     c_token++;
2146         
2147     if (END_OF_COMMAND) {
2148         free(df_commentschars);
2149         df_commentschars = gp_strdup(DEFAULT_COMMENTS_CHARS);
2150     } else if ((s = try_to_get_string())) {
2151         free(df_commentschars);
2152         df_commentschars = s;
2153     } else /* Leave it the way it was */
2154         int_error(c_token, "expected string with comments chars");
2155 }
2156
2157 /* process 'set missing' command */
2158 static void
2159 set_missing()
2160 {
2161     c_token++;
2162     if (END_OF_COMMAND) {
2163         free(missing_val);
2164         missing_val = NULL;
2165     } else if (!(missing_val = try_to_get_string()))
2166         int_error(c_token, "expected missing-value string");
2167 }
2168
2169 #ifdef USE_MOUSE
2170 static void
2171 set_mouse()
2172 {
2173     c_token++;
2174     mouse_setting.on = 1;
2175
2176     while (!END_OF_COMMAND) {
2177         if (almost_equals(c_token, "do$ubleclick")) {
2178             struct value a;
2179             ++c_token;
2180             mouse_setting.doubleclick = real(const_express(&a));
2181             if (mouse_setting.doubleclick < 0)
2182                 mouse_setting.doubleclick = 0;
2183         } else if (almost_equals(c_token, "nodo$ubleclick")) {
2184             mouse_setting.doubleclick = 0; /* double click off */
2185             ++c_token;
2186         } else if (almost_equals(c_token, "zoomco$ordinates")) {
2187             mouse_setting.annotate_zoom_box = 1;
2188             ++c_token;
2189         } else if (almost_equals(c_token, "nozoomco$ordinates")) {
2190             mouse_setting.annotate_zoom_box = 0;
2191             ++c_token;
2192         } else if (almost_equals(c_token, "po$lardistancedeg")) {
2193             mouse_setting.polardistance = 1;
2194             UpdateStatusline();
2195             ++c_token;
2196         } else if (almost_equals(c_token, "polardistancet$an")) {
2197             mouse_setting.polardistance = 2;
2198             UpdateStatusline();
2199             ++c_token;
2200         } else if (almost_equals(c_token, "nopo$lardistance")) {
2201             mouse_setting.polardistance = 0;
2202             UpdateStatusline();
2203             ++c_token;
2204         } else if (equals(c_token, "labels")) {
2205             mouse_setting.label = 1;
2206             ++c_token;
2207             /* check if the optional argument "<label options>" is present */
2208             if (isstring(c_token)) {
2209                 if (token_len(c_token) >= sizeof(mouse_setting.labelopts)) {
2210                     int_error(c_token, "option string too long");
2211                 } else {
2212                     quote_str(mouse_setting.labelopts,
2213                         c_token, token_len(c_token));
2214                 }
2215                 ++c_token;
2216             }
2217         } else if (almost_equals(c_token, "nola$bels")) {
2218             mouse_setting.label = 0;
2219             ++c_token;
2220         } else if (almost_equals(c_token, "ve$rbose")) {
2221             mouse_setting.verbose = 1;
2222             ++c_token;
2223         } else if (almost_equals(c_token, "nove$rbose")) {
2224             mouse_setting.verbose = 0;
2225             ++c_token;
2226         } else if (almost_equals(c_token, "zoomju$mp")) {
2227             mouse_setting.warp_pointer = 1;
2228             ++c_token;
2229         } else if (almost_equals(c_token, "nozoomju$mp")) {
2230             mouse_setting.warp_pointer = 0;
2231             ++c_token;
2232         } else if (almost_equals(c_token, "fo$rmat")) {
2233             ++c_token;
2234             if (isstring(c_token)) {
2235                 if (token_len(c_token) >= sizeof(mouse_setting.fmt)) {
2236                     int_error(c_token, "format string too long");
2237                 } else {
2238                     quote_str(mouse_setting.fmt, c_token, token_len(c_token));
2239                 }
2240             } else {
2241                 int_error(c_token, "expecting string format");
2242             }
2243             ++c_token;
2244         } else if (almost_equals(c_token, "cl$ipboardformat")) {
2245             ++c_token;
2246             if (isstring(c_token)) {
2247                 if (clipboard_alt_string)
2248                     free(clipboard_alt_string);
2249                 clipboard_alt_string = gp_alloc
2250                     (token_len(c_token), "set->mouse->clipboardformat");
2251                 quote_str(clipboard_alt_string, c_token, token_len(c_token));
2252                 if (clipboard_alt_string && !strlen(clipboard_alt_string)) {
2253                     free(clipboard_alt_string);
2254                     clipboard_alt_string = (char*) 0;
2255                     if (MOUSE_COORDINATES_ALT == mouse_mode) {
2256                         mouse_mode = MOUSE_COORDINATES_REAL;
2257                     }
2258                 } else {
2259                     clipboard_mode = MOUSE_COORDINATES_ALT;
2260                 }
2261                 c_token++;
2262             } else {
2263                 struct value a;
2264                 int itmp = (int) real(const_express(&a));
2265                 if (itmp >= MOUSE_COORDINATES_REAL
2266                     && itmp <= MOUSE_COORDINATES_XDATETIME) {
2267                     if (MOUSE_COORDINATES_ALT == itmp
2268                         && !clipboard_alt_string) {
2269                         fprintf(stderr,
2270                             "please 'set mouse clipboard <fmt>' first.\n");
2271                     } else {
2272                         clipboard_mode = itmp;
2273                     }
2274                 } else {
2275                     fprintf(stderr, "should be: %d <= clipboardformat <= %d\n",
2276                         MOUSE_COORDINATES_REAL, MOUSE_COORDINATES_XDATETIME);
2277                 }
2278             }
2279         } else if (almost_equals(c_token, "mo$useformat")) {
2280             ++c_token;
2281             if (isstring(c_token)) {
2282                 if (mouse_alt_string)
2283                     free(mouse_alt_string);
2284                 mouse_alt_string = gp_alloc
2285                     (token_len(c_token), "set->mouse->mouseformat");
2286                 quote_str(mouse_alt_string, c_token, token_len(c_token));
2287                 if (mouse_alt_string && !strlen(mouse_alt_string)) {
2288                     free(mouse_alt_string);
2289                     mouse_alt_string = (char*) 0;
2290                     if (MOUSE_COORDINATES_ALT == mouse_mode) {
2291                         mouse_mode = MOUSE_COORDINATES_REAL;
2292                     }
2293                 } else {
2294                     mouse_mode = MOUSE_COORDINATES_ALT;
2295                 }
2296                 c_token++;
2297             } else {
2298                 struct value a;
2299                 int itmp = (int) real(const_express(&a));
2300                 if (itmp >= MOUSE_COORDINATES_REAL
2301                     && itmp <= MOUSE_COORDINATES_XDATETIME) {
2302                     if (MOUSE_COORDINATES_ALT == itmp && !mouse_alt_string) {
2303                         fprintf(stderr,
2304                             "please 'set mouse mouseformat <fmt>' first.\n");
2305                     } else {
2306                         mouse_mode = itmp;
2307                     }
2308                 } else {
2309                     fprintf(stderr, "should be: %d <= mouseformat <= %d\n",
2310                         MOUSE_COORDINATES_REAL, MOUSE_COORDINATES_XDATETIME);
2311                 }
2312             }
2313         } else if (almost_equals(c_token, "noru$ler")) {
2314             c_token++;
2315             set_ruler(FALSE, -1, -1);
2316         } else if (almost_equals(c_token, "ru$ler")) {
2317             c_token++;
2318             if (END_OF_COMMAND || !equals(c_token, "at")) {
2319                 set_ruler(TRUE, -1, -1);
2320             } else { /* set mouse ruler at ... */
2321                 struct position where;
2322                 int x, y;
2323                 c_token++;
2324                 if (END_OF_COMMAND)
2325                     int_error(c_token, "expecting ruler coordinates");
2326                 get_position(&where);
2327                 map_position(&where, &x, &y, "ruler at");
2328                 set_ruler(TRUE, (int)x, (int)y);
2329             }
2330         } else {
2331             if (!END_OF_COMMAND)
2332                 int_error(c_token, "wrong option");
2333             break;
2334         }
2335     }
2336 #ifdef OS2
2337     PM_update_menu_items();
2338 #endif
2339 }
2340 #endif
2341
2342 /* process 'set offsets' command */
2343 static void
2344 set_offsets()
2345 {
2346     c_token++;
2347     if (END_OF_COMMAND) {
2348         loff = roff = toff = boff = 0.0;  /* Reset offsets */
2349     } else {
2350         load_offsets (&loff,&roff,&toff,&boff);
2351     }
2352 }
2353
2354
2355 /* process 'set origin' command */
2356 static void
2357 set_origin()
2358 {
2359     struct value a;
2360
2361     c_token++;
2362     if (END_OF_COMMAND) {
2363         xoffset = 0.0;
2364         yoffset = 0.0;
2365     } else {
2366         xoffset = real(const_express(&a));
2367         if (!equals(c_token,","))
2368             int_error(c_token, "',' expected");
2369         c_token++;
2370         yoffset = real(const_express(&a));
2371     }
2372 }
2373
2374
2375 /* process 'set output' command */
2376 static void
2377 set_output()
2378 {
2379     char *testfile;
2380
2381     c_token++;
2382     if (multiplot)
2383         int_error(c_token, "you can't change the output in multiplot mode");
2384
2385     if (END_OF_COMMAND) {       /* no file specified */
2386         term_set_output(NULL);
2387         if (outstr) {
2388             free(outstr);
2389             outstr = NULL; /* means STDOUT */
2390         }
2391     } else if ((testfile = try_to_get_string())) {
2392         gp_expand_tilde(&testfile);
2393         term_set_output(testfile);
2394         if (testfile != outstr) {
2395             if (testfile)
2396                 free(testfile);
2397             testfile = outstr;
2398         }
2399         /* if we get here then it worked, and outstr now = testfile */
2400     } else
2401         int_error(c_token, "expecting filename");
2402
2403     /* Invalidate previous palette */
2404     invalidate_palette();
2405         
2406 }
2407
2408
2409 /* process 'set print' command */
2410 static void
2411 set_print()
2412 {
2413     TBOOLEAN append_p = FALSE;
2414     char *testfile = NULL;
2415
2416     c_token++;
2417     if (END_OF_COMMAND) {       /* no file specified */
2418         print_set_output(NULL, append_p);
2419     } else if ((testfile = try_to_get_string())) {
2420         gp_expand_tilde(&testfile);
2421         if (!END_OF_COMMAND) {
2422             if (equals(c_token, "append")) {
2423                 append_p = TRUE;
2424                 c_token++;
2425             } else {
2426                 int_error(c_token, "expecting keyword \'append\'");
2427             }
2428         }
2429         print_set_output(testfile, append_p);
2430     } else
2431         int_error(c_token, "expecting filename");
2432 }
2433
2434
2435 /* process 'set parametric' command */
2436 static void
2437 set_parametric()
2438 {
2439     c_token++;
2440
2441     if (!parametric) {
2442         parametric = TRUE;
2443         if (!polar) { /* already done for polar */
2444             strcpy (set_dummy_var[0], "t");
2445             strcpy (set_dummy_var[1], "y");
2446             if (interactive)
2447                 (void) fprintf(stderr,"\n\tdummy variable is t for curves, u/v for surfaces\n");
2448         }
2449     }
2450 }
2451
2452
2453 /* is resetting palette enabled?
2454  * note: reset_palette() is disabled within 'test palette'
2455  */
2456 int enable_reset_palette = 1;
2457
2458 /* default settings for palette */
2459 void
2460 reset_palette()
2461 {
2462     if (!enable_reset_palette) return;
2463     sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
2464     sm_palette.formulaR = 7; sm_palette.formulaG = 5;
2465     sm_palette.formulaB = 15;
2466     sm_palette.positive = SMPAL_POSITIVE;
2467     sm_palette.ps_allcF = 0;
2468     sm_palette.use_maxcolors = 0;
2469     sm_palette.gradient_num = 0;
2470     free(sm_palette.gradient);
2471     sm_palette.gradient = NULL;
2472     free(sm_palette.color);
2473     sm_palette.color = NULL;
2474     sm_palette.cmodel = C_MODEL_RGB;
2475     sm_palette.gamma = 1.5;
2476     pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_NONE;
2477 }
2478
2479
2480
2481 /* Process 'set palette defined' gradient specification */
2482 /* Syntax
2483  *   set palette defined   -->  use default palette
2484  *   set palette defined ( <pos1> <colorspec1>, ... , <posN> <colorspecN> )
2485  *     <posX>  gray value, automatically rescaled to [0, 1]
2486  *     <colorspecX>   :=  { "<color_name>" | "<X-style-color>" |  <r> <g> <b> }
2487  *        <color_name>     predefined colors (see below)
2488  *        <X-style-color>  "#rrggbb" with 2char hex values for red, green, blue
2489  *        <r> <g> <b>      three values in [0, 1] for red, green and blue
2490  *   return 1 if named colors where used, 0 otherwise
2491  */
2492 static int
2493 set_palette_defined()
2494 {
2495     struct value a;
2496     double p=0, r=0, g=0, b=0;
2497     int num, named_colors=0;
2498     int actual_size=8;
2499
2500     /* Invalidate previous gradient */
2501     invalidate_palette();
2502
2503     free( sm_palette.gradient );
2504     sm_palette.gradient = gp_alloc( actual_size*sizeof(gradient_struct), "pm3d gradient" );
2505
2506     if (END_OF_COMMAND) {
2507         /* lets use some default gradient */
2508         double pal[][4] = { {0.0, 0.05, 0.05, 0.2}, {0.1, 0, 0, 1},
2509                             {0.25, 0.7, 0.85, 0.9}, {0.4, 0, 0.75, 0},
2510                             {0.5, 1, 1, 0}, {0.7, 1, 0, 0},
2511                             {0.9, 0.6, 0.6, 0.6}, {1.0, 0.95, 0.95, 0.95} };
2512         int i;
2513         for( i=0; i<8; ++i ) {
2514             sm_palette.gradient[i].pos = pal[i][0];
2515             sm_palette.gradient[i].col.r = pal[i][1];
2516             sm_palette.gradient[i].col.g = pal[i][2];
2517             sm_palette.gradient[i].col.b = pal[i][3];
2518         }
2519         sm_palette.gradient_num = 8;
2520         sm_palette.cmodel = C_MODEL_RGB;
2521         return 0;
2522     }
2523
2524     if ( !equals(c_token,"(") )
2525         int_error( c_token, "Expected ( to start gradient definition." );
2526
2527     ++c_token;
2528     num = -1;
2529
2530     while (!END_OF_COMMAND) {
2531         char *col_str;
2532         p = real(const_express(&a));
2533         col_str = try_to_get_string();
2534         if (col_str) {
2535             /* either color name or X-style rgb value "#rrggbb" */
2536             if (col_str[0] == '#') {
2537                 /* X-style specifier */
2538                 int rr,gg,bb;
2539                 if (sscanf( col_str, "#%2x%2x%2x", &rr, &gg, &bb ) != 3 )
2540                     int_error( c_token-1,
2541                                "Unknown color specifier. Use '#rrggbb'." );
2542                 r = (double)(rr)/255.;
2543                 g = (double)(gg)/255.;
2544                 b = (double)(bb)/255.;
2545             }
2546             else { /* some predefined names */
2547                 /* Maybe we could scan the X11 rgb.txt file to look up color
2548                  * names?  Or at least move these definitions to some file
2549                  * which is included somehow during compilation instead
2550                  * hardcoding them. */
2551                 /* Can't use lookupt_table() as it works for tokens only,
2552                    so we'll do it manually */
2553                 const struct gen_table *tbl = pm3d_color_names_tbl;
2554                 while (tbl->key) {
2555                     if (!strcmp(col_str, tbl->key)) {
2556                         r = (double)((tbl->value >> 16 ) & 255) / 255.;
2557                         g = (double)((tbl->value >> 8 ) & 255) / 255.;
2558                         b = (double)(tbl->value & 255) / 255.;
2559                         break;
2560                     }
2561                     tbl++;
2562                 }
2563                 if (!tbl->key)
2564                     int_error( c_token-1, "Unknown color name." );
2565                 named_colors = 1;
2566             }
2567             free(col_str);
2568         } else {
2569             /* numerical rgb, hsv, xyz, ... values  [0,1] */
2570             r = real(const_express(&a));
2571             if (r<0 || r>1 )  int_error(c_token-1,"Value out of range [0,1].");
2572             g = real(const_express(&a));
2573             if (g<0 || g>1 )  int_error(c_token-1,"Value out of range [0,1].");
2574             b = real(const_express(&a));
2575             if (b<0 || b>1 )  int_error(c_token-1,"Value out of range [0,1].");
2576         }
2577         ++num;
2578
2579         if ( num >= actual_size ) {
2580             /* get more space for the gradient */
2581             actual_size += 10;
2582             sm_palette.gradient = gp_realloc( sm_palette.gradient,
2583                           actual_size*sizeof(gradient_struct),
2584                           "pm3d gradient" );
2585         }
2586         sm_palette.gradient[num].pos = p;
2587         sm_palette.gradient[num].col.r = r;
2588         sm_palette.gradient[num].col.g = g;
2589         sm_palette.gradient[num].col.b = b;
2590         if (equals(c_token,")") ) break;
2591         if ( !equals(c_token,",") )
2592             int_error( c_token, "Expected comma." );
2593         ++c_token;
2594
2595     }
2596
2597     sm_palette.gradient_num = num + 1;
2598     check_palette_grayscale();
2599
2600     return named_colors;
2601 }
2602
2603
2604 /*  process 'set palette file' command
2605  *  load a palette from file, honor datafile modifiers
2606  */
2607 static void
2608 set_palette_file()
2609 {
2610     int specs;
2611     double v[4];
2612     int i, j, actual_size;
2613     char *file_name;
2614
2615     ++c_token;
2616
2617     /* get filename */
2618     if (!(file_name = try_to_get_string()))
2619         int_error(c_token, "missing filename");
2620
2621     df_set_plot_mode(MODE_QUERY);       /* Needed only for binary datafiles */
2622     specs = df_open(file_name, 4);
2623     free(file_name);
2624
2625     if (specs > 0 && specs < 3)
2626         int_error( c_token, "Less than 3 using specs for palette");
2627
2628     if (sm_palette.gradient) {
2629         free( sm_palette.gradient );
2630         sm_palette.gradient = 0;
2631     }
2632     actual_size = 10;
2633     sm_palette.gradient =
2634       gp_alloc( actual_size*sizeof(gradient_struct), "gradient" );
2635
2636     i = 0;
2637
2638 #define VCONSTRAIN(x) ( (x)<0 ? 0 : ( (x)>1 ? 1: (x) ) )
2639     /* values are simply clipped to [0,1] without notice */
2640     while ((j = df_readline(v, 4)) != DF_EOF) {
2641         if (i >= actual_size) {
2642           actual_size += 10;
2643           sm_palette.gradient = (gradient_struct*)
2644             gp_realloc( sm_palette.gradient,
2645                         actual_size*sizeof(gradient_struct),
2646                         "pm3d gradient" );
2647         }
2648         switch (j) {
2649             case 3:
2650                 sm_palette.gradient[i].col.r = VCONSTRAIN(v[0]);
2651                 sm_palette.gradient[i].col.g = VCONSTRAIN(v[1]);
2652                 sm_palette.gradient[i].col.b = VCONSTRAIN(v[2]);
2653                 sm_palette.gradient[i].pos = i ;
2654                 break;
2655             case 4:
2656                 sm_palette.gradient[i].col.r = VCONSTRAIN(v[1]);
2657                 sm_palette.gradient[i].col.g = VCONSTRAIN(v[2]);
2658                 sm_palette.gradient[i].col.b = VCONSTRAIN(v[3]);
2659                 sm_palette.gradient[i].pos = v[0];
2660                 break;
2661             default:
2662                 df_close();
2663                 int_error(c_token, "Bad data on line %d", df_line_number);
2664                 break;
2665         }
2666         ++i;
2667     }
2668 #undef VCONSTRAIN
2669     df_close();
2670     if (i==0)
2671         int_error( c_token, "No valid palette found" );
2672
2673     sm_palette.gradient_num = i;
2674     check_palette_grayscale();
2675
2676 }
2677
2678
2679 /* Process a 'set palette function' command.
2680  *  Three functions with fixed dummy variable gray are registered which
2681  *  map gray to the different color components.
2682  *  If ALLOW_DUMMY_VAR_FOR_GRAY is set:
2683  *    A different dummy variable may proceed the formulae in quotes.
2684  *    This syntax is different from the usual '[u=<start>:<end>]', but
2685  *    as <start> and <end> are fixed to 0 and 1 you would have to type
2686  *    always '[u=]' which looks strange, especially as just '[u]'
2687  *    wouldn't work.
2688  *  If unset:  dummy variable is fixed to 'gray'.
2689  */
2690 static void
2691 set_palette_function()
2692 {
2693     int start_token;
2694     char saved_dummy_var[MAX_ID_LEN+1];
2695
2696     ++c_token;
2697     strncpy( saved_dummy_var, c_dummy_var[0], MAX_ID_LEN );
2698
2699     /* set dummy variable */
2700 #ifdef ALLOW_DUMMY_VAR_FOR_GRAY
2701     if (isstring(c_token)) {
2702         quote_str( c_dummy_var[0], c_token, MAX_ID_LEN );
2703         ++c_token;
2704     }
2705     else
2706 #endif /* ALLOW_DUMMY_VAR_FOR_GRAY */
2707     strncpy( c_dummy_var[0], "gray", MAX_ID_LEN );
2708
2709
2710     /* Afunc */
2711     start_token = c_token;
2712     if (sm_palette.Afunc.at) {
2713         free_at( sm_palette.Afunc.at );
2714         sm_palette.Afunc.at = NULL;
2715     }
2716     dummy_func = &sm_palette.Afunc;
2717     sm_palette.Afunc.at = perm_at();
2718     if (! sm_palette.Afunc.at)
2719         int_error(start_token, "not enough memory for function");
2720     m_capture(&(sm_palette.Afunc.definition), start_token, c_token-1);
2721     dummy_func = NULL;
2722     if (!equals(c_token,","))
2723         int_error(c_token,"Expected comma" );
2724     ++c_token;
2725
2726     /* Bfunc */
2727     start_token = c_token;
2728     if (sm_palette.Bfunc.at) {
2729         free_at( sm_palette.Bfunc.at );
2730         sm_palette.Bfunc.at = NULL;
2731     }
2732     dummy_func = &sm_palette.Bfunc;
2733     sm_palette.Bfunc.at = perm_at();
2734     if (! sm_palette.Bfunc.at)
2735         int_error(start_token, "not enough memory for function");
2736     m_capture(&(sm_palette.Bfunc.definition), start_token, c_token-1);
2737     dummy_func = NULL;
2738     if (!equals(c_token,","))
2739         int_error(c_token,"Expected comma" );
2740     ++c_token;
2741
2742     /* Cfunc */
2743     start_token = c_token;
2744     if (sm_palette.Cfunc.at) {
2745         free_at( sm_palette.Cfunc.at );
2746         sm_palette.Cfunc.at = NULL;
2747     }
2748     dummy_func = &sm_palette.Cfunc;
2749     sm_palette.Cfunc.at = perm_at();
2750     if (! sm_palette.Cfunc.at)
2751         int_error(start_token, "not enough memory for function");
2752     m_capture(&(sm_palette.Cfunc.definition), start_token, c_token-1);
2753     dummy_func = NULL;
2754
2755     strncpy( c_dummy_var[0], saved_dummy_var, MAX_ID_LEN );
2756 }
2757
2758
2759 /*
2760  *  Normalize gray scale of gradient to fill [0,1] and
2761  *  complain if gray values are not strictly increasing.
2762  *  Maybe automatic sorting of the gray values could be a
2763  *  feature.
2764  */
2765 static void
2766 check_palette_grayscale()
2767 {
2768     int i;
2769     double off, f;
2770
2771     /* check if gray values are sorted */
2772     for (i=0; i<sm_palette.gradient_num-1; ++i ) {
2773         if (sm_palette.gradient[i].pos > sm_palette.gradient[i+1].pos) {
2774             int_error( c_token, "Gray scale not sorted in gradient." );
2775         }
2776     }
2777
2778     /* fit gray axis into [0:1]:  subtract offset and rescale */
2779     off = sm_palette.gradient[0].pos;
2780     f = 1.0 / ( sm_palette.gradient[sm_palette.gradient_num-1].pos-off );
2781     for (i=1; i<sm_palette.gradient_num-1; ++i ) {
2782         sm_palette.gradient[i].pos = f*(sm_palette.gradient[i].pos-off);
2783     }
2784
2785     /* paranoia on the first and last entries */
2786     sm_palette.gradient[0].pos = 0.0;
2787     sm_palette.gradient[sm_palette.gradient_num-1].pos = 1.0;
2788 }
2789
2790
2791 #define SCAN_RGBFORMULA(formula) do {                                                                  \
2792     c_token++;                                                                                         \
2793     i = (int) real(const_express(&a));                                                                 \
2794     if (abs(i) >= sm_palette.colorFormulae)                                                            \
2795         int_error(c_token,                                                                             \
2796                   "color formula out of range (use `show palette rgbformulae' to display the range)"); \
2797     formula = i;                                                                                       \
2798 } while(0)
2799
2800 #define CHECK_TRANSFORM  do {                                                     \
2801     if (transform_defined)                                                        \
2802         int_error(c_token,                                                        \
2803                   "Use either `rgbformulae`, `defined`, `file` or `formulae`." ); \
2804     transform_defined = 1;                                                        \
2805 }  while(0)
2806
2807 /* Process 'set palette' command */
2808 static void
2809 set_palette()
2810 {
2811     struct value a;
2812     int transform_defined, named_color;
2813     transform_defined = named_color = 0;
2814     c_token++;
2815
2816     if (END_OF_COMMAND) /* reset to default settings */
2817         reset_palette();
2818     else { /* go through all options of 'set palette' */
2819         for ( ; !END_OF_COMMAND; c_token++ ) {
2820             switch (lookup_table(&set_palette_tbl[0],c_token)) {
2821             /* positive and negative picture */
2822             case S_PALETTE_POSITIVE: /* "pos$itive" */
2823                 sm_palette.positive = SMPAL_POSITIVE;
2824                 continue;
2825             case S_PALETTE_NEGATIVE: /* "neg$ative" */
2826                 sm_palette.positive = SMPAL_NEGATIVE;
2827                 continue;
2828             /* Now the options that determine the palette of smooth colours */
2829             /* gray or rgb-coloured */
2830             case S_PALETTE_GRAY: /* "gray" */
2831                 sm_palette.colorMode = SMPAL_COLOR_MODE_GRAY;
2832                 continue;
2833             case S_PALETTE_GAMMA: /* "gamma" */
2834                 ++c_token;
2835                 sm_palette.gamma = real(const_express(&a));
2836                 --c_token;
2837                 continue;
2838             case S_PALETTE_COLOR: /* "col$or" */
2839                 if (pm3d_last_set_palette_mode != SMPAL_COLOR_MODE_NONE) {
2840                     sm_palette.colorMode = pm3d_last_set_palette_mode;
2841                 } else {
2842                 sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
2843                 }
2844                 continue;
2845             /* rgb color mapping formulae: rgb$formulae r,g,b (3 integers) */
2846             case S_PALETTE_RGBFORMULAE: { /* "rgb$formulae" */
2847                 int i;
2848
2849                 CHECK_TRANSFORM;
2850                 SCAN_RGBFORMULA( sm_palette.formulaR );
2851                 if (!equals(c_token,",")) { c_token--; continue; }
2852                 SCAN_RGBFORMULA( sm_palette.formulaG );
2853                 if (!equals(c_token,",")) { c_token--; continue; }
2854                 SCAN_RGBFORMULA( sm_palette.formulaB );
2855                 c_token--;
2856                 sm_palette.colorMode = SMPAL_COLOR_MODE_RGB;
2857                 pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_RGB;
2858                 continue;
2859             } /* rgbformulae */
2860             case S_PALETTE_DEFINED: { /* "def$ine" */
2861                 CHECK_TRANSFORM;
2862                 ++c_token;
2863                 named_color = set_palette_defined();
2864                 sm_palette.colorMode = SMPAL_COLOR_MODE_GRADIENT;
2865                 pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_GRADIENT;
2866                 continue;
2867             }
2868             case S_PALETTE_FILE: { /* "file" */
2869                 CHECK_TRANSFORM;
2870                 set_palette_file();
2871                 sm_palette.colorMode = SMPAL_COLOR_MODE_GRADIENT;
2872                 pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_GRADIENT;
2873                 --c_token;
2874                 continue;
2875             }
2876             case S_PALETTE_FUNCTIONS: { /* "func$tions" */
2877                 CHECK_TRANSFORM;
2878                 set_palette_function();
2879                 sm_palette.colorMode = SMPAL_COLOR_MODE_FUNCTIONS;
2880                 pm3d_last_set_palette_mode = SMPAL_COLOR_MODE_FUNCTIONS;
2881                 --c_token;
2882                 continue;
2883             }
2884             case S_PALETTE_MODEL: { /* "mo$del" */
2885                 int model;
2886
2887                 ++c_token;
2888                 if (END_OF_COMMAND)
2889                     int_error( c_token, "Expected color model." );
2890                 model = lookup_table(&color_model_tbl[0],c_token);
2891                 if (model == -1)
2892                     int_error(c_token,"Unknown color model.");
2893                 sm_palette.cmodel = model;
2894                 continue;
2895             }
2896             /* ps_allcF: write all rgb formulae into PS file? */
2897             case S_PALETTE_NOPS_ALLCF: /* "nops_allcF" */
2898                 sm_palette.ps_allcF = 0;
2899                 continue;
2900             case S_PALETTE_PS_ALLCF: /* "ps_allcF" */
2901                 sm_palette.ps_allcF = 1;
2902                 continue;
2903             /* max colors used */
2904             case S_PALETTE_MAXCOLORS: { /* "maxc$olors" */
2905                 struct value a;
2906                 int i;
2907
2908                 c_token++;
2909                 i = (int)real(const_express(&a));
2910                 if (i<0) int_error(c_token,"non-negative number required");
2911                 sm_palette.use_maxcolors = i;
2912                 --c_token;
2913                 continue;
2914             }
2915             } /* switch over palette lookup table */
2916             int_error(c_token,"invalid palette option");
2917         } /* end of while !end of command over palette options */
2918     } /* else(arguments found) */
2919
2920     if (named_color && sm_palette.cmodel != C_MODEL_RGB && interactive)
2921         int_warn(NO_CARET,
2922                  "Named colors will produce strange results if not in color mode RGB." );
2923
2924     /* Invalidate previous palette */
2925     invalidate_palette();
2926 }
2927
2928 #undef CHECK_TRANSFORM
2929 #undef SCAN_RGBFORMULA
2930
2931 /* process 'set colorbox' command */
2932 static void
2933 set_colorbox()
2934 {
2935     c_token++;
2936
2937     if (END_OF_COMMAND) /* reset to default position */
2938         color_box.where = SMCOLOR_BOX_DEFAULT;
2939     else { /* go through all options of 'set colorbox' */
2940         for ( ; !END_OF_COMMAND; c_token++ ) {
2941             switch (lookup_table(&set_colorbox_tbl[0],c_token)) {
2942             /* vertical or horizontal color gradient */
2943             case S_COLORBOX_VERTICAL: /* "v$ertical" */
2944                 color_box.rotation = 'v';
2945                 continue;
2946             case S_COLORBOX_HORIZONTAL: /* "h$orizontal" */
2947                 color_box.rotation = 'h';
2948                 continue;
2949             /* color box where: default position */
2950             case S_COLORBOX_DEFAULT: /* "def$ault" */
2951                 color_box.where = SMCOLOR_BOX_DEFAULT;
2952                 continue;
2953             /* color box where: position by user */
2954             case S_COLORBOX_USER: /* "u$ser" */
2955                 color_box.where = SMCOLOR_BOX_USER;
2956                 continue;
2957             /* color box layer: front or back */
2958             case S_COLORBOX_FRONT: /* "fr$ont" */
2959                 color_box.layer = LAYER_FRONT;
2960                 continue;
2961             case S_COLORBOX_BACK: /* "ba$ck" */
2962                 color_box.layer = LAYER_BACK;
2963                 continue;
2964             /* border of the color box */
2965             case S_COLORBOX_BORDER: /* "bo$rder" */
2966
2967                 color_box.border = 1;
2968                 c_token++;
2969
2970                 if (!END_OF_COMMAND) {
2971                     /* expecting a border line type */
2972                     struct value a;
2973                     color_box.border_lt_tag = real(const_express(&a));
2974                     if (color_box.border_lt_tag <= 0) {
2975                         color_box.border_lt_tag = 0;
2976                         int_error(c_token, "tag must be strictly positive (see `help set style line')");
2977                     }
2978                     --c_token;
2979                 }
2980                 continue;
2981             case S_COLORBOX_BDEFAULT: /* "bd$efault" */
2982                 color_box.border_lt_tag = -1; /* use default border */
2983                 continue;
2984             case S_COLORBOX_NOBORDER: /* "nobo$rder" */
2985                 color_box.border = 0;
2986                 continue;
2987             /* colorbox origin */
2988             case S_COLORBOX_ORIGIN: /* "o$rigin" */
2989                 c_token++;
2990                 if (END_OF_COMMAND) {
2991                     int_error(c_token, "expecting screen value [0 - 1]");
2992                 } else {
2993                     get_position_default(&color_box.origin, screen);
2994                 }
2995                 c_token--;
2996                 continue;
2997             /* colorbox size */
2998             case S_COLORBOX_SIZE: /* "s$ize" */
2999                 c_token++;
3000                 if (END_OF_COMMAND) {
3001                     int_error(c_token, "expecting screen value [0 - 1]");
3002                 } else {
3003                     get_position_default(&color_box.size, screen);
3004                 }
3005                 c_token--;
3006                 continue;
3007             } /* switch over colorbox lookup table */
3008             int_error(c_token,"invalid colorbox option");
3009         } /* end of while !end of command over colorbox options */
3010     if (color_box.where == SMCOLOR_BOX_NO) /* default: draw at default position */
3011         color_box.where = SMCOLOR_BOX_DEFAULT;
3012     }
3013 }
3014
3015
3016 /* process 'set pm3d' command */
3017 static void
3018 set_pm3d()
3019 {
3020     int c_token0 = ++c_token;
3021
3022     if (END_OF_COMMAND) { /* assume default settings */
3023         pm3d_reset(); /* sets pm3d.implicit to PM3D_IMPLICIT and pm3d.where to "s" */
3024         pm3d.implicit = PM3D_IMPLICIT; /* for historical reasons */
3025     }
3026     else { /* go through all options of 'set pm3d' */
3027         for ( ; !END_OF_COMMAND; c_token++ ) {
3028             switch (lookup_table(&set_pm3d_tbl[0],c_token)) {
3029             /* where to plot */
3030             case S_PM3D_AT: /* "at" */
3031                 c_token++;
3032                 if (get_pm3d_at_option(&pm3d.where[0]))
3033                     return; /* error */
3034                 c_token--;
3035 #if 1
3036                 if (c_token == c_token0+1)
3037                     /* for historical reasons: if "at" is the first option of pm3d,
3038                      * like "set pm3d at X other_opts;", then implicit is switched on */
3039                     pm3d.implicit = PM3D_IMPLICIT;
3040 #endif
3041                 continue;
3042             case S_PM3D_INTERPOLATE: /* "interpolate" */
3043                 c_token++;
3044                 if (END_OF_COMMAND) {
3045                     int_error(c_token, "expecting step values i,j");
3046                 } else {
3047                     struct value a;
3048                     pm3d.interp_i = real(const_express(&a));
3049                     if(pm3d.interp_i < 1)
3050                         pm3d.interp_i = 1;
3051                     if (!equals(c_token,","))
3052                         int_error(c_token, "',' expected");
3053                     c_token++;
3054                     pm3d.interp_j = real(const_express(&a));
3055                     if (pm3d.interp_j < 1)
3056                         pm3d.interp_j = 1;
3057                     c_token--;
3058                 }
3059                 continue;
3060             /* forward and backward drawing direction */
3061             case S_PM3D_SCANSFORWARD: /* "scansfor$ward" */
3062                 pm3d.direction = PM3D_SCANS_FORWARD;
3063                 continue;
3064             case S_PM3D_SCANSBACKWARD: /* "scansback$ward" */
3065                 pm3d.direction = PM3D_SCANS_BACKWARD;
3066                 continue;
3067             case S_PM3D_SCANS_AUTOMATIC: /* "scansauto$matic" */
3068                 pm3d.direction = PM3D_SCANS_AUTOMATIC;
3069                 continue;
3070             case S_PM3D_DEPTH: /* "dep$thorder" */
3071                 pm3d.direction = PM3D_DEPTH;
3072                 continue;
3073             /* flush scans: left, right or center */
3074             case S_PM3D_FLUSH:  /* "fl$ush" */
3075                 c_token++;
3076                 if (almost_equals(c_token, "b$egin"))
3077                     pm3d.flush = PM3D_FLUSH_BEGIN;
3078                 else if (almost_equals(c_token, "c$enter"))
3079                     pm3d.flush = PM3D_FLUSH_CENTER;
3080                 else if (almost_equals(c_token, "e$nd"))
3081                     pm3d.flush = PM3D_FLUSH_END;
3082                 else
3083                     int_error(c_token,"expecting flush 'begin', 'center' or 'end'");
3084                 continue;
3085             /* clipping method */
3086             case S_PM3D_CLIP_1IN: /* "clip1$in" */
3087                 pm3d.clip = PM3D_CLIP_1IN;
3088                 continue;
3089             case S_PM3D_CLIP_4IN: /* "clip4$in" */
3090                 pm3d.clip = PM3D_CLIP_4IN;
3091                 continue;
3092             /* setup everything for plotting a map */
3093             case S_PM3D_MAP: /* "map" */
3094                 pm3d.where[0] = 'b'; pm3d.where[1] = 0; /* set pm3d at b */
3095                 data_style = PM3DSURFACE;
3096                 func_style = PM3DSURFACE;
3097                 splot_map = TRUE;
3098                 continue;
3099             /* flushing triangles */
3100             case S_PM3D_FTRIANGLES: /* "ftr$iangles" */
3101                 pm3d.ftriangles = 1;
3102                 continue;
3103             case S_PM3D_NOFTRIANGLES: /* "noftr$iangles" */
3104                 pm3d.ftriangles = 0;
3105                 continue;
3106             /* pm3d-specific hidden line overwrite */
3107             case S_PM3D_HIDDEN: { /* "hi$dden3d" */
3108                 struct value a;
3109                 c_token++;
3110                 pm3d.hidden3d_tag = real(const_express(&a));
3111                 --c_token;
3112                 if (pm3d.hidden3d_tag <= 0) {
3113                     pm3d.hidden3d_tag = 0;
3114                     int_error(c_token,"tag must be strictly positive (see `help set style line')");
3115                 }
3116                 }
3117                 continue;
3118             case S_PM3D_NOHIDDEN: /* "nohi$dden3d" */
3119                 pm3d.hidden3d_tag = 0;
3120                 continue;
3121             case S_PM3D_SOLID: /* "so$lid" */
3122             case S_PM3D_NOTRANSPARENT: /* "notr$ansparent" */
3123 #if PM3D_HAVE_SOLID
3124                 pm3d.solid = 1;
3125
3126 #else
3127                 if (interactive)
3128                     int_warn(c_token, "Deprecated syntax --- ignored");
3129 #endif
3130                 continue;
3131             case S_PM3D_NOSOLID: /* "noso$lid" */
3132             case S_PM3D_TRANSPARENT: /* "tr$ansparent" */
3133 #if PM3D_HAVE_SOLID
3134                 pm3d.solid = 0;
3135                 continue;
3136 #else
3137                 if (interactive)
3138                     int_warn(c_token, "Deprecated syntax --- ignored");
3139 #endif
3140             case S_PM3D_IMPLICIT: /* "i$mplicit" */
3141             case S_PM3D_NOEXPLICIT: /* "noe$xplicit" */
3142                 pm3d.implicit = PM3D_IMPLICIT;
3143                 continue;
3144             case S_PM3D_NOIMPLICIT: /* "noi$mplicit" */
3145             case S_PM3D_EXPLICIT: /* "e$xplicit" */
3146                 pm3d.implicit = PM3D_EXPLICIT;
3147                 continue;
3148             case S_PM3D_WHICH_CORNER: /* "corners2color" */
3149                 c_token++;
3150                 if (equals(c_token, "mean"))
3151                     pm3d.which_corner_color = PM3D_WHICHCORNER_MEAN;
3152                 else if (equals(c_token, "geomean"))
3153                     pm3d.which_corner_color = PM3D_WHICHCORNER_GEOMEAN;
3154                 else if (equals(c_token, "median"))
3155                     pm3d.which_corner_color = PM3D_WHICHCORNER_MEDIAN;
3156                 else if (equals(c_token, "min"))
3157                     pm3d.which_corner_color = PM3D_WHICHCORNER_MIN;
3158                 else if (equals(c_token, "max"))
3159                     pm3d.which_corner_color = PM3D_WHICHCORNER_MAX;
3160                 else if (equals(c_token, "c1"))
3161                     pm3d.which_corner_color = PM3D_WHICHCORNER_C1;
3162                 else if (equals(c_token, "c2"))
3163                     pm3d.which_corner_color = PM3D_WHICHCORNER_C2;
3164                 else if (equals(c_token, "c3"))
3165                     pm3d.which_corner_color = PM3D_WHICHCORNER_C3;
3166                 else if (equals(c_token, "c4"))
3167                     pm3d.which_corner_color = PM3D_WHICHCORNER_C4;
3168                 else
3169                     int_error(c_token,"expecting 'mean', 'geomean', 'median', 'c1', 'c2', 'c3' or 'c4'");
3170                 continue;
3171             } /* switch over pm3d lookup table */
3172             int_error(c_token,"invalid pm3d option");
3173         } /* end of while !end of command over pm3d options */
3174         if (PM3D_SCANS_AUTOMATIC == pm3d.direction
3175             && PM3D_FLUSH_BEGIN != pm3d.flush) {
3176             pm3d.direction = PM3D_SCANS_FORWARD;
3177 #if 0
3178             /* be silent, don't print this warning */
3179             /* Rather FIXME that this combination is supported? Shouldn't be
3180                so big problem, I guess, just it is not implemented. */
3181             fprintf(stderr, "pm3d: `scansautomatic' and `flush %s' are incompatible\n",
3182                 PM3D_FLUSH_END == pm3d.flush ? "end": "center");
3183             fputs("   => setting `scansforward'\n", stderr);
3184 #endif
3185         }
3186     }
3187 }
3188
3189
3190 /* process 'set pointsize' command */
3191 static void
3192 set_pointsize()
3193 {
3194     struct value a;
3195
3196     c_token++;
3197     if (END_OF_COMMAND)
3198         pointsize = 1.0;
3199     else
3200         pointsize = real(const_express(&a));
3201     if(pointsize <= 0) pointsize = 1;
3202 }
3203
3204
3205 /* process 'set polar' command */
3206 static void
3207 set_polar()
3208 {
3209     c_token++;
3210
3211     if (!polar) {
3212         if (!parametric) {
3213             if (interactive)
3214                 (void) fprintf(stderr,"\n\tdummy variable is t for curves\n");
3215             strcpy (set_dummy_var[0], "t");
3216         }
3217         polar = TRUE;
3218         if (axis_array[T_AXIS].set_autoscale) {
3219             /* only if user has not set a range manually */
3220             axis_array[T_AXIS].set_min = 0.0;
3221             /* 360 if degrees, 2pi if radians */
3222             axis_array[T_AXIS].set_max = 2 * M_PI / ang2rad;
3223         }
3224     }
3225 }
3226
3227 #ifdef EAM_OBJECTS
3228 /* 
3229  * Process 'set object <tag> rectangle' command
3230  * set object {tag} rectangle {from <bottom_left> {to|rto} <top_right>}
3231  *                     {{at|center} <xcen>,<ycen> size <w>,<h>}
3232  *                     {fc|fillcolor <colorspec>} {lw|linewidth <lw>}
3233  *                     {fs <fillstyle>} {front|back|behind}
3234  *                     {default}
3235  * EAM Jan 2005
3236  */
3237
3238 static void
3239 set_object()
3240 {
3241     struct value a;
3242     int tag;
3243
3244     /* The next token must either be a tag or the object type */
3245     c_token++;
3246     if (almost_equals(c_token, "rect$angle"))
3247         tag = -1; /* We'll figure out what it really is later */
3248     else {
3249         tag = (int) real(const_express(&a));
3250         if (tag <= 0)
3251             int_error(c_token, "tag must be > zero");
3252     }
3253
3254     if (almost_equals(c_token, "rect$angle")) {
3255         set_rectangle(tag);
3256
3257     } else if (tag > 0) {
3258         /* Look for existing object with this tag */
3259         t_object *this_object = first_object;
3260         for (; this_object != NULL; this_object = this_object->next)
3261              if (tag == this_object->tag)
3262                 break;
3263         if (this_object && tag == this_object->tag
3264                         && this_object->object_type == OBJ_RECTANGLE) {
3265             c_token--;
3266             set_rectangle(tag);
3267         } else
3268             int_error(c_token, "unknown object");
3269
3270     } else
3271         int_error(c_token, "unrecognized object type");
3272
3273 }
3274
3275 static t_object *
3276 new_object(int tag, int object_type)
3277 {
3278     t_object *new = gp_alloc(sizeof(struct object), "object");
3279     if (object_type == OBJ_RECTANGLE) {
3280         t_object def = DEFAULT_RECTANGLE_STYLE;
3281         *new = def;
3282         new->tag = tag;
3283         new->object_type = object_type;
3284         new->lp_properties.l_type = LT_DEFAULT; /* Use default rectangle color */
3285         new->fillstyle.fillstyle = FS_DEFAULT;  /* and default fill style */
3286     } else
3287         fprintf(stderr,"object initialization failure\n");
3288     return new;
3289 }
3290
3291 static void
3292 set_rectangle(int tag)
3293 {
3294     t_rectangle *this_rect = NULL;
3295     t_object *this_object = NULL;
3296     t_object *new_obj = NULL;
3297     t_object *prev_object = NULL;
3298     struct value a;
3299     TBOOLEAN got_fill = FALSE;
3300     TBOOLEAN got_lt = FALSE;
3301     TBOOLEAN got_lw = FALSE;
3302     TBOOLEAN got_corners = FALSE;
3303     TBOOLEAN got_center = FALSE;
3304     double lw = 1.0;
3305
3306     c_token++;
3307     
3308     /* We are setting the default, not any particular rectangle */
3309     if (tag < -1) {
3310         this_object = &default_rectangle;
3311         this_rect = &default_rectangle.o.rectangle;
3312         c_token--;
3313
3314     } else {
3315         /* Look for existing rectangle with this tag */
3316         for (this_object = first_object; this_object != NULL;
3317              prev_object = this_object, this_object = this_object->next)
3318              /* is this the rect we want? */
3319              if (0 < tag  &&  tag <= this_object->tag)
3320                 break;
3321
3322         /* Insert this rect into the list if it is a new one */
3323         if (this_object == NULL || tag != this_object->tag) {
3324             if (tag == -1)
3325                 tag = (prev_object) ? prev_object->tag+1 : 1;
3326             new_obj = new_object(tag, OBJ_RECTANGLE);
3327             if (prev_object == NULL)
3328                 first_object = new_obj;
3329             else
3330                 prev_object->next = new_obj;
3331             new_obj->next = this_object;
3332             this_object = new_obj;
3333         }
3334         this_rect = &this_object->o.rectangle;
3335     }
3336
3337     while (!END_OF_COMMAND) {
3338         int save_token = c_token;
3339
3340         if (equals(c_token,"from")) {
3341             /* Read in the bottom left and upper right corners */
3342             c_token++;
3343             get_position(&this_rect->bl);
3344             if (equals(c_token,"to")) {
3345                 c_token++;
3346                 get_position(&this_rect->tr);
3347             } else if (equals(c_token,"rto")) {
3348                 c_token++;
3349                 get_position_default(&this_rect->tr,this_rect->bl.scalex);
3350                 if (this_rect->bl.scalex != this_rect->tr.scalex
3351                 ||  this_rect->bl.scaley != this_rect->tr.scaley)
3352                     int_error(c_token,"relative coordinates must match in type");
3353                 this_rect->tr.x += this_rect->bl.x;
3354                 this_rect->tr.y += this_rect->bl.y;
3355             } else
3356                 int_error(c_token,"Expecting to or rto");
3357             got_corners = TRUE;
3358             this_rect->type = 0;
3359             continue;
3360
3361         } else if (equals(c_token,"at") || almost_equals(c_token,"cen$ter")) {
3362             /* Read in the center position */
3363             c_token++;
3364             get_position(&this_rect->center);
3365             got_center = TRUE;
3366             this_rect->type = 1;
3367             continue;
3368         } else if (equals(c_token,"size")) {
3369             /* Read in the width and height */
3370             c_token++;
3371             get_position(&this_rect->extent);
3372             got_center = TRUE;
3373             this_rect->type = 1;
3374             continue;
3375
3376         } else if (equals(c_token,"front")) {
3377             this_object->layer = 1;
3378             c_token++;
3379             continue;
3380         } else if (equals(c_token,"back")) {
3381             this_object->layer = 0;
3382             c_token++;
3383             continue;
3384         } else if (equals(c_token,"behind")) {
3385             this_object->layer = -1;
3386             c_token++;
3387             continue;
3388         } else if (almost_equals(c_token,"def$ault")) {
3389             if (tag < 0) {
3390                 int_error(c_token,
3391                     "Invalid command - did you mean 'unset style rectangle'?");
3392             } else {
3393                 this_object->lp_properties.l_type = LT_DEFAULT;
3394                 this_object->fillstyle.fillstyle = FS_DEFAULT;
3395             }
3396             got_fill = got_lt = TRUE;
3397             c_token++;
3398             continue;
3399         }
3400
3401         /* Now parse the style options; default to whatever the global style is  */
3402         if (!got_fill) {
3403             if (new_obj)
3404                 parse_fillstyle(&this_object->fillstyle, default_rectangle.fillstyle.fillstyle,
3405                         default_rectangle.fillstyle.filldensity, default_rectangle.fillstyle.fillpattern,
3406                         default_rectangle.fillstyle.border_linetype);
3407             else
3408                 parse_fillstyle(&this_object->fillstyle, this_object->fillstyle.fillstyle,
3409                         this_object->fillstyle.filldensity, this_object->fillstyle.fillpattern,
3410                         this_object->fillstyle.border_linetype);
3411             if (c_token != save_token) {
3412                 got_fill = TRUE;
3413                 continue;
3414             }
3415         }
3416
3417         /* Parse the colorspec */
3418         if (!got_lt) {
3419             if (equals(c_token,"fc") || almost_equals(c_token,"fillc$olor")) {
3420                 this_object->lp_properties.use_palette = TRUE;
3421                 this_object->lp_properties.l_type = LT_BLACK; /* Anything but LT_DEFAULT */
3422                 parse_colorspec(&this_object->lp_properties.pm3d_color, TC_FRAC);
3423                 if (this_object->lp_properties.pm3d_color.type == TC_DEFAULT)
3424                     this_object->lp_properties.l_type = LT_DEFAULT;
3425             }
3426
3427             if (c_token != save_token) {
3428                 got_lt = TRUE;
3429                 continue;
3430             }
3431         }
3432
3433         /* And linewidth */
3434         if (!got_lw) {
3435             if (equals(c_token,"lw") || almost_equals(c_token,"linew$idth")) {
3436                 c_token++;
3437                 lw = real(const_express(&a));
3438             }
3439             if (c_token != save_token) {
3440                 got_lw = TRUE;
3441                 continue;
3442             }
3443         }
3444
3445         int_error(c_token, "Unrecognized or duplicate option");
3446     }
3447
3448     if (got_lw)
3449         this_object->lp_properties.l_width = lw;
3450
3451     if (got_center && got_corners)
3452         int_error(NO_CARET,"Inconsistent options");
3453
3454 }
3455 #endif
3456
3457 /* process 'set samples' command */
3458 static void
3459 set_samples()
3460 {
3461     int tsamp1, tsamp2;
3462     struct value a;
3463
3464     c_token++;
3465     tsamp1 = (int)magnitude(const_express(&a));
3466     tsamp2 = tsamp1;
3467     if (!END_OF_COMMAND) {
3468         if (!equals(c_token,","))
3469             int_error(c_token, "',' expected");
3470         c_token++;
3471         tsamp2 = (int)magnitude(const_express(&a));
3472     }
3473     if (tsamp1 < 2 || tsamp2 < 2)
3474         int_error(c_token, "sampling rate must be > 1; sampling unchanged");
3475     else {
3476         struct surface_points *f_3dp = first_3dplot;
3477
3478         first_3dplot = NULL;
3479         sp_free(f_3dp);
3480
3481         samples_1 = tsamp1;
3482         samples_2 = tsamp2;
3483     }
3484 }
3485
3486
3487 /* process 'set size' command */
3488 static void
3489 set_size()
3490 {
3491     struct value s;
3492
3493     c_token++;
3494     if (END_OF_COMMAND) {
3495         xsize = 1.0;
3496         ysize = 1.0;
3497     } else {
3498         if (almost_equals(c_token, "sq$uare")) {
3499             aspect_ratio = 1.0;
3500             ++c_token;
3501         } else if (almost_equals(c_token,"ra$tio")) {
3502             ++c_token;
3503             aspect_ratio = real(const_express(&s));
3504         } else if (almost_equals(c_token, "nora$tio") || almost_equals(c_token, "nosq$uare")) {
3505             aspect_ratio = 0.0;
3506             ++c_token;
3507         }
3508
3509         if (!END_OF_COMMAND) {
3510             xsize = real(const_express(&s));
3511             if (equals(c_token,",")) {
3512                 c_token++;
3513                 ysize = real(const_express(&s));
3514             } else {
3515                 ysize = xsize;
3516             }
3517         }
3518     }
3519     if (xsize <= 0 || ysize <=0) {
3520         xsize = ysize = 1.0;
3521         int_error(NO_CARET,"Illegal value for size");
3522     }
3523 }
3524
3525
3526 /* process 'set style' command */
3527 static void
3528 set_style()
3529 {
3530     c_token++;
3531
3532     switch(lookup_table(&show_style_tbl[0],c_token)){
3533     case SHOW_STYLE_DATA:
3534         data_style = get_style();
3535         if (data_style == FILLEDCURVES) {
3536             get_filledcurves_style_options(&filledcurves_opts_data);
3537             if (!filledcurves_opts_data.opt_given) /* default value */
3538                 filledcurves_opts_data.closeto = FILLEDCURVES_CLOSED;
3539         }
3540         break;
3541     case SHOW_STYLE_FUNCTION:
3542         {
3543             enum PLOT_STYLE temp_style = get_style();
3544
3545             if (temp_style & PLOT_STYLE_HAS_ERRORBAR
3546 #ifdef EAM_DATASTRINGS
3547                || (temp_style == LABELPOINTS)
3548 #endif
3549 #ifdef EAM_HISTOGRAMS
3550                 || (temp_style == HISTOGRAMS)
3551 #endif
3552                 )
3553                 int_error(c_token, "style not usable for function plots, left unchanged");
3554             else
3555                 func_style = temp_style;
3556             if (func_style == FILLEDCURVES) {
3557                 get_filledcurves_style_options(&filledcurves_opts_func);
3558                 if (!filledcurves_opts_func.opt_given) /* default value */
3559                     filledcurves_opts_func.closeto = FILLEDCURVES_CLOSED;
3560             }
3561             break;
3562         }
3563     case SHOW_STYLE_LINE:
3564         set_linestyle();
3565         break;
3566     case SHOW_STYLE_FILLING:
3567         parse_fillstyle( &default_fillstyle,
3568                         default_fillstyle.fillstyle,
3569                         default_fillstyle.filldensity,
3570                         default_fillstyle.fillpattern,
3571                         default_fillstyle.border_linetype);
3572         break;
3573     case SHOW_STYLE_ARROW:
3574         set_arrowstyle();
3575         break;
3576 #ifdef EAM_OBJECTS
3577     case SHOW_STYLE_RECTANGLE:
3578         c_token++;
3579         set_rectangle(-2);
3580         break;
3581 #endif
3582 #ifdef EAM_HISTOGRAMS
3583     case SHOW_STYLE_HISTOGRAM:
3584         parse_histogramstyle(&histogram_opts,HT_CLUSTERED,histogram_opts.gap);
3585         break;
3586 #endif
3587     case SHOW_STYLE_INCREMENT:
3588         c_token++;
3589         if (END_OF_COMMAND || almost_equals(c_token,"def$ault"))
3590             prefer_line_styles = FALSE;
3591         else if (almost_equals(c_token,"u$serstyles"))
3592             prefer_line_styles = TRUE;
3593         c_token++;
3594         break;
3595     default:
3596         int_error(c_token,
3597                   "expecting 'data', 'function', 'line', 'fill' or 'arrow'" );
3598     }
3599 }
3600
3601
3602 /* process 'set surface' command */
3603 static void
3604 set_surface()
3605 {
3606     c_token++;
3607     draw_surface = TRUE;
3608 }
3609
3610
3611 /* process 'set table' command */
3612 static void
3613 set_table()
3614 {
3615     char *tablefile;
3616
3617     c_token++;
3618
3619     if (table_outfile) {
3620         fclose(table_outfile);
3621         table_outfile = NULL;
3622     }
3623
3624     if ((tablefile = try_to_get_string())) {
3625     /* 'set table "foo"' creates a new output file */
3626         if (!(table_outfile = fopen(tablefile, "w")))
3627            os_error(c_token, "cannot open table output file");
3628         free(tablefile);
3629     }
3630
3631     table_mode = TRUE;
3632
3633 }
3634
3635
3636 /* process 'set terminal' comamnd */
3637 static void
3638 set_terminal()
3639 {
3640     c_token++;
3641
3642     if (multiplot)
3643         int_error(c_token, "You can't change the terminal in multiplot mode");
3644
3645     if (END_OF_COMMAND) {
3646         list_terms();
3647         screen_ok = FALSE;
3648         return;
3649     }
3650
3651 #ifdef BACKWARDS_COMPATIBLE
3652     if (equals(c_token,"table")) {
3653         set_table();
3654         if (interactive)
3655             int_warn(NO_CARET,"The command 'set term table' is deprecated.\n\t Please use 'set table \"outfile\"' instead.\n");
3656         return;
3657     } else
3658         table_mode = FALSE;
3659 #endif
3660
3661     /* `set term push' */
3662     if (equals(c_token,"push")) {
3663         push_terminal(interactive);
3664         c_token++;
3665         return;
3666     } /* set term push */
3667
3668 #ifdef EXTENDED_COLOR_SPECS
3669     /* each terminal is supposed to turn this on, probably
3670      * somewhere when the graphics is initialized */
3671     supply_extended_color_specs = 0;
3672 #endif
3673 #ifdef USE_MOUSE
3674     event_reset((void *)1);   /* cancel zoombox etc. */
3675 #endif
3676     term_reset();
3677
3678     /* `set term pop' */
3679     if (equals(c_token,"pop")) {
3680         pop_terminal();
3681         c_token++;
3682         return;
3683     } /* set term pop */
3684
3685     /* `set term <normal terminal>' */
3686     term = 0; /* in case set_term() fails */
3687     term = set_term(c_token);
3688     c_token++;
3689     /* get optional mode parameters
3690      * not all drivers reset the option string before
3691      * strcat-ing to it, so we reset it for them
3692      */
3693     *term_options = 0;
3694     if (term)
3695         (*term->options)();
3696     if (interactive && *term_options)
3697         fprintf(stderr,"Options are '%s'\n",term_options);
3698 }
3699
3700
3701 /* 
3702  * Accept a single terminal option to apply to the current terminal if
3703  * possible.  The options are intended to be limited to those which apply
3704  * to a large number of terminals.  It would be nice also to limit it to
3705  * options for which we can test in advance to see if the terminal will
3706  * support it; that allows us to silently ignore the command rather than
3707  * issuing an error when the current terminal would not be affected anyhow.
3708  *
3709  * If necessary, the code in term->options() can detect that it was called
3710  * from here because in this case (c_token == 2), whereas when called from 
3711  * 'set term foo ...' it will see (c_token == 3).
3712  */
3713
3714 static void
3715 set_termoptions()
3716 {
3717     int save_end_of_line = num_tokens;
3718     c_token++;
3719
3720     if (END_OF_COMMAND || !term) {
3721         return;
3722     } else if (almost_equals(c_token,"enh$anced")
3723            ||  almost_equals(c_token,"noenh$anced")) {
3724         num_tokens = GPMIN(num_tokens,c_token+1);
3725         if (term->enhanced_open) {
3726             *term_options = 0;
3727             (term->options)();
3728         } else
3729             c_token++;
3730     } else if (almost_equals(c_token,"font")
3731            ||  almost_equals(c_token,"fname")) {
3732         num_tokens = GPMIN(num_tokens,c_token+2);
3733         if (term->set_font) {
3734             *term_options = 0;
3735             (term->options)();
3736         } else
3737             c_token += 2;
3738     } else if (!strcmp(term->name,"gif") && equals(c_token,"delay") && num_tokens==4) {
3739         *term_options = 0;
3740         (term->options)();
3741     } else {
3742         int_error(c_token,"This option cannot be changed using 'set termoption'");
3743     }
3744     num_tokens = save_end_of_line;
3745 }
3746
3747
3748 /* process 'set tics' command */
3749 static void
3750 set_tics()
3751 {
3752     unsigned int i = 0;
3753     TBOOLEAN axisset = FALSE;
3754     TBOOLEAN mirror_opt = FALSE; /* set to true if (no)mirror option specified) */
3755
3756     ++c_token;
3757
3758     if (END_OF_COMMAND) {
3759         for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3760             axis_array[i].tic_in = TRUE;
3761     }
3762
3763     while (!END_OF_COMMAND) {
3764         if (almost_equals(c_token, "ax$is")) {
3765             axisset = TRUE;
3766             for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3767                 axis_array[i].ticmode &= ~TICS_ON_BORDER;
3768                 axis_array[i].ticmode |= TICS_ON_AXIS;
3769             }
3770             ++c_token;
3771         } else if (almost_equals(c_token, "bo$rder")) {
3772             for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3773                 axis_array[i].ticmode &= ~TICS_ON_AXIS;
3774                 axis_array[i].ticmode |= TICS_ON_BORDER;
3775             }
3776             ++c_token;
3777         } else if (almost_equals(c_token, "mi$rror")) {
3778             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3779                 axis_array[i].ticmode |= TICS_MIRROR;
3780             mirror_opt = TRUE;
3781             ++c_token;
3782         } else if (almost_equals(c_token, "nomi$rror")) {
3783             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3784                 axis_array[i].ticmode &= ~TICS_MIRROR;
3785             mirror_opt = TRUE;
3786             ++c_token;
3787         } else if (almost_equals(c_token,"in$wards")) {
3788             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3789                 axis_array[i].tic_in = TRUE;
3790             ++c_token;
3791         } else if (almost_equals(c_token,"out$wards")) {
3792             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3793                 axis_array[i].tic_in = FALSE;
3794             ++c_token;
3795         } else if (almost_equals(c_token, "sc$ale")) {
3796             struct value a;
3797             ++c_token;
3798             if (almost_equals(c_token, "def$ault")) {
3799                 for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3800                     axis_array[i].ticscale = 1.0;
3801                     axis_array[i].miniticscale = 0.5;
3802                 }
3803                 ++c_token;
3804             } else {
3805                 double lticscale, lminiticscale;
3806                 lticscale = real(const_express(&a));
3807                 if (equals(c_token, ",")) {
3808                     ++c_token;
3809                     lminiticscale = real(const_express(&a));
3810                 } else
3811                     lminiticscale = 0.5 * lticscale;
3812                 for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3813                     axis_array[i].ticscale = lticscale;
3814                     axis_array[i].miniticscale = lminiticscale;
3815                 }
3816             }
3817         } else if (almost_equals(c_token, "ro$tate")) {
3818             axis_array[i].tic_rotate = TEXT_VERTICAL;
3819             ++c_token;
3820             if (equals(c_token, "by")) {
3821                 struct value a;
3822                 int langle;
3823                 ++c_token;
3824                 langle = (int)real(const_express(&a));
3825                 for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3826                     axis_array[i].tic_rotate = langle;
3827             }
3828         } else if (almost_equals(c_token, "noro$tate")) {
3829             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3830                 axis_array[i].tic_rotate = 0;
3831             ++c_token;
3832         } else if (almost_equals(c_token, "off$set")) {
3833             struct position lpos;
3834             ++c_token;
3835             get_position_default(&lpos, character);
3836             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3837                 axis_array[i].ticdef.offset = lpos;
3838         } else if (almost_equals(c_token, "nooff$set")) {
3839             struct position tics_nooffset =
3840                 { character, character, character, 0., 0., 0.};
3841             ++c_token;
3842             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3843                 axis_array[i].ticdef.offset = tics_nooffset;
3844         } else if (almost_equals(c_token, "format")) {
3845             set_format();
3846         } else if (almost_equals(c_token, "f$ont")) {
3847             ++c_token;
3848             /* Make sure they've specified a font */
3849             if (!isstringvalue(c_token))
3850                 int_error(c_token,"expected font");
3851             else {
3852                 char *lfont = try_to_get_string();
3853                 for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3854                     free(axis_array[i].ticdef.font);
3855                     axis_array[i].ticdef.font = gp_strdup(lfont);
3856                 }
3857                 free(lfont);
3858             }
3859         } else if (equals(c_token,"tc") ||
3860                    almost_equals(c_token,"text$color")) {
3861             struct t_colorspec lcolor;
3862             parse_colorspec(&lcolor, TC_FRAC);
3863             for (i = 0; i < AXIS_ARRAY_SIZE; ++i)
3864                 axis_array[i].ticdef.textcolor = lcolor;
3865         } else if (equals(c_token,"front")) {
3866             grid_layer = 1;
3867             ++c_token;
3868         } else if (equals(c_token,"back")) {
3869             grid_layer = 0;
3870             ++c_token;
3871         } else if (!END_OF_COMMAND) {
3872             int_error(c_token, "extraneous arguments in set tics");
3873         }
3874     }
3875
3876     /* if tics are off and not set by axis, reset to default (border) */
3877     for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3878         if (((axis_array[i].ticmode & TICS_MASK) == NO_TICS) && (!axisset)) {
3879             if ((i == SECOND_X_AXIS) || (i == SECOND_Y_AXIS))
3880                 continue; /* don't switch on secondary axes by default */
3881             axis_array[i].ticmode = TICS_ON_BORDER;
3882             if ((mirror_opt == FALSE) && ((i == FIRST_X_AXIS) || (i == FIRST_Y_AXIS) || (i == COLOR_AXIS))) {
3883                 axis_array[i].ticmode |= TICS_MIRROR;
3884             }
3885         }
3886     }
3887 }
3888
3889
3890 /* process 'set ticscale' command */
3891 static void
3892 set_ticscale()
3893 {
3894     struct value tscl;
3895     double lticscale, lminiticscale;
3896     unsigned int i;
3897
3898     int_warn(c_token,
3899              "Deprecated syntax - please use 'set tics scale' keyword");
3900
3901     ++c_token;
3902     if (END_OF_COMMAND) {
3903         lticscale = 1.0;
3904         lminiticscale = 0.5;
3905     } else {
3906         lticscale = real(const_express(&tscl));
3907         if (END_OF_COMMAND) {
3908             lminiticscale = lticscale*0.5;
3909         } else {
3910             if (equals(c_token, ","))
3911                 ++c_token;
3912             lminiticscale = real(const_express(&tscl));
3913         }
3914     }
3915     for (i = 0; i < AXIS_ARRAY_SIZE; ++i) {
3916         axis_array[i].ticscale = lticscale;
3917         axis_array[i].miniticscale = lminiticscale;
3918     }
3919 }
3920
3921
3922 /* process 'set ticslevel' or 'set xyplane' command */
3923 /* is datatype 'time' relevant here ? */
3924 static void
3925 set_ticslevel()
3926 {
3927     struct value a;
3928
3929     if (equals(++c_token, "at")) {
3930         c_token++;
3931         xyplane.xyplane_z = real(const_express(&a));
3932         xyplane.absolute = TRUE;
3933     } else {
3934         xyplane.ticslevel = real(const_express(&a));
3935         xyplane.absolute = FALSE;
3936     }
3937 }
3938
3939
3940 /* Process 'set timefmt' command */
3941 /* HBB 20000507: changed this to a per-axis setting. I.e. you can now
3942  * have separate timefmt parse strings, different axes */
3943 static void
3944 set_timefmt()
3945 {
3946     int axis;
3947
3948     c_token++;
3949     if (END_OF_COMMAND) {
3950         /* set all axes to default */
3951         for (axis = 0; axis < AXIS_ARRAY_SIZE; axis++)
3952             strcpy(axis_array[axis].timefmt,TIMEFMT);
3953     } else {
3954         if ((axis = lookup_table(axisname_tbl, c_token)) >= 0) {
3955             c_token++;
3956         if (isstring(c_token)) {
3957                 quote_str(axis_array[axis].timefmt,c_token, MAX_ID_LEN);
3958                 c_token++;
3959             } else {
3960                 int_error(c_token, "time format string expected");
3961         }
3962         } else if (isstring(c_token)) {
3963             /* set the given parse string for all current timedata axes: */
3964             for (axis = 0; axis < AXIS_ARRAY_SIZE; axis++)
3965                 quote_str(axis_array[axis].timefmt, c_token, MAX_ID_LEN);
3966         c_token++;
3967         } else {
3968             int_error(c_token, "time format string expected");
3969         }
3970     }
3971 }
3972
3973
3974 /* process 'set timestamp' command */
3975 static void
3976 set_timestamp()
3977 {
3978     TBOOLEAN got_format = FALSE;
3979     char *new;
3980
3981     c_token++;
3982
3983     while (!END_OF_COMMAND) {
3984
3985         if (almost_equals(c_token,"t$op")) {
3986             timelabel_bottom = FALSE;
3987             c_token++;
3988             continue;
3989         } else if (almost_equals(c_token, "b$ottom")) {
3990             timelabel_bottom = TRUE;
3991             c_token++;
3992             continue;
3993         }
3994
3995         if (almost_equals(c_token,"r$otate")) {
3996             timelabel_rotate = TRUE;
3997             c_token++;
3998             continue;
3999         } else if (almost_equals(c_token, "n$orotate")) {
4000             timelabel_rotate = FALSE;
4001             c_token++;
4002             continue;
4003         }
4004
4005         if (almost_equals(c_token,"off$set")) {
4006             c_token++;
4007             get_position_default(&(timelabel.offset),character);
4008             continue;
4009         }
4010
4011         if (equals(c_token,"font")) {
4012             c_token++;
4013             new = try_to_get_string();
4014             free(timelabel.font);
4015             timelabel.font = new;
4016             continue;
4017         }
4018
4019         if (!got_format && ((new = try_to_get_string()))) {
4020             /* we have a format string */
4021             free(timelabel.text);
4022             timelabel.text = new;
4023             got_format = TRUE;
4024             continue;
4025         }
4026
4027 #ifdef BACKWARDS_COMPATIBLE
4028         /* The "font" keyword is new (v4.1), for backward compatibility we don't enforce it */
4029         if (!END_OF_COMMAND && ((new = try_to_get_string()))) {
4030             free(timelabel.font);
4031             timelabel.font = new;
4032             continue;
4033         }
4034         /* The "offset" keyword is new (v4.1); for backward compatibility we don't enforce it */
4035         get_position_default(&(timelabel.offset),character);
4036 #else
4037         int_error(c_token,"unrecognized option");
4038 #endif
4039
4040     }
4041
4042     if (!(timelabel.text))
4043         timelabel.text = gp_strdup(DEFAULT_TIMESTAMP_FORMAT);
4044
4045 }
4046
4047
4048 /* process 'set view' command */
4049 static void
4050 set_view()
4051 {
4052     int i;
4053     TBOOLEAN was_comma = TRUE;
4054     static const char errmsg1[] = "rot_%c must be in [0:%d] degrees range; view unchanged";
4055     static const char errmsg2[] = "%sscale must be > 0; view unchanged";
4056     double local_vals[4];
4057     struct value a;
4058
4059     c_token++;
4060     if (equals(c_token,"map")) {
4061             splot_map = TRUE;
4062             c_token++;
4063             return;
4064     };
4065
4066     if (splot_map == TRUE) {
4067         splot_map_deactivate();
4068         splot_map = FALSE; /* default is no map */
4069     }
4070
4071     if (almost_equals(c_token,"equal$_axes")) {
4072         c_token++;
4073         if (END_OF_COMMAND || equals(c_token,"xy")) {
4074             aspect_ratio_3D = 2;
4075             c_token++;
4076         } else if (equals(c_token,"xyz")) {
4077             aspect_ratio_3D = 3;
4078             c_token++;
4079         }
4080         return;
4081     } else if (almost_equals(c_token,"noequal$_axes")) {
4082         aspect_ratio_3D = 0;
4083         c_token++;
4084         return;
4085     }
4086
4087     local_vals[0] = surface_rot_x;
4088     local_vals[1] = surface_rot_z;
4089     local_vals[2] = surface_scale;
4090     local_vals[3] = surface_zscale;
4091     for (i = 0; i < 4 && !(END_OF_COMMAND);) {
4092         if (equals(c_token,",")) {
4093             if (was_comma) i++;
4094             was_comma = TRUE;
4095             c_token++;
4096         } else {
4097             if (!was_comma)
4098                 int_error(c_token, "',' expected");
4099             local_vals[i] = real(const_express(&a));
4100             i++;
4101             was_comma = FALSE;
4102         }
4103     }
4104
4105     if (local_vals[0] < 0 || local_vals[0] > 180)
4106         int_error(c_token, errmsg1, 'x', 180);
4107     if (local_vals[1] < 0 || local_vals[1] > 360)
4108         int_error(c_token, errmsg1, 'z', 360);
4109     if (local_vals[2] < 1e-6)
4110         int_error(c_token, errmsg2, "");
4111     if (local_vals[3] < 1e-6)
4112         int_error(c_token, errmsg2, "z");
4113
4114     surface_rot_x = local_vals[0];
4115     surface_rot_z = local_vals[1];
4116     surface_scale = local_vals[2];
4117     surface_zscale = local_vals[3];
4118
4119 }
4120
4121
4122 /* process 'set zero' command */
4123 static void
4124 set_zero()
4125 {
4126     struct value a;
4127     c_token++;
4128     zero = magnitude(const_express(&a));
4129 }
4130
4131
4132 /* process 'set {x|y|z|x2|y2}data' command */
4133 static void
4134 set_timedata(AXIS_INDEX axis)
4135 {
4136     c_token++;
4137     if(END_OF_COMMAND) {
4138         axis_array[axis].is_timedata = FALSE;
4139     } else {
4140         if ((axis_array[axis].is_timedata = almost_equals(c_token,"t$ime")))
4141             c_token++;
4142 }
4143 }
4144
4145
4146 static void
4147 set_range(AXIS_INDEX axis)
4148 {
4149     c_token++;
4150
4151     if (splot_map)
4152         splot_map_deactivate();
4153
4154     if(almost_equals(c_token,"re$store")) { /* ULIG */
4155         c_token++;
4156         axis_array[axis].set_min = get_writeback_min(axis);
4157         axis_array[axis].set_max = get_writeback_max(axis);
4158         axis_array[axis].set_autoscale = AUTOSCALE_NONE;
4159     } else {
4160         if (!equals(c_token,"["))
4161             int_error(c_token, "expecting '[' or 'restore'");
4162         c_token++;
4163         axis_array[axis].set_autoscale =
4164             load_range(axis,
4165                        &axis_array[axis].set_min,&axis_array[axis].set_max,
4166                        axis_array[axis].set_autoscale);
4167         if (!equals(c_token,"]"))
4168             int_error(c_token, "expecting ']'");
4169         c_token++;
4170         if (almost_equals(c_token, "rev$erse")) {
4171             ++c_token;
4172             axis_array[axis].range_flags |= RANGE_REVERSE;
4173         } else if (almost_equals(c_token, "norev$erse")) {
4174             ++c_token;
4175             axis_array[axis].range_flags &= ~RANGE_REVERSE;
4176         }
4177         if (almost_equals(c_token, "wr$iteback")) {
4178             ++c_token;
4179             axis_array[axis].range_flags |= RANGE_WRITEBACK;
4180         } else if (almost_equals(c_token, "nowri$teback")) {
4181             ++c_token;
4182             axis_array[axis].range_flags &= ~RANGE_WRITEBACK;
4183         }
4184     }
4185     if (splot_map)
4186         splot_map_activate();
4187 }
4188
4189 /* process 'set {xyz}zeroaxis' command */
4190 static void
4191 set_zeroaxis(AXIS_INDEX axis)
4192 {
4193
4194     c_token++;
4195     if (END_OF_COMMAND)
4196         axis_array[axis].zeroaxis.l_type = -1;
4197     else {
4198         struct value a;
4199         int old_token = c_token;
4200         axis_array[axis].zeroaxis.l_type = LT_AXIS;
4201         lp_parse(&axis_array[axis].zeroaxis, TRUE, FALSE);
4202         if (old_token == c_token)
4203             axis_array[axis].zeroaxis.l_type = real(const_express(&a)) - 1;
4204         }
4205
4206 }
4207
4208 /* process 'set zeroaxis' command */
4209 static void
4210 set_allzeroaxis()
4211 {
4212     set_zeroaxis(FIRST_X_AXIS);
4213     axis_array[FIRST_Y_AXIS].zeroaxis = axis_array[FIRST_X_AXIS].zeroaxis;
4214 #ifndef BACKWARDS_COMPATIBLE
4215     axis_array[FIRST_Z_AXIS].zeroaxis = axis_array[FIRST_X_AXIS].zeroaxis;
4216 #endif
4217 }
4218
4219 /*********** Support functions for set_command ***********/
4220
4221 /*
4222  * The set.c PROCESS_TIC_PROP macro has the following characteristics:
4223  *   (a) options must in the correct order
4224  *   (b) 'set xtics' (no option) resets only the interval (FREQ)
4225  *       {it will also negate NO_TICS, see (d)}
4226  *   (c) changing any property also resets the interval to automatic
4227  *   (d) set no[xy]tics; set [xy]tics changes border to nomirror, rather
4228  *       than to the default, mirror.
4229  *   (e) effect of 'set no[]tics; set []tics border ...' is compiler
4230  *       dependent;  if '!(TICS)' is evaluated first, 'border' is an
4231  *       undefined variable :-(
4232  *
4233  * This function replaces the macro, and introduces a new option
4234  * 'au$tofreq' to give somewhat different behaviour:
4235  *   (a) no change
4236  *   (b) 'set xtics' (no option) only affects NO_TICS;  'autofreq' resets
4237  *       the interval calulation to automatic
4238  *   (c) the interval mode is not affected by changing some other option
4239  *   (d) if NO_TICS, set []tics will restore defaults (borders, mirror
4240  *       where appropriate)
4241  *   (e) if (NO_TICS), border option is processed.
4242  *
4243  *  A 'default' option could easily be added to reset all options to
4244  *  the initial values - mostly book-keeping.
4245  *
4246  *  To retain tic properties after setting no[]tics may also be
4247  *  straightforward (save value as negative), but requires changes
4248  *  in other code ( e.g. for  'if (xtics)', use 'if (xtics > 0)'
4249  */
4250
4251 /*    generates PROCESS_TIC_PROP strings from tic_side, e.g. "x2"
4252  *  STRING, NOSTRING, MONTH, NOMONTH, DAY, NODAY, MINISTRING, NOMINI
4253  *  "nox2t$ics"     "nox2m$tics"  "nox2d$tics"    "nomx2t$ics"
4254  */
4255
4256 static int
4257 set_tic_prop(AXIS_INDEX axis)
4258 {
4259     int match = 0;              /* flag, set by matching a tic command */
4260     char nocmd[12];             /* fill w/ "no"+axis_name+suffix */
4261     char *cmdptr, *sfxptr;
4262
4263     (void) strcpy(nocmd, "no");
4264     cmdptr = &nocmd[2];
4265     (void) strcpy(cmdptr, axis_defaults[axis].name);
4266     sfxptr = &nocmd[strlen(nocmd)];
4267     (void) strcpy(sfxptr, "t$ics");     /* STRING */
4268
4269     if (almost_equals(c_token, cmdptr)) {
4270         TBOOLEAN axisset = FALSE;
4271         TBOOLEAN mirror_opt = FALSE; /* set to true if (no)mirror option specified) */
4272         axis_array[axis].ticdef.def.mix = FALSE;
4273         match = 1;
4274         ++c_token;
4275         do {
4276             if (almost_equals(c_token, "ax$is")) {
4277                 axisset = TRUE;
4278                 axis_array[axis].ticmode &= ~TICS_ON_BORDER;
4279                 axis_array[axis].ticmode |= TICS_ON_AXIS;
4280                 ++c_token;
4281             } else if (almost_equals(c_token, "bo$rder")) {
4282                 axis_array[axis].ticmode &= ~TICS_ON_AXIS;
4283                 axis_array[axis].ticmode |= TICS_ON_BORDER;
4284                 ++c_token;
4285             } else if (almost_equals(c_token, "mi$rror")) {
4286                 axis_array[axis].ticmode |= TICS_MIRROR;
4287                 mirror_opt = TRUE;
4288                 ++c_token;
4289             } else if (almost_equals(c_token, "nomi$rror")) {
4290                 axis_array[axis].ticmode &= ~TICS_MIRROR;
4291                 mirror_opt = TRUE;
4292                 ++c_token;
4293             } else if (almost_equals(c_token, "in$wards")) {
4294                 axis_array[axis].tic_in = TRUE;
4295                 ++c_token;
4296             } else if (almost_equals(c_token, "out$wards")) {
4297                 axis_array[axis].tic_in = FALSE;
4298                 ++c_token;
4299             } else if (almost_equals(c_token, "sc$ale")) {
4300                 struct value a;
4301                 ++c_token;
4302                 if (almost_equals(c_token, "def$ault")) {
4303                     axis_array[axis].ticscale = 1.0;
4304                     axis_array[axis].miniticscale = 0.5;
4305                     ++c_token;
4306                 } else {
4307                     axis_array[axis].ticscale = real(const_express(&a));
4308                     if (equals(c_token, ",")) {
4309                         ++c_token;
4310                         axis_array[axis].miniticscale =
4311                             real(const_express(&a));
4312                     } else
4313                         axis_array[axis].miniticscale =
4314                             0.5 * axis_array[axis].ticscale;
4315                 }
4316             } else if (almost_equals(c_token, "ro$tate")) {
4317                 axis_array[axis].tic_rotate = TEXT_VERTICAL;
4318                 ++c_token;
4319                 if (equals(c_token, "by")) {
4320                     struct value a;
4321                     c_token++;
4322                     axis_array[axis].tic_rotate =
4323                         (int)real(const_express(&a));
4324                 }
4325             } else if (almost_equals(c_token, "noro$tate")) {
4326                 axis_array[axis].tic_rotate = 0;
4327                 ++c_token;
4328             } else if (almost_equals(c_token, "off$set")) {
4329                 ++c_token;
4330                 get_position_default(&axis_array[axis].ticdef.offset,
4331                                      character);
4332             } else if (almost_equals(c_token, "nooff$set")) {
4333                 struct position tics_nooffset =
4334                     { character, character, character, 0., 0., 0.};
4335                 ++c_token;
4336                 axis_array[axis].ticdef.offset = tics_nooffset;
4337             } else if (almost_equals(c_token,"range$limited")) {
4338                 axis_array[axis].ticdef.rangelimited = TRUE;
4339                 ++c_token;
4340             } else if (almost_equals(c_token,"norange$limited")) {
4341                 axis_array[axis].ticdef.rangelimited = FALSE;
4342                 ++c_token;
4343             } else if (almost_equals(c_token, "f$ont")) {
4344                 ++c_token;
4345                 /* Make sure they've specified a font */
4346                 if (!isstringvalue(c_token))
4347                     int_error(c_token,"expected font");
4348                 else {
4349                     free(axis_array[axis].ticdef.font);
4350                     axis_array[axis].ticdef.font = NULL;
4351                     axis_array[axis].ticdef.font = try_to_get_string();
4352                 }
4353             } else if (equals(c_token,"format")) {
4354                 char *format;
4355                 ++c_token;
4356                 if (!((format = try_to_get_string())))
4357                     int_error(c_token,"expected format");
4358                 strncpy(axis_array[axis].formatstring, format,
4359                         sizeof(axis_array[axis].formatstring));
4360                 free(format);
4361                 axis_array[axis].format_is_numeric =
4362                         looks_like_numeric(axis_array[axis].formatstring);
4363             } else if (equals(c_token,"tc") ||
4364                        almost_equals(c_token,"text$color")) {
4365                 parse_colorspec(&axis_array[axis].ticdef.textcolor, TC_FRAC);
4366             } else if (almost_equals(c_token, "au$tofreq")) {
4367                 /* auto tic interval */
4368                 ++c_token;
4369                 if (!axis_array[axis].ticdef.def.mix) {
4370                     free_marklist(axis_array[axis].ticdef.def.user);
4371                     axis_array[axis].ticdef.def.user = NULL;
4372                 }
4373                 axis_array[axis].ticdef.type = TIC_COMPUTED;
4374             } else if (equals(c_token,"add")) {
4375                 ++c_token;
4376                 axis_array[axis].ticdef.def.mix = TRUE;
4377             } else if (!END_OF_COMMAND) {
4378                 load_tics(axis);
4379             }
4380         } while (!END_OF_COMMAND);
4381
4382         /* if tics are off and not set by axis, reset to default (border) */
4383         if (((axis_array[axis].ticmode & TICS_MASK) == NO_TICS) && (!axisset)) {
4384             axis_array[axis].ticmode |= TICS_ON_BORDER;
4385             if ((mirror_opt == FALSE) && ((axis == FIRST_X_AXIS) || (axis == FIRST_Y_AXIS) || (axis == COLOR_AXIS))) {
4386                 axis_array[axis].ticmode |= TICS_MIRROR;
4387             }
4388         }
4389
4390     }
4391
4392     if (almost_equals(c_token, nocmd)) {        /* NOSTRING */
4393         axis_array[axis].ticmode &= ~TICS_MASK;
4394         c_token++;
4395         match = 1;
4396     }
4397 /* other options */
4398
4399     (void) strcpy(sfxptr, "m$tics");    /* MONTH */
4400     if (almost_equals(c_token, cmdptr)) {
4401         if (!axis_array[axis].ticdef.def.mix) {
4402             free_marklist(axis_array[axis].ticdef.def.user);
4403             axis_array[axis].ticdef.def.user = NULL;
4404         }
4405         axis_array[axis].ticdef.type = TIC_MONTH;
4406         ++c_token;
4407         match = 1;
4408     }
4409     if (almost_equals(c_token, nocmd)) {        /* NOMONTH */
4410         axis_array[axis].ticdef.type = TIC_COMPUTED;
4411         ++c_token;
4412         match = 1;
4413     }
4414     (void) strcpy(sfxptr, "d$tics");    /* DAYS */
4415     if (almost_equals(c_token, cmdptr)) {
4416         match = 1;
4417         if (!axis_array[axis].ticdef.def.mix) {
4418             free_marklist(axis_array[axis].ticdef.def.user);
4419             axis_array[axis].ticdef.def.user = NULL;
4420         }
4421         axis_array[axis].ticdef.type = TIC_DAY;
4422         ++c_token;
4423     }
4424     if (almost_equals(c_token, nocmd)) {        /* NODAYS */
4425         axis_array[axis].ticdef.type = TIC_COMPUTED;
4426         ++c_token;
4427         match = 1;
4428     }
4429     *cmdptr = 'm';
4430     (void) strcpy(cmdptr + 1, axis_defaults[axis].name);
4431     (void) strcat(cmdptr, "t$ics");     /* MINISTRING */
4432
4433     if (almost_equals(c_token, cmdptr)) {
4434         struct value freq;
4435         c_token++;
4436         match = 1;
4437         if (END_OF_COMMAND) {
4438             axis_array[axis].minitics = MINI_AUTO;
4439         } else if (almost_equals(c_token, "def$ault")) {
4440             axis_array[axis].minitics = MINI_DEFAULT;
4441             ++c_token;
4442         } else {
4443             axis_array[axis].mtic_freq = floor(real(const_express(&freq)));
4444             axis_array[axis].minitics = MINI_USER;
4445         }
4446     }
4447     if (almost_equals(c_token, nocmd)) {        /* NOMINI */
4448         axis_array[axis].minitics = FALSE;
4449         c_token++;
4450         match = 1;
4451     }
4452     return (match);
4453 }
4454
4455 /* process a 'set {x/y/z}label command */
4456 /* set {x/y/z}label {label_text} {offset {x}{,y}} {<fontspec>} {<textcolor>} */
4457 static void
4458 set_xyzlabel(text_label *label)
4459 {
4460     char *text = NULL;
4461
4462     c_token++;
4463     if (END_OF_COMMAND) {       /* no label specified */
4464         free(label->text);
4465         label->text = NULL;
4466         return;
4467     }
4468
4469     parse_label_options(label);
4470
4471     if (!END_OF_COMMAND) {
4472         text = try_to_get_string();
4473         if (text) {
4474             free(label->text);
4475             label->text = text;
4476         }
4477 #ifdef BACKWARDS_COMPATIBLE
4478         if (isanumber(c_token) || equals(c_token, "-")) {
4479             /* Parse offset with missing keyword "set xlabel 'foo' 1,2 "*/
4480             get_position_default(&(label->offset),character);
4481         }
4482 #endif
4483     }
4484
4485     parse_label_options(label);
4486
4487 }
4488
4489
4490
4491 /* 'set style line' command */
4492 /* set style line {tag} {linetype n} {linewidth x} {pointtype n} {pointsize x} */
4493 static void
4494 set_linestyle()
4495 {
4496     struct value a;
4497     struct linestyle_def *this_linestyle = NULL;
4498     struct linestyle_def *new_linestyle = NULL;
4499     struct linestyle_def *prev_linestyle = NULL;
4500     struct lp_style_type loc_lp = DEFAULT_LP_STYLE_TYPE;
4501     int tag;
4502
4503     c_token++;
4504
4505     /* get tag */
4506     if (!END_OF_COMMAND) {
4507         /* must be a tag expression! */
4508         tag = (int) real(const_express(&a));
4509         if (tag <= 0)
4510             int_error(c_token, "tag must be > zero");
4511     } else
4512         tag = assign_linestyle_tag();   /* default next tag */
4513
4514     /* Default style is based on linetype with the same tag id */
4515     loc_lp.l_type = tag - 1;
4516     loc_lp.p_type = tag - 1;
4517
4518     /* Check if linestyle is already defined */
4519     if (first_linestyle != NULL) {      /* skip to last linestyle */
4520         for (this_linestyle = first_linestyle; this_linestyle != NULL;
4521              prev_linestyle = this_linestyle,
4522              this_linestyle = this_linestyle->next)
4523             /* is this the linestyle we want? */
4524             if (tag <= this_linestyle->tag)
4525                 break;
4526     }
4527
4528     if (this_linestyle == NULL || tag != this_linestyle->tag) {
4529         new_linestyle = gp_alloc(sizeof(struct linestyle_def), "linestyle");
4530         if (prev_linestyle != NULL)
4531             prev_linestyle->next = new_linestyle;       /* add it to end of list */
4532         else
4533             first_linestyle = new_linestyle;    /* make it start of list */
4534         new_linestyle->tag = tag;
4535         new_linestyle->next = this_linestyle;
4536         new_linestyle->lp_properties = loc_lp;
4537         this_linestyle = new_linestyle;
4538     }
4539
4540     /* Reset to default values */
4541     if (END_OF_COMMAND)
4542         this_linestyle->lp_properties = loc_lp;
4543     else if (almost_equals(c_token, "def$ault")) {
4544         this_linestyle->lp_properties = loc_lp;
4545         c_token++;
4546     } else
4547         /* pick up a line spec; dont allow ls, do allow point type */
4548         lp_parse(&this_linestyle->lp_properties, FALSE, TRUE);
4549
4550     if (!END_OF_COMMAND)
4551         int_error(c_token,"Extraneous arguments to set style line");
4552
4553 }
4554
4555 /* assign a new linestyle tag
4556  * linestyles are kept sorted by tag number, so this is easy
4557  * returns the lowest unassigned tag number
4558  */
4559 static int
4560 assign_linestyle_tag()
4561 {
4562     struct linestyle_def *this;
4563     int last = 0;               /* previous tag value */
4564
4565     for (this = first_linestyle; this != NULL; this = this->next)
4566         if (this->tag == last + 1)
4567             last++;
4568         else
4569             break;
4570
4571     return (last + 1);
4572 }
4573
4574 /* delete linestyle from linked list started by first_linestyle.
4575  * called with pointers to the previous linestyle (prev) and the
4576  * linestyle to delete (this).
4577  * If there is no previous linestyle (the linestyle to delete is
4578  * first_linestyle) then call with prev = NULL.
4579  */
4580 void
4581 delete_linestyle(struct linestyle_def *prev, struct linestyle_def *this)
4582 {
4583     if (this != NULL) {         /* there really is something to delete */
4584         if (prev != NULL)       /* there is a previous linestyle */
4585             prev->next = this->next;
4586         else                    /* this = first_linestyle so change first_linestyle */
4587             first_linestyle = this->next;
4588         free(this);
4589     }
4590 }
4591
4592
4593 /* ======================================================== */
4594 /* process a 'set arrowstyle' command */
4595 /* set style arrow {tag} {nohead|head|backhead|heads} {size l,a{,b}} {{no}filled} {linestyle...} {layer n}*/
4596 static void
4597 set_arrowstyle()
4598 {
4599     struct value a;
4600     struct arrowstyle_def *this_arrowstyle = NULL;
4601     struct arrowstyle_def *new_arrowstyle = NULL;
4602     struct arrowstyle_def *prev_arrowstyle = NULL;
4603     struct arrow_style_type loc_arrow;
4604     int tag;
4605
4606     default_arrow_style(&loc_arrow);
4607
4608     c_token++;
4609
4610     /* get tag */
4611     if (!END_OF_COMMAND) {
4612         /* must be a tag expression! */
4613         tag = (int) real(const_express(&a));
4614         if (tag <= 0)
4615             int_error(c_token, "tag must be > zero");
4616     } else
4617         tag = assign_arrowstyle_tag();  /* default next tag */
4618
4619     /* search for arrowstyle */
4620     if (first_arrowstyle != NULL) {     /* skip to last arrowstyle */
4621         for (this_arrowstyle = first_arrowstyle; this_arrowstyle != NULL;
4622              prev_arrowstyle = this_arrowstyle,
4623              this_arrowstyle = this_arrowstyle->next)
4624             /* is this the arrowstyle we want? */
4625             if (tag <= this_arrowstyle->tag)
4626                 break;
4627     }
4628
4629     if (this_arrowstyle == NULL || tag != this_arrowstyle->tag) {
4630         /* adding the arrowstyle */
4631         new_arrowstyle = (struct arrowstyle_def *)
4632             gp_alloc(sizeof(struct arrowstyle_def), "arrowstyle");
4633         default_arrow_style(&(new_arrowstyle->arrow_properties));
4634         if (prev_arrowstyle != NULL)
4635             prev_arrowstyle->next = new_arrowstyle;     /* add it to end of list */
4636         else
4637             first_arrowstyle = new_arrowstyle;  /* make it start of list */
4638         new_arrowstyle->tag = tag;
4639         new_arrowstyle->next = this_arrowstyle;
4640         this_arrowstyle = new_arrowstyle;
4641     }
4642
4643     if (END_OF_COMMAND)
4644         this_arrowstyle->arrow_properties = loc_arrow;
4645     else if (almost_equals(c_token, "def$ault")) {
4646         this_arrowstyle->arrow_properties = loc_arrow;
4647         c_token++;
4648     } else
4649         /* pick up a arrow spec : dont allow arrowstyle */
4650         arrow_parse(&this_arrowstyle->arrow_properties, FALSE);
4651
4652     if (!END_OF_COMMAND)
4653         int_error(c_token, "extraneous or out-of-order arguments in set arrowstyle");
4654
4655 }
4656
4657 /* assign a new arrowstyle tag
4658  * arrowstyles are kept sorted by tag number, so this is easy
4659  * returns the lowest unassigned tag number
4660  */
4661 static int
4662 assign_arrowstyle_tag()
4663 {
4664     struct arrowstyle_def *this;
4665     int last = 0;               /* previous tag value */
4666
4667     for (this = first_arrowstyle; this != NULL; this = this->next)
4668         if (this->tag == last + 1)
4669             last++;
4670         else
4671             break;
4672
4673     return (last + 1);
4674 }
4675
4676 /* For set [xy]tics... command */
4677 static void
4678 load_tics(AXIS_INDEX axis)
4679 {
4680     if (equals(c_token, "(")) { /* set : TIC_USER */
4681         if (equals(++c_token, ")"))
4682             c_token++;
4683         else
4684             load_tic_user(axis);
4685     } else {                    /* series : TIC_SERIES */
4686         load_tic_series(axis);
4687     }
4688 }
4689
4690 /* load TIC_USER definition */
4691 /* (tic[,tic]...)
4692  * where tic is ["string"] value [level]
4693  * Left paren is already scanned off before entry.
4694  */
4695 static void
4696 load_tic_user(AXIS_INDEX axis)
4697 {
4698     char *ticlabel;
4699     double ticposition;
4700
4701     /* Free any old tic labels */
4702     if (!axis_array[axis].ticdef.def.mix) {
4703         free_marklist(axis_array[axis].ticdef.def.user);
4704         axis_array[axis].ticdef.def.user = NULL;
4705     }
4706
4707     while (!END_OF_COMMAND) {
4708         int ticlevel=0;
4709         int save_token;
4710         /* syntax is  (  {'format'} value {level} {, ...} )
4711          * but for timedata, the value itself is a string, which
4712          * complicates things somewhat
4713          */
4714
4715         /* has a string with it? */
4716         save_token = c_token;
4717         ticlabel = try_to_get_string();
4718         if (ticlabel && axis_array[axis].is_timedata
4719             && (equals(c_token,",") || equals(c_token,")"))) {
4720             c_token = save_token;
4721             free(ticlabel);
4722             ticlabel = NULL;
4723         }
4724
4725         /* in any case get the value */
4726         GET_NUM_OR_TIME(ticposition, axis);
4727
4728         if (!END_OF_COMMAND &&
4729             !equals(c_token, ",") &&
4730             !equals(c_token, ")")) {
4731           struct value value;
4732           ticlevel = (int) real(const_express(&value)); /* tic level */
4733         }
4734
4735         /* add to list */
4736         add_tic_user(axis, ticlabel, ticposition, ticlevel);
4737         free(ticlabel);
4738
4739         /* expect "," or ")" here */
4740         if (!END_OF_COMMAND && equals(c_token, ","))
4741             c_token++;          /* loop again */
4742         else
4743             break;              /* hopefully ")" */
4744     }
4745
4746     if (END_OF_COMMAND || !equals(c_token, ")")) {
4747         free_marklist(axis_array[axis].ticdef.def.user);
4748         axis_array[axis].ticdef.def.user = NULL;
4749         int_error(c_token, "expecting right parenthesis )");
4750     }
4751     c_token++;
4752 }
4753
4754 void
4755 free_marklist(struct ticmark *list)
4756 {
4757     while (list != NULL) {
4758         struct ticmark *freeable = list;
4759         list = list->next;
4760         if (freeable->label != NULL)
4761             free(freeable->label);
4762         free(freeable);
4763     }
4764 }
4765
4766 /* Remove tic labels that were read from a datafile during a previous plot
4767  * via the 'using xtics(n)' mechanism.  These have tick level < 0.
4768  */
4769 struct ticmark *
4770 prune_dataticks(struct ticmark *list)
4771 {
4772     struct ticmark a = {0.0,NULL,0,NULL};
4773     struct ticmark *b = &a;
4774     struct ticmark *tmp;
4775
4776     while (list) {
4777         if (list->level < 0) {
4778             free(list->label);
4779             tmp = list->next;
4780             free(list);
4781             list = tmp;
4782         } else {
4783             b->next = list;
4784             b = list;
4785             list = list->next;
4786         }
4787     }
4788     b->next = NULL;
4789     return a.next;
4790 }
4791
4792 /* load TIC_SERIES definition */
4793 /* [start,]incr[,end] */
4794 static void
4795 load_tic_series(AXIS_INDEX axis)
4796 {
4797     double start, incr, end;
4798     int incr_token;
4799
4800     struct ticdef *tdef = &axis_array[axis].ticdef;
4801
4802     GET_NUM_OR_TIME(start, axis);
4803
4804     if (!equals(c_token, ",")) {
4805         /* only step specified */
4806         incr = start;
4807         start = -VERYLARGE;
4808         end = VERYLARGE;
4809     } else {
4810         c_token++;
4811         incr_token = c_token;
4812         GET_NUM_OR_TIME(incr, axis);
4813
4814         if (!equals(c_token, ",")) {
4815             /* only step and increment specified */
4816             end = VERYLARGE;
4817         } else {
4818             c_token++;
4819             GET_NUM_OR_TIME(end, axis);
4820         }
4821
4822         if (start < end && incr <= 0)
4823             int_error(incr_token, "increment must be positive");
4824         if (start > end && incr >= 0)
4825             int_error(incr_token, "increment must be negative");
4826         if (start > end) {
4827             /* put in order */
4828             double numtics = floor((end * (1 + SIGNIF) - start) / incr);
4829
4830             end = start;
4831             start = end + numtics * incr;
4832             incr = -incr;
4833         }
4834     }
4835
4836     if (!tdef->def.mix) { /* remove old list */
4837         free_marklist(tdef->def.user);
4838         tdef->def.user = NULL;
4839     }
4840     tdef->type = TIC_SERIES;
4841     tdef->def.series.start = start;
4842     tdef->def.series.incr = incr;
4843     tdef->def.series.end = end;
4844 }
4845
4846 static void
4847 load_offsets(double *a, double *b, double *c, double *d)
4848 {
4849     struct value t;
4850
4851     *a = real(const_express(&t));       /* loff value */
4852     if (!equals(c_token, ","))
4853         return;
4854
4855     c_token++;
4856     *b = real(const_express(&t));       /* roff value */
4857     if (!equals(c_token, ","))
4858         return;
4859
4860     c_token++;
4861     *c = real(const_express(&t));       /* toff value */
4862     if (!equals(c_token, ","))
4863         return;
4864
4865     c_token++;
4866     *d = real(const_express(&t));       /* boff value */
4867 }
4868
4869 /* return 1 if format looks like a numeric format
4870  * ie more than one %{efg}, or %something-else
4871  */
4872 /* FIXME HBB 20000430: as coded, this will only check the *first*
4873  * format string, not all of them. */
4874 static int
4875 looks_like_numeric(char *format)
4876 {
4877     if (!(format = strchr(format, '%')))
4878         return 0;
4879
4880     while (++format && (*format == ' '
4881                         || *format == '-'
4882                         || *format == '+'
4883                         || *format == '#'))
4884         ;                       /* do nothing */
4885
4886     while (isdigit((unsigned char) *format)
4887            || *format == '.')
4888         ++format;
4889
4890     return (*format == 'f' || *format == 'g' || *format == 'e');
4891 }
4892
4893
4894 /*
4895  * Backwards compatibility ...
4896  */
4897 static void set_nolinestyle()
4898 {
4899     struct value a;
4900     struct linestyle_def *this, *prev;
4901     int tag;
4902
4903     if (END_OF_COMMAND) {
4904         /* delete all linestyles */
4905         while (first_linestyle != NULL)
4906             delete_linestyle((struct linestyle_def *) NULL, first_linestyle);
4907     } else {
4908         /* get tag */
4909         tag = (int) real(const_express(&a));
4910         if (!END_OF_COMMAND)
4911             int_error(c_token, "extraneous arguments to set nolinestyle");
4912         for (this = first_linestyle, prev = NULL;
4913              this != NULL;
4914              prev = this, this = this->next) {
4915             if (this->tag == tag) {
4916                 delete_linestyle(prev, this);
4917                 return;         /* exit, our job is done */
4918             }
4919         }
4920         int_error(c_token, "linestyle not found");
4921     }
4922 }
4923
4924
4925 /* HBB 20001021: new function: make label texts decoratable with numbers */
4926 static char *
4927 fill_numbers_into_string(char *pattern)
4928 {
4929     size_t pattern_length = strlen(pattern) + 1;
4930     size_t newlen = pattern_length;
4931     char *output = gp_alloc(newlen, "fill_numbers output buffer");
4932     size_t output_end = 0;
4933
4934     do {                        /* loop over string/value pairs */
4935         struct value a;
4936         double value;
4937
4938         if (isstring(++c_token)) {
4939             free(output);
4940             free(pattern);
4941             int_error(c_token, "constant expression expected");
4942         }
4943
4944         /* assume it's a numeric expression, concatenate it to output
4945          * string: parse value, enlarge output buffer, and gprintf()
4946          * it. */
4947         value = real(const_express(&a));
4948         newlen += pattern_length + 30;
4949         output = gp_realloc(output, newlen, "fill_numbers next number");
4950         gprintf(output + output_end, newlen - output_end,
4951                 pattern, 1.0, value);
4952         output_end += strlen(output + output_end);
4953
4954         /* allow a string to follow, after another comma: */
4955         if (END_OF_COMMAND || !equals(c_token, ",")) {
4956             /* no comma followed the number --> we're done. Jump out
4957              * directly, as falling out of the while loop means
4958              * something slightly different. */
4959             free(pattern);
4960             return output;
4961         }
4962         c_token++;
4963
4964         if (!END_OF_COMMAND && isstring(c_token)) {
4965             size_t length = token_len(c_token);
4966
4967             if (length >= pattern_length)
4968                 pattern = gp_realloc(pattern, pattern_length = length,
4969                                      "fill_numbers resize pattern");
4970             quote_str(pattern, c_token, length);
4971             c_token++;
4972         } else {
4973             free(pattern);
4974             free(output);
4975             int_error(c_token, "string expected");
4976         } /* if (string after comma) */
4977     } while (!END_OF_COMMAND && equals(c_token, ","));
4978
4979     /* came out here --> the last element was a string, not a number.
4980      * that means that there is a string in pattern which was not yet
4981      * copied to 'output' */
4982     output = gp_realloc(output, newlen += pattern_length,
4983                         "fill_numbers closing");
4984     strcpy(output + output_end, pattern);
4985     free(pattern);
4986     return output;
4987 }
4988
4989 /*
4990  * new_text_label() allocates and initializes a text_label structure.
4991  * This routine is also used by the plot and splot with labels commands.
4992  */
4993 struct text_label *
4994 new_text_label(int tag)
4995 {
4996     struct text_label *new;
4997     struct position default_offset = { character, character, character,
4998                                        0., 0., 0. };
4999
5000     new = gp_alloc( sizeof(struct text_label), "text_label");
5001     new->next = NULL;
5002     new->tag = tag;
5003     new->place = default_position;
5004     new->pos = LEFT;
5005     new->rotate = 0;
5006     new->layer = 0;
5007     new->text = (char *)NULL;
5008     new->font = (char *)NULL;
5009     new->textcolor.type = TC_DEFAULT;
5010     new->lp_properties.pointflag = 0;
5011     new->lp_properties.p_type = 1;
5012     new->offset = default_offset;
5013     new->noenhanced = FALSE;
5014
5015     return(new);
5016 }
5017
5018 /*
5019  * Parse the sub-options for label style and placement.
5020  * This is called from set_label, and from plot2d and plot3d 
5021  * to handle options for 'plot with labels'
5022  */
5023 void
5024 parse_label_options( struct text_label *this_label )
5025 {
5026     struct value a;
5027     struct position pos;
5028     char *font = NULL;
5029     enum JUSTIFY just = LEFT;
5030     int rotate = 0;
5031     TBOOLEAN set_position = FALSE, set_just = FALSE,
5032         set_rot = FALSE, set_font = FALSE, set_offset = FALSE,
5033         set_layer = FALSE, set_textcolor = FALSE;
5034     int layer = 0;
5035     TBOOLEAN axis_label = (this_label->tag == -2);
5036     struct position offset = { character, character, character, 0., 0., 0. };
5037     t_colorspec textcolor = {TC_DEFAULT,0,0.0};
5038     struct lp_style_type loc_lp = DEFAULT_LP_STYLE_TYPE;
5039     loc_lp.pointflag = -2;
5040
5041    /* Now parse the label format and style options */
5042     while (!END_OF_COMMAND) {
5043         /* get position */
5044         if (! set_position && equals(c_token, "at") && !axis_label) {
5045             c_token++;
5046             get_position(&pos);
5047             set_position = TRUE;
5048             continue;
5049         }
5050
5051         /* get justification */
5052         if (! set_just) {
5053             if (almost_equals(c_token, "l$eft")) {
5054                 just = LEFT;
5055                 c_token++;
5056                 set_just = TRUE;
5057                 continue;
5058             } else if (almost_equals(c_token, "c$entre")
5059                        || almost_equals(c_token, "c$enter")) {
5060                 just = CENTRE;
5061                 c_token++;
5062                 set_just = TRUE;
5063                 continue;
5064             } else if (almost_equals(c_token, "r$ight")) {
5065                 just = RIGHT;
5066                 c_token++;
5067                 set_just = TRUE;
5068                 continue;
5069             }
5070         }
5071
5072         /* get rotation (added by RCC) */
5073         if (! set_rot) {
5074             if (almost_equals(c_token, "rot$ate")) {
5075                 rotate = TEXT_VERTICAL;
5076                 c_token++;
5077                 set_rot = TRUE;
5078                 if (equals(c_token, "by")) {
5079                     c_token++;
5080                     rotate = (int)real(const_express(&a));
5081                 }
5082                 continue;
5083             } else if (almost_equals(c_token, "norot$ate")) {
5084                 rotate = 0;
5085                 c_token++;
5086                 set_rot = TRUE;
5087                 continue;
5088             }
5089         }
5090
5091         /* get font (added by DJL) */
5092         if (! set_font && equals(c_token, "font")) {
5093             c_token++;
5094             if ((font = try_to_get_string())) {
5095                 set_font = TRUE;
5096                 continue;
5097             } else
5098                 int_error(c_token, "'fontname,fontsize' expected");
5099         }
5100
5101         /* get front/back (added by JDP) */
5102         if (! set_layer && !axis_label) {
5103             if (equals(c_token, "back")) {
5104                 layer = 0;
5105                 c_token++;
5106                 set_layer = TRUE;
5107                 continue;
5108             } else if (equals(c_token, "front")) {
5109                 layer = 1;
5110                 c_token++;
5111                 set_layer = TRUE;
5112                 continue;
5113             }
5114         }
5115
5116         if (loc_lp.pointflag == -2 && !axis_label) {
5117             if (almost_equals(c_token, "po$int")) {
5118                 int stored_token = ++c_token;
5119                 struct lp_style_type tmp_lp;
5120                 loc_lp.pointflag = 1;
5121                 tmp_lp = loc_lp;
5122                 lp_parse(&tmp_lp, TRUE, TRUE);
5123                 if (stored_token != c_token)
5124                     loc_lp = tmp_lp;
5125                 continue;
5126             } else if (almost_equals(c_token, "nopo$int")) {
5127                 loc_lp.pointflag = 0;
5128                 c_token++;
5129                 continue;
5130             }
5131         }
5132
5133         if (! set_offset && almost_equals(c_token, "of$fset")) {
5134             c_token++;
5135             get_position_default(&offset,character);
5136             set_offset = TRUE;
5137             continue;
5138         }
5139
5140         if ((equals(c_token,"tc") || almost_equals(c_token,"text$color"))
5141             && ! set_textcolor ) {
5142             parse_colorspec( &textcolor, TC_Z );
5143             set_textcolor = TRUE;
5144             continue;
5145         }
5146
5147         /* EAM FIXME: Option to disable enhanced text processing currently not */
5148         /* documented for ordinary labels. */
5149         if (almost_equals(c_token,"noenh$anced")) {
5150             this_label->noenhanced = TRUE;
5151             c_token++;
5152             continue;
5153         } else if (almost_equals(c_token,"enh$anced")) {
5154             this_label->noenhanced = FALSE;
5155             c_token++;
5156             continue;
5157         }
5158
5159         /* Coming here means that none of the previous 'if's struck
5160          * its "continue" statement, i.e.  whatever is in the command
5161          * line is forbidden by the 'set label' command syntax.
5162          * On the other hand, 'plot with labels' may have additional stuff coming up.
5163          */
5164 #ifdef GP_STRING_VARS
5165         break;
5166 #else
5167         if (this_label->tag == -1 || this_label->tag == -2)
5168             break;
5169         else
5170             int_error(c_token, "extraneous or contradicting arguments in label options");
5171 #endif
5172
5173     } /* while(!END_OF_COMMAND) */
5174
5175     /* HBB 20011120: this chunk moved here, behind the while()
5176      * loop. Only after all options have been parsed it's safe to
5177      * overwrite the position if none has been specified. */
5178     if (!set_position)
5179         pos = default_position;
5180
5181     /* OK! copy the requested options into the label */
5182         if (set_position)
5183             this_label->place = pos;
5184         if (set_just)
5185             this_label->pos = just;
5186         if (set_rot)
5187             this_label->rotate = rotate;
5188         if (set_layer)
5189             this_label->layer = layer;
5190         if (set_font)
5191             this_label->font = font;
5192         if (set_textcolor)
5193             memcpy(&(this_label->textcolor), &textcolor, sizeof(t_colorspec));
5194         if (loc_lp.pointflag >= 0)
5195             memcpy(&(this_label->lp_properties), &loc_lp, sizeof(loc_lp));
5196         if (set_offset)
5197             this_label->offset = offset;
5198
5199     /* Make sure the z coord and the z-coloring agree */
5200     if (this_label->textcolor.type == TC_Z)
5201         this_label->textcolor.value = this_label->place.z;
5202 }
5203
5204
5205 #ifdef EAM_HISTOGRAMS
5206 /* <histogramstyle> = {clustered {gap <n>} | rowstacked | columnstacked */
5207 /*                     errorbars {gap <n>} {linewidth <lw>}}            */
5208 /*                    {title <title_options>}                           */
5209 static void
5210 parse_histogramstyle( histogram_style *hs, 
5211                 t_histogram_type def_type,
5212                 int def_gap)
5213 {
5214     struct value a;
5215     text_label title_specs = EMPTY_LABELSTRUCT;
5216
5217     /* Set defaults */
5218     hs->type  = def_type;
5219     hs->gap   = def_gap;
5220
5221     if (END_OF_COMMAND)
5222         return;
5223     if (!equals(c_token,"hs") && !almost_equals(c_token,"hist$ogram"))
5224         return;
5225     c_token++;
5226
5227     while (!END_OF_COMMAND) {
5228         if (almost_equals(c_token, "clust$ered")) {
5229             hs->type = HT_CLUSTERED;
5230             c_token++;
5231         } else if (almost_equals(c_token, "error$bars")) {
5232             hs->type = HT_ERRORBARS;
5233             c_token++;
5234         } else if (almost_equals(c_token, "rows$tacked")) {
5235             hs->type = HT_STACKED_IN_LAYERS;
5236             c_token++;
5237         } else if (almost_equals(c_token, "columns$tacked")) {
5238             hs->type = HT_STACKED_IN_TOWERS;
5239             c_token++;
5240         } else if (equals(c_token, "gap")) {
5241             if (isanumber(++c_token))
5242                 hs->gap = (int)real(const_express(&a));
5243             else
5244                 int_error(c_token,"expected gap value");
5245         } else if (almost_equals(c_token, "ti$tle")) {
5246             title_specs.offset = hs->title.offset;
5247             set_xyzlabel(&title_specs);
5248             memcpy(&hs->title.textcolor,&title_specs.textcolor,sizeof(t_colorspec));
5249             hs->title.offset = title_specs.offset;
5250             /* EAM FIXME - could allocate space and copy parsed font instead */
5251             hs->title.font = axis_array[FIRST_X_AXIS].label.font;
5252         } else if ((equals(c_token,"lw") || almost_equals(c_token,"linew$idth"))
5253                   && (hs->type == HT_ERRORBARS)) {
5254             c_token++;
5255             hs->bar_lw = real(const_express(&a));
5256             if (hs->bar_lw < 0)
5257                 hs->bar_lw = 0;
5258         } else
5259             /* We hit something unexpected */
5260             break;
5261     }
5262 }
5263 #endif /* EAM_HISTOGRAMS */