1 /*
2  * xgraph - A Simple Plotter for X
3  *
4  * David Harrison
5  * University of California,  Berkeley
6  * 1986, 1987, 1988, 1989
7  *
8  * Please see copyright.h concerning the formal reproduction rights
9  * of this software.
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include "copyright.h"
15 #include <stdio.h>
16 #include <math.h>
17 #include <pwd.h>
18 #include <ctype.h>
19 #include "xgout.h"
20 #include "xgraph.h"
21 #include "xtb.h"
22 #include "hard_devices.h"
23 #include "params.h"
24 
25 
26 /*
27  * USE_NEW_COLORS selects the default color scheme. Set to one of the following:
28  *
29  *   0 if you want the original colors.
30  *   1 for less garish colors (preferred by Radford Neal)
31  *   2 for less garish "Web-Safe Colors".
32  *   3 for close to original colors, but Web-Safe (prefered by Peter D Gilbert)
33  */
34 
35 #define USE_NEW_COLORS 1
36 
37 #define USE_GEOMETRY 1		/* Set to 0 to disable =WxH+X+Y argument    */
38 
39 #define PDG 1			/* Set to 0 to disable PDG's modifications  */
40 #if PDG
41 /*
42  * Changes bracketed by 'PDG' were made by Peter D Gilbert
43  * (peter.gilbert@digital.com).
44  */
45 #endif
46 
47 #define ZOOM
48 #define TOOLBOX
49 #define NEW_READER
50 
51 /* Portability */
52 #ifdef  CRAY
53 #undef  MAXFLOAT
54 #define MAXFLOAT 10.e300
55 #endif  /* CRAY */
56 
57 #ifndef MAXFLOAT
58 #ifndef __CYGWIN__
59 #define MAXFLOAT	HUGE
60 #else
61 #define MAXFLOAT	HUGE_VAL
62 #endif
63 #endif
64 
65 #define BIGINT		0xfffffff
66 
67 #define GRIDPOWER 	10
68 #define INITSIZE 	128
69 
70 #define CONTROL_D	'\004'
71 #define CONTROL_C	'\003'
72 #define TILDE		'~'
73 
74 #define BTNPAD		1
75 #define BTNINTER	3
76 
77 #define ABS(x)		((x) < 0 ? -(x) : (x))
78 #define ZERO_THRES	1.0E-07
79 
80 /* To get around an inaccurate log */
81 #define nlog10(x)	(x == 0.0 ? 0.0 : log10(x) + 1e-15)
82 
83 #define ISCOLOR		(wi->dev_info.dev_flags & D_COLOR)
84 
85 #define PIXVALUE(set) 	((set) % MAXATTR)
86 
87 #define LINESTYLE(set) \
88 (ISCOLOR ?  ((set)/MAXATTR) : ((set) % MAXATTR))
89 
90 #define MARKSTYLE(set) \
91 (colorMark ? COLMARK(set) : BWMARK(set))
92 
93 #define COLMARK(set) \
94 ((set) / MAXATTR)
95 
96 #define BWMARK(set) \
97 ((set) % MAXATTR)
98 
99 #define LOG_X	0x01
100 #define LOG_Y	0x02
101 
102 
103 /*
104  * Default settings for xgraph parameters
105  */
106 
107 #define DEF_BORDER_WIDTH	"2"
108 #define DEF_BORDER_COLOR	"Black"
109 #define DEF_TITLE_TEXT		""
110 #define DEF_XUNIT_TEXT		"X"
111 #define DEF_YUNIT_TEXT		"Y"
112 #define DEF_TICK_FLAG		"off"
113 #define DEF_MARK_FLAG		"off"
114 #define DEF_PIXMARK_FLAG	"off"
115 #define DEF_LARGEPIX_FLAG	"off"
116 #define DEF_DIFFMARK_FLAG	"off"
117 #define DEF_BB_FLAG		"off"
118 #define DEF_NOLINE_FLAG		"off"
119 #define DEF_LOGX_FLAG		"off"
120 #define DEF_LOGY_FLAG		"off"
121 #define DEF_BAR_FLAG		"off"
122 #define DEF_BAR_BASE		"0.0"
123 #define DEF_BAR_WIDTH		"-1.0"
124 #define DEF_LINE_WIDTH		"0"
125 #define DEF_GRID_SIZE		"0"
126 #define DEF_GRID_STYLE		"10"
127 #define DEF_LABEL_FONT		"helvetica-12"
128 #define DEF_TITLE_FONT		"helvetica-18"
129 #define DEF_GEOMETRY		"600x600+100+100"
130 #define DEF_REVERSE		"off"
131 #define DEF_DEVICE		"Postscript"
132 #define DEF_DISPOSITION		"To Device"
133 #define DEF_FILEORDEV		""
134 
135 #define DEF_MARKER_FLAG		"off"
136 #define DEF_DIFFMARK_FLAG	"off"
137 #define DEF_PIXMARK_FLAG	"off"
138 #define DEF_LARGEPIX_FLAG	"off"
139 
140 /* Low > High means set it based on the data */
141 #define DEF_LOW_LIMIT		"1.0"
142 #define DEF_HIGH_LIMIT		"0.0"
143 
144 /* Black and white defaults */
145 #define DEF_BW_BACKGROUND	"white"
146 #define DEF_BW_BORDER		"black"
147 #define DEF_BW_ZEROCOLOR	"black"
148 #define DEF_BW_ZEROWIDTH	"3"
149 #define DEF_BW_ZEROSTYLE	"1"
150 #define DEF_BW_FOREGROUND	"black"
151 
152 /* Color defaults */
153 
154 #if USE_NEW_COLORS == 0
155 #define DEF_COL_BACKGROUND      "#ccc"
156 #elif USE_NEW_COLORS == 1
157 #define DEF_COL_BACKGROUND      "#b0b0b0"
158 #else
159 /* For some reason, "#cccccc" doesn't work as well as the following */
160 #define DEF_COL_BACKGROUND      "#ccc"
161 #endif
162 
163 #define DEF_COL_BORDER		"black"
164 #define DEF_COL_ZEROCOLOR	"white"
165 #define DEF_COL_ZEROWIDTH	"0"
166 #define DEF_COL_ZEROSTYLE	"1"
167 #define DEF_COL_FOREGROUND	"black"
168 #define DEF_COL_FIRSTSTYLE	"1"
169 
170 /* Default line styles */
171 static char *defStyle[MAXATTR] = {
172     "1", "10", "11110000", "010111", "1110",
173     "1111111100000000", "11001111", "0011000111"
174 };
175 
176 /* Default color names */
177 
178 #if USE_NEW_COLORS == 0 /* These were the original colors */
179 static char *defColors[MAXATTR] = {
180     "red", "SpringGreen", "blue", "yellow",
181     "cyan", "sienna", "orange", "coral"
182 };
183 #elif USE_NEW_COLORS == 1 /* Less garish ones preferred by R. Neal */
184 static char *defColors[MAXATTR] = {
185     "#a00000", "#009000", "#0000a0", "#908000",
186     "#007070", "#800080", "#b05050", "#306020",
187 };
188 #elif USE_NEW_COLORS == 2 /* Less garish "Web-Safe Colors" */
189 static char *defColors[MAXATTR] = {
190     "#990000", "#009900", "#000099", "#999900",
191     "#006666", "#990099", "#cc6666", "#336633",
192 };
193 #elif USE_NEW_COLORS == 3 /* Close to the original colors, but web-safe */
194 static char *defColors[MAXATTR] = {
195 /* From /usr/lib/X11/rgb.txt : */
196 /* orange = 255 160 122 = ff a0 00 -> #ff9900 has troubles; #cc9900 works */
197 /* coral  = 255 127  80 = ff 7f 50 -> #ff6666 has troubles; #ff6633 works */
198     "#ff0000", "#00ff99", "#0000ff", "#ffff00",
199     "#00ffff", "#996633", "#cc9900", "#ff6633"
200 };
201 #endif
202 
203 
204 extern void init_X();
205 extern void do_error();
206 
207 static char *tildeExpand();
208 static void ReverseIt();
209 static void set_mark_flags();
210 static void Traverse();
211 
212 typedef struct point_list {
213     int numPoints;		/* Number of points in group */
214     int allocSize;		/* Allocated size            */
215     double *xvec;		/* X values                  */
216     double *yvec;		/* Y values                  */
217     struct point_list *next;	/* Next set of points        */
218 } PointList;
219 
220 typedef struct new_data_set {
221     char *setName;		/* Name of data set     */
222     PointList *list;		/* List of point arrays */
223 } NewDataSet;
224 
225 static NewDataSet PlotData[MAXSETS];
226 
227 static XSegment *Xsegs;		/* Point space for X */
228 
229 /* Basic transformation stuff */
230 static double llx, lly, urx, ury; /* Bounding box of all data */
231 
232 #define HARDCOPY_IN_PROGRESS	0x01
233 
234 typedef struct local_win {
235     double loX, loY, hiX, hiY;	/* Local bounding box of window         */
236     int XOrgX, XOrgY;		/* Origin of bounding box on screen     */
237     int XOppX, XOppY;		/* Other point defining bounding box    */
238     double UsrOrgX, UsrOrgY;	/* Origin of bounding box in user space */
239     double UsrOppX, UsrOppY;	/* Other point of bounding box          */
240     double XUnitsPerPixel;	/* X Axis scale factor                  */
241     double YUnitsPerPixel;	/* Y Axis scale factor                  */
242     xgOut dev_info;		/* Device information                   */
243     Window close, hardcopy;	/* Buttons for closing and hardcopy     */
244     Window about;		/* Version information                  */
245     int flags;			/* Window flags                         */
246 } LocalWin;
247 
248 #define SCREENX(ws, userX) \
249 	(((int) (((userX) - ws->UsrOrgX)/ws->XUnitsPerPixel + 0.5)) + ws->XOrgX)
250 #define SCREENY(ws, userY) \
251 	(ws->XOppY - ((int) (((userY) - ws->UsrOrgY)/ws->YUnitsPerPixel + 0.5)))
252 
253 static XContext win_context = (XContext) 0;
254 
255 /* Other globally set defaults */
256 
257 Display *disp;			/* Open display            */
258 Visual *vis;			/* Standard visual         */
259 Colormap cmap;			/* Standard colormap       */
260 int screen;			/* Screen number           */
261 int depth;			/* Depth of screen         */
262 
263 static int numFiles = 0;		/* Number of input files   */
264 static char *inFileNames[MAXSETS]; 	/* File names              */
265 
266 /* Total number of active windows */
267 static int Num_Windows = 0;
268 static char *Prog_Name;
269 #if PDG
270 static char *Window_Name;
271 #endif
272 
273 
274 
275 static int XErrHandler();	/* Handles error messages */
276 
main(argc,argv)277 main(argc, argv)
278 int argc;
279 char *argv[];
280 /*
281  * This sets up the hard-wired defaults and reads the X defaults.
282  * The command line format is: xgraph [host:display].
283  */
284 {
285     Window primary, NewWindow();
286     XEvent theEvent;
287     LocalWin *win_info;
288     Cursor zoomCursor;
289     FILE *strm;
290     XColor fg_color, bg_color;
291     char keys[MAXKEYS], *disp_name;
292     int nbytes, idx, maxitems, flags;
293     int errs = 0;
294 
295     /* Open up new display */
296     Prog_Name = argv[0];
297 #if PDG
298     Window_Name = NULL;
299 #endif
300     disp_name = NULL;	/* PDG 1/7/99 */
301     for (idx = 1;  idx < argc-1;  idx++) {
302 	if (strcmp(argv[idx], "-display") == 0) {
303 	    disp_name = argv[idx+1];
304 	    break;
305 	}
306     }
307     disp = XOpenDisplay(disp_name);
308     if (!disp) {
309 	(void) fprintf(stderr, "%s: cannot open display `%s'\n", argv[0], disp_name);
310 	abort();
311     }
312     XSetErrorHandler(XErrHandler);
313 
314     /* Set up hard-wired defaults and allocate spaces */
315     InitSets();
316 
317     /* Read X defaults and override hard-coded defaults */
318     ReadDefaults();
319 
320     /* Parse the argument list looking for input files */
321     ParseArgs(argc, argv, 0);
322 
323     /* Read the data into the data sets */
324     llx = lly = MAXFLOAT;
325     urx = ury = -MAXFLOAT;
326     for (idx = 0;  idx < numFiles;  idx++) {
327 #if PDG
328 	if (!Window_Name) Window_Name = inFileNames[idx];
329 #endif
330 	strm = fopen(inFileNames[idx], "r");
331 	if (!strm) {
332 	    (void) fprintf(stderr, "Warning:  cannot open file `%s'\n",
333 			   inFileNames[idx]);
334 	    errs++;
335 	} else {
336 	    if ((maxitems = ReadData(strm, inFileNames[idx])) < 0) {
337 		errs++;
338 	    }
339 	    (void) fclose(strm);
340 	}
341     }
342     if (!numFiles) {
343 	if ((maxitems = ReadData(stdin, (char *) 0)) < 0) {
344 	    errs++;
345 	}
346     }
347     if (errs) {
348 	(void) fprintf(stderr, "Problems found with input data.\n");
349 	exit(1);
350     }
351 #if PDG
352     if (!Window_Name) Window_Name = Prog_Name;
353 #endif
354 
355     /* Parse the argument list to set options */
356     ParseArgs(argc, argv, 1);
357 
358     Xsegs = (XSegment *) malloc((unsigned) (maxitems * sizeof(XSegment)));
359 
360     /* Reverse Video Hack */
361     if (PM_BOOL("ReverseVideo")) ReverseIt();
362     hard_init();
363     if (PM_BOOL("Debug")) {
364 	(void) XSynchronize(disp, 1);
365 	param_dump();
366     }
367 
368     /* Logarithmic and bounding box computation */
369     flags = 0;
370     if (PM_BOOL("LogX")) flags |= LOG_X;
371     if (PM_BOOL("LogY")) flags |= LOG_Y;
372     Traverse(flags);
373 
374     /* Nasty hack here for bar graphs */
375     if (PM_BOOL("BarGraph")) {
376 	double base;
377 
378 	llx -= PM_DBL("BarWidth");
379 	urx += PM_DBL("BarWidth");
380 	base = PM_DBL("BarBase");
381 	if (base < lly) lly = base;
382 	if (base > ury) ury = base;
383     }
384 
385     /* Create initial window */
386     xtb_init(disp, screen, PM_PIXEL("Foreground"), PM_PIXEL("Background"),
387 	     PM_FONT("LabelFont"));
388 #if !PDG
389     primary = NewWindow(Prog_Name,
390 #else
391     primary = NewWindow(Window_Name,
392 #endif
393 			PM_DBL("XLowLimit"), PM_DBL("YLowLimit"),
394 			PM_DBL("XHighLimit"), PM_DBL("YHighLimit"),
395 			1.0, 1);
396     if (!primary) {
397 	(void) fprintf(stderr, "Main window would not open\n");
398 	exit(1);
399     }
400 
401     zoomCursor = XCreateFontCursor(disp, XC_sizing);
402     fg_color = PM_COLOR("Foreground");
403     bg_color = PM_COLOR("Background");
404     XRecolorCursor(disp, zoomCursor, &fg_color, &bg_color);
405     init_X(NULL);
406 
407     Num_Windows = 1;
408     while (Num_Windows > 0) {
409 	XNextEvent(disp, &theEvent);
410 	if (xtb_dispatch(&theEvent) != XTB_NOTDEF) continue;
411 	if (XFindContext(theEvent.xany.display,
412 			 theEvent.xany.window,
413 			 win_context, (caddr_t *) &win_info)) {
414 	    /* Nothing found */
415 	    continue;
416 	}
417 	switch (theEvent.type) {
418 	case ConfigureNotify:
419 		win_info->dev_info.area_w = theEvent.xconfigure.width;
420 		win_info->dev_info.area_h = theEvent.xconfigure.height;
421 		init_X(win_info->dev_info.user_state);
422 		XClearArea(disp, theEvent.xany.window, 1, 1, win_info->dev_info.area_w, win_info->dev_info.area_h, 0);
423 		DrawWindow(win_info);
424 		break;
425 	case Expose:
426 	    if (theEvent.xexpose.count <= 0) {
427 		XWindowAttributes win_attr;
428 
429 		XGetWindowAttributes(disp, theEvent.xany.window, &win_attr);
430 		win_info->dev_info.area_w = win_attr.width;
431 		win_info->dev_info.area_h = win_attr.height;
432 		init_X(win_info->dev_info.user_state);
433 		XClearArea(disp, theEvent.xany.window, 1, 1, win_info->dev_info.area_w, win_info->dev_info.area_h, 0);
434 		DrawWindow(win_info);
435 	    }
436 	    break;
437 	case KeyPress:
438 	    nbytes = XLookupString(&theEvent.xkey, keys, MAXKEYS,
439 				   (KeySym *) 0, (XComposeStatus *) 0);
440 	    for (idx = 0;  idx < nbytes;  idx++) {
441 		if (keys[idx] == CONTROL_D) {
442 		    /* Delete this window */
443 		    DelWindow(theEvent.xkey.window, win_info);
444 		} else if (keys[idx] == CONTROL_C) {
445 		    /* Exit program */
446 		    Num_Windows = 0;
447 		} else if (keys[idx] == 'h') {
448 		    PrintWindow(theEvent.xany.window, win_info);
449 		}
450 	    }
451 	    break;
452 	case ButtonPress:
453 	    /* Handle creating a new window */
454 #if !PDG
455 	    Num_Windows += HandleZoom(Prog_Name,
456 #else
457 	    Num_Windows += HandleZoom(Window_Name,
458 #endif
459 				      &theEvent.xbutton,
460 				      win_info, zoomCursor);
461 	    break;
462 	default:
463 	    (void) fprintf(stderr, "Unknown event type: %x\n", theEvent.type);
464 	    break;
465 	}
466     }
467     return 0;
468 }
469 
470 
471 #define BLACK_THRES	30000
472 
ReversePix(param_name)473 static void ReversePix(param_name)
474 char *param_name;		/* Name of color parameter */
475 /*
476  * Looks up `param_name' in the parameters database.  If found, the
477  * color is examined and judged to be either black or white based
478  * upon its red, green, and blue intensities.  The sense of the
479  * color is then reversed and reset to its opposite.
480  */
481 {
482     params val;
483 
484     if (param_get(param_name, &val)) {
485 	if ((val.pixv.value.red < BLACK_THRES) &&
486 	    (val.pixv.value.green < BLACK_THRES) &&
487 	    (val.pixv.value.blue < BLACK_THRES)) {
488 	    /* Color is black */
489 	    param_reset(param_name, "white");
490 	} else {
491 	    /* Color is white */
492 	    param_reset(param_name, "black");
493 	}
494     } else {
495 	(void) fprintf(stderr, "Cannot reverse color `%s'\n", param_name);
496     }
497 }
498 
ReverseIt()499 static void ReverseIt()
500 /*
501  * This routine attempts to implement reverse video.  It steps through
502  * all of the important colors in the parameters database and makes
503  * black white (and vice versa).
504  */
505 {
506     int i;
507     char buf[1024];
508 
509     for (i = 0;  i < MAXATTR;  i++) {
510 	(void) sprintf(buf, "%d.Color", i);
511 	ReversePix(buf);
512     }
513     ReversePix("Foreground");
514     ReversePix("Border");
515     ReversePix("ZeroColor");
516     ReversePix("Background");
517 }
518 
519 
Traverse(flags)520 static void Traverse(flags)
521 int flags;			/* Options */
522 /*
523  * Traverses through all of the data applying certain options to the
524  * data and computing the overall bounding box.  The flags are:
525  *   LOG_X	Take the log of the X axis
526  *   LOG_Y	Take the log of the Y axis
527  */
528 {
529     int i, j;
530     PointList *spot;
531 
532     for (i = 0;  i < MAXSETS;  i++) {
533 	for (spot = PlotData[i].list;  spot;  spot = spot->next) {
534 	    for (j = 0;  j < spot->numPoints;  j++) {
535 		if (flags & LOG_Y) {
536 		    if (spot->yvec[j] > 0.0) {
537 			spot->yvec[j] = log10(spot->yvec[j]);
538 		    } else {
539 			(void) fprintf(stderr, "Cannot plot non-positive Y values\n\
540 when the logarithmic option is selected.\n");
541 			exit(1);
542 		    }
543 		}
544 		if (flags & LOG_X) {
545 		    if (spot->xvec[j] > 0.0) {
546 			spot->xvec[j] = log10(spot->xvec[j]);
547 		    } else {
548 			(void) fprintf(stderr, "Cannot plot non-positive X values\n\
549 when the logarithmic option is selected.\n");
550 			exit(1);
551 		    }
552 		}
553 		/* Update global bounding box */
554 		if (spot->xvec[j] < llx) llx = spot->xvec[j];
555 		if (spot->xvec[j] > urx) urx = spot->xvec[j];
556 		if (spot->yvec[j] < lly) lly = spot->yvec[j];
557 		if (spot->yvec[j] > ury) ury = spot->yvec[j];
558 	    }
559 	}
560     }
561 }
562 
563 
564 
565 /*
566  * Button handling functions
567  */
568 
569 /*ARGSUSED*/
del_func(win,bval,info)570 xtb_hret del_func(win, bval, info)
571 Window win;			/* Button window    */
572 int bval;			/* Button value     */
573 char *info;			/* User information */
574 /*
575  * This routine is called when the `Close' button is pressed in
576  * an xgraph window.  It causes the window to go away.
577  */
578 {
579     Window the_win = (Window) info;
580     LocalWin *win_info;
581 
582     xtb_bt_set(win, 1, (char *) 0, 0);
583     if (!XFindContext(disp, the_win, win_context, (caddr_t *) &win_info)) {
584 	if (win_info->flags & HARDCOPY_IN_PROGRESS) {
585 	    do_error("Can't close window while\nhardcopy dialog is posted.\n");
586 	    xtb_bt_set(win, 0, (char *) 0, 0);
587 	} else {
588 	    DelWindow(the_win, win_info);
589 	}
590     }
591     return XTB_HANDLED;
592 }
593 
594 /*ARGSUSED*/
hcpy_func(win,bval,info)595 xtb_hret hcpy_func(win, bval, info)
596 Window win;			/* Button Window    */
597 int bval;			/* Button value     */
598 char *info;			/* User Information */
599 /*
600  * This routine is called when the hardcopy button is pressed
601  * in an xgraph window.  It causes the output dialog to be
602  * posted.
603  */
604 {
605     Window the_win = (Window) info;
606     LocalWin *win_info;
607 
608     xtb_bt_set(win, 1, (char *) 0, 0);
609     if (!XFindContext(disp, the_win, win_context, (caddr_t *) &win_info)) {
610 	win_info->flags |= HARDCOPY_IN_PROGRESS;
611 	PrintWindow(the_win, win_info);
612 	win_info->flags &= (~HARDCOPY_IN_PROGRESS);
613     }
614     xtb_bt_set(win, 0, (char *) 0, 0);
615     return XTB_HANDLED;
616 }
617 
618 static
619 
620 /*ARGSUSED*/
abt_func(win,bval,info)621 xtb_hret abt_func(win, bval, info)
622 Window win;			/* Button window    */
623 int bval;			/* Button value     */
624 char *info;			/* User information */
625 {
626     static char *msg_fmt =
627 "Version %s\n\
628 Written by David Harrison,\n\
629 University of California, Berkeley\n\
630 Modified and packaged by Radford Neal,\n\
631 2000-09-10.  Send comments to\n\
632 radford@cs.toronto.edu";
633     static int active = 0;
634     char msg_buf[1024];
635 
636     if (!active) {
637 	active = 1;
638 	xtb_bt_set(win, 1, (char *) 0, 0);
639 	(void) sprintf(msg_buf, msg_fmt, VERSION_STRING);
640 	msg_box("XGraph", msg_buf);
641 	xtb_bt_set(win, 0, (char *) 0, 0);
642 	active = 0;
643     }
644     return XTB_HANDLED;
645 }
646 
647 
648 #define NORMSIZE	600
649 #define MINDIM		100
650 
NewWindow(progname,lowX,lowY,upX,upY,asp,primary)651 Window NewWindow(progname, lowX, lowY, upX, upY, asp, primary)
652 char *progname;			/* Name of program    */
653 double lowX, lowY;		/* Lower left corner  */
654 double upX, upY;		/* Upper right corner */
655 double asp;			/* Aspect ratio       */
656 int primary;			/* Is this the primary window? */
657 /*
658  * Creates and maps a new window.  This includes allocating its
659  * local structure and associating it with the XId for the window.
660  * The aspect ratio is specified as the ratio of width over height.
661  */
662 {
663     Window new_window;
664     LocalWin *new_info;
665     static Cursor theCursor = (Cursor) 0;
666     XSizeHints sizehints;
667     XSetWindowAttributes wattr;
668     XWMHints wmhints;
669     XColor fg_color, bg_color;
670     int geo_mask;
671     int width, height;
672     unsigned long wamask;
673     double pad;
674     params geom;
675 
676     new_info = (LocalWin *) malloc(sizeof(LocalWin));
677 
678     if (upX > lowX) {
679 	new_info->loX = lowX;
680 	new_info->hiX = upX;
681     } else {
682 	new_info->loX = llx;
683 	new_info->hiX = urx;
684     }
685     if (upY > lowY) {
686 	new_info->loY = lowY;
687 	new_info->hiY = upY;
688     } else {
689 	new_info->loY = lly;
690 	new_info->hiY = ury;
691     }
692 
693     /* Increase the padding for aesthetics */
694     if (new_info->hiX - new_info->loX == 0.0) {
695 	pad = MAX(0.5, fabs(new_info->hiX/2.0));
696 	new_info->hiX += pad;
697 	new_info->loX -= pad;
698     }
699     if (new_info->hiY - new_info->loY == 0) {
700 	pad = MAX(0.5, fabs(ury/2.0));
701 	new_info->hiY += pad;
702 	new_info->loY -= pad;
703     }
704 
705     /* Add 10% padding to bounding box (div by 20 yeilds 5%) */
706     pad = (new_info->hiX - new_info->loX) / 20.0;
707     new_info->loX -= pad;  new_info->hiX += pad;
708     pad = (new_info->hiY - new_info->loY) / 20.0;
709     new_info->loY -= pad;  new_info->hiY += pad;
710 
711 
712     sizehints.x = sizehints.y = 100;
713     width = height = NORMSIZE;
714 
715 #if USE_GEOMETRY
716     if (param_get("GEOMETRY",&geom)) {
717         sscanf (geom.strv.value, "%dx%d%d%d",
718                 &width, &height, &sizehints.x, &sizehints.y);
719     }
720 
721     if (!primary) {
722         sizehints.x -= 35;
723         if (sizehints.x<0) sizehints.x = 0;
724         sizehints.y += 25;
725     }
726 #else
727 
728     /* Aspect ratio computation */
729     if (asp < 1.0) {
730 	width = ((int) (((double) NORMSIZE) * asp));
731     } else {
732 	height = ((int) (((double) NORMSIZE) / asp));
733     }
734 #endif
735     height = MAX(MINDIM, height);
736     width = MAX(MINDIM, width);
737 
738     wamask = CWBackPixel | CWBorderPixel | CWColormap;
739     wattr.background_pixel = PM_PIXEL("Background");
740     wattr.border_pixel = PM_PIXEL("Border");
741     wattr.colormap = cmap;
742 
743     sizehints.flags = PPosition|PSize;
744 
745     sizehints.width = width;
746     sizehints.height = height;
747 
748     new_window = XCreateWindow(disp, RootWindow(disp, screen),
749 			       sizehints.x, sizehints.y,
750 			       (unsigned int) sizehints.width,
751 			       (unsigned int) sizehints.height,
752 			       (unsigned int) PM_INT("BorderSize"),
753 			       depth, InputOutput, vis,
754 			       wamask, &wattr);
755 
756     if (new_window) {
757 	xtb_frame cl_frame, hd_frame, ab_frame;
758 
759 	XStoreName(disp, new_window, progname);
760 	XSetIconName(disp, new_window, progname);
761 
762 	wmhints.flags = InputHint | StateHint;
763 	wmhints.input = True;
764 	wmhints.initial_state = NormalState;
765 	XSetWMHints(disp, new_window, &wmhints);
766 
767 	geo_mask = XParseGeometry(PM_STR("Geometry"), &sizehints.x, &sizehints.y,
768 				  (unsigned int *) &sizehints.width,
769 				  (unsigned int *) &sizehints.height);
770 	if (geo_mask & (XValue | YValue)) {
771 	    sizehints.flags = (sizehints.flags & ~PPosition) | USPosition;
772 	}
773 	if (geo_mask & (WidthValue | HeightValue)) {
774 	    sizehints.flags = (sizehints.flags & ~PSize) | USSize;
775 	}
776 	XSetNormalHints(disp, new_window, &sizehints);
777 
778 	/* Set device info */
779 	set_X(new_window, &(new_info->dev_info));
780 
781 	/* Make buttons */
782 	xtb_bt_new(new_window, "Close", del_func,
783 		   (xtb_data) new_window, &cl_frame);
784 	new_info->close = cl_frame.win;
785 	XMoveWindow(disp, new_info->close, (int) BTNPAD, (int) BTNPAD);
786 	xtb_bt_new(new_window, "Hardcopy", hcpy_func,
787 		   (xtb_data) new_window, &hd_frame);
788 	new_info->hardcopy = hd_frame.win;
789 	XMoveWindow(disp, new_info->hardcopy,
790 		    (int) (BTNPAD + cl_frame.width + BTNINTER),
791 		    BTNPAD);
792 	xtb_bt_new(new_window, "About", abt_func,
793 		   (xtb_data) new_window, &ab_frame);
794 	new_info->about = ab_frame.win;
795 	XMoveWindow(disp, new_info->about,
796 		    (int) (BTNPAD + cl_frame.width + BTNINTER +
797 			   hd_frame.width + BTNINTER), BTNPAD);
798 
799 	new_info->flags = 0;
800 	XSelectInput(disp, new_window,
801 		     ExposureMask|KeyPressMask|ButtonPressMask|StructureNotifyMask);
802 	if (!theCursor) {
803 	    theCursor = XCreateFontCursor(disp, XC_top_left_arrow);
804 	    fg_color = PM_COLOR("Foreground");
805 	    bg_color = PM_COLOR("Background");
806 	    XRecolorCursor(disp, theCursor, &fg_color, &bg_color);
807 	}
808 	XDefineCursor(disp, new_window, theCursor);
809 	if (!win_context) {
810 	    win_context = XUniqueContext();
811 	}
812 	XSaveContext(disp, new_window, win_context, (caddr_t) new_info);
813 	XMapWindow(disp, new_window);
814 	return new_window;
815     } else {
816 	return (Window) 0;
817     }
818 }
819 
820 
DelWindow(win,win_info)821 DelWindow(win, win_info)
822 Window win;			/* Window     */
823 LocalWin *win_info;		/* Local Info */
824 /*
825  * This routine actually deletes the specified window and
826  * decrements the window count.
827  */
828 {
829     xtb_data info;
830 
831     XDeleteContext(disp, win, win_context);
832     xtb_bt_del(win_info->close, &info);
833     xtb_bt_del(win_info->hardcopy, &info);
834     xtb_bt_del(win_info->about, &info);
835     free((char *) win_info);
836     XDestroyWindow(disp, win);
837     Num_Windows -= 1;
838 }
839 
PrintWindow(win,win_info)840 PrintWindow(win, win_info)
841 Window win;			/* Window       */
842 LocalWin *win_info;		/* Local Info   */
843 /*
844  * This routine posts a dialog asking about the hardcopy
845  * options desired.  If the user hits `OK',  the hard
846  * copy is performed.
847  */
848 {
849     ho_dialog(win, Prog_Name, (char *) win_info);
850 }
851 
852 
853 static XRectangle boxEcho;
854 static GC echoGC = (GC) 0;
855 
856 #define DRAWBOX \
857 if (startX < curX) { \
858    boxEcho.x = startX; \
859    boxEcho.width = curX - startX; \
860 } else { \
861    boxEcho.x = curX; \
862    boxEcho.width = startX - curX; \
863 } \
864 if (startY < curY) { \
865    boxEcho.y = startY; \
866    boxEcho.height = curY - startY; \
867 } else { \
868    boxEcho.y = curY; \
869    boxEcho.height = startY - curY; \
870 } \
871 XDrawRectangles(disp, win, echoGC, &boxEcho, 1);
872 
873 #define TRANX(xval) \
874 (((double) ((xval) - wi->XOrgX)) * wi->XUnitsPerPixel + wi->UsrOrgX)
875 
876 #define TRANY(yval) \
877 (wi->UsrOppY - (((double) ((yval) - wi->XOrgY)) * wi->YUnitsPerPixel))
878 
879 
HandleZoom(progname,evt,wi,cur)880 int HandleZoom(progname, evt, wi, cur)
881 char *progname;
882 XButtonPressedEvent *evt;
883 LocalWin *wi;
884 Cursor cur;
885 {
886     Window win, new_win;
887     Window root_rtn, child_rtn;
888     XEvent theEvent;
889     int startX, startY, curX, curY, newX, newY, stopFlag, numwin;
890     int root_x, root_y;
891     unsigned int mask_rtn;
892     double loX, loY, hiX, hiY, asp;
893 
894     win = evt->window;
895     if (XGrabPointer(disp, win, True,
896 		     (unsigned int) (ButtonPressMask|ButtonReleaseMask|
897 				     PointerMotionMask|PointerMotionHintMask),
898 		     GrabModeAsync, GrabModeAsync,
899 		     win, cur, CurrentTime) != GrabSuccess) {
900 	XBell(disp, 0);
901 	return 0;
902     }
903     if (echoGC == (GC) 0) {
904 	unsigned long gcmask;
905 	XGCValues gcvals;
906 
907 	gcmask = GCForeground | GCFunction;
908 	gcvals.foreground = PM_PIXEL("ZeroColor") ^ PM_PIXEL("Background");
909 	gcvals.function = GXxor;
910 	echoGC = XCreateGC(disp, win, gcmask, &gcvals);
911     }
912     startX = evt->x;  startY = evt->y;
913     XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y,
914 		  &curX, &curY, &mask_rtn);
915     /* Draw first box */
916     DRAWBOX;
917     stopFlag = 0;
918     while (!stopFlag) {
919 	XNextEvent(disp, &theEvent);
920 	switch (theEvent.xany.type) {
921 	case MotionNotify:
922 	    XQueryPointer(disp, win, &root_rtn, &child_rtn, &root_x, &root_y,
923 			  &newX, &newY, &mask_rtn);
924 	    /* Undraw the old one */
925 	    DRAWBOX;
926 	    /* Draw the new one */
927 	    curX = newX;  curY = newY;
928 	    DRAWBOX;
929 	    break;
930 	case ButtonRelease:
931 	    DRAWBOX;
932 	    XUngrabPointer(disp, CurrentTime);
933 	    stopFlag = 1;
934 	    if ((startX-curX != 0) && (startY-curY != 0)) {
935 		/* Figure out relative bounding box */
936 		loX = TRANX(startX);   loY = TRANY(startY);
937 		hiX = TRANX(curX);     hiY = TRANY(curY);
938 		if (loX > hiX) {
939 		    double temp;
940 
941 		    temp = hiX;
942 		    hiX = loX;
943 		    loX = temp;
944 		}
945 		if (loY > hiY) {
946 		    double temp;
947 
948 		    temp = hiY;
949 		    hiY = loY;
950 		    loY = temp;
951 		}
952 		/* physical aspect ratio */
953 		asp = ((double) ABS(startX-curX))/((double) ABS(startY-curY));
954 		new_win = NewWindow(progname, loX, loY, hiX, hiY, asp, 0);
955 		if (new_win) {
956 		    numwin = 1;
957 		} else {
958 		    numwin = 0;
959 		}
960 	    } else {
961 		numwin = 0;
962 	    }
963 	    break;
964 #if PDG
965 	case Expose:	/* 12 */
966 	    DRAWBOX;
967 	    XUngrabPointer(disp, CurrentTime);
968 	    stopFlag = 1;
969 	    numwin = 0;
970 	    if (theEvent.xexpose.count <= 0) {
971 		XWindowAttributes win_attr;
972 
973 		XGetWindowAttributes(disp, theEvent.xany.window, &win_attr);
974 		wi->dev_info.area_w = win_attr.width;
975 		wi->dev_info.area_h = win_attr.height;
976 		init_X(wi->dev_info.user_state);
977 		DrawWindow(wi);
978 	    }
979 	    break;
980 #endif
981 	default:
982 	    printf("unknown event: %d\n", theEvent.xany.type);
983 	    break;
984 	}
985     }
986     return numwin;
987 }
988 
989 
InitSets()990 int InitSets()
991 /*
992  * Initializes the data sets with default information.  Sets up
993  * original values for parameters in parameters package.
994  */
995 {
996     int idx;
997     char buf[1024];
998 
999     /*
1000      * Used to do all kinds of searching through visuals, etc.
1001      * Got complaints -- so back to the simple version.
1002      */
1003     vis = DefaultVisual(disp, DefaultScreen(disp));
1004     cmap = DefaultColormap(disp, DefaultScreen(disp));
1005     screen = DefaultScreen(disp);
1006     depth = DefaultDepth(disp, DefaultScreen(disp));
1007 
1008     param_init(disp, cmap);
1009 
1010     param_set("Debug", BOOL, "false");
1011     param_set("Geometry", STR, DEF_GEOMETRY);
1012     param_set("ReverseVideo", BOOL, DEF_REVERSE);
1013 
1014     param_set("BorderSize", INT, DEF_BORDER_WIDTH);
1015     param_set("TitleText", STR, DEF_TITLE_TEXT);
1016     param_set("XUnitText", STR, DEF_XUNIT_TEXT);
1017     param_set("YUnitText", STR, DEF_YUNIT_TEXT); /* YUnits */
1018     param_set("Ticks", BOOL, DEF_TICK_FLAG);
1019 
1020     param_set("Markers", BOOL, DEF_MARKER_FLAG); /* markFlag (-m) */
1021     param_set("StyleMarkers", BOOL, DEF_DIFFMARK_FLAG); /* colorMark (-M) */
1022     param_set("PixelMarkers", BOOL, DEF_PIXMARK_FLAG); /* pixelMarks  (-p) */
1023     param_set("LargePixels", BOOL, DEF_LARGEPIX_FLAG); /* bigPixel (-P) */
1024 
1025     param_set("BoundBox", BOOL, DEF_BB_FLAG);
1026     param_set("NoLines", BOOL, DEF_NOLINE_FLAG);
1027     param_set("LogX", BOOL, DEF_LOGX_FLAG);
1028     param_set("LogY", BOOL, DEF_LOGY_FLAG); /* logYFlag */
1029     param_set("BarGraph", BOOL, DEF_BAR_FLAG);
1030     param_set("BarBase", DBL, DEF_BAR_BASE);
1031     param_set("BarWidth", DBL, DEF_BAR_WIDTH);
1032     param_set("LineWidth", INT, DEF_LINE_WIDTH);
1033     param_set("GridSize", INT, DEF_GRID_SIZE);
1034     param_set("GridStyle", STYLE, DEF_GRID_STYLE);
1035 
1036     param_set("Device", STR, DEF_DEVICE);
1037     param_set("Disposition", STR, DEF_DISPOSITION);
1038     param_set("FileOrDev", STR, DEF_FILEORDEV);
1039 
1040     /* Set the user bounding box */
1041     param_set("XLowLimit", DBL, DEF_LOW_LIMIT);
1042     param_set("YLowLimit", DBL, DEF_LOW_LIMIT);
1043     param_set("XHighLimit", DBL, DEF_HIGH_LIMIT);
1044     param_set("YHighLimit", DBL, DEF_HIGH_LIMIT);
1045 
1046     /* Depends critically on whether the display has color */
1047     if (depth < 4) {
1048 	/* Its black and white */
1049 	param_set("Background", PIXEL, DEF_BW_BACKGROUND);
1050 	param_set("Border", PIXEL, DEF_BW_BORDER);
1051 	param_set("ZeroColor", PIXEL, DEF_BW_ZEROCOLOR);
1052 	param_set("ZeroWidth", INT, DEF_BW_ZEROWIDTH);
1053 	param_set("ZeroStyle", STYLE, DEF_BW_ZEROSTYLE);
1054 	param_set("Foreground", PIXEL, DEF_BW_FOREGROUND);
1055 	/* Initialize set defaults */
1056 	for (idx = 0;  idx < MAXATTR;  idx++) {
1057 	    (void) sprintf(buf, "%d.Style", idx);
1058 	    param_set(buf, STYLE, defStyle[idx]);
1059 	    (void) sprintf(buf, "%d.Color", idx);
1060 	    param_set(buf, PIXEL, DEF_BW_FOREGROUND);
1061 	}
1062     } else {
1063 	/* Its color */
1064 	param_set("Background", PIXEL, DEF_COL_BACKGROUND);
1065 	param_set("Border", PIXEL, DEF_COL_BORDER);
1066 	param_set("ZeroColor", PIXEL, DEF_COL_ZEROCOLOR);
1067 	param_set("ZeroWidth", INT, DEF_COL_ZEROWIDTH);
1068 	param_set("ZeroStyle", STYLE, DEF_COL_ZEROSTYLE);
1069 	param_set("Foreground", PIXEL, DEF_COL_FOREGROUND);
1070 	/* Initalize attribute colors defaults */
1071 	for (idx = 0;  idx < MAXATTR;  idx++) {
1072 	    (void) sprintf(buf, "%d.Style", idx);
1073 	    param_set(buf, STYLE, defStyle[idx]);
1074 	    (void) sprintf(buf, "%d.Color", idx);
1075 	    param_set(buf, PIXEL, defColors[idx]);
1076 	}
1077     }
1078 
1079     param_set("LabelFont", FONT, DEF_LABEL_FONT);
1080     param_set("TitleFont", FONT, DEF_TITLE_FONT);
1081 
1082     /* Initialize the data sets */
1083     for (idx = 0;  idx < MAXSETS;  idx++) {
1084 	(void) sprintf(buf, "Set %d", idx);
1085 	PlotData[idx].setName = STRDUP(buf);
1086 	PlotData[idx].list = (PointList *) 0;
1087     }
1088 }
1089 
1090 
1091 
1092 static char *def_str;
1093 
1094 #define DEF(name, type) \
1095 if (def_str = XGetDefault(disp, Prog_Name, name)) { \
1096     param_set(name, type, def_str); \
1097 }
1098 
ReadDefaults()1099 int ReadDefaults()
1100 /*
1101  * Reads X default values which override the hard-coded defaults
1102  * set up by InitSets.
1103  */
1104 {
1105     char newname[100];
1106     int idx;
1107 
1108     DEF("Debug", BOOL);
1109     DEF("Geometry", STR);
1110     DEF("Background", PIXEL);
1111     DEF("BorderSize", INT);
1112     DEF("Border", PIXEL);
1113     DEF("GridSize", INT);
1114     DEF("GridStyle", STYLE);
1115     DEF("Foreground", PIXEL);
1116     DEF("ZeroColor", PIXEL);
1117     DEF("ZeroStyle", STYLE);
1118     DEF("ZeroWidth", INT);
1119     DEF("LabelFont", FONT);
1120     DEF("TitleFont", FONT);
1121     DEF("Ticks", BOOL);
1122     DEF("Device", STR);
1123     DEF("Disposition", STR);
1124     DEF("FileOrDev", STR);
1125     DEF("PixelMarkers", BOOL);
1126     DEF("LargePixels", BOOL);
1127     DEF("Markers", BOOL);
1128     DEF("StyleMarkers", BOOL);
1129     DEF("BoundBox", BOOL);
1130     DEF("NoLines", BOOL);
1131     DEF("LineWidth", INT);
1132 
1133     /* Read device specific parameters */
1134     for (idx = 0;  idx < hard_count;  idx++) {
1135 	sprintf(newname, "%s.Dimension", hard_devices[idx].dev_name);
1136 	DEF(newname, DBL);	/* hard_devices[idx].dev_max_dim */
1137 	sprintf(newname, "%s.OutputTitleFont", hard_devices[idx].dev_name);
1138 	DEF(newname, STR);	/* hard_devices[idx].dev_title_font */
1139 	sprintf(newname, "%s.OutputTitleSize", hard_devices[idx].dev_name);
1140 	DEF(newname, DBL);	/* hard_devices[idx].dev_title_size */
1141 	sprintf(newname, "%s.OutputAxisFont", hard_devices[idx].dev_name);
1142 	DEF(newname, STR);	/* hard_devices[idx].dev_axis_font */
1143 	sprintf(newname, "%s.OutputAxisSize", hard_devices[idx].dev_name);
1144 	DEF(newname, DBL);	/* hard_devices[idx].dev_axis_size */
1145     }
1146 
1147 
1148     /* Read the default line and color attributes */
1149     for (idx = 0;  idx < MAXATTR;  idx++) {
1150 	(void) sprintf(newname, "%d.Style", idx);
1151 	DEF(newname, STYLE);	/* AllAttrs[idx].lineStyleLen */
1152 	(void) sprintf(newname, "%d.Color", idx);
1153 	DEF(newname, PIXEL);	/* AllAttrs[idx].pixelValue */
1154     }
1155 
1156     DEF("ReverseVideo", BOOL);
1157 }
1158 
1159 
1160 #define FS(str)	(void) fprintf(stderr, str)
1161 
argerror(err,val)1162 int argerror(err, val)
1163 char *err, *val;
1164 {
1165     (void) fprintf(stderr, "Error: %s: %s\n\n", val, err);
1166 
1167     FS("format: xgraph [-<digit> set_name] [-bar] [-bb] [-bd border_color]\n");
1168     FS("         [-bg background_color] [-brb bar_base] [-brw bar_width]\n");
1169     FS("         [-bw bdr_width] [-db] [-fg foreground_color] [-gw grid_size]\n");
1170     FS("         [-gs grid_style] [-lf label_font] [-lnx] [-lny] [-lw line_width]\n");
1171     FS("         [-lx x1,x2] [-ly y1,y2] [-m] [-M] [-nl] [-p] [-P] [-rv]\n");
1172     FS("         [-t title] [-tf title_font] [-tk] [-x x_unit_name]\n");
1173     FS("         [-y y_unit_name] [-zg zero_color] [-zw zero_size]\n");
1174     FS("         [=WxH+X+Y] [-display <host>:<disp>.<screen>] file...\n\n");
1175     FS("-bar   Draw bar graph with base -brb and width -brw\n");
1176     FS("-bb    Draw bounding box around data\n");
1177     FS("-db    Turn on debugging\n");
1178     FS("-lnx   Logarithmic scale for X axis\n");
1179     FS("-lny   Logarithmic scale for Y axis\n");
1180     FS("-m -M  Mark points distinctively (M varies with color)\n");
1181     FS("-nl    Don't draw lines (scatter plot)\n");
1182     FS("-p -P  Mark points with dot (P means big dot)\n");
1183     FS("-rv    Reverse video on black and white displays\n");
1184     FS("-tk    Draw tick marks instead of full grid\n");
1185 
1186     exit(1);
1187 }
1188 
1189 #define ARG(opt, name) \
1190 if (strcmp(argv[idx], opt) == 0) { \
1191     if (do_it) param_set(name, BOOL, "on"); \
1192     idx++; continue; \
1193 }
1194 
1195 #define ARG2(opt, name, type, missing) \
1196 if (strcmp(argv[idx], opt) == 0) { \
1197    if (idx+1 >= argc) argerror(missing, argv[idx]); \
1198    if (do_it) param_set(name, type, argv[idx+1]); \
1199    idx += 2; continue;\
1200 }
1201 
1202 #define MAXLO	30
1203 
ParseArgs(argc,argv,do_it)1204 int ParseArgs(argc, argv, do_it)
1205 int argc;
1206 char *argv[];
1207 int do_it;
1208 /*
1209  * This routine parses the argument list for xgraph.  There are too
1210  * many to mention here so I won't.  If `do_it' is non-zero, options
1211  * are actually changed.  If `do_it' is zero, the argument list
1212  * is parsed but the options aren't set.  The routine is called
1213  * once to obtain the input files then again after the data is
1214  * read to set the options.
1215  */
1216 {
1217     int idx, set;
1218     char *hi;
1219 
1220     idx = 1;
1221     while (idx < argc) {
1222 	if (argv[idx][0] == '-') {
1223 	    /* Check to see if its a data set name */
1224 	    if (sscanf(argv[idx], "-%d", &set) == 1) {
1225 		/* The next string is a set name */
1226 		if (idx+1 >= argc) argerror("missing set name", argv[idx]);
1227 		if (do_it) {
1228 		    PlotData[set].setName = argv[idx+1];
1229 		}
1230 		idx += 2;
1231 	    } else {
1232 		/* Some non-dataset option */
1233 		ARG2("-x", "XUnitText", STR, "missing axis name");
1234 		ARG2("-y", "YUnitText", STR, "missing axis name");
1235 		ARG2("-t", "TitleText", STR, "missing plot title");
1236 		ARG2("-fg", "Foreground", PIXEL, "missing color name");
1237 		ARG2("-bg", "Background", PIXEL, "missing color name");
1238 		ARG2("-bd", "Border", PIXEL, "missing color name");
1239 		ARG2("-bw", "BorderSize", INT, "missing border size");
1240 		ARG2("-zg", "ZeroColor", PIXEL, "missing color name");
1241 		ARG2("-zw", "ZeroWidth", INT, "missing width");
1242 		ARG2("-tf", "TitleFont", FONT, "missing font name");
1243 		ARG2("-lf", "LabelFont", FONT, "missing font name");
1244 		ARG("-rv", "ReverseVideo");
1245 		ARG("-tk", "Ticks");
1246 		ARG("-bb", "BoundBox");
1247 		if (strcmp(argv[idx], "-lx") == 0) {
1248 		    /* Limit the X coordinates */
1249 		    if (idx+1 >= argc) argerror("missing coordinate(s)",
1250 						argv[idx]);
1251 		    if (hi = index(argv[idx+1], ',')) {
1252 			char low[MAXLO];
1253 
1254 			(void) strncpy(low, argv[idx+1], hi-argv[idx+1]);
1255 			low[hi-argv[idx+1]] = '\0';
1256 			hi++;
1257 			if (do_it) {
1258 			    param_set("XLowLimit", DBL, argv[idx+1]);
1259 			    param_set("XHighLimit", DBL, hi);
1260 			}
1261 		    } else {
1262 			argerror("limit coordinates not specified right",
1263 				 argv[idx]);
1264 		    }
1265 		    idx += 2;
1266 		    continue;
1267 		}
1268 		if (strcmp(argv[idx], "-ly") == 0) {
1269 		    /* Limit the Y coordinates */
1270 		    if (idx+1 >= argc) argerror("missing coordinate(s)",
1271 						  argv[idx]);
1272 		    if (hi = index(argv[idx+1], ',')) {
1273 			char low[MAXLO];
1274 
1275 			(void) strncpy(low, argv[idx+1], hi-argv[idx+1]);
1276 			low[hi-argv[idx+1]] = '\0';
1277 			hi++;
1278 			if (do_it) {
1279 			    param_set("YLowLimit", DBL, argv[idx+1]);
1280 			    param_set("YHighLimit", DBL, hi);
1281 			}
1282 		    } else {
1283 			argerror("limit coordinates not specified right",
1284 				 argv[idx]);
1285 		    }
1286 		    idx += 2;
1287 		    continue;
1288 		}
1289 		ARG2("-lw", "LineWidth", INT, "missing line width");
1290 		ARG("-nl", "NoLines");
1291 		ARG("-m", "Markers");
1292 		ARG("-M", "StyleMarkers");
1293 		ARG("-p", "PixelMarkers");
1294 		ARG("-P", "LargePixels");
1295 		ARG("-lnx", "LogX");
1296 		ARG("-lny", "LogY");
1297 		ARG("-bar", "BarGraph");
1298 		ARG2("-brw", "BarWidth", DBL, "missing width");
1299 		ARG2("-brb", "BarBase", DBL, "missing base");
1300 		ARG("-db", "Debug");
1301 		ARG2("-gw", "GridSize", INT, "missing grid size");
1302 		ARG2("-gs", "GridStyle", STYLE, "missing grid style");
1303 		if (strcmp(argv[idx], "-display") == 0) {
1304 		    /* Harmless display specification */
1305 		    idx += 2;
1306 		    continue;
1307 		}
1308 		argerror("unknown option", argv[idx]);
1309 	    }
1310 	} else if (argv[idx][0] == '=') {
1311 	    /* Its a geometry specification */
1312 	    if (do_it) param_set("Geometry", STR, argv[idx]+1);
1313 	    idx++;
1314 	} else {
1315 	    /* It might be the host:display string */
1316 	    if (rindex(argv[idx], ':') == (char *) 0) {
1317 		/* Should be an input file */
1318 		inFileNames[numFiles] = argv[idx];
1319 		numFiles++;
1320 	    }
1321 	    idx++;
1322 	}
1323     }
1324 }
1325 
1326 /*
1327  * New dataset reading code
1328  */
1329 
1330 static int setNumber = 0;
1331 static PointList **curSpot = (PointList **) 0;
1332 static PointList *curList = (PointList *) 0;
1333 static int newGroup = 0;
1334 static int redundant_set = 0;
1335 
rdSet(fn)1336 static int rdSet(fn)
1337 char *fn;			/* Reading from file `fn' */
1338 /*
1339  * Set up new dataset.  Will return zero if there are too many data sets.
1340  */
1341 {
1342     char setname[100];
1343 
1344     if (!redundant_set) {
1345 	if (setNumber < MAXSETS) {
1346 	    (void) sprintf(setname, "Set %d", setNumber);
1347 	    if ((strcmp(PlotData[setNumber].setName, setname) == 0) && fn) {
1348 		PlotData[setNumber].setName = fn;
1349 	    }
1350 	    curSpot = &(PlotData[setNumber].list);
1351 	    PlotData[setNumber].list = (PointList *) 0;
1352 	    newGroup = 1;
1353 	    setNumber++;
1354 	    redundant_set = 1;
1355 	    return 1;
1356 	} else {
1357 	    return 0;
1358 	}
1359     } else {
1360 	return 1;
1361     }
1362 }
1363 
rdSetName(name)1364 static void rdSetName(name)
1365 char *name;			/* New set name */
1366 /*
1367  * Sets the name of a data set.  Automatically makes a copy.
1368  */
1369 {
1370     PlotData[setNumber-1].setName = STRDUP(name);
1371 }
1372 
rdGroup()1373 static void rdGroup()
1374 /*
1375  * Set up for reading new group of points within a dataset.
1376  */
1377 {
1378     newGroup = 1;
1379 }
1380 
rdPoint(xval,yval)1381 static void rdPoint(xval, yval)
1382 double xval, yval;		/* New point         */
1383 /*
1384  * Adds a new point to the current group of the current
1385  * data set.
1386  */
1387 {
1388     if (newGroup) {
1389 	*curSpot = (PointList *) malloc(sizeof(PointList));
1390 	curList = *curSpot;
1391 	curSpot = &(curList->next);
1392 	curList->numPoints = 0;
1393 	curList->allocSize = INITSIZE;
1394 	curList->xvec = (double *) malloc((unsigned)(INITSIZE * sizeof(double)));
1395 	curList->yvec = (double *) malloc((unsigned)(INITSIZE * sizeof(double)));
1396 	curList->next = (PointList *) 0;
1397 	newGroup = 0;
1398     }
1399     if (curList->numPoints >= curList->allocSize) {
1400 	curList->allocSize *= 2;
1401 	curList->xvec = (double *) realloc((char *) curList->xvec,
1402 					   (unsigned) (curList->allocSize *
1403 						       sizeof(double)));
1404 	curList->yvec = (double *) realloc((char *) curList->yvec,
1405 					   (unsigned) (curList->allocSize *
1406 						       sizeof(double)));
1407     }
1408 
1409     curList->xvec[curList->numPoints] = xval;
1410     curList->yvec[curList->numPoints] = yval;
1411 
1412     (curList->numPoints)++;
1413     redundant_set = 0;
1414 }
1415 
rdFindMax()1416 static int rdFindMax()
1417 /*
1418  * Returns the maximum number of items in any one group of any
1419  * data set.
1420  */
1421 {
1422     int i;
1423     PointList *list;
1424     int max = -1;
1425 
1426     for (i = 0;  i < setNumber;  i++) {
1427 	for (list = PlotData[i].list;  list;  list = list->next) {
1428 	    if (list->numPoints > max) max = list->numPoints;
1429 	}
1430     }
1431     return max;
1432 }
1433 
1434 
1435 typedef enum line_type {
1436     EMPTY, COMMENT, SETNAME, DRAWPNT, MOVEPNT, SETPARAM, ERROR
1437 } LineType;
1438 
1439 typedef struct point_defn {
1440     double xval, yval;
1441 } Point;
1442 
1443 typedef struct parmval_defn {
1444     char *name, *value;
1445 } ParmVals;
1446 
1447 typedef struct line_info {
1448     LineType type;
1449     union val_defn {
1450 	char *str;		/* SETNAME, ERROR   */
1451 	Point pnt;		/* DRAWPNT, MOVEPNT */
1452 	ParmVals parm;		/* SETPARAM         */
1453     } val;
1454 } LineInfo;
1455 
parse_line(line,result)1456 static LineType parse_line(line, result)
1457 char *line;			/* Line to parse   */
1458 LineInfo *result;		/* Returned result */
1459 /*
1460  * Parses `line' into one of the types given in the definition
1461  * of LineInfo.  The appropriate values are filled into `result'.
1462  * Below are the current formats for each type:
1463  *   EMPTY:	All white space
1464  *   COMMENT:	Starts with "#"
1465  *   SETNAME:	A name enclosed in double quotes
1466  *   DRAWPNT:	Two numbers optionally preceded by keyword "draw"
1467  *   MOVEPNT:	Two numbers preceded by keyword "move"
1468  *   SETPARAM:  Two non-null strings separated by ":"
1469  *   ERROR:	Not any of the above (an error message is returned)
1470  * Note that often the values are pointers into the line itself
1471  * and should be copied if they are to be used over a long period.
1472  */
1473 {
1474     char *first;
1475 
1476     /* Find first non-space character */
1477     while (*line && isspace(*line)) line++;
1478     if (*line) {
1479 	if (*line == '#') {
1480 	    /* comment */
1481 	    result->type = COMMENT;
1482 	} else if (*line == '"') {
1483 	    /* setname */
1484 	    result->type = SETNAME;
1485 	    line++;
1486 	    result->val.str = line;
1487 	    while (*line && (*line != '\n') && (*line != '"')) line++;
1488 	    if (*line) *line = '\0';
1489 	} else {
1490 	    /* treat comma's as white space */
1491 	    for (first=line; *first; ++first) if (*first == ',') *first = ' ';
1492 	    first = line;
1493 	    while (*line && !isspace(*line)) line++;
1494 	    if (*line) {
1495 		*line = '\0';
1496 		if (stricmp(first, "move") == 0) {
1497 		    /* MOVEPNT */
1498 		    if (sscanf(line+1, "%lf %lf",
1499 			       &result->val.pnt.xval,
1500 			       &result->val.pnt.yval) == 2) {
1501 			result->type = MOVEPNT;
1502 		    } else {
1503 			result->type = ERROR;
1504 			result->val.str = "Cannot read move coordinates";
1505 		    }
1506 		} else if (stricmp(first, "draw") == 0) {
1507 		    /* DRAWPNT */
1508 		    if (sscanf(line+1, "%lf %lf",
1509 			       &result->val.pnt.xval,
1510 			       &result->val.pnt.yval) == 2) {
1511 			result->type = DRAWPNT;
1512 		    } else {
1513 			result->type = ERROR;
1514 			result->val.str = "Cannot read draw coordinates";
1515 		    }
1516 		} else if (first[strlen(first)-1] == ':') {
1517 		    /* SETPARAM */
1518 		    first[strlen(first)-1] = '\0';
1519 		    result->val.parm.name = first;
1520 		    line++;
1521 		    while (*line && isspace(*line)) line++;
1522 		    /* may be a \n at end of it */
1523 		    if (line[strlen(line)-1] == '\n') {
1524 			line[strlen(line)-1] = '\0';
1525 		    }
1526 		    result->val.parm.value = line;
1527 		    result->type = SETPARAM;
1528 		} else if (sscanf(first, "%lf", &result->val.pnt.xval) == 1) {
1529 		    /* DRAWPNT */
1530 		    if (sscanf(line+1, "%lf", &result->val.pnt.yval) == 1) {
1531 			result->type = DRAWPNT;
1532 		    } else {
1533 			result->type = ERROR;
1534 			result->val.str = "Cannot read second coordinate";
1535 		    }
1536 		} else {
1537 		    /* ERROR */
1538 		    result->type = ERROR;
1539 		    result->val.str = "Unknown line type";
1540 		}
1541 	    } else {
1542 		/* ERROR */
1543 		result->type = ERROR;
1544 		result->val.str = "Premature end of line";
1545 	    }
1546 	}
1547     } else {
1548 	/* empty */
1549 	result->type = EMPTY;
1550     }
1551     return result->type;
1552 }
1553 
1554 
ReadData(stream,filename)1555 int ReadData(stream, filename)
1556 FILE *stream;
1557 char *filename;
1558 /*
1559  * Reads in the data sets from the supplied stream.  If the format
1560  * is correct,  it returns the current maximum number of points across
1561  * all data sets.  If there is an error,  it returns -1.
1562  */
1563 {
1564     char buffer[MAXBUFSIZE];
1565     LineInfo info;
1566     int line_count = 0;
1567     int errors = 0;
1568 
1569     if (!rdSet(filename)) {
1570 	(void) fprintf(stderr, "Error in file `%s' at line %d:\n  %s\n",
1571 		       filename, line_count,
1572 		       "Too many data sets - extra data ignored");
1573 	return -1;
1574     }
1575     while (fgets(buffer, MAXBUFSIZE, stream)) {
1576 	line_count++;
1577 	switch (parse_line(buffer, &info)) {
1578 	case EMPTY:
1579 	    if (!rdSet(filename)) {
1580 		(void) fprintf(stderr, "Error in file `%s' at line %d:\n  %s\n",
1581 			       filename, line_count,
1582 			       "Too many data sets - extra data ignored");
1583 		return -1;
1584 	    }
1585 	    break;
1586 	case COMMENT:
1587 	    /* nothing */
1588 	    break;
1589 	case SETNAME:
1590 	    rdSetName(info.val.str);
1591 	    break;
1592 	case DRAWPNT:
1593 	    rdPoint(info.val.pnt.xval, info.val.pnt.yval);
1594 	    break;
1595 	case MOVEPNT:
1596 	    rdGroup();
1597 	    rdPoint(info.val.pnt.xval, info.val.pnt.yval);
1598 	    break;
1599 	case SETPARAM:
1600 	    param_reset(info.val.parm.name, info.val.parm.value);
1601 	    break;
1602 	default:
1603 	    if (filename) {
1604 		(void) fprintf(stderr, "Error in file `%s' at line %d:\n  %s\n",
1605 			       filename, line_count, info.val.str);
1606 		errors++;
1607 	    }
1608 	    break;
1609 	}
1610     }
1611     if (errors) return -1; else return rdFindMax();
1612 }
1613 
1614 
1615 
1616 
DrawWindow(win_info)1617 int DrawWindow(win_info)
1618 LocalWin *win_info;		/* Window information */
1619 /*
1620  * Draws the data in the window.  Does not clear the window.
1621  * The data is scaled so that all of the data will fit.
1622  * Grid lines are drawn at the nearest power of 10 in engineering
1623  * notation.  Draws axis numbers along bottom and left hand edges.
1624  * Centers title at top of window.
1625  */
1626 {
1627     /* Figure out the transformation constants */
1628     if (TransformCompute(win_info)) {
1629 
1630 	/* Draw the title */
1631 	DrawTitle(win_info);
1632 
1633 	/* Draw the legend */
1634 	DrawLegend(win_info);
1635 
1636 	/* Draw the axis unit labels,  grid lines,  and grid labels */
1637 	DrawGridAndAxis(win_info);
1638 
1639 	/* Draw the data sets themselves */
1640 	DrawData(win_info);
1641     }
1642 }
1643 
1644 
1645 
1646 
DrawTitle(wi)1647 DrawTitle(wi)
1648 LocalWin *wi;		/* Window information    */
1649 /*
1650  * This routine draws the title of the graph centered in
1651  * the window.  It is spaced down from the top by an amount
1652  * specified by the constant PADDING.  The font must be
1653  * fixed width.  The routine returns the height of the
1654  * title in pixels.
1655  */
1656 {
1657     wi->dev_info.xg_text(wi->dev_info.user_state,
1658 			 wi->dev_info.area_w/2,
1659 			 wi->dev_info.axis_pad,
1660 			 PM_STR("TitleText"), T_TOP, T_TITLE);
1661 }
1662 
1663 
1664 
1665 
TransformCompute(wi)1666 int TransformCompute(wi)
1667 LocalWin *wi;			/* Window information          */
1668 /*
1669  * This routine figures out how to draw the axis labels and grid lines.
1670  * Both linear and logarithmic axes are supported.  Axis labels are
1671  * drawn in engineering notation.  The power of the axes are labeled
1672  * in the normal axis labeling spots.  The routine also figures
1673  * out the necessary transformation information for the display
1674  * of the points (it touches XOrgX, XOrgY, UsrOrgX, UsrOrgY, and
1675  * UnitsPerPixel).
1676  */
1677 {
1678     double bbCenX, bbCenY, bbHalfWidth, bbHalfHeight;
1679     int idx, maxName, leftWidth;
1680     char err[MAXBUFSIZE];
1681     char *XUnitText = PM_STR("XUnitText");
1682 
1683     /*
1684      * First,  we figure out the origin in the X window.  Above
1685      * the space we have the title and the Y axis unit label.
1686      * To the left of the space we have the Y axis grid labels.
1687      */
1688 
1689     wi->XOrgX = wi->dev_info.bdr_pad + (7 * wi->dev_info.axis_width)
1690       + wi->dev_info.bdr_pad;
1691     wi->XOrgY = wi->dev_info.bdr_pad + wi->dev_info.title_height
1692       + wi->dev_info.bdr_pad + wi->dev_info.axis_height
1693 	+ wi->dev_info.axis_height/2 + wi->dev_info.bdr_pad;
1694 
1695     /*
1696      * Now we find the lower right corner.  Below the space we
1697      * have the X axis grid labels.  To the right of the space we
1698      * have the X axis unit label and the legend.  We assume the
1699      * worst case size for the unit label.
1700      */
1701 
1702     maxName = 0;
1703     for (idx = 0;  idx < MAXSETS;  idx++) {
1704 	if (PlotData[idx].list) {
1705 	    int tempSize;
1706 
1707 	    tempSize = strlen(PlotData[idx].setName);
1708 	    if (tempSize > maxName) maxName = tempSize;
1709 	}
1710     }
1711     /* Worst case size of the X axis label: */
1712     leftWidth = (strlen(XUnitText)) * wi->dev_info.axis_width;
1713     if ((maxName*wi->dev_info.axis_width)+wi->dev_info.bdr_pad > leftWidth)
1714       leftWidth = maxName * wi->dev_info.axis_width + wi->dev_info.bdr_pad;
1715 
1716     wi->XOppX = wi->dev_info.area_w - wi->dev_info.bdr_pad - leftWidth;
1717     wi->XOppY = wi->dev_info.area_h - wi->dev_info.bdr_pad
1718       - wi->dev_info.axis_height - wi->dev_info.bdr_pad;
1719 
1720     if ((wi->XOrgX >= wi->XOppX) || (wi->XOrgY >= wi->XOppY)) {
1721 	do_error(strcpy(err, "Drawing area is too small\n"));
1722 	return 0;
1723     }
1724 
1725     /*
1726      * We now have a bounding box for the drawing region.
1727      * Figure out the units per pixel using the data set bounding box.
1728      */
1729     wi->XUnitsPerPixel = (wi->hiX - wi->loX)/((double) (wi->XOppX - wi->XOrgX));
1730     wi->YUnitsPerPixel = (wi->hiY - wi->loY)/((double) (wi->XOppY - wi->XOrgY));
1731 
1732     /*
1733      * Find origin in user coordinate space.  We keep the center of
1734      * the original bounding box in the same place.
1735      */
1736     bbCenX = (wi->loX + wi->hiX) / 2.0;
1737     bbCenY = (wi->loY + wi->hiY) / 2.0;
1738     bbHalfWidth = ((double) (wi->XOppX - wi->XOrgX))/2.0 * wi->XUnitsPerPixel;
1739     bbHalfHeight = ((double) (wi->XOppY - wi->XOrgY))/2.0 * wi->YUnitsPerPixel;
1740     wi->UsrOrgX = bbCenX - bbHalfWidth;
1741     wi->UsrOrgY = bbCenY - bbHalfHeight;
1742     wi->UsrOppX = bbCenX + bbHalfWidth;
1743     wi->UsrOppY = bbCenY + bbHalfHeight;
1744 
1745     /*
1746      * Everything is defined so we can now use the SCREENX and SCREENY
1747      * transformations.
1748      */
1749     return 1;
1750 }
1751 
DrawGridAndAxis(wi)1752 int DrawGridAndAxis(wi)
1753 LocalWin *wi;			/* Window information         */
1754 /*
1755  * This routine draws grid line labels in engineering notation,
1756  * the grid lines themselves,  and unit labels on the axes.
1757  */
1758 {
1759     int expX, expY;		/* Engineering powers */
1760     int startX;
1761     int Yspot, Xspot;
1762 #if !PDG
1763     char power[10], value[10], final[MAXBUFSIZE+10];
1764 #else
1765     char power[10], value[40], final[MAXBUFSIZE+10];
1766     int Xdigits, Ydigits;
1767 #endif
1768     double Xincr, Yincr, Xstart, Ystart, Yindex, Xindex, larger;
1769     XSegment segs[2];
1770     double initGrid(), stepGrid();
1771     int tickFlag = PM_BOOL("Ticks");
1772     int logXFlag = PM_BOOL("LogX");
1773     int logYFlag = PM_BOOL("LogY");
1774     char *XUnitText = PM_STR("XUnitText");
1775     char *YUnitText = PM_STR("YUnitText");
1776 
1777     /*
1778      * Grid display powers are computed by taking the log of
1779      * the largest numbers and rounding down to the nearest
1780      * multiple of 3.
1781      */
1782     if (logXFlag) {
1783 	expX = 0;
1784     } else {
1785 	if (fabs(wi->UsrOrgX) > fabs(wi->UsrOppX)) {
1786 	    larger = fabs(wi->UsrOrgX);
1787 	} else {
1788 	    larger = fabs(wi->UsrOppX);
1789 	}
1790 	expX = ((int) floor(nlog10(larger)/3.0)) * 3;
1791     }
1792     if (logYFlag) {
1793 	expY = 0;
1794     } else {
1795 	if (fabs(wi->UsrOrgY) > fabs(wi->UsrOppY)) {
1796 	    larger = fabs(wi->UsrOrgY);
1797 	} else {
1798 	    larger = fabs(wi->UsrOppY);
1799 	}
1800 	expY = ((int) floor(nlog10(larger)/3.0)) * 3;
1801     }
1802 
1803     /*
1804      * With the powers computed,  we can draw the axis labels.
1805      */
1806     if (expY != 0) {
1807 	(void) strcpy(final, YUnitText);
1808 	(void) strcat(final, " x 10");
1809 	Xspot = wi->dev_info.bdr_pad +
1810 	  ((strlen(YUnitText)+5) * wi->dev_info.axis_width);
1811 	Yspot = wi->dev_info.bdr_pad * 2 + wi->dev_info.title_height +
1812 	  wi->dev_info.axis_height/2;
1813 	wi->dev_info.xg_text(wi->dev_info.user_state,
1814 			     Xspot, Yspot, final, T_RIGHT, T_AXIS);
1815 	(void) sprintf(power, "%d", expY);
1816 	wi->dev_info.xg_text(wi->dev_info.user_state,
1817 			     Xspot, Yspot, power, T_LOWERLEFT, T_AXIS);
1818     } else {
1819 	Yspot = wi->dev_info.bdr_pad * 2 + wi->dev_info.title_height;
1820 	wi->dev_info.xg_text(wi->dev_info.user_state,
1821 			     wi->dev_info.bdr_pad, Yspot, YUnitText,
1822 			     T_UPPERLEFT, T_AXIS);
1823     }
1824 
1825     startX = wi->dev_info.area_w - wi->dev_info.bdr_pad;
1826     if (expX != 0) {
1827 	(void) sprintf(power, "%d", expX);
1828 	startX -= (strlen(power) * wi->dev_info.axis_width);
1829 	wi->dev_info.xg_text(wi->dev_info.user_state,
1830 			     startX, wi->XOppY, power, T_LOWERLEFT, T_AXIS);
1831 	(void) strcpy(final, XUnitText);
1832 	(void) strcat(final, " x 10");
1833 	wi->dev_info.xg_text(wi->dev_info.user_state,
1834 			     startX, wi->XOppY, final, T_RIGHT, T_AXIS);
1835     } else {
1836 	wi->dev_info.xg_text(wi->dev_info.user_state,
1837 			     startX, wi->XOppY, XUnitText, T_RIGHT, T_AXIS);
1838     }
1839 
1840     /*
1841      * First,  the grid line labels
1842      */
1843     Yincr = (wi->dev_info.axis_pad + wi->dev_info.axis_height) * wi->YUnitsPerPixel;
1844 #if PDG
1845     Ydigits = getDigits(wi->UsrOrgY, wi->UsrOppY, Yincr, expY, logYFlag);
1846 #endif
1847     Ystart = initGrid(wi->UsrOrgY, Yincr, logYFlag);
1848     for (Yindex = Ystart;  Yindex < wi->UsrOppY;  Yindex = stepGrid()) {
1849 	Yspot = SCREENY(wi, Yindex);
1850 	/* Write the axis label */
1851 #if !PDG
1852 	WriteValue(value, Yindex, expY, logYFlag);
1853 #else
1854 	WriteValue(value, Yindex, expY, logYFlag, Ydigits);
1855 #endif
1856 	wi->dev_info.xg_text(wi->dev_info.user_state,
1857 			     wi->dev_info.bdr_pad +
1858 				     (7 * wi->dev_info.axis_width),
1859 			     Yspot, value, T_RIGHT, T_AXIS);
1860     }
1861 
1862     Xincr = (wi->dev_info.axis_pad + (wi->dev_info.axis_width * 7)) * wi->XUnitsPerPixel;
1863 #if PDG
1864     Xdigits = getDigits(wi->UsrOrgX, wi->UsrOppX, Xincr, expX, logXFlag);
1865 #endif
1866     Xstart = initGrid(wi->UsrOrgX, Xincr, logXFlag);
1867     for (Xindex = Xstart;  Xindex < wi->UsrOppX;  Xindex = stepGrid()) {
1868 	Xspot = SCREENX(wi, Xindex);
1869 	/* Write the axis label */
1870 #if !PDG
1871 	WriteValue(value, Xindex, expX, logXFlag);
1872 #else
1873 	WriteValue(value, Xindex, expX, logXFlag, Xdigits);
1874 #endif
1875 	wi->dev_info.xg_text(wi->dev_info.user_state,
1876 			     Xspot,
1877 			     wi->dev_info.area_h - wi->dev_info.bdr_pad,
1878 			     value, T_BOTTOM, T_AXIS);
1879     }
1880 
1881     /*
1882      * Now,  the grid lines or tick marks
1883      */
1884     Yincr = (wi->dev_info.axis_pad + wi->dev_info.axis_height) * wi->YUnitsPerPixel;
1885     Ystart = initGrid(wi->UsrOrgY, Yincr, logYFlag);
1886     for (Yindex = Ystart;  Yindex < wi->UsrOppY;  Yindex = stepGrid()) {
1887 	Yspot = SCREENY(wi, Yindex);
1888 	/* Draw the grid line or tick mark */
1889 	if (tickFlag) {
1890 	    segs[0].x1 = wi->XOrgX;
1891 	    segs[0].x2 = wi->XOrgX + wi->dev_info.tick_len;
1892 	    segs[1].x1 = wi->XOppX - wi->dev_info.tick_len;
1893 	    segs[1].x2 = wi->XOppX;
1894 	    segs[0].y1 = segs[0].y2 = segs[1].y1 = segs[1].y2 = Yspot;
1895 	} else {
1896 	    segs[0].x1 = wi->XOrgX;  segs[0].x2 = wi->XOppX;
1897 	    segs[0].y1 = segs[0].y2 = Yspot;
1898 	}
1899 	if ((ABS(Yindex) < ZERO_THRES) && !logYFlag) {
1900 	    wi->dev_info.xg_seg(wi->dev_info.user_state,
1901 				 1, segs, PM_INT("ZeroWidth"),
1902 				 L_ZERO, 0, 0);
1903 	    if (tickFlag) {
1904 		wi->dev_info.xg_seg(wi->dev_info.user_state,
1905 				     1, &(segs[1]), PM_INT("ZeroWidth"),
1906 				     L_ZERO, 0, 0);
1907 	    }
1908 	} else {
1909 	    wi->dev_info.xg_seg(wi->dev_info.user_state,
1910 				 1, segs, PM_INT("GridSize"),
1911 				 L_AXIS, 0, 0);
1912 	    if (tickFlag) {
1913 		wi->dev_info.xg_seg(wi->dev_info.user_state,
1914 				     1, &(segs[1]), PM_INT("GridSize"),
1915 				     L_AXIS, 0, 0);
1916 	    }
1917 	}
1918     }
1919 
1920     Xincr = (wi->dev_info.axis_pad + (wi->dev_info.axis_width * 7)) * wi->XUnitsPerPixel;
1921     Xstart = initGrid(wi->UsrOrgX, Xincr, logXFlag);
1922     for (Xindex = Xstart;  Xindex < wi->UsrOppX;  Xindex = stepGrid()) {
1923 	Xspot = SCREENX(wi, Xindex);
1924 	/* Draw the grid line or tick marks */
1925 	if (tickFlag) {
1926 	    segs[0].x1 = segs[0].x2 = segs[1].x1 = segs[1].x2 = Xspot;
1927 	    segs[0].y1 = wi->XOrgY;
1928 	    segs[0].y2 = wi->XOrgY + wi->dev_info.tick_len;
1929 	    segs[1].y1 = wi->XOppY - wi->dev_info.tick_len;
1930 	    segs[1].y2 = wi->XOppY;
1931 	} else {
1932 	    segs[0].x1 = segs[0].x2 = Xspot;
1933 	    segs[0].y1 = wi->XOrgY; segs[0].y2 = wi->XOppY;
1934 	}
1935 	if ((ABS(Xindex) < ZERO_THRES) && !logXFlag) {
1936 	    wi->dev_info.xg_seg(wi->dev_info.user_state,
1937 				 1, segs, PM_INT("ZeroWidth"), L_ZERO, 0, 0);
1938 	    if (tickFlag) {
1939 		wi->dev_info.xg_seg(wi->dev_info.user_state,
1940 				    1, &(segs[1]), PM_INT("ZeroWidth"),
1941 				    L_ZERO, 0, 0);
1942 
1943 	    }
1944 	} else {
1945 	    wi->dev_info.xg_seg(wi->dev_info.user_state,
1946 				 1, segs, PM_INT("GridSize"), L_AXIS, 0, 0);
1947 	    if (tickFlag) {
1948 		wi->dev_info.xg_seg(wi->dev_info.user_state,
1949 				     1, &(segs[1]), PM_INT("GridSize"), L_AXIS, 0, 0);
1950 	    }
1951 	}
1952     }
1953     /* Check to see if he wants a bounding box */
1954     if (PM_BOOL("BoundBox")) {
1955 	XSegment bb[4];
1956 
1957 	/* Draw bounding box */
1958 	bb[0].x1 = bb[0].x2 = bb[1].x1 = bb[3].x2 = wi->XOrgX;
1959 	bb[0].y1 = bb[2].y2 = bb[3].y1 = bb[3].y2 = wi->XOrgY;
1960 	bb[1].x2 = bb[2].x1 = bb[2].x2 = bb[3].x1 = wi->XOppX;
1961 	bb[0].y2 = bb[1].y1 = bb[1].y2 = bb[2].y1 = wi->XOppY;
1962 	wi->dev_info.xg_seg(wi->dev_info.user_state,
1963 			     4, bb, PM_INT("GridSize"), L_AXIS, 0, 0);
1964     }
1965 }
1966 
1967 static double gridBase, gridStep, gridJuke[101];
1968 static int gridNJuke, gridCurJuke;
1969 
1970 #define ADD_GRID(val)	(gridJuke[gridNJuke++] = log10(val))
1971 
initGrid(low,step,logFlag)1972 double initGrid(low, step, logFlag)
1973 double low;			/* desired low value          */
1974 double step;			/* desired step (user coords) */
1975 int logFlag;			/* is axis logarithmic?       */
1976 {
1977     double ratio, x;
1978     double RoundUp(), stepGrid();
1979 
1980     gridNJuke = gridCurJuke = 0;
1981     gridJuke[gridNJuke++] = 0.0;
1982 
1983     if (logFlag) {
1984 	ratio = pow(10.0, step);
1985 	gridBase = floor(low);
1986 	gridStep = ceil(step);
1987 	if (ratio <= 3.0) {
1988 	    if (ratio > 2.0) {
1989 		ADD_GRID(3.0);
1990 	    } else if (ratio > 1.333) {
1991 		ADD_GRID(2.0);	ADD_GRID(5.0);
1992 	    } else if (ratio > 1.25) {
1993 		ADD_GRID(1.5);	ADD_GRID(2.0);	ADD_GRID(3.0);
1994 		ADD_GRID(5.0);	ADD_GRID(7.0);
1995 	    } else {
1996 		for (x = 1.0; x < 10.0 && (x+.5)/(x+.4) >= ratio; x += .5) {
1997 		    ADD_GRID(x + .1);	ADD_GRID(x + .2);
1998 		    ADD_GRID(x + .3);	ADD_GRID(x + .4);
1999 		    ADD_GRID(x + .5);
2000 		}
2001 		if (floor(x) != x) ADD_GRID(x += .5);
2002 		for ( ; x < 10.0 && (x+1.0)/(x+.5) >= ratio; x += 1.0) {
2003 		    ADD_GRID(x + .5);	ADD_GRID(x + 1.0);
2004 		}
2005 		for ( ; x < 10.0 && (x+1.0)/x >= ratio; x += 1.0) {
2006 		    ADD_GRID(x + 1.0);
2007 		}
2008 		if (x == 7.0) {
2009 		    gridNJuke--;
2010 		    x = 6.0;
2011 		}
2012 		if (x < 7.0) {
2013 		    ADD_GRID(x + 2.0);
2014 		}
2015 		if (x == 10.0) gridNJuke--;
2016 	    }
2017 	    x = low - gridBase;
2018 	    for (gridCurJuke = -1; x >= gridJuke[gridCurJuke+1]; gridCurJuke++){
2019 	    }
2020 	}
2021     } else {
2022 	gridStep = RoundUp(step);
2023 	gridBase = floor(low / gridStep) * gridStep;
2024     }
2025     return(stepGrid());
2026 }
2027 
stepGrid()2028 double stepGrid()
2029 {
2030     if (++gridCurJuke >= gridNJuke) {
2031 	gridCurJuke = 0;
2032 	gridBase += gridStep;
2033     }
2034     return(gridBase + gridJuke[gridCurJuke]);
2035 }
2036 
RoundUp(val)2037 double RoundUp(val)
2038 double val;			/* Value */
2039 /*
2040  * This routine rounds up the given positive number such that
2041  * it is some power of ten times either 1, 2, or 5.  It is
2042  * used to find increments for grid lines.
2043  */
2044 {
2045     int exponent, idx;
2046 
2047     exponent = (int) floor(nlog10(val));
2048     if (exponent < 0) {
2049 	for (idx = exponent;  idx < 0; idx++) {
2050 	    val *= 10.0;
2051 	}
2052     } else {
2053 	for (idx = 0;  idx < exponent; idx++) {
2054 	    val /= 10.0;
2055 	}
2056     }
2057     if (val > 5.0) val = 10.0;
2058     else if (val > 2.0) val = 5.0;
2059     else if (val > 1.0) val = 2.0;
2060     else val = 1.0;
2061     if (exponent < 0) {
2062 	for (idx = exponent;  idx < 0;  idx++) {
2063 	    val /= 10.0;
2064 	}
2065     } else {
2066 	for (idx = 0;  idx < exponent;  idx++) {
2067 	    val *= 10.0;
2068 	}
2069     }
2070     return val;
2071 }
2072 
2073 #if PDG
getDigits(low,high,step,expv,logFlag)2074 int getDigits(low, high, step, expv, logFlag)
2075 double low;			/* desired low value          */
2076 double high;			/* desired high value         */
2077 double step;			/* desired step (user coords) */
2078 int expv;			/* Exponent             */
2079 int logFlag;			/* is axis logarithmic?       */
2080 {
2081     int digits;
2082     for (digits = 2; digits < 30; digits++)
2083     {
2084         double start, index;
2085 	char str1[40], str2[40];
2086         start = initGrid(low, step, logFlag);
2087         WriteValue(str1, start, expv, logFlag, digits);
2088         for (index = stepGrid();  index < high;  index = stepGrid()) {
2089 	    WriteValue(str2, index, expv, logFlag, digits);
2090 	    if (!strcmp(str1, str2)) goto next_digits;
2091 	    strcpy(str1, str2);
2092 	}
2093 	/* All adjacent values compare unequal */
2094 	return digits;
2095 next_digits: ;
2096     }
2097     /* We've reached the limit on number of digits */
2098     return digits;
2099 }
2100 #endif
2101 
2102 #if !PDG
2103 int WriteValue(str, val, expv, logFlag)
2104 #else
2105 int WriteValue(str, val, expv, logFlag, digits)
2106 #endif
2107 char *str;			/* String to write into */
2108 double val;			/* Value to print       */
2109 int expv;			/* Exponent             */
2110 int logFlag;			/* Is this a log axis?  */
2111 #if PDG
2112 int digits;			/* digits after decimal */
2113 #endif
2114 /*
2115  * Writes the value provided into the string in a fixed format
2116  * consisting of seven characters.  The format is:
2117  *   -ddd.dd
2118  */
2119 {
2120     int idx;
2121 
2122     if (logFlag) {
2123 	if (val == floor(val)) {
2124 	    (void) sprintf(str, "%.0e", pow(10.0, val));
2125 	} else {
2126 #if !PDG
2127 	    (void) sprintf(str, "%.2g", pow(10.0, val - floor(val)));
2128 #else
2129 	    (void) sprintf(str, "%.*g", digits, pow(10.0, val - floor(val)));
2130 #endif
2131 	}
2132     } else {
2133 	if (expv < 0) {
2134 	    for (idx = expv;  idx < 0;  idx++) {
2135 		val *= 10.0;
2136 	    }
2137 	} else {
2138 	    for (idx = 0;  idx < expv;  idx++) {
2139 		val /= 10.0;
2140 	    }
2141 	}
2142 #if !PDG
2143 	(void) sprintf(str, "%.2f", val);
2144 #else
2145 	(void) sprintf(str, "%.*f", digits, val);
2146 #endif
2147     }
2148 }
2149 
2150 
2151 #define LEFT_CODE	0x01
2152 #define RIGHT_CODE	0x02
2153 #define BOTTOM_CODE	0x04
2154 #define TOP_CODE	0x08
2155 
2156 /* Clipping algorithm from Neumann and Sproull by Cohen and Sutherland */
2157 #define C_CODE(xval, yval, rtn) \
2158 rtn = 0; \
2159 if ((xval) < wi->UsrOrgX) rtn = LEFT_CODE; \
2160 else if ((xval) > wi->UsrOppX) rtn = RIGHT_CODE; \
2161 if ((yval) < wi->UsrOrgY) rtn |= BOTTOM_CODE; \
2162 else if ((yval) > wi->UsrOppY) rtn |= TOP_CODE
2163 
DrawData(wi)2164 int DrawData(wi)
2165 LocalWin *wi;
2166 /*
2167  * This routine draws the data sets themselves using the macros
2168  * for translating coordinates.
2169  */
2170 {
2171     double sx1, sy1, sx2, sy2, tx, ty;
2172     int idx, subindex;
2173     int code1, code2, cd, mark_inside;
2174     int X_idx;
2175     XSegment *ptr;
2176     PointList *thisList;
2177     int markFlag, pixelMarks, bigPixel, colorMark;
2178     int noLines = PM_BOOL("NoLines");
2179     int lineWidth = PM_INT("LineWidth");
2180 
2181     set_mark_flags(&markFlag, &pixelMarks, &bigPixel, &colorMark);
2182     for (idx = 0;  idx < MAXSETS;  idx++) {
2183 	thisList = PlotData[idx].list;
2184 	while (thisList) {
2185 	    X_idx = 0;
2186 	    for (subindex = 0;  subindex < thisList->numPoints-1;  subindex++) {
2187 		/* Put segment in (sx1,sy1) (sx2,sy2) */
2188 		sx1 = thisList->xvec[subindex];
2189 		sy1 = thisList->yvec[subindex];
2190 		sx2 = thisList->xvec[subindex+1];
2191 		sy2 = thisList->yvec[subindex+1];
2192 		/* Now clip to current window boundary */
2193 		C_CODE(sx1, sy1, code1);
2194 		C_CODE(sx2, sy2, code2);
2195 		mark_inside = (code1 == 0);
2196 		while (code1 || code2) {
2197 		    if (code1 & code2) break;
2198 		    cd = (code1 ? code1 : code2);
2199 		    if (cd & LEFT_CODE) {	/* Crosses left edge */
2200 			ty = sy1 + (sy2 - sy1) * (wi->UsrOrgX - sx1) / (sx2 - sx1);
2201 			tx = wi->UsrOrgX;
2202 		    } else if (cd & RIGHT_CODE) { /* Crosses right edge */
2203 			ty = sy1 + (sy2 - sy1) * (wi->UsrOppX - sx1) / (sx2 - sx1);
2204 			tx = wi->UsrOppX;
2205 		    } else if (cd & BOTTOM_CODE) { /* Crosses bottom edge */
2206 			tx = sx1 + (sx2 - sx1) * (wi->UsrOrgY - sy1) / (sy2 - sy1);
2207 			ty = wi->UsrOrgY;
2208 		    } else if (cd & TOP_CODE) { /* Crosses top edge */
2209 			tx = sx1 + (sx2 - sx1) * (wi->UsrOppY - sy1) / (sy2 - sy1);
2210 			ty = wi->UsrOppY;
2211 		    }
2212 		    if (cd == code1) {
2213 			sx1 = tx;  sy1 = ty;
2214 			C_CODE(sx1, sy1, code1);
2215 		    } else {
2216 			sx2 = tx;  sy2 = ty;
2217 			C_CODE(sx2, sy2, code2);
2218 		    }
2219 		}
2220 		if (!code1 && !code2) {
2221 		    /* Add segment to list */
2222 		    Xsegs[X_idx].x1 = SCREENX(wi, sx1);
2223 		    Xsegs[X_idx].y1 = SCREENY(wi, sy1);
2224 		    Xsegs[X_idx].x2 = SCREENX(wi, sx2);
2225 		    Xsegs[X_idx].y2 = SCREENY(wi, sy2);
2226 		    X_idx++;
2227 		}
2228 
2229 		/* Draw markers if requested and they are in drawing region */
2230 		if (markFlag && mark_inside) {
2231 		    if (pixelMarks) {
2232 			if (bigPixel) {
2233 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2234 						Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1,
2235 						P_DOT, 0, idx % MAXATTR);
2236 			} else {
2237 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2238 						Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1,
2239 						P_PIXEL, 0, PIXVALUE(idx));
2240 			}
2241 		    } else {
2242 			/* Distinctive markers */
2243 			wi->dev_info.xg_dot(wi->dev_info.user_state,
2244 					    Xsegs[X_idx-1].x1, Xsegs[X_idx-1].y1,
2245 					    P_MARK, MARKSTYLE(idx),
2246 					    PIXVALUE(idx));
2247 		    }
2248 		}
2249 
2250 		/* Draw bar elements if requested */
2251 		if (PM_BOOL("BarGraph")) {
2252 		    int barPixels, baseSpot;
2253 		    XSegment line;
2254 
2255 		    barPixels = (int) ((PM_DBL("BarWidth")/wi->XUnitsPerPixel) + 0.5);
2256 		    if (barPixels <= 0) barPixels = 1;
2257 		    baseSpot = SCREENY(wi, PM_DBL("BarBase"));
2258 		    line.x1 = line.x2 = Xsegs[X_idx-1].x1;
2259 		    line.y1 = baseSpot;  line.y2 = Xsegs[X_idx-1].y1;
2260 		    wi->dev_info.xg_seg(wi->dev_info.user_state,
2261 					1, &line, barPixels, L_VAR,
2262 					LINESTYLE(idx), PIXVALUE(idx));
2263 		}
2264 	    }
2265 	    /* Handle last marker */
2266 	    if (markFlag && (thisList->numPoints > 0)) {
2267 		C_CODE(thisList->xvec[thisList->numPoints-1],
2268 		       thisList->yvec[thisList->numPoints-1],
2269 		       mark_inside);
2270 		if (mark_inside == 0) {
2271 #if !PDG
2272 		    if (pixelMarks) {
2273 			if (bigPixel) {
2274 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2275 						Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2,
2276 						P_DOT, 0, idx % MAXATTR);
2277 			} else {
2278 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2279 						Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2,
2280 						P_PIXEL, 0, PIXVALUE(idx));
2281 			}
2282 		    } else {
2283 			/* Distinctive markers */
2284 			wi->dev_info.xg_dot(wi->dev_info.user_state,
2285 					    Xsegs[X_idx-1].x2, Xsegs[X_idx-1].y2,
2286 					    P_MARK, MARKSTYLE(idx),
2287 					    PIXVALUE(idx));
2288 		    }
2289 #else
2290 		    /* Do it this way to handle a PointList of a single point */
2291 		    XPoint d;
2292 		    if (X_idx > 0) {
2293 			d.x = Xsegs[X_idx-1].x2;	/* This was computed above */
2294 			d.y = Xsegs[X_idx-1].y2;
2295 		    } else {
2296 			d.x = SCREENX(wi, thisList->xvec[thisList->numPoints-1]);
2297 			d.y = SCREENY(wi, thisList->yvec[thisList->numPoints-1]);
2298 		    }
2299 		    if (pixelMarks) {
2300 			if (bigPixel) {
2301 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2302 						d.x, d.y,
2303 						P_DOT, 0, idx % MAXATTR);
2304 			} else {
2305 			    wi->dev_info.xg_dot(wi->dev_info.user_state,
2306 						d.x, d.y,
2307 						P_PIXEL, 0, PIXVALUE(idx));
2308 			}
2309 		    } else {
2310 			/* Distinctive markers */
2311 			wi->dev_info.xg_dot(wi->dev_info.user_state,
2312 					    d.x, d.y,
2313 					    P_MARK, MARKSTYLE(idx),
2314 					    PIXVALUE(idx));
2315 		    }
2316 #endif
2317 		}
2318 	    }
2319 	    /* Handle last bar */
2320 	    if ((thisList->numPoints > 0) && PM_BOOL("BarGraph")) {
2321 		int barPixels, baseSpot;
2322 		XSegment line;
2323 
2324 		barPixels = (int) ((PM_DBL("BarWidth")/wi->XUnitsPerPixel) + 0.5);
2325 		if (barPixels <= 0) barPixels = 1;
2326 		baseSpot = SCREENY(wi, PM_DBL("BarBase"));
2327 		line.x1 = line.x2 = Xsegs[X_idx-1].x2;
2328 		line.y1 = baseSpot;  line.y2 = Xsegs[X_idx-1].y2;
2329 		wi->dev_info.xg_seg(wi->dev_info.user_state,
2330 				    1, &line, barPixels, L_VAR,
2331 				    LINESTYLE(idx), PIXVALUE(idx));
2332 	    }
2333 
2334 	    /* Draw segments */
2335 	    if (thisList->numPoints > 0 && (!noLines) && (X_idx > 0)) {
2336 		ptr = Xsegs;
2337 		while (X_idx > wi->dev_info.max_segs) {
2338 		    wi->dev_info.xg_seg(wi->dev_info.user_state,
2339 					wi->dev_info.max_segs, ptr,
2340 					lineWidth, L_VAR,
2341 					LINESTYLE(idx), PIXVALUE(idx));
2342 		    ptr += wi->dev_info.max_segs;
2343 		    X_idx -= wi->dev_info.max_segs;
2344 		}
2345 		wi->dev_info.xg_seg(wi->dev_info.user_state,
2346 				    X_idx, ptr,
2347 				    lineWidth, L_VAR,
2348 				    LINESTYLE(idx), PIXVALUE(idx));
2349 	    }
2350 	    /* Next subset */
2351 	    thisList = thisList->next;
2352 	}
2353     }
2354 }
2355 
2356 
2357 
DrawLegend(wi)2358 int DrawLegend(wi)
2359 LocalWin *wi;
2360 /*
2361  * This draws a legend of the data sets displayed.  Only those that
2362  * will fit are drawn.
2363  */
2364 {
2365     int idx, spot, lineLen, oneLen;
2366     XSegment leg_line;
2367     int markFlag, pixelMarks, bigPixel, colorMark;
2368 
2369     set_mark_flags(&markFlag, &pixelMarks, &bigPixel, &colorMark);
2370     spot = wi->XOrgY;
2371     lineLen = 0;
2372     /* First pass draws the text */
2373     for (idx = 0;  idx < MAXSETS;  idx++) {
2374 	if ((PlotData[idx].list) &&
2375 	    (spot + wi->dev_info.axis_height + 2 < wi->XOppY))
2376 	  {
2377 	      /* Meets the criteria */
2378 	      oneLen = strlen(PlotData[idx].setName);
2379 	      if (oneLen > lineLen) lineLen = oneLen;
2380 	      wi->dev_info.xg_text(wi->dev_info.user_state,
2381 				   wi->XOppX + wi->dev_info.bdr_pad,
2382 				   spot+2,
2383 				   PlotData[idx].setName,
2384 				   T_UPPERLEFT, T_AXIS);
2385 	      spot += 2 + wi->dev_info.axis_height + wi->dev_info.bdr_pad;
2386 	  }
2387     }
2388     lineLen = lineLen * wi->dev_info.axis_width;
2389     leg_line.x1 = wi->XOppX + wi->dev_info.bdr_pad;
2390     leg_line.x2 = leg_line.x1 + lineLen;
2391     spot = wi->XOrgY;
2392     /* second pass draws the lines */
2393     for (idx = 0;  idx < MAXSETS;  idx++) {
2394 	if ((PlotData[idx].list) &&
2395 	    (spot + wi->dev_info.axis_height + 2 < wi->XOppY))
2396 	  {
2397 	      leg_line.y1 = leg_line.y2 = spot - wi->dev_info.legend_pad;
2398 	      wi->dev_info.xg_seg(wi->dev_info.user_state,
2399 				  1, &leg_line, 1, L_VAR,
2400 				  LINESTYLE(idx), PIXVALUE(idx));
2401 	      if (markFlag && !pixelMarks) {
2402 		  wi->dev_info.xg_dot(wi->dev_info.user_state,
2403 				      leg_line.x1, leg_line.y1,
2404 				      P_MARK, MARKSTYLE(idx), PIXVALUE(idx));
2405 
2406 	      }
2407 	      spot += 2 + wi->dev_info.axis_height + wi->dev_info.bdr_pad;
2408 	  }
2409     }
2410 }
2411 
2412 
2413 
set_mark_flags(markFlag,pixelMarks,bigPixel,colorMark)2414 static void set_mark_flags(markFlag, pixelMarks, bigPixel, colorMark)
2415 int *markFlag;
2416 int *pixelMarks;
2417 int *bigPixel;
2418 int *colorMark;
2419 /*
2420  * Determines the values of the old boolean flags based on the
2421  * new values in the parameters database.
2422  */
2423 {
2424     *markFlag = 0;  *pixelMarks = 0;  *colorMark = 0;  *bigPixel = 0;
2425     if (PM_BOOL("Markers")) {
2426 	*markFlag = 1;  *pixelMarks = 0;  *colorMark = 0;
2427     }
2428     if (PM_BOOL("PixelMarkers")) {
2429 	*markFlag = 1;  *pixelMarks = 1;  *bigPixel = 0;
2430     }
2431     if (PM_BOOL("LargePixels")) {
2432 	*markFlag = 1;  *pixelMarks = 1;  *bigPixel = 1;
2433     }
2434     if (PM_BOOL("StyleMarkers")) {
2435 	*markFlag = 1;  *pixelMarks = 0;  *colorMark = 1;
2436     }
2437 }
2438 
2439 
2440 
2441 #define RND(val)	((int) ((val) + 0.5))
2442 
2443 /*ARGSUSED*/
do_hardcopy(prog,info,init_fun,dev_spec,file_or_dev,maxdim,ti_fam,ti_size,ax_fam,ax_size,doc_p)2444 void do_hardcopy(prog, info, init_fun, dev_spec, file_or_dev, maxdim,
2445 		 ti_fam, ti_size, ax_fam, ax_size, doc_p)
2446 char *prog;			/* Program name for Xdefaults    */
2447 char *info;			/* Some state information        */
2448 int (*init_fun)();		/* Hardcopy init function        */
2449 char *dev_spec;			/* Device specification (if any) */
2450 char *file_or_dev;		/* Filename or device spec       */
2451 double maxdim;			/* Maximum dimension in cm       */
2452 char *ti_fam, *ax_fam;		/* Font family names             */
2453 double ti_size, ax_size;	/* Font sizes in points          */
2454 int doc_p;			/* Documentation predicate       */
2455 /*
2456  * This routine resets the function pointers to those specified
2457  * by `init_fun' and causes a screen redisplay.  If `dev_spec'
2458  * is non-zero,  it will be considered a sprintf string with
2459  * one %s which will be filled in with `file_or_dev' and fed
2460  * to popen(3) to obtain a stream.  Otherwise,  `file_or_dev'
2461  * is considered to be a file and is opened for writing.  The
2462  * resulting stream is fed to the initialization routine for
2463  * the device.
2464  */
2465 {
2466     LocalWin *curWin = (LocalWin *) info;
2467     LocalWin thisWin;
2468     FILE *out_stream;
2469     char buf[MAXBUFSIZE], err[MAXBUFSIZE], ierr[ERRBUFSIZE];
2470     char tilde[MAXBUFSIZE*10];
2471     int final_w, final_h, flags;
2472     double ratio;
2473 
2474     if (dev_spec) {
2475 	(void) sprintf(buf, dev_spec, file_or_dev);
2476 	out_stream = popen(buf, "w");
2477 	if (!out_stream) {
2478 	    do_error(sprintf(err, "Unable to issue command:\n  %s\n", buf));
2479 	    return;
2480 	}
2481     } else {
2482 	tildeExpand(tilde, file_or_dev);
2483 	out_stream = fopen(tilde, "w");
2484 	if (!out_stream) {
2485 	    do_error(sprintf(err, "Unable to open file `%s'\n", tilde));
2486 	    return;
2487 	}
2488     }
2489     thisWin = *curWin;
2490     ratio = ((double) thisWin.dev_info.area_w) /
2491       ((double) thisWin.dev_info.area_h);
2492     if (thisWin.dev_info.area_w > thisWin.dev_info.area_h) {
2493 	final_w = RND(maxdim * 10000.0);
2494 	final_h = RND(maxdim/ratio * 10000.0);
2495     } else {
2496 	final_w = RND(maxdim * ratio * 10000.0);
2497 	final_h = RND(maxdim * 10000.0);
2498     }
2499     ierr[0] = '\0';
2500     flags = 0;
2501     if (doc_p) flags |= D_DOCU;
2502     if ((*init_fun)(out_stream, final_w, final_h, ti_fam, ti_size,
2503 		    ax_fam, ax_size, flags, &(thisWin.dev_info), ierr)) {
2504 	DrawWindow(&thisWin);
2505 	if (thisWin.dev_info.xg_end) {
2506 	    thisWin.dev_info.xg_end(thisWin.dev_info.user_state);
2507 	}
2508     } else {
2509 	do_error(ierr);
2510     }
2511     if (dev_spec) {
2512 	(void) pclose(out_stream);
2513     } else {
2514 	(void) fclose(out_stream);
2515     }
2516 }
2517 
2518 
tildeExpand(out,in)2519 static char *tildeExpand(out, in)
2520 char *out;			/* Output space for expanded file name */
2521 char *in;			/* Filename with tilde                 */
2522 /*
2523  * This routine expands out a file name passed in `in' and places
2524  * the expanded version in `out'.  It returns `out'.
2525  */
2526 {
2527     char username[50], *userPntr;
2528     struct passwd *userRecord;
2529 
2530     out[0] = '\0';
2531 
2532     /* Skip over the white space in the initial path */
2533     while ((*in == ' ') || (*in == '\t')) in++;
2534 
2535     /* Tilde? */
2536     if (in[0] == TILDE) {
2537 	/* Copy user name into 'username' */
2538 	in++;  userPntr = &(username[0]);
2539 	while ((*in != '\0') && (*in != '/')) {
2540 	    *(userPntr++) = *(in++);
2541 	}
2542 	*(userPntr) = '\0';
2543 	/* See if we have to fill in the user name ourselves */
2544 	if (strlen(username) == 0) {
2545 	    userRecord = getpwuid(getuid());
2546 	} else {
2547 	    userRecord = getpwnam(username);
2548 	}
2549 	if (userRecord) {
2550 	    /* Found user in passwd file.  Concatenate user directory */
2551 	    strcat(out, userRecord->pw_dir);
2552 	}
2553     }
2554 
2555     /* Concantenate remaining portion of file name */
2556     strcat(out, in);
2557     return out;
2558 }
2559 
2560 
2561 
2562 #define ERR_MSG_SIZE	2048
2563 
2564 /*ARGSUSED*/
XErrHandler(disp_ptr,evt)2565 static int XErrHandler(disp_ptr, evt)
2566 Display *disp_ptr;
2567 XErrorEvent *evt;
2568 /*
2569  * Displays a nicely formatted message and core dumps.
2570  */
2571 {
2572     char err_buf[ERR_MSG_SIZE], mesg[ERR_MSG_SIZE], number[ERR_MSG_SIZE];
2573     char *mtype = "XlibMessage";
2574 
2575     XGetErrorText(disp_ptr, evt->error_code, err_buf, ERR_MSG_SIZE);
2576     (void) fprintf(stderr, "X Error: %s\n", err_buf);
2577     XGetErrorDatabaseText(disp_ptr, mtype, "MajorCode",
2578 			  "Request Major code %d", mesg, ERR_MSG_SIZE);
2579     (void) fprintf(stderr, mesg, evt->request_code);
2580     (void) sprintf(number, "%d", evt->request_code);
2581     XGetErrorDatabaseText(disp_ptr, "XRequest", number, "", err_buf,
2582 			  ERR_MSG_SIZE);
2583     (void) fprintf(stderr, " (%s)\n", err_buf);
2584 
2585     abort();
2586 }
2587 
2588