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