1 /* This file is part of the GNU plotutils package.  Copyright (C) 1989,
2    1990, 1991, 1995, 1996, 1997, 1998, 1999, 2000, 2005, 2008, 2009, Free
3    Software Foundation, Inc.
4 
5    The GNU plotutils package is free software.  You may redistribute it
6    and/or modify it under the terms of the GNU General Public License as
7    published by the Free Software foundation; either version 2, or (at your
8    option) any later version.
9 
10    The GNU plotutils package is distributed in the hope that it will be
11    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with the GNU plotutils package; see the file COPYING.  If not, write to
17    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
18    Boston, MA 02110-1301, USA. */
19 
20 /* This file contains the main routine, and a few support subroutines, for
21    GNU graph. */
22 
23 #include "sys-defines.h"
24 #include "extern.h"
25 #include "libcommon.h"
26 #include "getopt.h"
27 #include "fontlist.h"
28 
29 /* options */
30 
31 #define	ARG_NONE	0
32 #define	ARG_REQUIRED	1
33 #define	ARG_OPTIONAL	2
34 
35 const char *optstring = "-BCHOQVstE:F:f:g:h:k:K:I:l:L:m:N:q:R:r:T:u:w:W:X:Y:a::x::y::S::"; /* initial hyphen requests no reordering */
36 
37 struct option long_options[] =
38 {
39   /* The most important option ("--display-type" is an obsolete variant) */
40   { "output-format",	ARG_REQUIRED,	NULL, 'T'},
41   { "display-type",	ARG_REQUIRED,	NULL, 'T' << 8 }, /* hidden */
42   /* Other frequently used options */
43   {"auto-abscissa",	ARG_OPTIONAL,	NULL, 'a'}, /* 0 or 1 or 2 */
44   {"clip-mode",		ARG_REQUIRED,	NULL, 'K'},
45   {"fill-fraction",	ARG_REQUIRED,	NULL, 'q'},
46   {"font-name",		ARG_REQUIRED,	NULL, 'F'},
47   {"font-size",		ARG_REQUIRED,	NULL, 'f'},
48   {"grid-style",	ARG_REQUIRED,	NULL, 'g'},
49   {"height-of-plot",	ARG_REQUIRED,	NULL, 'h'},
50   {"input-format",	ARG_REQUIRED,	NULL, 'I'},
51   {"line-mode",		ARG_REQUIRED,	NULL, 'm'},
52   {"line-width",	ARG_REQUIRED,	NULL, 'W'},
53   {"right-shift",	ARG_REQUIRED,	NULL, 'r'},
54   {"save-screen",	ARG_NONE,	NULL, 's'},
55   {"symbol",		ARG_OPTIONAL,	NULL, 'S'}, /* 0 or 1 or 2 */
56   {"tick-size",		ARG_REQUIRED,	NULL, 'k'},
57   {"toggle-auto-bump",	ARG_NONE,	NULL, 'B'},
58   {"toggle-axis-end",	ARG_REQUIRED,	NULL, 'E'},
59   {"toggle-frame-on-top",	ARG_NONE,	NULL, 'H'},
60   {"toggle-log-axis",	ARG_REQUIRED,	NULL, 'l'},
61   {"toggle-no-ticks",	ARG_REQUIRED,	NULL, 'N'},
62   {"toggle-rotate-y-label",	ARG_NONE,	NULL, 'Q'},
63   {"toggle-round-to-next-tick",	ARG_REQUIRED,	NULL, 'R'},
64   {"toggle-transpose-axes",	ARG_NONE,	NULL, 't'},
65   {"toggle-use-color",	ARG_NONE,	NULL, 'C'},
66   {"top-label",		ARG_REQUIRED,	NULL, 'L'},
67   {"upward-shift",      ARG_REQUIRED,	NULL, 'u'},
68   {"width-of-plot",	ARG_REQUIRED,	NULL, 'w'},
69   {"x-label",		ARG_REQUIRED,	NULL, 'X'},
70   {"x-limits",		ARG_OPTIONAL,	NULL, 'x'}, /* 0, 1, 2, or 3 */
71   {"y-label",		ARG_REQUIRED,	NULL, 'Y'},
72   {"y-limits",		ARG_OPTIONAL,	NULL, 'y'}, /* 0, 1, 2, or 3 */
73   /* Long options with no equivalent short option alias */
74   {"bg-color",		ARG_REQUIRED,	NULL, 'q' << 8},
75   {"bitmap-size",	ARG_REQUIRED,	NULL, 'B' << 8},
76   {"blankout",		ARG_REQUIRED,	NULL, 'b' << 8},
77   {"emulate-color",	ARG_REQUIRED,	NULL, 'e' << 8},
78   {"frame-line-width",	ARG_REQUIRED,	NULL, 'W' << 8},
79   {"frame-color",	ARG_REQUIRED,	NULL, 'C' << 8},
80   {"max-line-length",	ARG_REQUIRED,	NULL, 'M' << 8},
81   {"pen-colors",	ARG_REQUIRED,	NULL, 'p' << 8},
82   {"reposition",	ARG_REQUIRED,	NULL, 'R' << 8}, /* 3 */
83   {"rotation",		ARG_REQUIRED,	NULL, 'r' << 8},
84   {"symbol-font-name",	ARG_REQUIRED,	NULL, 'G' << 8},
85   {"title-font-name",	ARG_REQUIRED,	NULL, 'Z' << 8},
86   {"title-font-size",	ARG_REQUIRED,	NULL, 'F' << 8},
87   {"page-size",		ARG_REQUIRED,	NULL, 'P' << 8},
88   /* Options relevant only to raw graph (refers to plot(5) output) */
89   {"portable-output",	ARG_NONE,	NULL, 'O'},
90   /* Documentation options */
91   {"help-fonts",	ARG_NONE,	NULL, 'f' << 8},
92   {"list-fonts",	ARG_NONE,	NULL, 'l' << 8},
93   {"version",		ARG_NONE,	NULL, 'V' << 8},
94   {"help",		ARG_NONE,	NULL, 'h' << 8},
95   {NULL, 0, 0, 0}
96 };
97 
98 /* null-terminated list of options, such as obsolete-but-still-maintained
99    options or undocumented options, which we don't show to the user */
100 const int hidden_options[] = { (int)('T' << 8), 0 };
101 
102 const char *progname = "graph";	/* name of this program */
103 const char *written = "Written by Robert S. Maier.";
104 const char *copyright = "Copyright (C) 2009 Free Software Foundation, Inc.";
105 
106 const char *usage_appendage = " [FILE]...\n\
107 With no FILE, or when FILE is -, read standard input.\n";
108 
109 /* forward references */
110 static void close_file (char *filename, FILE *stream);
111 static void open_file_for_reading (char *filename, FILE **input);
112 static bool parse_pen_string (const char *pen_s);
113 
114 int
main(int argc,char * argv[])115 main (int argc, char *argv[])
116 {
117   /* Variables related to getopt parsing */
118 
119   int option;
120   int opt_index;
121   int errcnt = 0;		/* errors encountered in getopt parsing */
122   int matched;
123   bool using_getopt = true;	/* true until end of command-line options */
124   bool continue_parse = true;	/* reset e.g. when --help or --version seen */
125   bool show_version = false;	/* show version message? */
126   bool show_usage = false;	/* show usage message? */
127   bool show_fonts = false;	/* supply help on fonts? */
128   bool do_list_fonts = false;	/* show a list of fonts? */
129   bool filter = false;		/* will we act as a filter? */
130   bool new_symbol = false;
131   bool new_symbol_size = false;
132   bool new_symbol_font_name = false;
133   bool new_linemode = false;
134   bool new_plot_line_width = false;
135   bool new_fill_fraction = false;
136   bool new_use_color = false;
137   bool first_file_of_graph = true;
138   bool first_graph_of_multigraph = true;
139   FILE *data_file = NULL;
140 
141   /* Variables related to the point reader */
142 
143   Reader *reader = NULL;
144   data_type input_type = T_ASCII; /* by default we read ascii data */
145   bool auto_bump = true;	/* auto-bump linemode between polylines? */
146   bool auto_abscissa = false;	/* generate abscissa values automatically? */
147   double x_start = 0.;		/* start and increment, for auto-abscissa */
148   double delta_x = 1.;
149   /* polyline attributes */
150   int linemode_index = 1;	/* linemode for polylines, 1=solid, etc. */
151   double plot_line_width = -0.001; /* polyline width (as frac. of display width), negative means default provided by libplot) */
152   int symbol_index = 0;		/* 0=none, 1=dot, 2=plus, 3=asterisk, etc. */
153   double symbol_size = .03;	/* symbol size (frac. of plotting box size) */
154   double fill_fraction = -1.0;	/* negative means regions aren't filled */
155   bool use_color = false;	/* color / monochrome */
156 
157   /* Variables related to both the point reader and the point plotter */
158 
159   bool transpose_axes = false;	/* true means -x applies to y axis, etc. */
160 
161   /* Variables related to the multigrapher, i.e. point plotter */
162 
163   Multigrapher *multigrapher = NULL;
164 
165   /* command-line parameters (constant over multigrapher operation) */
166   const char *output_format = "meta";/* libplot output format */
167   const char *bg_color = NULL;	/* color of background, if non-NULL */
168   const char *bitmap_size = NULL;
169   const char *emulate_color = NULL;
170   const char *max_line_length = NULL;
171   const char *meta_portable = NULL;
172   const char *page_size = NULL;
173   const char *rotation_angle = NULL;
174   bool save_screen = false;	/* save screen, i.e. no erase before plot? */
175 
176   /* graph-specific parameters (may change from graph to graph) */
177 
178   grid_type grid_spec = AXES_AND_BOX; /* frame type for current graph */
179   bool no_rotate_y_label = false; /* used for pre-X11R6 servers */
180   const char *frame_color = "black"; /* color of frame (and graph, if no -C)*/
181   int clip_mode = 1;		/* clipping mode (cf. gnuplot) */
182   /* following variables are portmanteau: x and y are included as bitfields*/
183   int log_axis = 0;		/* log axes or linear axes? */
184   int round_to_next_tick = 0;	/* round axis limits to nearest tick? */
185   int switch_axis_end = 0;	/* axis at top/right instead of bottom/left? */
186   int omit_ticks = 0;		/* omit ticks and tick labels from an axis? */
187 
188   /* graph dimensions, expressed as fractions of the width of the libplot
189      graphics display [by convention square]; <0.0 means use libplot default */
190   double frame_line_width = -0.001; /* width of lines in the graph frame */
191 
192   /* dimensions of graphing area, expressed as fractions of the width of
193      the libplot graphics display [by convention square] */
194   double margin_below = .2;	/* margin below the plot */
195   double margin_left = .2;	/* margin left of the plot */
196   double plot_height = .6;	/* height of the plot */
197   double plot_width = .6;	/* width of the plot */
198 
199   /* dimensions, expressed as fractions of the size of the plotting area */
200   double tick_size = .02;	/* size of tick marks (< 0.0 allowed) */
201   double font_size = 0.0525;	/* fontsize */
202   double title_font_size = 0.07; /* title fontsize */
203   double blankout_fraction = 1.3; /* this fraction of size of plotting box
204 				   is erased before the plot is drawn */
205 
206   /* text-related */
207   const char *font_name = NULL;	/* font name, NULL -> device default */
208   const char *title_font_name = NULL; /* title font name, NULL -> default */
209   const char *symbol_font_name = "ZapfDingbats"; /* symbol font name, NULL -> default */
210   const char *x_label = NULL;	/* label for the x axis, NULL -> no label */
211   const char *y_label = NULL;	/* label for the y axis, NULL -> no label */
212   const char *top_label = NULL;	/* title above the plot, NULL -> no title */
213 
214   /* user-specified limits on the axes */
215   double min_x = 0.0, min_y = 0.0, max_x = 0.0, max_y = 0.0;
216   double spacing_x = 0.0, spacing_y = 0.0;
217 
218   /* flags indicating which axis limits the user has specified */
219   bool spec_min_x = false, spec_min_y = false;
220   bool spec_max_x = false, spec_max_y = false;
221   bool spec_spacing_x = false, spec_spacing_y = false;
222 
223   /* misc. local variables used in getopt parsing, counterparts to the above */
224   double local_x_start, local_delta_x;
225   int local_grid_style;
226   int local_symbol_index;
227   int local_clip_mode;
228   double local_symbol_size, local_font_size, local_title_font_size;
229   double local_frame_line_width, local_plot_line_width;
230   double local_min_x, local_min_y;
231   double local_max_x, local_max_y;
232   double local_spacing_x, local_spacing_y;
233   double local_fill_fraction;
234 
235   /* `finalized' arguments to set_graph_parameters() (computed at the time
236      the first file of a graph is seen, and continuing in effect over the
237      duration of the graph) */
238   int final_log_axis = 0;
239   int final_round_to_next_tick = 0;
240   double final_min_x = 0.0, final_max_x = 0.0, final_spacing_x = 0.0;
241   double final_min_y = 0.0, final_max_y = 0.0, final_spacing_y = 0.0;
242   bool final_spec_min_x = false, final_spec_min_y = false;
243   bool final_spec_max_x = false, final_spec_max_y = false;
244   bool final_spec_spacing_x = false, final_spec_spacing_y = false;
245   bool final_transpose_axes = false;
246 
247   /* for storage of data points (if we're not acting as a filter) */
248   Point *p;			/* points array */
249   int points_length = 1024;	/* length of the points array, in points */
250   int no_of_points = 0;		/* number of points stored in it */
251 
252   /* support for multigraphing */
253   double reposition_trans_x = 0.0, reposition_trans_y = 0.0;
254   double reposition_scale = 1.0;
255   double old_reposition_trans_x, old_reposition_trans_y;
256   double old_reposition_scale;
257 
258   /* sui generis */
259   bool frame_on_top = false;
260 
261   /* The main command-line parsing loop, which uses getopt to scan argv[]
262      without reordering, i.e. to process command-line arguments (options
263      and filenames) sequentially.
264 
265      From a logical point of view, a multigraph consists of a sequence of
266      graphs, with a `--reposition' flag serving as a separator between
267      graphs.  A graph is drawn from one or more files.
268 
269      So this parsing loop invokes Multigrapher methods (1) when a file name
270      is seen, (2) when a `--reposition' directive is seen, and (3) at the
271      end of the scan over argv[].
272 
273      If at the end of the scan no file names have been seen, stdin is used
274      instead as an input stream.  (As a file name, `-' means stdin.) */
275 
276   while (continue_parse)
277     {
278       if (using_getopt)
279 	/* end of options not reached yet */
280 	{
281 	  option = getopt_long (argc, argv,
282 				/* initial hyphen requests no reordering */
283 				optstring,
284 				long_options, &opt_index);
285 	  if (option == EOF)	/* end of options */
286 	    {
287 	      using_getopt = false;
288 	      continue;		/* back to top of while loop */
289 	    }
290 	  if (option == 1)	/* filename embedded among options */
291 	    {
292 	      if (strcmp (optarg, "-") == 0)
293 		data_file = stdin; /* interpret "-" as stdin */
294 	      else
295 		open_file_for_reading (optarg, &data_file);
296 	    }
297 	}
298       else
299 	/* end of options reached, processing filenames manually */
300 	{
301 	  if (optind >= argc)	/* all files processed */
302 	    {
303 	      if (first_graph_of_multigraph && first_file_of_graph)
304 		/* no file appeared on command line, read stdin instead */
305 		{
306 		  data_file = stdin;
307 		  option = 1;	/* code for pseudo-option */
308 		}
309 	      else
310 		break;		/* all files done, break out of while loop */
311 	    }
312 	  else			/* have files yet to process */
313 	    {
314 	      if (strcmp (argv[optind], "-") == 0)
315 		data_file = stdin;
316 	      else
317 		open_file_for_reading (argv[optind], &data_file);
318 	      optarg = argv[optind]; /* keep track of name of opened file */
319 	      optind++;
320 	      option = 1;	/* code for pseudo-option */
321 	    }
322 	}
323 
324       /* Parse an option flag, which may be a genuine option flag obtained
325 	 from getopt, or a fake (a `1', indicating that a filename has been
326 	 seen by getopt, or that filename has been seen on the command line
327 	 after all genuine options have been processed, or that stdin
328 	 must be read because no filenames have been seen). */
329 
330       switch (option)
331 	{
332 	  /* ----------- options with no argument --------------*/
333 
334 	case 's':		/* Don't erase display before plot, ARG NONE */
335 	  save_screen = true;
336 	  break;
337 	case 't':		/* Toggle transposition of axes, ARG NONE */
338 	  transpose_axes = (transpose_axes == true ? false : true);
339 	  break;
340 	case 'B':		/* Toggle linemode auto-bumping, ARG NONE */
341 	  auto_bump = (auto_bump == true ? false : true);
342 	  break;
343 	case 'C':		/* Toggle color/monochrome, ARG NONE */
344 	  new_use_color = true;
345 	  use_color = (use_color == true ? false : true);
346 	  break;
347 	case 'H':		/* Toggle frame-on-top, ARG NONE */
348 	  frame_on_top = (frame_on_top == true ? false : true);
349 	  break;
350 	case 'O':		/* portable format, ARG NONE */
351 	  meta_portable = "yes";
352 	  break;
353 	case 'e' << 8:		/* emulate color, ARG NONE */
354 	  emulate_color = xstrdup (optarg);
355 	  break;
356 	case 'V' << 8:		/* Version, ARG NONE		*/
357 	  show_version = true;
358 	  continue_parse = false;
359 	  break;
360 	case 'h' << 8:		/* Help, ARG NONE		*/
361 	  show_usage = true;
362 	  continue_parse = false;
363 	  break;
364 	case 'f' << 8:		/* Help on fonts, ARG NONE	*/
365 	  show_fonts = true;
366 	  continue_parse = false;
367 	  break;
368 	case 'l' << 8:		/* List fonts, ARG NONE		*/
369 	  do_list_fonts = true;
370 	  continue_parse = false;
371 	  break;
372 	case 'Q':		/* Toggle rotation of y-label, ARG NONE */
373 	  no_rotate_y_label = (no_rotate_y_label == true ? false : true);
374 	  break;
375 
376 	  /*----------- options with a single argument --------------*/
377 
378 	case 'I':		/* Input format, ARG REQUIRED	*/
379 	  switch (*optarg)
380 	    {
381 	    case 'a':
382 	    case 'A':
383 	      /* ASCII format, records and fields within records are
384 		 separated by whitespace, and datasets are separated by a
385 		 pair of newlines.  Record length = 2. */
386 	      input_type = T_ASCII;
387 	      break;
388 	    case 'f':
389 	    case 'F':
390 	      /* Binary single precision, records and fields within records
391 		 are contiguous, and datasets are separated by a FLT_MAX.
392 		 Record length = 2. */
393 	      input_type = T_SINGLE;
394 	      break;
395 	    case 'd':
396 	    case 'D':
397 	      /* Binary double precision, records and fields within records
398 		 are contiguous, and datasets are separated by a DBL_MAX.
399 		 Record length = 2. */
400 	      input_type = T_DOUBLE;
401 	      break;
402 	    case 'i':
403 	    case 'I':
404 	      /* Binary integer, records and fields within records are
405 		 contiguous, and datasets are separated by an occurrence of
406 		 INT_MAX.  Record length = 2. */
407 	      input_type = T_INTEGER;
408 	      break;
409 	    case 'e':
410 	    case 'E':
411 	      /* Same as T_ASCII, but record length = 3. */
412 	      input_type = T_ASCII_ERRORBAR;
413 	      break;
414 	    case 'g':
415 	    case 'G':
416 	      /* Sui generis. */
417 	      input_type = T_GNUPLOT;	/* gnuplot `table' format */
418 	      break;
419 	    default:
420 	      fprintf (stderr,
421 		       "%s: error: `%s' is an unrecognized data option\n",
422 		       progname, optarg);
423 	      errcnt++;
424 	    }
425 	  break;
426 	case 'f':		/* Font size, ARG REQUIRED	*/
427 	  if (sscanf (optarg, "%lf", &local_font_size) <= 0)
428 	    {
429 	      fprintf (stderr,
430 		       "%s: error: the font size should be a number, but it was `%s'\n",
431 		       progname, optarg);
432 	      errcnt++;
433 	    }
434 	  else
435 	    {
436 	      if (local_font_size >= 1.0)
437 		fprintf (stderr, "%s: the too-large font size `%f' is disregarded (it should be less than 1.0)\n",
438 			 progname, local_font_size);
439 	      else if (local_font_size < 0.0)
440 		fprintf (stderr, "%s: the negative font size `%f' is disregarded\n",
441 			 progname, local_font_size);
442 	      else
443 		font_size = local_font_size;
444 	    }
445 	  break;
446 	case 'g':		/* Grid style, ARG REQUIRED	*/
447 	  if (sscanf (optarg, "%d", &local_grid_style) <= 0)
448 	    {
449 	      fprintf (stderr,
450 		       "%s: error: the grid style should be a (small) integer, but it was `%s'\n",
451 		       progname, optarg);
452 	      errcnt++;
453 	      break;
454 	    }
455 	  switch (local_grid_style)
456 	    /* the subset ordering is: 0 < 1 < 2 < 3; 4 is different */
457 	    {
458 	    case 0:
459 	      /* no frame at all; just the plot */
460 	      grid_spec = NO_AXES;
461 	      break;
462 	    case 1:
463 	      /* box, ticks, gridlines, labels */
464 	      grid_spec = AXES;
465 	      break;
466 	    case 2:
467 	      /* box, ticks, no gridlines, labels */
468 	      grid_spec = AXES_AND_BOX;
469 	      break;
470 	    case 3:
471 	      /* `half-box', partial ticks, no gridlines, labels */
472 	      grid_spec = AXES_AND_BOX_AND_GRID;
473 	      break;
474 	    case 4:
475 	      /* no box, no gridlines; specially positioned axes, labels */
476 	      grid_spec = AXES_AT_ORIGIN;
477 	      break;
478 	    default:
479 	      fprintf (stderr,
480 		       "%s: error: the grid style number `%s' is out of bounds\n",
481 		       progname, optarg);
482 	      errcnt++;
483 	    }
484 	  break;
485 	case 'h':		/* Height of plot, ARG REQUIRED	*/
486 	  if (sscanf (optarg, "%lf", &plot_height) <= 0)
487 	    {
488 	      fprintf (stderr,
489 		       "%s: error: the plot height should be a number, but it was `%s'\n",
490 		       progname, optarg);
491 	      errcnt++;
492 	    }
493 	  break;
494 	case 'K':		/* Clip mode, ARG REQUIRED */
495 	  if ((sscanf (optarg, "%d", &local_clip_mode) <= 0)
496 	      || local_clip_mode < 0 || local_clip_mode > 2)
497 	    fprintf (stderr,
498 		     "%s: the bad clip mode `%s' is disregarded (it should be 0, 1, or 2)\n",
499 		     progname, optarg);
500 	  else
501 	    clip_mode = local_clip_mode;
502 	  break;
503 	case 'l':		/* Toggle log/linear axis, ARG REQUIRED */
504 	  switch (*optarg)
505 	    {
506 	    case 'x':
507 	    case 'X':
508 	      log_axis ^= X_AXIS;
509 	      break;
510 	    case 'y':
511 	    case 'Y':
512 	      log_axis ^= Y_AXIS;
513 	      break;
514 	    default:
515 	      fprintf (stderr,
516 		       "%s: the unrecognized axis specification `%s' is disregarded\n",
517 		       progname, optarg);
518 	      break;
519 	    }
520 	  break;
521 	case 'N':		/* Toggle omission of labels, ARG REQUIRED */
522 	  switch (*optarg)
523 	    {
524 	    case 'x':
525 	    case 'X':
526 	      omit_ticks ^= X_AXIS;
527 	      break;
528 	    case 'y':
529 	    case 'Y':
530 	      omit_ticks ^= Y_AXIS;
531 	      break;
532 	    default:
533 	      fprintf (stderr,
534 		       "%s: the unrecognized axis specification `%s' is disregarded\n",
535 		       progname, optarg);
536 	      break;
537 	    }
538 	  break;
539 	case 'm':		/* Linemode, ARG REQUIRED	*/
540 	  new_linemode = true;
541 	  if (sscanf (optarg, "%d", &linemode_index) <= 0)
542 	    {
543 	      fprintf (stderr,
544 		       "%s: error: the linemode should be a (small) integer, but it was `%s'\n",
545 		       progname, optarg);
546 	      errcnt++;
547 	    }
548 	  break;
549 	case 'q':		/* Fill fraction, ARG REQUIRED	*/
550 	  if (sscanf (optarg, "%lf", &local_fill_fraction) <= 0)
551 	    {
552 	      fprintf (stderr,
553 		       "%s: error: the fill fraction should be a number, but it was `%s'\n",
554 		       progname, optarg);
555 	      errcnt++;
556 	    }
557 	  else
558 	    {
559 	      if (local_fill_fraction > 1.0)
560 		fprintf (stderr,
561 			 "%s: the region fill fraction `%f' was disregarded (it should be less than or equal to 1.0)\n",
562 			 progname, local_fill_fraction);
563 	      else
564 		{
565 		  fill_fraction = local_fill_fraction;
566 		  new_fill_fraction = true;
567 		}
568 	    }
569 	  break;
570 	case 'r':		/* Right shift, ARG REQUIRED */
571 	  if (sscanf (optarg, "%lf", &margin_left) <= 0)
572 	    {
573 	      fprintf (stderr,
574 		       "%s: error: the rightward displacement for the plot should be a number, but it was `%s'\n",
575 		       progname, optarg);
576 	      errcnt++;
577 	    }
578 	  break;
579 	case 'u':		/* Upward shift, ARG REQUIRED */
580 	  if (sscanf (optarg, "%lf", &margin_below) <= 0)
581 	    {
582 	      fprintf (stderr,
583 		       "%s: error: the upward displacement for the plot should be a number, but it was `%s'\n",
584 		       progname, optarg);
585 	      errcnt++;
586 	    }
587 	  break;
588 	case 'w':		/* Width of plot, ARG REQUIRED 	*/
589 	  if (sscanf (optarg, "%lf", &plot_width) <= 0)
590 	    {
591 	      fprintf (stderr,
592 		       "%s: error: the plot width should be a number, but it was `%s'\n",
593 		       progname, optarg);
594 	      errcnt++;
595 	    }
596 	  break;
597 	case 'T':		/* Output format, ARG REQUIRED      */
598 	case 'T' << 8:
599 	  output_format = xstrdup (optarg);
600 	  break;
601 	case 'F':		/* Font name, ARG REQUIRED      */
602 	  font_name = xstrdup (optarg);
603 	  break;
604 	case 'r' << 8:		/* Rotation angle, ARG REQUIRED      */
605 	  rotation_angle = xstrdup (optarg);
606 	  break;
607 	case 'Z' << 8:		/* Title Font name, ARG REQUIRED      */
608 	  title_font_name = xstrdup (optarg);
609 	  break;
610 	case 'G' << 8:		/* Symbol Font name, ARG REQUIRED      */
611 	  symbol_font_name = xstrdup (optarg);
612 	  new_symbol_font_name = true;
613 	  break;
614 	case 'R':		/* Toggle rounding to next tick, ARG REQUIRED*/
615 	  switch (*optarg)
616 	    {
617 	    case 'x':
618 	    case 'X':
619 	      round_to_next_tick ^= X_AXIS;
620 	      break;
621 	    case 'y':
622 	    case 'Y':
623 	      round_to_next_tick ^= Y_AXIS;
624 	      break;
625 	    default:
626 	      fprintf (stderr,
627 		       "%s: the unrecognized axis specification `%s' is disregarded\n",
628 		       progname, optarg);
629 	      break;
630 	    }
631 	  break;
632 	case 'L':		/* Top title, ARG REQUIRED	*/
633 	  top_label = xstrdup (optarg);
634 	  break;
635 	case 'k':		/* Tick size, ARG REQUIRED	*/
636 	  if (sscanf (optarg, "%lf", &tick_size) <= 0)
637 	    {
638 	      fprintf (stderr,
639 		       "%s: error: the tick size should be a number, but it was `%s'\n",
640 		       progname, optarg);
641 	      errcnt++;
642 	    }
643 	  break;
644 	case 'W':		/* Line width, ARG REQUIRED	*/
645 	  if (sscanf (optarg, "%lf", &local_plot_line_width) <= 0)
646 	    {
647 	      fprintf (stderr,
648 		       "%s: error: the line thickness for the plot should be a number, but it was `%s'\n",
649 		       progname, optarg);
650 	      errcnt++;
651 	    }
652 	  if (local_plot_line_width < 0.0)
653 	    fprintf (stderr, "%s: the negative plot line thickness `%f' is disregarded\n",
654 		     progname, local_plot_line_width);
655 	  else
656 	    {
657 	      plot_line_width = local_plot_line_width;
658 	      new_plot_line_width = true;
659 	    }
660 	  break;
661 	case 'X':		/* X axis title, ARG REQUIRED	*/
662 	  x_label = xstrdup (optarg);
663 	  break;
664 	case 'Y':		/* Y axis title, ARG REQUIRED	*/
665 	  y_label = xstrdup (optarg);
666 	  break;
667 	case 'E':		/* Toggle switching of axis to other end,
668 				   ARG REQUIRED */
669 	  switch (*optarg)
670 	    {
671 	    case 'x':
672 	    case 'X':
673 	      switch_axis_end ^= Y_AXIS;
674 	      break;
675 	    case 'y':
676 	    case 'Y':
677 	      switch_axis_end ^= X_AXIS;
678 	      break;
679 	    default:
680 	      fprintf (stderr,
681 		       "%s: the unrecognized axis specification `%s' is disregarded\n",
682 		       progname, optarg);
683 	      break;
684 	    }
685 	  break;
686 	case 'b' << 8:		/* Blankout fraction, ARG REQUIRED */
687 	  if (sscanf (optarg, "%lf", &blankout_fraction) <= 0)
688 	    {
689 	      fprintf (stderr,
690 		       "%s: error: the fractional blankout should be a number, but it was `%s'\n",
691 		       progname, optarg);
692 	      errcnt++;
693 	    }
694 	  break;
695 	case 'B' << 8:		/* Bitmap size, ARG REQUIRED	*/
696 	  bitmap_size = xstrdup (optarg);
697 	  break;
698 	case 'F' << 8:		/* Title font size, ARG REQUIRED	*/
699 	  if (sscanf (optarg, "%lf", &local_title_font_size) <= 0)
700 	    {
701 	      fprintf (stderr,
702 		       "%s: error: the font size for the title should be a number, but it was `%s'\n",
703 		       progname, optarg);
704 	      errcnt++;
705 	    }
706 	  else if (local_title_font_size >= 1.0)
707 	    fprintf (stderr, "%s: the too-large title font size `%f' is disregarded (it should be less than 1.0)\n",
708 		     progname, local_title_font_size);
709 	  else if (local_title_font_size < 0.0)
710 	    fprintf (stderr, "%s: the negative title font size `%f' is disregarded\n",
711 		     progname, local_title_font_size);
712 	  if (local_title_font_size == 0.0)
713 	    fprintf (stderr, "%s: the request for a zero title font size is disregarded\n",
714 		     progname);
715 	  else
716 	    title_font_size = local_title_font_size;
717 	  break;
718 	case 'W' << 8:		/* Frame line width, ARG REQUIRED	*/
719 	  if (sscanf (optarg, "%lf", &local_frame_line_width) <= 0)
720 	    {
721 	      fprintf (stderr,
722 		       "%s: error: the line thickness for the frame should be a number, but it was `%s'\n",
723 		       progname, optarg);
724 	      errcnt++;
725 	    }
726 	  if (local_frame_line_width < 0.0)
727 	    fprintf (stderr, "%s: the negative frame line thickness `%f' is disregarded\n",
728 		     progname, local_frame_line_width);
729 	  else
730 	    frame_line_width = local_frame_line_width;
731 	  break;
732 	case 'M' << 8:		/* Max line length, ARG REQUIRED	*/
733 	  max_line_length = xstrdup (optarg);
734 	  break;
735 	case 'P' << 8:		/* Page size, ARG REQUIRED	*/
736 	  page_size = xstrdup (optarg);
737 	  break;
738 	case 'p' << 8:		/* Pen color string, ARG REQUIRED      */
739 	  if (parse_pen_string (optarg) == false)
740 	    {
741 	      fprintf (stderr, "%s: the unparseable pen string `%s' is disregarded\n",
742 		       progname, optarg);
743 	    }
744 	  break;
745 	case 'q' << 8:		/* Background color, ARG REQUIRED      */
746 	  bg_color = xstrdup (optarg);
747 	  break;
748 	case 'C' << 8:		/* Frame color, ARG REQUIRED      */
749 	  frame_color = xstrdup (optarg);
750 	  break;
751 
752 
753 	  /*------ options with zero or more arguments ---------*/
754 
755 	case 'a':		/* Auto-abscissa, ARG OPTIONAL [0,1,2] */
756 	  auto_abscissa = true;
757 	  if (optind >= argc)
758 	    break;
759 	  if (sscanf (argv[optind], "%lf", &local_delta_x) <= 0)
760 	    break;
761 	  optind++;	/* tell getopt we recognized delta_x */
762 	  if (local_delta_x == 0.0)
763 	    /* "-a 0" turns off auto-abscissa for next file */
764 	    {
765 	      auto_abscissa = false;
766 	      break;
767 	    }
768 	  delta_x = local_delta_x;
769 	  if (optind >= argc)
770 	    break;
771 	  if (sscanf (argv[optind], "%lf", &local_x_start) <= 0)
772 	    break;
773 	  x_start = local_x_start;
774 	  optind++;	/* tell getopt we recognized x_start */
775 	  break;
776 	case 'x':		/* X limits, ARG OPTIONAL [0,1,2,3] */
777 	  matched = 0;
778 	  if (optind >= argc
779 	      || ((strcmp (argv[optind], "-") != 0)
780 		  && (matched
781 		      = sscanf (argv[optind], "%lf", &local_min_x)) <= 0))
782 	    {
783 	      spec_min_x = spec_max_x = spec_spacing_x = false;
784 	      break;
785 	    }
786 	  if (matched > 0)
787 	    {
788 	      spec_min_x = true;
789 	      min_x = local_min_x;
790 	    }
791 	  else
792 	      spec_min_x = false;
793 	  optind++;	/* tell getopt we recognized min_x */
794 
795 	  matched = 0;
796 	  if (optind >= argc
797 	      || ((strcmp (argv[optind], "-") != 0)
798 		  && (matched
799 		      = sscanf (argv[optind], "%lf", &local_max_x)) <= 0))
800 	    {
801 	      spec_max_x = spec_spacing_x = false;
802 	      break;
803 	    }
804 	  if (matched > 0)
805 	    {
806 	      spec_max_x = true;
807 	      max_x = local_max_x;
808 	    }
809 	  else
810 	    spec_max_x = false;
811 	  optind++;	/* tell getopt we recognized max_x */
812 
813 	  matched = 0;
814 	  if (optind >= argc
815 	      || ((strcmp (argv[optind], "-") != 0)
816 		  && (matched
817 		      = sscanf (argv[optind], "%lf", &local_spacing_x)) <= 0))
818 	    {
819 	      spec_spacing_x = false;
820 	      break;
821 	    }
822 	  if (matched > 0)
823 	    {
824 	      spec_spacing_x = true;
825 	      spacing_x = local_spacing_x;
826 	    }
827 	  else
828 	      spec_spacing_x = false;
829 	  optind++;	/* tell getopt we recognized spacing_x */
830 	  break;
831 
832 	case 'y':		/* Y limits, ARG OPTIONAL [0,1,2,3] */
833 	  matched = 0;
834 	  if (optind >= argc
835 	      || ((strcmp (argv[optind], "-") != 0)
836 		  && (matched
837 		      = sscanf (argv[optind], "%lf", &local_min_y)) <= 0))
838 	    {
839 	      spec_min_y = spec_max_y = spec_spacing_y = false;
840 	      break;
841 	    }
842 	  if (matched > 0)
843 	    {
844 	      spec_min_y = true;
845 	      min_y = local_min_y;
846 	    }
847 	  else
848 	      spec_min_y = false;
849 	  optind++;	/* tell getopt we recognized min_y */
850 
851 	  matched = 0;
852 	  if (optind >= argc
853 	      || ((strcmp (argv[optind], "-") != 0)
854 		  && (matched
855 		      = sscanf (argv[optind], "%lf", &local_max_y)) <= 0))
856 	    {
857 	      spec_max_y = spec_spacing_y = false;
858 	      break;
859 	    }
860 	  if (matched > 0)
861 	    {
862 	      spec_max_y = true;
863 	      max_y = local_max_y;
864 	    }
865 	  else
866 	      spec_max_y = false;
867 	  optind++;	/* tell getopt we recognized max_y */
868 
869 	  matched = 0;
870 	  if (optind >= argc
871 	      || ((strcmp (argv[optind], "-") != 0)
872 		  && (matched
873 		      = sscanf (argv[optind], "%lf", &local_spacing_y)) <= 0))
874 	    {
875 	      spec_spacing_y = false;
876 	      break;
877 	    }
878 	  if (matched > 0)
879 	    {
880 	      spec_spacing_y = true;
881 	      spacing_y = local_spacing_y;
882 	    }
883 	  else
884 	      spec_spacing_y = false;
885 	  optind++;	/* tell getopt we recognized spacing_y */
886 	  break;
887 
888 	case 'S':		/* Symbol, ARG OPTIONAL	[0,1,2]		*/
889 	  new_symbol = true;
890 	  symbol_index = 1;	/* symbol # 1 is switched to by -S alone */
891 	  if (optind >= argc)
892 	    break;
893 	  if (sscanf (argv[optind], "%d", &local_symbol_index) <= 0)
894 	    break;
895 	  if (local_symbol_index < 0 || local_symbol_index > 255)
896 	    fprintf (stderr, "%s: the symbol type `%d' is disregarded (it should be in the range 0..255)\n",
897 		     progname, local_symbol_index);
898 	  else
899 	    symbol_index = local_symbol_index;
900 	  optind++;		/* tell getopt we recognized symbol_index */
901 	  if (optind >= argc)
902 	    break;
903 	  if (sscanf (argv[optind], "%lf", &local_symbol_size) <= 0)
904 	    break;
905 	  if (local_symbol_size < 0.0)
906 	    fprintf (stderr, "%s: the negative symbol size `%f' is disregarded\n",
907 		     progname, local_symbol_size);
908 	  else if (local_symbol_size == 0.0)
909 	    fprintf (stderr, "%s: the request for a zero symbol size is disregarded\n",
910 		     progname);
911 	  else
912 	    {
913 	      symbol_size = local_symbol_size;
914 	      new_symbol_size = true;
915 	    }
916 	  optind++;		/* tell getopt we recognized symbol_size */
917 	  break;
918 
919 	  /* ---------- options with one or more arguments ---------- */
920 
921 	case 'R' << 8:		/* End graph and reposition, ARG REQUIRED [3]*/
922 	  old_reposition_trans_x = reposition_trans_x;
923 	  old_reposition_trans_y = reposition_trans_y;
924 	  old_reposition_scale = reposition_scale;
925 
926 	  if (sscanf (optarg, "%lf", &reposition_trans_x) <= 0)
927 	    {
928 	      fprintf (stderr,
929 		       "%s: error: the x repositioning should be a number, but it was `%s'\n",
930 		       progname, optarg);
931 	      return EXIT_FAILURE;
932 	    }
933 	  if (optind >= argc)
934 	    {
935 	      fprintf (stderr,
936 		       "%s: error: one or more arguments to the --reposition option were missing\n",
937 		       progname);
938 	      return EXIT_FAILURE;
939 	    }
940 	  if (sscanf (argv[optind], "%lf", &reposition_trans_y) <= 0)
941 	    {
942 	      fprintf (stderr,
943 		       "%s: error: the y repositioning should be a number, but it was `%s'\n",
944 		       progname, argv[optind]);
945 	      return EXIT_FAILURE;
946 	    }
947 	  optind++;		/* tell getopt we recognized trans_y */
948 	  if (optind >= argc)
949 	    {
950 	      fprintf (stderr,
951 		       "%s: error: one or more arguments to the --reposition option were missing\n",
952 		       progname);
953 	      return EXIT_FAILURE;
954 	    }
955 	  if (sscanf (argv[optind], "%lf", &reposition_scale) <= 0)
956 	    {
957 	      fprintf (stderr,
958 		       "%s: error: the reposition scale factor should be a number, but it was `%s'\n",
959 		       progname, optarg);
960 	      return EXIT_FAILURE;
961 	    }
962 	  if (reposition_scale == 0.0)
963 	    {
964 	      fprintf (stderr,
965 		       "%s: error: the reposition scale factor should not be zero\n", progname);
966 	      return EXIT_FAILURE;
967 	    }
968 	  optind++;		/* tell getopt we recognized trans_x */
969 
970 	  if (!first_file_of_graph)
971 	    /* a graph is in progress (at least one file has been read), so
972 	       it must be ended before we begin the next one */
973 	    {
974 	      if (!filter)
975 		/* We haven't been acting as a real-time filter for the
976 		   duration of this graph, so the graph isn't already drawn
977 		   on the display.  Instead, we have a points array and we
978 		   need to plot it, after computing bounds. */
979 		{
980 		  /* fill in any of min_? and max_? that user didn't
981 		     specify (the prefix "final_" means these arguments
982 		     were finalized at the time the first file of the plot
983 		     was processed) */
984 		  array_bounds (p, no_of_points,
985 				final_transpose_axes, clip_mode,
986 				&final_min_x, &final_min_y,
987 				&final_max_x, &final_max_y,
988 				final_spec_min_x, final_spec_min_y,
989 				final_spec_max_x, final_spec_max_y);
990 
991 		  if (first_graph_of_multigraph)
992 		    /* haven't created multigrapher yet, do so now */
993 		    {
994 		      if ((multigrapher = new_multigrapher (output_format, bg_color, bitmap_size, emulate_color, max_line_length, meta_portable, page_size, rotation_angle, save_screen)) == NULL)
995 			{
996 			  fprintf (stderr,
997 				   "%s: error: the graphing device could not be opened\n", progname);
998 			  return EXIT_FAILURE;
999 			}
1000 		    }
1001 
1002 		  /* begin graph: push new libplot drawing state onto stack
1003 		     of states; also concatenate the current transformation
1004 		     matrix with a matrix formed from the repositioning
1005 		     parameters (this will be in effect for duration of the
1006 		     graph) */
1007 		  begin_graph (multigrapher,
1008 			       old_reposition_scale,
1009 			       old_reposition_trans_x, old_reposition_trans_y);
1010 
1011 		  /* font selection, saves typing */
1012 		  if ((title_font_name == NULL) && (font_name != NULL))
1013 		    title_font_name = font_name;
1014 
1015 		  /* initialize, using (in part) finalized arguments */
1016 		  set_graph_parameters (multigrapher,
1017 					frame_line_width,
1018 					frame_color,
1019 					top_label,
1020 					title_font_name, title_font_size, /* for title */
1021 					tick_size, grid_spec,
1022 					final_min_x, final_max_x, final_spacing_x,
1023 					final_min_y, final_max_y, final_spacing_y,
1024 					final_spec_spacing_x,
1025 					final_spec_spacing_y,
1026 					plot_width, plot_height, margin_below, margin_left,
1027 					font_name, font_size, /* for abs. label */
1028 					x_label,
1029 					font_name, font_size, /* for ord. label */
1030 					y_label,
1031 					no_rotate_y_label,
1032 					/* these args are portmanteaux */
1033 					final_log_axis,
1034 					final_round_to_next_tick,
1035 					switch_axis_end, omit_ticks,
1036 					/* more args */
1037 					clip_mode,
1038 					blankout_fraction,
1039 					final_transpose_axes);
1040 
1041 		  /* draw the graph frame (grid, ticks, etc.); draw a
1042 		     `canvas' (a background opaque white rectangle) only if
1043 		     this isn't the first graph */
1044 		  draw_frame_of_graph (multigrapher,
1045 				       (first_graph_of_multigraph ? false : true));
1046 
1047 		  /* plot the laboriously read-in array */
1048 		  plot_point_array (multigrapher, p, no_of_points);
1049 
1050 		  /* free points array */
1051 		  free (p);
1052 		  no_of_points = 0;
1053 		  first_file_of_graph = false;
1054 
1055 		} /* end of not-filter case */
1056 
1057 	      /* draw graph frame on top of graph, if user requested it */
1058 	      if (frame_on_top)
1059 		{
1060 		  end_polyline_and_flush (multigrapher);
1061 		  draw_frame_of_graph (multigrapher, false);
1062 		}
1063 
1064 	      /* end graph: pop the graph-specific libplot drawing state off
1065                  the stack of drawing states */
1066 	      end_graph (multigrapher);
1067 
1068 	      /* on to next graph */
1069 	      first_graph_of_multigraph = false;
1070 	      first_file_of_graph = true;
1071 
1072 	    } /* end of not first-file-of-plot case */
1073 
1074 	  break;		/* end of `--reposition' option */
1075 
1076 	  /* ---------------- pseudo-options -------------- */
1077 
1078 	  /* File specified on command line, returned in order (along with
1079 	     command-line options).  The first time we reach this point in
1080 	     any plot, we perform special initializations and in particular
1081 	     determine whether or not, for the duration of this plot, we'll
1082 	     be acting as a filter.  We can do so if xmin, xmax, ymin, ymax
1083 	     have all been specified, by this point, on the command line.
1084 
1085 	     A plot may consist of many files.  A plot in progress is
1086 	     terminated if a --reposition option (which moves us to the
1087 	     next plot of a multiplot) is seen, or when the last
1088 	     command-line option is processed. */
1089 	case 1:
1090 	  if (first_file_of_graph)
1091 	    {
1092 	      /* For plots with a logarithmic axis, compute logs of axis
1093 		 limits, since coordinates along the axis, as obtained from
1094 		 the reader, are stored in logarithmic form. */
1095 	      if (log_axis & X_AXIS)
1096 		{
1097 		  if (spec_min_x)
1098 		    {
1099 		      if (min_x > 0.0)
1100 			min_x = log10(min_x);
1101 		      else
1102 			{
1103 			  fprintf(stderr,
1104 				  "%s: error: the limit %g on a logarithmic axis is nonpositive\n",
1105 				  progname, min_x);
1106 			  return EXIT_FAILURE;
1107 			}
1108 		    }
1109 		  if (spec_max_x)
1110 		    {
1111 		      if (max_x > 0.0)
1112 			max_x = log10(max_x);
1113 		      else
1114 			{
1115 			  fprintf(stderr,
1116 				  "%s: error: the limit %g on a logarithmic axis is nonpositive\n",
1117 				  progname, max_x);
1118 			  return EXIT_FAILURE;
1119 			}
1120 		    }
1121 		}
1122 
1123 	      if (log_axis & Y_AXIS)
1124 		{
1125 		  if (spec_min_y)
1126 		    {
1127 		      if (min_y > 0.0)
1128 			min_y = log10(min_y);
1129 		      else
1130 			{
1131 			  fprintf(stderr,
1132 				  "%s: error: the limit %g on a logarithmic axis is nonpositive\n",
1133 				  progname, min_y);
1134 			  return EXIT_FAILURE;
1135 			}
1136 		    }
1137 		  if (spec_max_y)
1138 		    {
1139 		      if (max_y > 0.0)
1140 			max_y = log10(max_y);
1141 		      else
1142 			{
1143 			  fprintf(stderr,
1144 				  "%s: error: the limit %g on a logarithmic axis is nonpositive\n",
1145 				  progname, max_y);
1146 			  return EXIT_FAILURE;
1147 			}
1148 		    }
1149 		}
1150 
1151 	      /* We now finalize the following parameters (arguments to
1152 		 set_graph_parameters()), even though we won't call
1153 		 set_graph_parameters() for a while yet, if it turns out we
1154 		 need to act as a real-time filter. */
1155 
1156 	      /* portmanteaux */
1157 	      final_log_axis = log_axis;
1158 	      final_round_to_next_tick = round_to_next_tick;
1159 
1160 	      /* bool */
1161 	      final_transpose_axes = transpose_axes;
1162 
1163 	      /* x-axis specific */
1164 	      final_min_x = min_x;
1165 	      final_max_x = max_x;
1166 	      final_spacing_x = spacing_x;
1167 	      final_spec_min_x = spec_min_x;
1168 	      final_spec_max_x = spec_max_x;
1169 	      final_spec_spacing_x = spec_spacing_x;
1170 
1171 	      /* y-axis specific */
1172 	      final_min_y = min_y;
1173 	      final_max_y = max_y;
1174 	      final_spec_min_y = spec_min_y;
1175 	      final_spec_max_y = spec_max_y;
1176 	      final_spacing_y = spacing_y;
1177 	      final_spec_spacing_y = spec_spacing_y;
1178 
1179 	      /* If user didn't specify either the lower limit or the upper
1180 		 limit for an axis, by default we'll round the axis limits
1181 		 to the nearest tick, after computing them.  (If either
1182 		 limit was specified by the user, to request rounding the
1183 		 user must specify the -R option as well.) */
1184 	      if (!final_spec_min_x && !final_spec_max_x)
1185 		final_round_to_next_tick |= X_AXIS;
1186 	      if (!final_spec_min_y && !final_spec_max_y)
1187 		final_round_to_next_tick |= Y_AXIS;
1188 
1189 	      /* The case when x_min, x_max, y_min, y_max are all specified
1190 		 by the luser is special: we set the `filter' flag for the
1191 		 duration of this plot, to indicate that we can function as
1192 		 a real-time filter, calling read_and_plot_file() on each
1193 		 file, rather than calling read_file() on each one
1194 		 separately to create an array of points, and then calling
1195 		 plot_point_array(). */
1196 	      filter = ((final_spec_min_x && final_spec_max_x
1197 			 && final_spec_min_y && final_spec_max_y)
1198 			? true : false);
1199 
1200 	    } /* end of first-file-of-plot case */
1201 
1202 	  if (filter)
1203 	    /* filter flag is set, will call read_and_plot() on this file */
1204 	    {
1205 	      if (first_file_of_graph)
1206 		{
1207 		  if (first_graph_of_multigraph)
1208 		    /* need to create the multigrapher */
1209 		    {
1210 		      if ((multigrapher = new_multigrapher (output_format, bg_color, bitmap_size, emulate_color, max_line_length, meta_portable, page_size, rotation_angle, save_screen)) == NULL)
1211 			{
1212 			  fprintf (stderr,
1213 				   "%s: error: the graphing device could not be opened\n",
1214 				   progname);
1215 			  return EXIT_FAILURE;
1216 			}
1217 		    }
1218 
1219 		  /* begin graph: push a graph-specific drawing state onto
1220 		     libplot's stack of drawing states; also concatenate
1221 		     the current transformation matrix with a matrix formed
1222 		     from the repositioning parameters (this will take
1223 		     effect for the duration of the graph) */
1224 		  begin_graph (multigrapher,
1225 			       reposition_scale,
1226 			       reposition_trans_x, reposition_trans_y);
1227 
1228 		  /* font selection, saves typing */
1229 		  if ((title_font_name == NULL) && (font_name != NULL))
1230 		    title_font_name = font_name;
1231 
1232 		  /* following will be in effect for the entire plot */
1233 		  set_graph_parameters (multigrapher,
1234 					frame_line_width,
1235 					frame_color,
1236 					top_label,
1237 					title_font_name, title_font_size, /* for title */
1238 					tick_size, grid_spec,
1239 					final_min_x, final_max_x, final_spacing_x,
1240 					final_min_y, final_max_y, final_spacing_y,
1241 					final_spec_spacing_x,
1242 					final_spec_spacing_y,
1243 					plot_width, plot_height,
1244 					margin_below, margin_left,
1245 					font_name, font_size, /* on abscissa */
1246 					x_label,
1247 					font_name, font_size, /* on ordinate */
1248 					y_label,
1249 					no_rotate_y_label,
1250 					/* these args are portmanteaux */
1251 					final_log_axis,
1252 					final_round_to_next_tick,
1253 					switch_axis_end,
1254 					omit_ticks,
1255 					/* more args */
1256 					clip_mode,
1257 					blankout_fraction,
1258 					final_transpose_axes);
1259 
1260 		  /* draw the graph frame (grid, ticks, etc.); draw a
1261 		     `canvas' (a background opaque white rectangle) only if
1262 		     this isn't the first graph */
1263 		  draw_frame_of_graph (multigrapher,
1264 				       first_graph_of_multigraph ? false : true);
1265 
1266 		  reader = new_reader (data_file, input_type,
1267 				       auto_abscissa, delta_x, x_start,
1268 				       /* following three are graph-specific */
1269 				       final_transpose_axes,
1270 				       final_log_axis, auto_bump,
1271 				       /* following args are file-specific
1272 					  (they set dataset attributes) */
1273 				       symbol_index, symbol_size,
1274 				       symbol_font_name,
1275 				       linemode_index, plot_line_width,
1276 				       fill_fraction, use_color);
1277 		  new_symbol = new_symbol_size = new_symbol_font_name = false;
1278 		  new_linemode = new_plot_line_width = false;
1279 		  new_fill_fraction = new_use_color = false;
1280 		}
1281 	      else
1282 		/* not first file of plot; do some things anyway */
1283 		{
1284 		  /* set reader parameters that may change when we move
1285 		     from file to file within a plot */
1286 		  alter_reader_parameters (reader,
1287 					   data_file, input_type,
1288 					   auto_abscissa, delta_x, x_start,
1289 					   /* following args set dataset
1290 					      attributes */
1291 					   symbol_index, symbol_size,
1292 					   symbol_font_name,
1293 					   linemode_index, plot_line_width,
1294 					   fill_fraction, use_color,
1295 					   /* following bools make up a mask*/
1296 					   new_symbol, new_symbol_size,
1297 					   new_symbol_font_name,
1298 					   new_linemode, new_plot_line_width,
1299 					   new_fill_fraction, new_use_color);
1300 
1301 		  new_symbol = new_symbol_size = new_symbol_font_name = false;
1302 		  new_linemode = new_plot_line_width = false;
1303 		  new_fill_fraction = new_use_color = false;
1304 		}
1305 
1306 	      /* call read_and_plot_file() on the file; each dataset in the
1307 		 file yields a polyline */
1308 	      read_and_plot_file (reader, multigrapher);
1309 
1310 	    } /* end of filter case */
1311 
1312 	  else
1313 	    /* filter flag is set, will read and plot this file separately */
1314 
1315 	    /* Luser didn't specify enough information for us to act as a
1316 	       filter, so we do things the hard way: we call read_file() on
1317 	       each file to create a points array, and at the end of the
1318 	       plot we'll call plot_point_array() on the array.  For now,
1319 	       we don't even call set_graph_parameters(). */
1320 	    {
1321 	      if (first_file_of_graph)	/* some additional initializations */
1322 		{
1323 		  p = (Point *)xmalloc (points_length * sizeof (Point));
1324 
1325 		  reader = new_reader (data_file, input_type,
1326 				       auto_abscissa, delta_x, x_start,
1327 				       /* following are graph-specific */
1328 				       final_transpose_axes,
1329 				       final_log_axis, auto_bump,
1330 				       /* following args are file-specific
1331 					  (they set dataset attributes) */
1332 				       symbol_index, symbol_size,
1333 				       symbol_font_name,
1334 				       linemode_index, plot_line_width,
1335 				       fill_fraction, use_color);
1336 		  new_symbol = new_symbol_size = new_symbol_font_name = false;
1337 		  new_linemode = new_plot_line_width = false;
1338 		  new_fill_fraction = new_use_color = false;
1339 		}
1340 	      else	/* not first file of plot, but do some things anyway */
1341 		{
1342 		  /* set reader parameters that may change when we move
1343 		     from file to file within a plot */
1344 		  alter_reader_parameters (reader,
1345 					   data_file, input_type,
1346 					   auto_abscissa, delta_x, x_start,
1347 					   /* following args set dataset
1348 					      attributes */
1349 					   symbol_index, symbol_size,
1350 					   symbol_font_name,
1351 					   linemode_index, plot_line_width,
1352 					   fill_fraction, use_color,
1353 					   /* following bools make up a mask*/
1354 					   new_symbol, new_symbol_size,
1355 					   new_symbol_font_name,
1356 					   new_linemode, new_plot_line_width,
1357 					   new_fill_fraction, new_use_color);
1358 
1359 		  new_symbol = new_symbol_size = new_symbol_font_name = false;
1360 		  new_linemode = new_plot_line_width = false;
1361 		  new_fill_fraction = new_use_color = false;
1362 		}
1363 
1364 	      /* add points to points array by calling read_file() on file */
1365 	      read_file (reader, &p, &points_length, &no_of_points);
1366 
1367 	    } /* end of not-filter case */
1368 
1369 	  /* close file */
1370 	  if (data_file != stdin)
1371 	    close_file (optarg, data_file);
1372 
1373 	  first_file_of_graph = false;
1374 	  break;	/* end of `case 1' in switch() [i.e., filename seen] */
1375 
1376 	  /*---------------- End of options ----------------*/
1377 
1378 	default:		/* Default, unknown option */
1379 	  errcnt++;
1380 	  continue_parse = false;
1381 	  break;
1382 	}			/* end of switch() */
1383 
1384       if (errcnt > 0)
1385 	continue_parse = false;
1386     }				/* end of while loop */
1387 
1388   if (errcnt > 0)
1389     {
1390       fprintf (stderr, "Try `%s --help' for more information\n", progname);
1391       return EXIT_FAILURE;
1392     }
1393   if (show_version)
1394     {
1395       display_version (progname, written, copyright);
1396       return EXIT_SUCCESS;
1397     }
1398   if (do_list_fonts)
1399     {
1400       int success;
1401 
1402       success = list_fonts (output_format, progname);
1403       if (success)
1404 	return EXIT_SUCCESS;
1405       else
1406 	return EXIT_FAILURE;
1407     }
1408   if (show_fonts)
1409     {
1410       int success;
1411 
1412       success = display_fonts (output_format, progname);
1413       if (success)
1414 	return EXIT_SUCCESS;
1415       else
1416 	return EXIT_FAILURE;
1417     }
1418   if (show_usage)
1419     {
1420       display_usage (progname, hidden_options, usage_appendage, 2);
1421       return EXIT_SUCCESS;
1422     }
1423 
1424   /* End of command-line parse.  At this point, we need to terminate the
1425      graph currently in progress, if it's nonempty (i.e. if one or more
1426      files have been read). */
1427 
1428   if (first_file_of_graph == false)
1429     {
1430       /* At least one file was read.  If we're acting as a real-time
1431 	 filter, then the graph is already drawn on the display and there's
1432 	 nothing for us to do.  Instead, we have a points array and we need
1433 	 to plot it, after computing bounds. */
1434       if (!filter)
1435 	{
1436 
1437 	  /* fill in any of min_? and max_? that user didn't specify (the
1438 	     prefix "final_" means these arguments were finalized at the
1439 	     time the first file of the plot was processed) */
1440 	  array_bounds (p, no_of_points,
1441 			final_transpose_axes, clip_mode,
1442 			&final_min_x, &final_min_y,
1443 			&final_max_x, &final_max_y,
1444 			final_spec_min_x, final_spec_min_y,
1445 			final_spec_max_x, final_spec_max_y);
1446 
1447 	  if (first_graph_of_multigraph)
1448 	    /* still haven't created multigrapher, do so now */
1449 	    {
1450 	      if ((multigrapher = new_multigrapher (output_format, bg_color, bitmap_size, emulate_color, max_line_length, meta_portable, page_size, rotation_angle, save_screen)) == NULL)
1451 		{
1452 		  fprintf (stderr,
1453 			   "%s: error: the graphing device could not be opened\n", progname);
1454 		  return EXIT_FAILURE;
1455 		}
1456 	    }
1457 
1458 	  /* begin graph: push new libplot drawing state onto stack of
1459 	     states; also concatenate the current transformation matrix
1460 	     with a matrix formed from the repositioning parameters (this
1461 	     will take effect for the duration of the graph) */
1462 	  begin_graph (multigrapher,
1463 		       reposition_scale,
1464 		       reposition_trans_x, reposition_trans_y);
1465 
1466 	  /* font selection, saves typing */
1467 	  if ((title_font_name == NULL) && (font_name != NULL))
1468 	    title_font_name = font_name;
1469 
1470 	  set_graph_parameters (multigrapher,
1471 				frame_line_width,
1472 				frame_color,
1473 				top_label,
1474 				title_font_name, title_font_size, /*for title*/
1475 				tick_size, grid_spec,
1476 				final_min_x, final_max_x, final_spacing_x,
1477 				final_min_y, final_max_y, final_spacing_y,
1478 				final_spec_spacing_x,
1479 				final_spec_spacing_y,
1480 				plot_width, plot_height,
1481 				margin_below, margin_left,
1482 				font_name, font_size, /* for abscissa label */
1483 				x_label,
1484 				font_name, font_size, /* for ordinate label */
1485 				y_label,
1486 				no_rotate_y_label,
1487 				/* these args are portmanteaux */
1488 				final_log_axis,
1489 				final_round_to_next_tick,
1490 				switch_axis_end, omit_ticks,
1491 				/* more args */
1492 				clip_mode,
1493 				blankout_fraction,
1494 				final_transpose_axes);
1495 
1496 	  /* draw the graph frame (grid, ticks, etc.); draw a `canvas' (a
1497 	     background opaque white rectangle) only if this isn't the
1498 	     first graph */
1499 	  draw_frame_of_graph (multigrapher,
1500 			       first_graph_of_multigraph ? false : true);
1501 
1502 	  /* plot the laboriously read-in array */
1503 	  plot_point_array (multigrapher, p, no_of_points);
1504 
1505 	  /* free points array */
1506 	  free (p);
1507 	  no_of_points = 0;
1508 
1509 	} /* end of not-filter case */
1510 
1511       /* draw graph frame on top of graph, if user requested it */
1512       if (frame_on_top)
1513 	{
1514 	  end_polyline_and_flush (multigrapher);
1515 	  draw_frame_of_graph (multigrapher, false);
1516 	}
1517 
1518       /* end graph: pop drawing state off the stack of drawing states */
1519       end_graph (multigrapher);
1520 
1521     } /* end of nonempty-graph case */
1522 
1523   /* finish up by deleting our multigrapher (one must have been created,
1524      since we always read at least stdin) */
1525   if (delete_multigrapher (multigrapher) < 0)
1526     {
1527       fprintf (stderr, "%s: error: the graphing device could not be closed\n",
1528 	       progname);
1529       return EXIT_FAILURE;
1530     }
1531 
1532   return EXIT_SUCCESS;
1533 }
1534 
1535 
1536 static void
open_file_for_reading(char * filename,FILE ** input)1537 open_file_for_reading (char *filename, FILE **input)
1538 {
1539   FILE *data_file;
1540 
1541   data_file = fopen (filename, "r");
1542   if (data_file == NULL)
1543     {
1544       fprintf (stderr, "%s: %s: %s\n", progname, filename, strerror(errno));
1545       exit (EXIT_FAILURE);
1546     }
1547   else
1548     *input = data_file;
1549 }
1550 
1551 static void
close_file(char * filename,FILE * stream)1552 close_file (char *filename, FILE *stream)
1553 {
1554   if (fclose (stream) < 0)
1555     fprintf (stderr,
1556 	     "%s: the input file `%s' could not be closed\n",
1557 	     progname, filename);
1558 }
1559 
1560 static bool
parse_pen_string(const char * pen_s)1561 parse_pen_string (const char *pen_s)
1562 {
1563   const char *charp;
1564   char name[MAX_COLOR_NAME_LEN];
1565   int i;
1566 
1567   charp = pen_s;
1568   while (*charp)
1569     {
1570       int pen_num;
1571       bool got_digit;
1572       const char *tmp;
1573 
1574       if (*charp == ':')	/* skip any ':' */
1575 	{
1576 	  charp++;
1577 	  continue;		/* back to top of while loop */
1578 	}
1579       pen_num = 0;
1580       got_digit = false;
1581       while (*charp >= '0' && *charp <= '9')
1582 	{
1583 	  pen_num = 10 * pen_num + (int)*charp - (int)'0';
1584 	  got_digit = true;
1585 	  charp++;
1586 	}
1587       if (!got_digit || pen_num < 1 || pen_num > NO_OF_LINEMODES)
1588 	return false;
1589       if (*charp != '=')
1590 	return false;
1591       charp++;
1592       for (tmp = charp, i = 0; i < MAX_COLOR_NAME_LEN; tmp++, i++)
1593 	{
1594 	  if (*tmp == ':') /* end of color name string */
1595 	    {
1596 	      name[i] = '\0';
1597 	      charp = tmp + 1;
1598 	      break;
1599 	    }
1600 	  else if (*tmp == '\0') /* end of name string */
1601 	    {
1602 	      name[i] = '\0';
1603 	      charp = tmp;
1604 	      break;
1605 	    }
1606 	  else
1607 	    name[i] = *tmp;
1608 	}
1609 
1610       /* replace pen color name by user-specified color name */
1611       colorstyle[pen_num - 1] = xstrdup (name);
1612     }
1613   return true;
1614 }
1615