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