1 /*
2  * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
3  *               2002, 2003, 2004
4  *	Ohio University.
5  *
6  * ---
7  *
8  * Starting with the release of tcptrace version 6 in 2001, tcptrace
9  * is licensed under the GNU General Public License (GPL).  We believe
10  * that, among the available licenses, the GPL will do the best job of
11  * allowing tcptrace to continue to be a valuable, freely-available
12  * and well-maintained tool for the networking community.
13  *
14  * Previous versions of tcptrace were released under a license that
15  * was much less restrictive with respect to how tcptrace could be
16  * used in commercial products.  Because of this, I am willing to
17  * consider alternate license arrangements as allowed in Section 10 of
18  * the GNU GPL.  Before I would consider licensing tcptrace under an
19  * alternate agreement with a particular individual or company,
20  * however, I would have to be convinced that such an alternative
21  * would be to the greater benefit of the networking community.
22  *
23  * ---
24  *
25  * This file is part of Tcptrace.
26  *
27  * Tcptrace was originally written and continues to be maintained by
28  * Shawn Ostermann with the help of a group of devoted students and
29  * users (see the file 'THANKS').  The work on tcptrace has been made
30  * possible over the years through the generous support of NASA GRC,
31  * the National Science Foundation, and Sun Microsystems.
32  *
33  * Tcptrace is free software; you can redistribute it and/or modify it
34  * under the terms of the GNU General Public License as published by
35  * the Free Software Foundation; either version 2 of the License, or
36  * (at your option) any later version.
37  *
38  * Tcptrace is distributed in the hope that it will be useful, but
39  * WITHOUT ANY WARRANTY; without even the implied warranty of
40  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
41  * General Public License for more details.
42  *
43  * You should have received a copy of the GNU General Public License
44  * along with Tcptrace (in the file 'COPYING'); if not, write to the
45  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
46  * MA 02111-1307 USA
47  *
48  * Author:	Shawn Ostermann
49  * 		School of Electrical Engineering and Computer Science
50  * 		Ohio University
51  * 		Athens, OH
52  *		ostermann@cs.ohiou.edu
53  *		http://www.tcptrace.org/
54  */
55 #include "tcptrace.h"
56 static char const GCC_UNUSED copyright[] =
57     "@(#)Copyright (c) 2004 -- Ohio University.\n";
58 static char const GCC_UNUSED rcsid[] =
59     "@(#)$Header$";
60 
61 
62 
63 /* info that I keep about each plotter */
64 struct plotter_info {
65     MFILE *fplot;		/* the file that hold the plot */
66     tcb *p2plast;		/* the TCB that this goes with (if any) */
67     timeval zerotime;		/* first time stamp in this plot (see -z) */
68     char *filename;		/* redundant copy of name for debugging */
69     Bool header_done;           /* Flag indicating plotter header written to file */
70     Bool axis_switched;         /* Switch x & y axis types.
71 				 * (Needed for Time Line Charts,
72 				 * Default = FALSE)
73 				 */
74     char *title;                /* Plotter title */
75     char *xlabel;               /* Plotter x-axis label */
76     char *ylabel;               /* Plotter y-axis label */
77 };
78 
79 
80 
81 /* locally global parameters */
82 static int max_plotters;
83 static PLOTTER plotter_ix = NO_PLOTTER;
84 static char *temp_color = NULL;
85 static struct plotter_info *pplotters;
86 
87 
88 /* local routine declarations */
89 static char *xp_timestamp(PLOTTER pl, struct timeval time);
90 static char *TSGPlotName(tcb *plast, PLOTTER, char *suffix);
91 static void DoPlot(PLOTTER pl, char *fmt, ...);
92 static void WritePlotHeader(PLOTTER pl);
93 static void CallDoPlot(PLOTTER pl, char *plot_cmd, int plot_argc, ...);
94 
95 
96 /*
97  * Return a string suitable for use as a timestamp in the xplot output.
98  * sdo fix: originally, we were just plotting to the accuracy of 1/10 ms
99  *   this was mostly to help keep the .xpl files from being too large.  However,
100  *   on faster networks this just isn't enough, so we'll now use all 6 digits
101  *   of the microsecond counter.  Note that there's no guarantee that the
102  *   timestamps are really all that accurate!
103  */
104 static char *
xp_timestamp(PLOTTER pl,struct timeval time)105 xp_timestamp(
106     PLOTTER pl,
107     struct timeval time)
108 {
109 #define NUM_BUFS 4
110     static char bufs[NUM_BUFS][20];	/* several of them for multiple calls in one printf */
111     static int bufix = 0;
112     unsigned secs;
113     unsigned usecs;
114     unsigned decimal;
115     char *pbuf;
116     struct plotter_info *ppi;
117 
118     ppi = &pplotters[pl];
119 
120     /* see if we're graphing from "0" OR if the axis type is switched */
121     if (graph_time_zero || ppi->axis_switched) {
122 
123 
124 	if (ZERO_TIME(&ppi->zerotime)) {
125 	    /* set "zero point" */
126 	    ppi->zerotime = time;
127 	}
128 
129 	/* (in)sanity check */
130 	if (tv_lt(time,ppi->zerotime)) {
131 	    fprintf(stderr,"Internal error in plotting (plot file '%s')...\n\
132 ZERO-based X-axis plotting requested and elements are not plotted in\n\
133 increasing time order.  Try without the '-z' flag\n",
134 		    ppi->filename);
135 /* 	    exit(-5); */
136 	    time.tv_sec = time.tv_usec = 0;
137 	} else {
138 	    /* computer offset from first plotter point */
139 	    tv_sub(&time, ppi->zerotime);
140 	}
141     }
142 
143     /* calculate time components */
144     secs = time.tv_sec;
145     usecs = time.tv_usec;
146     decimal = usecs;
147 
148     /* use one of 4 rotating static buffers (for multiple calls per printf) */
149     bufix = (bufix+1)%NUM_BUFS;
150     pbuf = bufs[bufix];
151 
152     snprintf(pbuf,sizeof(bufs[bufix]),"%u.%06u",secs,decimal);
153 
154     return(pbuf);
155 }
156 
157 
158 
159 void
plot_init(void)160 plot_init(void)
161 {
162     max_plotters = 256;  /* just a default, make more on the fly */
163 
164     pplotters = MallocZ(max_plotters * sizeof(struct plotter_info));
165 }
166 
167 
168 static void
plotter_makemore(void)169 plotter_makemore(void)
170 {
171     int new_max_plotters = max_plotters * 4;
172 
173     if (debug)
174 	fprintf(stderr,"plotter: making more space for %d total plotters\n",
175 		new_max_plotters);
176 
177     /* reallocate the memory to make more space */
178     pplotters = ReallocZ(pplotters,
179 			 max_plotters * sizeof(struct plotter_info),
180 			 new_max_plotters * sizeof(struct plotter_info));
181 
182     max_plotters = new_max_plotters;
183 }
184 
185 
186 
187 
188 /* max number of letters in endpoint name */
189 /* (8 allows 26**8 different endpoints (209,000,000,000)
190     probably plenty for now!!!!!) */
191 
192 /* #define MAX_HOSTLETTER_LEN 8
193    Moving this definition to tcptrace.h so other modules can use it. */
194 
195 char *
HostLetter(llong ix)196 HostLetter(
197      llong ix)
198 {
199     static char name[MAX_HOSTLETTER_LEN+1];
200     static char *pname;
201 
202     /* basically, just convert to base 26 */
203     pname = &name[sizeof(name)-1];
204     *pname-- = '\00';
205     while (pname >= name) {
206 	unsigned digit = ix % 26;
207 	*pname-- = 'a'+digit;
208 	ix = (ix / 26) - 1;
209 	if (ix == -1)
210 	    return(pname+1);
211     }
212    fprintf(stderr,"Fatal, too many hosts to name (max length %d)\n\nNOTE:\nIf you are using gcc version 2.95.3, then this may be a compiler bug. This particular version\nis known to generate incorrect assembly code when used with CCOPT=-O2.\nSuggested fixes are:\n   1. Update gcc to the latest version and recompile tcptrace.\n   2. Use the same version of gcc, but edit the tcptrace Makefile, setting CCOPT=-O instead of\n      CCOPT=-O2, and then recompile tcptrace.\nEither of these steps should hopefully fix the problem.\n\n", MAX_HOSTLETTER_LEN);
213    exit(-1);
214    return(NULL);  /* NOTREACHED */
215 }
216 
217 
218 char *
NextHostLetter(void)219 NextHostLetter(void)
220 {
221     static llong count = 0;
222     return(HostLetter(count++));
223 }
224 
225 
226 
227 static char *
TSGPlotName(tcb * plast,PLOTTER pl,char * suffix)228 TSGPlotName(
229     tcb *plast,
230     PLOTTER pl,
231     char *suffix)
232 {
233 	static char filename[25];
234 
235 
236     snprintf(filename,sizeof(filename),"%s2%s%s",
237 	    plast->host_letter, plast->ptwin->host_letter, suffix);
238 
239     return(filename);
240 }
241 
242 
243 
244 static void
DoPlot(PLOTTER pl,char * fmt,...)245 DoPlot(
246      PLOTTER	pl,
247      char	*fmt,
248      ...)
249 {
250     va_list	ap;
251     MFILE *f = NULL;
252     struct plotter_info *ppi;
253 
254     va_start(ap,fmt);
255 
256 /*     if (!graph_tsg) */
257 /* 	return; */
258 
259     if (pl == NO_PLOTTER) {
260 	va_end(ap);
261 	return;
262     }
263 
264     if (pl > plotter_ix) {
265 	fprintf(stderr,"Illegal plotter: %d\n", pl);
266 	exit(-1);
267     }
268 
269     ppi = &pplotters[pl];
270 
271     if ((f = ppi->fplot) == NULL) {
272 	va_end(ap);
273 	return;
274     }
275 
276     /* Write the plotter header if not already written */
277     if(!ppi->header_done)
278      WritePlotHeader(pl);
279 
280     Mvfprintf(f,fmt,ap);
281     if (temp_color) {
282 	Mfprintf(f," %s",temp_color);
283 	temp_color = NULL;
284     }
285     Mfprintf (f,"\n");
286 
287     va_end(ap);
288 
289     return;
290 }
291 
292 
293 PLOTTER
new_plotter(tcb * plast,char * filename,char * title,char * xlabel,char * ylabel,char * suffix)294 new_plotter(
295     tcb *plast,
296     char *filename,	/* if NULL, use default name from plast */
297     char *title,
298     char *xlabel,
299     char *ylabel,
300     char *suffix)
301 {
302     PLOTTER pl;
303     MFILE *f;
304     struct plotter_info *ppi;
305 
306     ++plotter_ix;
307     if (plotter_ix >= max_plotters) {
308 	plotter_makemore();
309     }
310 
311     pl = plotter_ix;
312     ppi = &pplotters[pl];
313 
314     if (filename == NULL)
315 	filename = TSGPlotName(plast,pl,suffix);
316     else if (suffix != NULL) {
317 	char buf[100];
318 	snprintf(buf,sizeof(buf),"%s%s", filename, suffix);
319 	filename = buf;
320     }
321 
322     if (debug)
323 	fprintf(stderr,"Plotter %d file is '%s'\n", pl, filename);
324 
325     if ((f = Mfopen(filename,"w")) == NULL) {
326 	perror(filename);
327 	return(NO_PLOTTER);
328     }
329 
330     ppi->fplot = f;
331     ppi->p2plast = plast;
332     ppi->filename = strdup(filename);
333     ppi->axis_switched = FALSE;
334     ppi->header_done = FALSE;
335 
336     /* Save these fields to be writtn to the plotter header later in DoPlot() */
337     ppi->title  = strdup(title);
338     ppi->xlabel = strdup(xlabel);
339     ppi->ylabel = strdup(ylabel);
340 
341     return(pl);
342 }
343 
344 
345 void
plotter_done(void)346 plotter_done(void)
347 {
348     PLOTTER pl;
349     MFILE *f;
350     char *fname;
351 	static struct dstring *xplot_cmd_buff=NULL;
352 
353 	if(plotter_ix>0) {
354 		if(xplot_all_files) {
355 			xplot_cmd_buff=DSNew();
356 			DSAppendString(xplot_cmd_buff,"xplot");
357 			DSAppendString(xplot_cmd_buff," ");
358 			if(xplot_args!=NULL) {
359 				DSAppendString(xplot_cmd_buff,xplot_args);
360 				DSAppendString(xplot_cmd_buff," ");
361 			}
362 		}
363 	}
364 
365     for (pl = 0; pl <= plotter_ix; ++pl) {
366 	struct plotter_info *ppi = &pplotters[pl];
367 
368 
369 	if ((f = ppi->fplot) == NULL)
370 	    continue;
371 
372         /* Write the plotter header if not already written */
373         if(!ppi->header_done)
374 	 WritePlotHeader(pl);
375 
376 	if (!ignore_non_comp ||
377 	    ((ppi->p2plast != NULL) && (ConnComplete(ppi->p2plast->ptp)))) {
378 	    Mfprintf(f,"go\n");
379 	    Mfclose(f);
380 	} else {
381 	    fname = ppi->p2plast->tsg_plotfile;
382 	    if (debug)
383 		fprintf(stderr,"Removing incomplete plot file '%s'\n",
384 			fname);
385 	    Mfclose(f);
386 	    if (unlink(fname) != 0)
387 		perror(fname);
388 	}
389 
390 	if(xplot_all_files){
391 		if(output_file_dir!=NULL) {
392 			DSAppendString(xplot_cmd_buff,output_file_dir);
393 			DSAppendString(xplot_cmd_buff,"/");
394 		}
395 		DSAppendString(xplot_cmd_buff,ppi->filename);
396 		DSAppendString(xplot_cmd_buff," ");
397 	}
398     }
399 
400 	if(plotter_ix>0) {
401 		if(xplot_all_files) {
402 			fprintf(stdout,"%s\n",DSVal(xplot_cmd_buff));
403 			system(DSVal(xplot_cmd_buff));
404 			DSDestroy(&xplot_cmd_buff);
405 		}
406 	}
407 }
408 
409 
410 
411 void
plotter_temp_color(PLOTTER pl,char * color)412 plotter_temp_color(
413     PLOTTER pl,
414     char *color)
415 {
416     if (colorplot)
417 	temp_color = color;
418 }
419 
420 
421 void
plotter_perm_color(PLOTTER pl,char * color)422 plotter_perm_color(
423     PLOTTER pl,
424     char *color)
425 {
426    if (colorplot)
427 	CallDoPlot(pl, color, 0);
428 }
429 
430 
431 void
plotter_line(PLOTTER pl,struct timeval t1,u_long x1,struct timeval t2,u_long x2)432 plotter_line(
433     PLOTTER pl,
434     struct timeval	t1,
435     u_long		x1,
436     struct timeval	t2,
437     u_long		x2)
438 {
439     CallDoPlot(pl,"line", 4, t1, x1, t2, x2);
440 }
441 
442 
443 void
plotter_dline(PLOTTER pl,struct timeval t1,u_long x1,struct timeval t2,u_long x2)444 plotter_dline(
445     PLOTTER pl,
446     struct timeval	t1,
447     u_long		x1,
448     struct timeval	t2,
449     u_long		x2)
450 {
451     CallDoPlot(pl,"dline", 4, t1, x1, t2, x2);
452 }
453 
454 
455 void
plotter_diamond(PLOTTER pl,struct timeval t,u_long x)456 plotter_diamond(
457     PLOTTER pl,
458     struct timeval	t,
459     u_long		x)
460 {
461     CallDoPlot(pl,"diamond", 2, t, x);
462 }
463 
464 
465 void
plotter_dot(PLOTTER pl,struct timeval t,u_long x)466 plotter_dot(
467     PLOTTER pl,
468     struct timeval	t,
469     u_long		x)
470 {
471     CallDoPlot(pl,"dot", 2, t, x);
472 }
473 
474 
475 void
plotter_plus(PLOTTER pl,struct timeval t,u_long x)476 plotter_plus(
477     PLOTTER pl,
478     struct timeval	t,
479     u_long		x)
480 {
481     CallDoPlot(pl,"plus", 2, t, x);
482 }
483 
484 
485 void
plotter_box(PLOTTER pl,struct timeval t,u_long x)486 plotter_box(
487     PLOTTER pl,
488     struct timeval	t,
489     u_long		x)
490 {
491     CallDoPlot(pl,"box", 2, t, x);
492 }
493 
494 
495 
496 void
plotter_arrow(PLOTTER pl,struct timeval t,u_long x,char dir)497 plotter_arrow(
498     PLOTTER pl,
499     struct timeval	t,
500     u_long		x,
501     char	dir)
502 {
503     char arrow_type[7];
504     snprintf(arrow_type, sizeof(arrow_type), "%carrow", dir);
505     CallDoPlot(pl, arrow_type, 2, t, x);
506 }
507 
508 
509 void
plotter_uarrow(PLOTTER pl,struct timeval t,u_long x)510 plotter_uarrow(
511     PLOTTER pl,
512     struct timeval	t,
513     u_long		x)
514 {
515     plotter_arrow(pl,t,x,'u');
516 }
517 
518 
519 void
plotter_darrow(PLOTTER pl,struct timeval t,u_long x)520 plotter_darrow(
521     PLOTTER pl,
522     struct timeval	t,
523     u_long		x)
524 {
525     plotter_arrow(pl,t,x,'d');
526 }
527 
528 
529 void
plotter_rarrow(PLOTTER pl,struct timeval t,u_long x)530 plotter_rarrow(
531     PLOTTER pl,
532     struct timeval	t,
533     u_long		x)
534 {
535     plotter_arrow(pl,t,x,'r');
536 }
537 
538 
539 void
plotter_larrow(PLOTTER pl,struct timeval t,u_long x)540 plotter_larrow(
541     PLOTTER pl,
542     struct timeval	t,
543     u_long		x)
544 {
545     plotter_arrow(pl,t,x,'l');
546 }
547 
548 
549 void
plotter_tick(PLOTTER pl,struct timeval t,u_long x,char dir)550 plotter_tick(
551     PLOTTER pl,
552     struct timeval	t,
553     u_long		x,
554     char		dir)
555 {
556     char tick_type[6];
557     snprintf(tick_type, sizeof(tick_type), "%ctick", dir);
558     CallDoPlot(pl, tick_type, 2, t, x);
559 }
560 
561 
562 void
plotter_dtick(PLOTTER pl,struct timeval t,u_long x)563 plotter_dtick(
564     PLOTTER pl,
565     struct timeval	t,
566     u_long		x)
567 {
568     plotter_tick(pl,t,x,'d');
569 }
570 
571 
572 void
plotter_utick(PLOTTER pl,struct timeval t,u_long x)573 plotter_utick(
574     PLOTTER pl,
575     struct timeval	t,
576     u_long		x)
577 {
578     plotter_tick(pl,t,x,'u');
579 }
580 
581 
582 void
plotter_ltick(PLOTTER pl,struct timeval t,u_long x)583 plotter_ltick(
584     PLOTTER pl,
585     struct timeval	t,
586     u_long		x)
587 {
588     plotter_tick(pl,t,x,'l');
589 }
590 
591 
592 void
plotter_rtick(PLOTTER pl,struct timeval t,u_long x)593 plotter_rtick(
594     PLOTTER pl,
595     struct timeval	t,
596     u_long		x)
597 {
598     plotter_tick(pl,t,x,'r');
599 }
600 
601 
602 void
plotter_htick(PLOTTER pl,struct timeval t,u_long x)603 plotter_htick(
604     PLOTTER pl,
605     struct timeval	t,
606     u_long		x)
607 {
608     plotter_tick(pl,t,x,'h');
609 }
610 
611 
612 void
plotter_vtick(PLOTTER pl,struct timeval t,u_long x)613 plotter_vtick(
614     PLOTTER pl,
615     struct timeval	t,
616     u_long		x)
617 {
618     plotter_tick(pl,t,x,'v');
619 }
620 
621 
622 
623 /* don't plot ANYTHING, just make sure ZERO point is set! */
624 void
plotter_nothing(PLOTTER pl,struct timeval t)625 plotter_nothing(
626     PLOTTER pl,
627     struct timeval	t)
628 {
629     char *ret;
630     ret = xp_timestamp(pl,t);
631     if (debug > 10)
632 	printf("plotter_nothing(%s) gets '%s'\n", ts2ascii(&t), ret);
633 }
634 
635 
636 
637 void
plotter_text(PLOTTER pl,struct timeval t,u_long x,char * where,char * str)638 plotter_text(
639     PLOTTER pl,
640     struct timeval	t,
641     u_long		x,
642     char		*where,
643     char		*str)
644 {
645     char text_type[6];
646     snprintf(text_type, sizeof(text_type), "%stext", where);
647 
648     CallDoPlot(pl, text_type, 2, t, x);
649     /* fix by Bill Fenner - Wed Feb  5, 1997, thanks */
650     /* This is a little ugly.  Text commands take 2 lines. */
651     /* A temporary color could have been */
652     /* inserted after that line, but would NOT be inserted after */
653     /* the next line, so we'll be OK.  I can't think of a better */
654     /* way right now, and this works fine (famous last words) */
655     CallDoPlot(pl, str, 0);
656 }
657 
658 void
plotter_invisible(PLOTTER pl,struct timeval t,u_long x)659 plotter_invisible(
660     PLOTTER pl,
661     struct timeval	t,
662     u_long		x)
663 {
664     CallDoPlot(pl,"invisible", 2, t, x);
665 }
666 
667 
668 /* high-level line-drawing package */
669 struct pl_line {
670     char *color;
671     char *label;
672     int last_y;
673     timeval last_time;
674     PLOTTER plotter;
675     Bool labelled;
676 };
677 
678 
679 PLINE
new_line(PLOTTER plotter,char * label,char * color)680 new_line(
681     PLOTTER plotter,
682     char *label,
683     char *color)
684 {
685     struct pl_line *pl;
686 
687     pl = MallocZ(sizeof(struct pl_line));
688     pl->plotter = plotter;
689     pl->label = label;
690     pl->color = color;
691 
692     return(pl);
693 }
694 
695 
696 void
extend_line(PLINE pline,timeval xval,int yval)697 extend_line(
698     PLINE pline,
699     timeval xval,
700     int yval)
701 {
702     PLOTTER p;
703 
704     if (!pline)
705 	return;
706 
707     p = pline->plotter;
708 
709 #ifdef OLD
710     /* attach a label to the first non-zero point */
711     if (!pline->labelled) {
712 	if (yval != 0) {
713 	    plotter_temp_color(p, pline->color);
714 	    plotter_text(p, xval, yval, "l", pline->label);
715 	    pline->labelled = 1;
716 	}
717     }
718 #endif
719 
720     /* attach a label midway on the first line segment above 0 */
721     /* for whom the second point is NOT 0 */
722     if (!pline->labelled) {
723 	if ((yval != 0) && (!ZERO_TIME(&pline->last_time))) {
724 	    timeval tv_elapsed;
725 	    timeval tv_avg;
726 	    int avg_yval;
727 
728 	    /* computer elapsed time for these 2 points */
729 	    tv_elapsed = xval;
730 	    tv_sub(&tv_elapsed,pline->last_time);
731 
732 	    /* divide elapsed time by 2 */
733 	    tv_elapsed.tv_sec /= 2;
734 	    tv_elapsed.tv_usec /= 2;
735 
736 	    /* add 1/2 of the elapsed time to the oldest point */
737 	    /* (giving us the average time) */
738 	    tv_avg = pline->last_time;
739 	    tv_add(&tv_avg, tv_elapsed);
740 
741 	    /* average the Y values */
742 	    avg_yval = (1 + pline->last_y+yval)/2;
743 	    /* (rounding UP, please) */
744 
745 	    /* draw the label */
746 	    plotter_temp_color(p, pline->color);
747 	    plotter_text(p, tv_avg, avg_yval, "l", pline->label);
748 
749 	    /* remember that it's been done */
750 	    pline->labelled = 1;
751 	}
752     }
753 
754     /* draw a dot at the current point */
755     plotter_perm_color(p, pline->color);
756     plotter_dot(p, xval, yval);
757 
758     /* if this isn't the FIRST point, connect with a line */
759     if (!ZERO_TIME(&pline->last_time)) {
760 	plotter_line(p,
761 		     xval, yval,
762 		     pline->last_time, pline->last_y);
763     }
764 
765     /* remember this point for the next line segment */
766     pline->last_time = xval;
767     pline->last_y = yval;
768 }
769 
770 /* This function may be called with 0, 2 or 4 arguments depending on plot command. */
771 static void
CallDoPlot(PLOTTER pl,char * plot_cmd,int plot_argc,...)772 CallDoPlot(
773     PLOTTER pl,
774     char *plot_cmd,
775     int plot_argc,
776     ...)
777 {
778    struct timeval t1;
779    u_long x1 = 0;
780    struct timeval t2;
781    u_long x2 = 0;
782    va_list ap;
783    struct plotter_info *ppi;
784    char fmt[200];
785 
786    if (pl == NO_PLOTTER)
787      return;
788 
789    if (pl > plotter_ix) {
790       fprintf(stderr,"Illegal plotter: %d\n", pl);
791       exit(-1);
792    }
793 
794    ppi = &pplotters[pl];
795 
796    /* Get the arguments from the variable list */
797    va_start(ap, plot_argc);
798    if(plot_argc > 0)
799      {
800 	t1 = va_arg(ap, struct timeval);
801 	x1 = va_arg(ap, u_long);
802      }
803    if(plot_argc > 2)
804      {
805 	t2 = va_arg(ap, struct timeval);
806 	x2 = va_arg(ap, u_long);
807      }
808    va_end(ap);
809 
810    memset(fmt, 0, sizeof(fmt));
811 
812    if(ppi->axis_switched) {
813       switch(plot_argc) {
814        case 0:
815 	 snprintf(fmt, sizeof(fmt), "%s", plot_cmd);
816 	 DoPlot(pl, fmt);
817 	 break;
818        case 2:
819 	 snprintf(fmt, sizeof(fmt), "%s %%u -%%s", plot_cmd);
820 	 DoPlot(pl, fmt,
821 		x1, xp_timestamp(pl,t1));
822 	 break;
823        case 4:
824 	 snprintf(fmt, sizeof(fmt), "%s %%u -%%s %%u -%%s", plot_cmd);
825 	 DoPlot(pl, fmt,
826 		x1, xp_timestamp(pl,t1),
827 		x2, xp_timestamp(pl,t2));
828 	 break;
829        default:
830 	 fprintf(stderr, "CallDoPlot: Illegal number of arguments (%d)\n", plot_argc);
831       }
832    }
833    else {
834       switch(plot_argc) {
835        case 0:
836 	 snprintf(fmt, sizeof(fmt), "%s", plot_cmd);
837 	 DoPlot(pl, fmt);
838 	 break;
839        case 2:
840 	 snprintf(fmt, sizeof(fmt), "%s %%s %%u", plot_cmd);
841 	 DoPlot(pl, fmt,
842 		xp_timestamp(pl,t1), x1);
843 	 break;
844        case 4:
845 	 snprintf(fmt, sizeof(fmt), "%s %%s %%u %%s %%u", plot_cmd);
846 	 DoPlot(pl, fmt,
847 		xp_timestamp(pl,t1), x1,
848 		xp_timestamp(pl,t2), x2);
849 	 break;
850        default:
851 	 fprintf(stderr, "CallDoPlot: Illegal number of arguments (%d)\n", plot_argc);
852       }
853    }
854 
855    return;
856 }
857 
858 static void
WritePlotHeader(PLOTTER pl)859 WritePlotHeader(
860     PLOTTER pl)
861 {
862    MFILE *f = NULL;
863    struct plotter_info *ppi;
864 
865    if (pl == NO_PLOTTER)
866      return;
867 
868    if (pl > plotter_ix) {
869       fprintf(stderr,"Illegal plotter: %d\n", pl);
870       exit(-1);
871    }
872 
873    ppi = &pplotters[pl];
874 
875    if ((f = ppi->fplot) == NULL)
876      return;
877 
878    if(ppi->axis_switched) {
879       /* Header for the Time Line Charts */
880       Mfprintf(f,"%s %s\n", "unsigned", "dtime");
881    }
882    else {
883       /* Header for all other plots */
884       /* graph coordinates... */
885       /*  X coord is timeval unless graph_time_zero is true */
886       /*  Y is signed except when it's a sequence number */
887       /* ugly hack -- unsigned makes the graphs hard to work with and is
888        only needed for the time sequence graphs */
889       /* suggestion by Michele Clark at UNC - make them double instead */
890       Mfprintf(f,"%s %s\n",
891 	       graph_time_zero?"dtime":"timeval",
892 	       ((strcmp(ppi->ylabel,"sequence number") == 0)&&(!graph_seq_zero))?
893 	       "double":"signed");
894    }
895 
896    if (show_title) {
897       if (xplot_title_prefix)
898 	Mfprintf(f,"title\n%s %s\n",
899 		 ExpandFormat(xplot_title_prefix),
900 		 ppi->title);
901       else
902 	Mfprintf(f,"title\n%s\n", ppi->title);
903    }
904 
905    Mfprintf(f,"xlabel\n%s\n", ppi->xlabel);
906    Mfprintf(f,"ylabel\n%s\n", ppi->ylabel);
907 
908    /* Indicate that the header has now been written to the plotter file */
909    ppi->header_done = TRUE;
910 
911    return;
912 }
913 
914 /* Switch the x and y axis type (Needed for Time Line Charts. Default = FLASE) */
plotter_switch_axis(PLOTTER pl,Bool flag)915 void plotter_switch_axis(
916     PLOTTER pl,
917     Bool flag)
918 {
919    struct plotter_info *ppi = &pplotters[pl];
920 
921    ppi->axis_switched = flag;
922 }
923 
924