1 /*
2  MRTG 2.17.4  -- Rateup
3  *********************
4 
5  Rateup is a fast add-on to the great MRTG Traffic monitor.  It makes
6  the database file updates much faster, and creates the graphic image
7  files, ready for processing by PPMTOGIF.  It also reduces memory
8  requirements by a factor of 10, and increases the speed of updates
9  by a factor of at least 10.  This makes it feasible to run mrtg
10  every 5 minutes.
11 
12  rateup attempts to compensate for missed updates by repeating the last
13  sample, and also tries to catch bad update times.  The .log file stores
14  real history every five minutes for 31 hours, then 'compresses' the
15  history into 30 minute samples for a week, then 2-hour samples for
16  31 days, then daily samples for two years.  This ensures that the
17  log files don't grow in size.
18 
19  The log files are a slightly different format, but convert.perl
20  will fix that for you.
21 
22  Enjoy!
23  Dave Rand
24  dlr@bungi.com
25 
26  04/26/99 - There was some compilation bug under Watcom 10.6
27             which was fixed when recompiled with VC++ 6.0
28 			Alexandre Steinberg
29 			steinberg@base.com.br
30 
31 */
32 
33 #include <stdlib.h>
34 #include <string.h>
35 /* VC++ does not have unistd.h */
36 #ifndef WIN32
37 #ifndef NETWARE
38 #include "../config.h"
39 #endif
40 #include <unistd.h>
41 #endif
42 #include <limits.h>
43 #include <stdio.h>
44 #include <time.h>
45 #include <math.h>
46 #include <ctype.h>
47 #ifndef GFORM_GD
48 #define GFORM_GD gdImagePng
49 #endif
50 
51 /* BSD* does not have/need malloc.h */
52 #if !defined(bsdi) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__DragonFly__)
53 #include <malloc.h>
54 #endif
55 
56 /* MSVCRT.DLL does not know %ll in printf */
57 #ifdef __MINGW32_VERSION
58 #define LLD "%I64d"
59 #define LLD_FORMAT "I64d"
60 #endif
61 
62 #ifdef __EMX__			/* OS/2 */
63 #define strtoll _strtoll
64 #define LLD "%Ld"		/* EMX lib use %Ld for long long */
65 #define LLD_FORMAT "Ld"
66 #endif
67 
68 #ifndef LLD
69 #define  LLD "%lld"
70 #define  LLD_FORMAT "lld"
71 #endif
72 
73 
74 /* WATCOM C/C++ 10.6 under Win95/NT */
75 /* VC++ 6.0 under Win95/NT */
76 #if defined(__WATCOMC__) || defined(WIN32)
77 #include <string.h>
78 #include <sys/types.h>
79 #include <direct.h>
80 #include <io.h>
81 #endif
82 
83 #include <gd.h>
84 #include <gdfonts.h>
85 
86 char *VERSION = "2.17.4";
87 char *program, *router, *routerpath;
88 int histvalid;
89 
90 /* Options */
91 short options = 0;
92 #define OPTION_WITHZEROES	0x0001	/* withzeros */
93 #define OPTION_UNKNASZERO	0x0002	/* unknaszero */
94 #define OPTION_TRANSPARENT	0x0004	/* transparent */
95 #define OPTION_DORELPERCENT	0x0008	/* dorelpercent */
96 #define OPTION_NOBORDER		0x0010	/* noborder */
97 #define OPTION_NOARROW		0x0020	/* noarrow */
98 #define OPTION_NO_I		0x0040	/* ignore 'I' (first) variable */
99 #define OPTION_NO_O		0x0080	/* ignore 'O' (second) variable */
100 #define OPTION_PRINTROUTER	0x0200	/* show title in graph */
101 #define OPTION_LOGGRAPH		0x0400	/* Use a logarithmic Y axis */
102 #define OPTION_MEANOVER		0x0800	/* max Y = mean-above-the-mean */
103 #define OPTION_EXPGRAPH		0x1000	/* exponential scale (opposite of logscale) */
104 
105 time_t NOW;
106 
107 /* jpt, april 2006 : added 3 lines for date & time logging */
108 struct tm * stLocal;
109 time_t timestamp;
110 char bufftime[32];
111 
112 char *short_si_def[] = { "", "k", "M", "G", "T" };
113 int kMGnumber = 4;
114 char **short_si = short_si_def;
115 char *longup = NULL;
116 char *shortup = NULL;
117 char *pngtitle = NULL;
118 char *rtimezone = NULL;
119 char weekformat = 'V';		/* strftime() fmt char for week #  */
120 
121 #define DAY_COUNT (600)		/* 400 samples is 33.33 hours */
122 #define DAY_SAMPLE (5*60)	/* Sample every 5 minutes */
123 #define WEEK_COUNT (600)	/* 400 samples is 8.33 days */
124 #define WEEK_SAMPLE (30*60)	/* Sample every 30 minutes */
125 #define MONTH_COUNT (600)	/* 400 samples is 33.33 days */
126 #define MONTH_SAMPLE (2*60*60)	/* Sample every 2 hours */
127 #define YEAR_COUNT  (2 * 366)	/* 1 sample / day, 366 days, 2 years */
128 #define YEAR_SAMPLE (24*60*60)	/* Sample every 24 hours */
129 
130 /* One 'rounding error' per sample period, so add 4 to total and for
131    good mesure we take 10 :-) */
132 #define MAX_HISTORY (DAY_COUNT+WEEK_COUNT+MONTH_COUNT+YEAR_COUNT+10)
133 
134 /* These are the colors used for the variouse parts of the graph */
135 /* the format is Red,Green,Blue */
136 
137 #define c_blank 245,245,245
138 #define c_light 194,194,194
139 #define c_dark 100,100,100
140 #define c_major 255,0,0
141 #define c_in    0,235,12
142 #define c_out   0,94,255
143 #define c_grid  0,0,0
144 #define c_inm   0,166,33
145 #define c_outm  255,0,255
146 #define c_outp  239,159,79
147 
148 int col_in[3];
149 int col_out[3];
150 int col_inm[3];
151 int col_outm[3];
152 int col_outp[3];
153 
154 long long kilo = (long long) 1000;
155 char *kMG = (char *) NULL;
156 
157 
158 #define MAXL	200		/* Maximum length of last in & out fields */
159 
160 
161 struct HISTORY
162 {
163   time_t time;
164   double in, out, percent, inmax, outmax;
165 }
166  *history;
167 int Mh;
168 
169 struct LAST
170 {
171   time_t time;
172   char in[MAXL], out[MAXL];
173 }
174 last;
175 
176 #ifndef max
177 #define max(a,b) ((a) > (b) ? (a) : (b))
178 #endif
179 #ifndef min
180 #define min(a,b) ((a) < (b) ? (a) : (b))
181 #endif
182 
183 /*
184 
185 notes about the NEXT macro ....
186 
187 * position n to the entry in the history array so that NOW is between
188   history[n] and history[n+1]
189 
190 * calculate the interval according to steptime and position of
191   now within the history array.
192 
193 for debuging
194 
195     fprintf (stderr,"%s, NOW: %8lu  ST: %4lu  N: %4u HTN: %8lu HTN+1: %8lu IV: %6.0f\n", \
196             bufftime,now,steptime,n,history[n].time, history[n+1].time, interval);\
197 
198 */
199 
200 #define NEXT(steptime) \
201   inmax = outmax = avc = 0; \
202   inr = outr = 0.0;\
203   nextnow = now - steptime;\
204   if (now == history[n].time && \
205       n<histvalid && \
206       nextnow == history[n+1].time) { \
207     inr = (double) history[n].in;\
208     outr = (double) history[n].out;\
209     inmax = history[n].inmax;\
210     outmax = history[n].outmax;\
211     n++;\
212   } else {\
213     if (now > history[n].time) {\
214       fprintf(stderr,"%s, ERROR: Rateup is trying to read ahead of the available data\n" ,bufftime);\
215     } else {\
216       while (now <= history[n+1].time && n < histvalid){n++;}\
217       do {\
218 	if (now >= history[n].time) {\
219 	  if (nextnow <= history[n+1].time) {\
220 	    interval = history[n].time - history[n+1].time;\
221 	  } else {\
222 	    interval = history[n].time - nextnow;\
223 	  }\
224 	} else {\
225 	  if (nextnow > history[n+1].time) {\
226 	    interval = steptime;\
227 	  } else {\
228 	    interval = now - history[n+1].time;\
229 	  }\
230 	}\
231 	inr  += (double) history[n].in * interval;\
232 	outr += (double) history[n].out * interval;\
233 	avc += interval;\
234        inmax =  (long long) max(inmax, history[n].inmax);\
235        outmax = (long long) max(outmax,history[n].outmax);\
236 	if (nextnow <= history[n+1].time) n++; else break;\
237       } while (n < histvalid && nextnow < history[n].time);\
238 \
239       if (avc != steptime) {\
240        fprintf(stderr,"%s, ERROR: StepTime does not match Avc %8" LLD_FORMAT ". Please Report this.\n", bufftime, avc);\
241       }\
242 \
243       inr /= avc; outr /= avc;\
244     }\
245   }
246 
logscale(double y,double maxy)247 static double logscale(double y, double maxy)
248 {
249      y = (y * (maxy - 1) / maxy) + 1;
250      y = log(y) / log (maxy) * maxy;
251      if (y < 0) return 0;
252      if (y > maxy) return maxy;
253      return y;
254 }
255 
expscale(double y,double maxy)256 static double expscale(double y, double maxy)
257 {
258     y = exp(y / maxy * log(maxy));
259     return (y - 1) * maxy / (maxy - 1);
260 }
261 
262 static void
image(file,maxvi,maxvo,maxx,maxy,xscale,yscale,growright,step,bits,ytics,yticsf,peak,currdatetimeformat,currdatetimepos)263 image (file, maxvi, maxvo, maxx, maxy, xscale, yscale, growright, step, bits,
264        ytics, yticsf, peak, currdatetimeformat, currdatetimepos)
265      char *file;
266      long long maxvi, maxvo;
267      long maxx;
268      long maxy, growright, step, bits;
269      double xscale, yscale;
270      int ytics;			/* number of tics on the y axis */
271      double yticsf;		/* scale everything on the y axis with this factor */
272      int peak;
273      char *currdatetimeformat;
274      int currdatetimepos;
275 {
276   FILE *fo;
277   char file_tmp[10240];
278   int i, x, n, type;
279 
280   long long maxv;
281   double origmaxvi, origmaxvo;
282   long long maxs, avc, inmax, outmax;
283   long long ytrmax;
284   double y, lmx1, lmx2, mea1, mea2, temp;
285   double inr, outr, muli = 1, interval;
286   time_t now, onow, nextnow;
287   struct tm tm2, *tm = &tm2;
288   char **graph_label;
289   char ylab[30];
290   /* scaling helpers */
291   long long maxv_q;
292   long long valsamp, maxin, maxout, digits, digits1, maxpercent = 0;
293   long long sca_ten, sca_hun;
294   double nmax_q;
295   double avmxin, avmxout, avin, avout, latestout = 0, latestin =
296     0, nmax, avpercent = 0, latestpercent = 0;
297   double nex_ten, nex_hun, nex_rnd;
298   double sca_max_q, dummy;
299   double percent;
300   char *short_si_out;
301   char currdatetimestr[256];
302   time_t currdatetime;
303   int currdatetimepos_x, currdatetimepos_y;
304 
305 #define NO_TIMESTAMPSTR (0)
306 #define LU_CORNER (1)
307 #define RU_CORNER (2)
308 #define LL_CORNER (3)
309 #define RL_CORNER (4)
310 
311 
312   struct HISTORY *lhist;
313   /* ################################################# */
314   /* Some general definitions for the graph generation */
315 #define XSIZE (long)((maxx*xscale)+100+((options & OPTION_DORELPERCENT) ? 1 : 0)*30)
316 #define YSIZE (long)((maxy*yscale)+35)
317   /* position the graph */
318 #define ytr(y) (long)(maxy*yscale+14-((y)*yscale))
319   /* translate x/y coord into graph coord */
320 #define xtr(x) (long)((growright) ? (maxx*xscale+81-((x)*xscale)) : (81+((x)*xscale)))
321   /* ################################################# */
322 
323 
324 
325   /* GD LIB declarations */
326   /* Declare the image */
327   gdImagePtr graph, brush_out, brush_outm, brush_outp;
328   /* Declare color indexes */
329   int i_light, i_dark, i_blank, i_major, i_in, i_out, i_grid, i_inm, i_outm;
330   int i_outp, i_outpg;
331   /* Dotted style */
332   int styleDotted[3];
333   if ((graph_label = (char **) calloc (1, sizeof (char *) * maxx)) == NULL)
334     {
335       fprintf (stderr, "%s, Rateup ERROR: Out of memory in graph creation\n", bufftime);
336       exit (1);
337     }
338 
339   /* multiplicator for bits/bytes */
340   if (bits) {
341       muli = 8;
342   }
343 
344   maxv = (long long) max (maxvi, maxvo);
345   maxv *= (long long) muli;
346 
347   origmaxvi = maxvi < (long long) 0 ? -maxvi : maxvi;
348   origmaxvo = maxvo < (long long) 0 ? -maxvo : maxvo;
349 
350   if (step > MONTH_SAMPLE)
351     {
352       type = 4;
353       now = (long) (NOW / YEAR_SAMPLE) * YEAR_SAMPLE;
354 
355     }
356   else if (step > WEEK_SAMPLE)
357     {
358       type = 3;
359       now = (long) (NOW / MONTH_SAMPLE) * MONTH_SAMPLE;
360     }
361   else if (step > DAY_SAMPLE)
362     {
363       type = 2;
364       now = (long) (NOW / WEEK_SAMPLE) * WEEK_SAMPLE;
365     }
366   else
367     {
368       type = 1;
369       now = (long) (NOW / DAY_SAMPLE) * DAY_SAMPLE;
370     }
371   if ((lhist = calloc (1, sizeof (struct HISTORY) * maxx)) == NULL)
372     {
373       fprintf (stderr, "%s, Rateup ERROR: Out of memory in graph creation\n", bufftime);
374       exit (1);
375     }
376   onow = now;
377   avin = avout = avmxin = avmxout = 0.0;
378   inmax = outmax = maxin = maxout = 0;
379   valsamp = 0;
380   for (maxs = 0, n = 0, x = 0; x < maxx; x++)
381     {
382       NEXT (step);
383       /*scale with muli */
384       inr *= muli;
385       outr *= muli;
386       inmax *= muli;
387       outmax *= muli;
388       /* ignore times when we have not sampled */
389       if (inmax > 0 || outmax > 0 || inr > 0 || outr > 0
390 	  || (options & OPTION_WITHZEROES)) valsamp++;
391       if (x == 0)
392 	{
393 	  latestin = inr;
394 	  latestout = outr;
395 	  if (outr)
396 	    {
397 	      latestpercent = inr * (double) 100. / outr;
398 	    }
399 	}
400       avin += inr;
401       avout += outr;
402       avmxin += inmax;
403       avmxout += outmax;
404 
405       if (peak)
406 	{
407 	  maxin = (long long) max (maxin, inmax);
408 	  maxout = (long long) max (maxout, outmax);
409           if (!(options & OPTION_NO_I)){
410    	     maxs = (long long) max (maxs, inmax);
411           }
412           if (!(options & OPTION_NO_O)){
413   	     maxs = (long long) max (maxs, outmax);
414           }
415 	}
416       else
417 	{
418 	  maxin = (long long) max (maxin, inr);
419 	  maxout = (long long) max (maxout, outr);
420           if (!(options & OPTION_NO_I)){
421   	     maxs = (long long) max (maxs, inr);
422           }
423           if (!(options & OPTION_NO_O)){
424   	     maxs = (long long) max (maxs, outr);
425           }
426 	}
427       if ((options & OPTION_DORELPERCENT) && outr)
428 	{
429 	  dummy = (double) 100. *inr / outr;
430 	  maxpercent = max (dummy, maxpercent);
431 	}
432       now -= step;
433     }
434   if (options & OPTION_DORELPERCENT)
435     {
436       if (avout && valsamp)
437 	{
438 	  avpercent = (double) 100. *avin / avout;
439 	}
440       else
441 	{
442 	  avpercent = 0;
443 	}
444     }
445   if (valsamp)
446     {
447       avin /= valsamp;
448       avout /= valsamp;
449       avmxin /= valsamp;
450       avmxout /= valsamp;
451     }
452 
453   printf ("" LLD "\n", (long long) (maxin / (long long) muli + .5));
454   printf ("" LLD "\n", (long long) (maxout / (long long) muli + .5));
455   if (options & OPTION_DORELPERCENT)
456     {
457       printf ("" LLD "\n", (long long) (maxpercent + .5));
458     }
459   printf ("" LLD "\n", (long long) (avin / (long long) muli + .5));
460   printf ("" LLD "\n", (long long) (avout / (long long) muli + .5));
461   if (options & OPTION_DORELPERCENT)
462     {
463       printf ("" LLD "\n", (long long) (avpercent + .5));
464     }
465   printf ("" LLD "\n", (long long) (latestin / (long long) muli + .5));
466   printf ("" LLD "\n", (long long) (latestout / (long long) muli + .5));
467   if (options & OPTION_DORELPERCENT)
468     {
469       printf ("" LLD "\n", (long long) (latestpercent + .5));
470     }
471   printf ("" LLD "\n", (long long) (avmxin / (long long) muli + .5));
472   printf ("" LLD "\n", (long long) (avmxout / (long long) muli + .5));
473 
474   if (maxv < 0 || maxv < maxs)
475     {
476       maxv = maxs;
477     }
478 
479   now = onow;
480 
481   if (maxv <= 0)
482     maxv = 1;
483 
484   if (kMG)
485     {
486       if (short_si[0] != kMG)
487         {
488           short_si_out = kMG;
489           kMGnumber = 0;
490           while ((short_si_out = strchr (short_si_out, ',')) != NULL)
491 	    {
492 	      short_si_out++;
493               kMGnumber++;
494 	    }
495           short_si = calloc(kMGnumber + 1, sizeof(*short_si));
496           short_si_out = kMG;
497           for (kMGnumber = 0; ; kMGnumber++)
498             {
499               short_si[kMGnumber] = short_si_out;
500              short_si_out = strchr(short_si_out, ',');
501               if (short_si_out == NULL) break;
502               short_si_out[0] = '\0';
503               short_si_out++;
504             }
505         }
506      }
507 
508   /* mangle the 0.25*maxv value so, that we get a number with either */
509   /* one or two digits != 0 and these digits should be at the  */
510   /* start of the number ... */
511 
512   /* the ceil compensates for rounding with small numbers */
513   maxv_q = ceil ((double) maxv / (double) ytics);	/* int */
514 
515   digits = 0;
516   {
517     double number = (double) maxv_q;
518     number *= yticsf;		/* we just want to scale the lables nothing else */
519 
520 /*
521        while (number/(double) kilo >= (double)kilo && digits<kMGnumber*3) {
522 */
523     /* yes this should be kilo, but then the log and pow bits below
524        should be base 'kilo' as well and not base 10 */
525 
526     while (digits < (long long) kMGnumber * 3 &&
527            (number >= (double) 1000 || short_si[digits / 3][0] == '-'))
528       {
529 	number /= (double) 1000;
530 	digits += 3;
531       }
532     sca_max_q = (double) ((int) (((double) 100. * (double) number) /
533 				 (pow
534 				  ((double) 10.,
535 				   (double) (int) (log10 ((double) number))))
536 				 +
537 				 (double) 9.999) / (int) 10) / (double) 10 *
538       (pow ((double) 10., (double) (int) (log10 ((double) number))));
539   }
540 
541   short_si_out = short_si[min ((signed) (digits / 3), kMGnumber)];
542 
543   if (maxv_q * yticsf >= 1)
544     {
545       digits1 = log10 ((double) maxv_q * yticsf);
546     }
547   else
548     {
549       digits1 = 0;
550     }
551 
552 /*  sca_ten = maxv_q / pow(10.0,(double)digits); */
553 /*  sca_hun = maxv_q / pow(10.0,(double)digits-1); */
554   sca_ten = (double) maxv_q *yticsf / pow (10.0, (double) digits1);
555   sca_hun = (double) maxv_q *yticsf / pow (10.0, (double) digits1 - 1);
556   nex_rnd = (sca_hun) * pow (10.0, (double) digits1 - 1);
557   nex_ten = (sca_ten + 1) * pow (10.0, (double) digits1);
558   nex_hun = (sca_hun + 1) * pow (10.0, (double) digits1 - 1);
559 
560 /*  if (nex_ten <= (1.1 * maxv_q))  { */
561   if (nex_ten <= (1.1 * maxv_q * yticsf))
562     {
563       nmax_q = nex_ten;
564 /*  } else if (maxv_q == nex_rnd) {  */
565     }
566   else if ((maxv_q * yticsf) == nex_rnd)
567     {
568       nmax_q = nex_rnd;
569     }
570   else
571     {
572       nmax_q = nex_hun;
573     }
574 
575   sca_max_q = nmax_q / (pow (10.0, (double) ((int) (digits / 3)) * 3));
576 /*  nmax=sca_max_q*ytics*(pow((double)kilo,(double)((int)(digits1/3))));  */
577   nmax =
578     (sca_max_q / yticsf) * ytics *
579     (pow ((double) kilo, (double) ((int) (digits / 3))));
580 
581   if (nmax < 1.)
582     {
583       nmax = 1.;
584     }
585 
586   for (n = 0, x = 0; x < maxx; x++)
587     {
588       lhist[x].time = 0;
589       graph_label[x] = NULL;
590       /* this seesm to be necessary to work a round a bug in solaris
591          where tm for complex TZ settings gets modified after the
592          fact ... so we whisk the stuff away into our own memspace
593          just in time */
594       memcpy (tm, localtime (&history[n].time), sizeof (struct tm));
595       switch (type)
596 	{
597 	default:
598 	case 1:
599 	  if (tm->tm_min < 5)
600 	    {
601 	      lhist[x].time |= 1;
602 	      if (tm->tm_hour == 0)
603 		lhist[x].time |= 2;
604 	    }
605 	  if ((tm->tm_min < 5) && (tm->tm_hour % 2 == 0))
606 	    {
607 	      if ((graph_label[x] = calloc (1, sizeof (char) * 4)) == NULL)
608 		{
609 		  fprintf (stderr,
610 			   "%s, Rateup ERROR: Out of memory in graph labeling\n", bufftime);
611 		  exit (0);
612 		}
613 	      else
614 		{
615 		  sprintf (graph_label[x], "%i", tm->tm_hour);
616 		}
617 	    }
618 	  break;
619 	case 2:
620 	  if (tm->tm_min < 30 && tm->tm_hour == 0)
621 	    {
622 	      lhist[x].time |= 1;
623 	      if (tm->tm_wday == 1)
624 		lhist[x].time |= 2;
625 	    }
626 
627 	  /* fprintf(stderr,"%s, x: %i, min:%i, hour:%i day: %i\n",
628 	     bufftime,x,tm->tm_min,tm->tm_hour,tm->tm_wday); */
629 	  if ((tm->tm_min < 30) && (tm->tm_hour == 12))
630 	    {
631 	      if ((graph_label[x] = calloc (1, sizeof (char) * 5)) == NULL)
632 		{
633 		  fprintf (stderr,
634 			   "%s, Rateup ERROR: Out of memory in graph labeling\n", bufftime);
635 		  exit (0);
636 		}
637 	      else
638 		{
639 		  strftime (graph_label[x], 4, "%a", tm);
640 		}
641 	    }
642 
643 	  break;
644 	case 3:
645 	  if (tm->tm_hour < 2)
646 	    {
647 	      if (tm->tm_wday == 1)
648 		lhist[x].time |= 1;
649 	      if (tm->tm_mday == 1)
650 		lhist[x].time |= 2;
651 	    }
652 	  /* label goes to thursday noon */
653 
654 	  if ((tm->tm_hour > 10) && (tm->tm_hour <= 12) && (tm->tm_wday == 4))
655 	    {
656 	      if ((graph_label[x] = calloc (1, sizeof (char) * 16)) == NULL)
657 		{
658 		  fprintf (stderr,
659 			   "%s, Rateup ERROR: Out of memory in graph labeling\n", bufftime);
660 		  exit (0);
661 		}
662 	      else
663 		{
664 		  char fmtbuf[10];
665 		  sprintf (fmtbuf, "Week %%%c", weekformat);
666 		  strftime (graph_label[x], 8, fmtbuf, tm);
667 		}
668 	    }
669 	  break;
670 	case 4:
671 	  if (tm->tm_mday == 1)
672 	    lhist[x].time |= 1;
673 	  if (tm->tm_yday == 0)
674 	    lhist[x].time |= 2;
675 
676 
677 	  if (tm->tm_mday == 15)
678 	    {
679 	      if ((graph_label[x] = calloc (1, sizeof (char) * 5)) == NULL)
680 		{
681 		  fprintf (stderr,
682 			   "%s, Rateup Error: Out of memory in graph labeling\n", bufftime);
683 		  exit (0);
684 		}
685 	      else
686 		{
687 		  strftime (graph_label[x], 4, "%b", tm);
688 		}
689 	    }
690 	  break;
691 	}
692 
693       NEXT (step);
694 
695       /*scale with muli */
696       inr *= muli;
697       outr *= muli;
698       inmax *= muli;
699       outmax *= muli;
700 
701 
702       y = ((double) inr / nmax) * maxy;
703       if (y >= maxy)
704 	y = maxy;
705       lhist[x].in = y;
706 
707       y = ((double) outr / nmax) * maxy;
708       if (y >= maxy)
709 	y = maxy;
710       lhist[x].out = y;
711 
712       y = ((double) inmax / nmax) * maxy;
713       if (y >= maxy)
714 	y = maxy;
715       lhist[x].inmax = y;
716 
717       y = ((double) outmax / nmax) * maxy;
718       if (y >= maxy)
719 	y = maxy;
720       lhist[x].outmax = y;
721 
722       if (options & OPTION_DORELPERCENT)
723 	{
724 	  if (outr != (long long) 0)
725 	    {
726 	      percent = (double) inr / (double) outr;
727 	    }
728 	  else
729 	    {
730 	      percent = (double) 0.;
731 	    }
732 	  if (percent > (double) 1)
733 	    percent = (double) 1.;
734 	  percent *= maxy;
735 	  lhist[x].percent = (long long) percent;
736 	}
737       else
738 	{
739 	  lhist[x].percent = (long long) 0;
740 	}
741 
742       now -= step;
743     }
744   origmaxvi = (origmaxvi * muli / nmax ) * maxy;
745   origmaxvo = (origmaxvo * muli / nmax ) * maxy;
746 
747   /* Log and second-mean scaling added by Benjamin Despres, 2004-10-13 */
748   if (options & OPTION_MEANOVER)
749     {
750       lmx1 = lmx2 = mea1 = mea2 = temp = 0.0;
751       for (x = 0; x < maxx; x++)
752 	{
753 	  if (lhist[x].in < 1.0)
754 	    lhist[x].in = 1.0;
755 	  if (lhist[x].out < 1.0)
756 	    lhist[x].out = 1.0;
757 	  if (lhist[x].inmax < 1.0)
758 	    lhist[x].inmax = 1.0;
759 	  if (lhist[x].outmax < 1.0)
760 	    lhist[x].outmax = 1.0;
761 	  if (lhist[x].in > lmx1)
762 	    lmx1 = lhist[x].in;
763 	  if (lhist[x].out > lmx1)
764 	    lmx1 = lhist[x].out;
765 	  if (lhist[x].inmax > lmx1)
766 	    lmx1 = lhist[x].inmax;
767 	  if (lhist[x].outmax > lmx1)
768 	    lmx1 = lhist[x].outmax;
769 	  mea1 +=
770 	    (lhist[x].in + lhist[x].out + lhist[x].inmax +
771 	     lhist[x].outmax) / 4.0;
772 	}
773       if (origmaxvi < 1.0)
774 	origmaxvi = 1.0;
775       if (origmaxvo < 1.0)
776 	origmaxvo = 1.0;
777 
778       mea1 /= (double) maxx;
779 	  for (x = 0; x < maxx; x++)
780 	    {
781 	      y =
782 		(lhist[x].in + lhist[x].out + lhist[x].inmax +
783 		 lhist[x].outmax) / 4.0;
784 	      if (y > mea1)
785 		{
786 		  mea2 += y;
787 		  temp += 1.0;
788 		}
789 	    }
790 	  mea2 /= temp;
791 	  for (x = 0; x < maxx; x++)
792 	    {
793 	      if (lhist[x].in > mea2)
794 		lhist[x].in = mea2;
795 	      if (lhist[x].out > mea2)
796 		lhist[x].out = mea2;
797 	      if (lhist[x].inmax > mea2)
798 		lhist[x].inmax = mea2;
799 	      if (lhist[x].outmax > mea2)
800 		lhist[x].outmax = mea2;
801 	    }
802       for (x = 0; x < maxx; x++)
803 	{
804 	  if (lhist[x].in > lmx2)
805 	    lmx2 = lhist[x].in;
806 	  if (lhist[x].out > lmx2)
807 	    lmx2 = lhist[x].out;
808 	  if (lhist[x].inmax > lmx2)
809 	    lmx2 = lhist[x].inmax;
810 	  if (lhist[x].outmax > lmx2)
811 	    lmx2 = lhist[x].outmax;
812 	}
813       if (lmx2 > 0.0)
814 	lmx2 = (lmx1 / lmx2);
815       for (x = 0; x < maxx; x++)
816 	{
817 	  lhist[x].in *= lmx2;
818 	  lhist[x].out *= lmx2;
819 	  lhist[x].inmax *= lmx2;
820 	  lhist[x].outmax *= lmx2;
821 	}
822       origmaxvi *= lmx2;
823       origmaxvo *= lmx2;
824 
825       sca_max_q *= ((float) mea2 / (float) maxy);
826     }
827   else if (options & OPTION_LOGGRAPH)
828     {
829       for (x = 0; x < maxx; x++)
830         {
831           lhist[x].in = logscale (lhist[x].in, maxy);
832           lhist[x].out = logscale (lhist[x].out, maxy);
833           lhist[x].inmax = logscale (lhist[x].inmax, maxy);
834           lhist[x].outmax = logscale (lhist[x].outmax, maxy);
835         }
836       origmaxvi = logscale (origmaxvi, maxy);
837       origmaxvo = logscale (origmaxvo, maxy);
838     }	/* end of primary log and second-mean scaling code (more below) */
839   else if (options & OPTION_EXPGRAPH)
840     {
841       for (x = 0; x < maxx; x++)
842         {
843           lhist[x].in = expscale (lhist[x].in, maxy);
844           lhist[x].out = expscale (lhist[x].out, maxy);
845           lhist[x].inmax = expscale (lhist[x].inmax, maxy);
846           lhist[x].outmax = expscale (lhist[x].outmax, maxy);
847         }
848       origmaxvi = expscale (origmaxvi, maxy);
849       origmaxvo = expscale (origmaxvo, maxy);
850     }
851 
852   /* the graph is made ten pixels higher to acomodate the x labels */
853   graph = gdImageCreate (XSIZE, YSIZE);
854   brush_out = gdImageCreate (1, 2);
855   brush_outm = gdImageCreate (1, 2);
856   brush_outp = gdImageCreate (1, 2);
857 
858   /* the first color allocated will be the background color. */
859   i_blank = gdImageColorAllocate (graph, c_blank);
860   i_light = gdImageColorAllocate (graph, c_light);
861   i_dark = gdImageColorAllocate (graph, c_dark);
862 
863   if (options & OPTION_TRANSPARENT)
864     {
865       gdImageColorTransparent (graph, i_blank);
866     }
867   gdImageInterlace (graph, 1);
868 
869   /* do NOT delete the out variables. they are dummies, but the actual color
870      allocation for the brush is essential */
871   i_major = gdImageColorAllocate (graph, c_major);
872   i_in = gdImageColorAllocate (graph, col_in[0], col_in[1], col_in[2]);
873   i_out =
874     gdImageColorAllocate (brush_out, col_out[0], col_out[1], col_out[2]);
875   i_grid = gdImageColorAllocate (graph, c_grid);
876   i_inm = gdImageColorAllocate (graph, col_inm[0], col_inm[1], col_inm[2]);
877   i_outm =
878     gdImageColorAllocate (brush_outm, col_outm[0], col_outm[1], col_outm[2]);
879   i_outp =
880     gdImageColorAllocate (brush_outp, col_outp[0], col_outp[1], col_outp[2]);
881   i_outpg =
882     gdImageColorAllocate (graph, col_outp[0], col_outp[1], col_outp[2]);
883 
884   /* draw the image border */
885   if (!(options & OPTION_NOBORDER))
886     {
887       gdImageLine (graph, 0, 0, XSIZE - 1, 0, i_light);
888       gdImageLine (graph, 1, 1, XSIZE - 2, 1, i_light);
889       gdImageLine (graph, 0, 0, 0, YSIZE - 1, i_light);
890       gdImageLine (graph, 1, 1, 1, YSIZE - 2, i_light);
891       gdImageLine (graph, XSIZE - 1, 0, XSIZE - 1, YSIZE - 1, i_dark);
892       gdImageLine (graph, 0, YSIZE - 1, XSIZE - 1, YSIZE - 1, i_dark);
893       gdImageLine (graph, XSIZE - 2, 1, XSIZE - 2, YSIZE - 2, i_dark);
894       gdImageLine (graph, 1, YSIZE - 2, XSIZE - 2, YSIZE - 2, i_dark);
895     }
896 
897   /* draw the incoming traffic */
898   if (!(options & OPTION_NO_I))
899     {
900       for (x = 0; x < maxx; x++)
901 	{
902 	  /* peak is always above average, we therefore only draw the upper part */
903 	  if (peak)
904 	    gdImageLine (graph, xtr (x), ytr (lhist[x].in),
905 			 xtr (x), ytr (lhist[x].inmax), i_inm);
906 #ifdef INCOMING_UNFILLED
907 	  gdImageLine (graph, xtr (x), ytr (lhist[x].in), xtr (x),
908 		       ytr (lhist[x + 1].in), i_in);
909 #else
910 	  gdImageLine (graph, xtr (x), ytr (0), xtr (x), ytr (lhist[x].in),
911 		       i_in);
912 	  /* draw the line a second time offset slightly. makes the graph
913 	   * look better if xscale is fractional */
914 	  gdImageLine (graph, xtr (x + 0.5), ytr (0), xtr (x + 0.5),
915 		       ytr (lhist[x].in), i_in);
916 #endif
917 	}
918     }
919 
920   /* draw the percentage */
921   if (options & OPTION_DORELPERCENT)
922     {
923       gdImageSetBrush (graph, brush_outp);
924       for (x = 0; x < maxx - 1; x++)
925 	gdImageLine (graph, xtr (x), ytr (lhist[x].percent),
926 		     xtr (x + 1), ytr (lhist[x + 1].percent), gdBrushed);
927     }
928 
929   /* draw the outgoing traffic */
930   if (!(options & OPTION_NO_O))
931     {
932       gdImageSetBrush (graph, brush_outm);
933       if (peak)
934 	for (x = 0; x < maxx - 1; x++)
935 	  gdImageLine (graph, xtr (x), ytr (lhist[x].outmax),
936 		       xtr (x + 1), ytr (lhist[x + 1].outmax), gdBrushed);
937       gdImageSetBrush (graph, brush_out);
938       for (x = 0; x < maxx - 1; x++)
939 	gdImageLine (graph, xtr (x), ytr (lhist[x].out),
940 		     xtr (x + 1), ytr (lhist[x + 1].out), gdBrushed);
941     }
942 
943   /* print the graph title */
944   if (pngtitle != NULL)
945     {
946       gdImageString (graph, gdFontSmall, 81, 1,
947 		     (unsigned char *) pngtitle, i_grid);
948     }
949   else
950     {
951       if (options & OPTION_PRINTROUTER)
952 	{
953 	  gdImageString (graph, gdFontSmall, 81, 1,
954 			 (unsigned char *) router, i_grid);
955 	}
956     }
957 
958   /* draw the graph border */
959   gdImageRectangle (graph, xtr (0), ytr (0), xtr (maxx), ytr (maxy), i_grid);
960 
961   /*create a dotted style for the grid lines */
962   styleDotted[0] = i_grid;
963   styleDotted[1] = gdTransparent;
964   styleDotted[2] = gdTransparent;
965   gdImageSetStyle (graph, styleDotted, 3);
966 
967   /* draw the horizontal grid */
968   if ((longup == NULL) || (shortup == NULL))
969     {
970       if (!bits)
971 	{
972 	  longup = "Bytes per Second";
973 	  shortup = "Bytes/s";
974 	}
975       else
976 	{
977 	  longup = "Bits per Second";
978 	  shortup = "Bits/s";
979 	}
980     }
981   if (maxy < gdFontSmall->w * 16)
982     {
983       gdImageStringUp (graph, gdFontSmall, 8,
984 		       ytr ((maxy - gdFontSmall->w * strlen (shortup)) / 2),
985 		       (unsigned char *) shortup, i_grid);
986     }
987   else
988     {
989       gdImageStringUp (graph, gdFontSmall, 8,
990 		       ytr ((maxy - gdFontSmall->w * strlen (longup)) / 2),
991 		       (unsigned char *) longup, i_grid);
992     }
993 
994       for (i = 0; i <= ytics; i++)
995 	{
996 
997 	  gdImageLine (graph, xtr (-2), ytr (i * maxy / ytics),
998 		       xtr (1), ytr (i * maxy / ytics), i_grid);
999 
1000 	  gdImageLine (graph, xtr (maxx + 2), ytr (i * maxy / ytics),
1001 		       xtr (maxx - 1), ytr (i * maxy / ytics), i_grid);
1002 
1003 	  gdImageLine (graph, xtr (0), ytr (i * maxy / ytics),
1004 		       xtr (maxx), ytr (i * maxy / ytics), gdStyled);
1005 
1006 /*
1007     	sprintf(ylab,"%6.1f %s",sca_max_q*i,short_si[digits/3]);
1008 */
1009 /*    	sprintf(ylab,"%6.1f %s",sca_max_q*i*yticsf,short_si_out);  */
1010 	  temp = sca_max_q * i;
1011 	  if (options & OPTION_LOGGRAPH)
1012 	     temp = expscale(maxy * i / ytics, maxy) * ytics * sca_max_q / maxy;
1013 	  else if (options & OPTION_EXPGRAPH)
1014 	     temp = logscale(maxy * i / ytics, maxy) * ytics * sca_max_q / maxy;
1015 	  else
1016 	     temp = sca_max_q * i;
1017 	  sprintf (ylab, "%6.1f %s", temp, short_si_out);
1018 
1019 /*        sprintf(ylab,"%6.1f %s",sca_max_q*i,short_si_out); */
1020 	  gdImageString (graph, gdFontSmall, 23,
1021 			 ytr (i * maxy / ytics + gdFontSmall->h / 2),
1022 			 (unsigned char *) ylab, i_grid);
1023 
1024 	  if (options & OPTION_DORELPERCENT)
1025 	    {
1026 	      /* sprintf(ylab,"% 6.1f%%",(float)25.*i); */
1027 	      sprintf (ylab, "% 6.1f%%", (float) (temp / (sca_max_q * ytics) * 100));
1028 	      gdImageString (graph, gdFontSmall, 77 + ((maxx) * xscale) + 1,
1029 			     ytr (i * maxy / ytics + gdFontSmall->h / 2),
1030 			     (unsigned char *) ylab, i_outpg);
1031 	    }
1032 	}
1033 
1034   /* draw vertical grid and horizontal labels */
1035   for (x = 0; x < maxx; x++)
1036     {
1037       if (lhist[x].time)
1038 	{
1039 	  gdImageLine (graph, xtr (x), ytr (-2), xtr (x), ytr (1), i_grid);
1040 	  gdImageLine (graph, xtr (x), ytr (0), xtr (x), ytr (maxy),
1041 		       gdStyled);
1042 	}
1043       if (graph_label[x] != NULL)
1044 	{
1045 	  gdImageString (graph, gdFontSmall,
1046 			 (xtr (x) - (strlen (graph_label[x]) *
1047 				     gdFontSmall->w / 2)),
1048 			 ytr (-4), (unsigned char *) graph_label[x], i_grid);
1049 	  free (graph_label[x]);
1050 	}
1051       if (lhist[x].time & 2)
1052 	gdImageLine (graph, xtr (x), ytr (0), xtr (x), ytr (maxy), i_major);
1053     }
1054 
1055   ytrmax = ytr (origmaxvi);
1056 
1057   /* draw line at peak In value in i_major color */
1058   /* only draw the line if it's within the graph ... */
1059   if (ytr (maxy) < ytrmax)
1060     {
1061       styleDotted[0] = i_major;
1062       gdImageSetStyle (graph, styleDotted, 3);
1063       gdImageLine (graph, xtr (0), ytrmax, xtr (maxx), ytrmax, gdStyled);
1064     }
1065 
1066   /* draw line at peak Out value in i_major color */
1067   /* only draw the line if it's within the graph ... */
1068   ytrmax = ytr (origmaxvo);
1069 
1070   if (ytr (maxy) < ytrmax)
1071     {
1072       styleDotted[0] = i_major;
1073       gdImageSetStyle (graph, styleDotted, 3);
1074       gdImageLine (graph, xtr (0), ytrmax, xtr (maxx), ytrmax, gdStyled);
1075     }
1076 
1077   /* draw a red arrow a 0,0 */
1078   if (!(options & OPTION_NOARROW))
1079     {
1080       gdImageLine (graph, xtr (2), ytr (3), xtr (2), ytr (-3), i_major);
1081       gdImageLine (graph, xtr (1), ytr (3), xtr (1), ytr (-3), i_major);
1082       gdImageLine (graph, xtr (0), ytr (2), xtr (0), ytr (-2), i_major);
1083       gdImageLine (graph, xtr (-1), ytr (1), xtr (-1), ytr (-1), i_major);
1084       gdImageLine (graph, xtr (-2), ytr (1), xtr (-2), ytr (-1), i_major);
1085       gdImageLine (graph, xtr (-3), ytr (0), xtr (-3), ytr (0), i_major);
1086     }
1087 
1088   if (currdatetimepos > NO_TIMESTAMPSTR)
1089     {
1090       currdatetime = time (NULL);
1091       strftime (currdatetimestr, 250, currdatetimeformat,
1092 		localtime (&currdatetime));
1093       switch (currdatetimepos)
1094 	{
1095 	case LL_CORNER:
1096 	  currdatetimepos_x = 3;
1097 	  currdatetimepos_y = YSIZE - gdFontSmall->h - 3;
1098 	  break;
1099 	case RL_CORNER:
1100 	  currdatetimepos_x =
1101 	    XSIZE - strlen (currdatetimestr) * gdFontSmall->w - 3;
1102 	  currdatetimepos_y = YSIZE - gdFontSmall->h - 3;
1103 	  break;
1104 	case LU_CORNER:
1105 	  currdatetimepos_x = 3;
1106 	  currdatetimepos_y = 1;
1107 	  break;
1108 	case RU_CORNER:
1109 	default:
1110 	  currdatetimepos_x =
1111 	    XSIZE - strlen (currdatetimestr) * gdFontSmall->w - 3;
1112 	  currdatetimepos_y = 1;
1113 	};
1114       gdImageString (graph, gdFontSmall,
1115 		     currdatetimepos_x, currdatetimepos_y,
1116 		     (unsigned char *)currdatetimestr, i_grid);
1117     }
1118 
1119   snprintf(file_tmp,1000,"%s.tmp_%lu",file,(unsigned long)getpid());
1120 
1121   if ((fo = fopen (file_tmp, "wb")) == NULL)
1122     {
1123       perror (program);
1124       fprintf (stderr, "%s, Rateup Error: Can't open %s for write\n", bufftime, file_tmp);
1125       exit (1);
1126     }
1127   GFORM_GD (graph, fo);
1128   fclose (fo);
1129   gdImageDestroy (graph);
1130   gdImageDestroy (brush_out);
1131   gdImageDestroy (brush_outm);
1132   gdImageDestroy (brush_outp);
1133   free (lhist);
1134   free (graph_label);
1135   if (kMG) {
1136     free(short_si);
1137     short_si = short_si_def;
1138   }
1139 
1140 #ifdef WIN32
1141   /* got to remove the target under win32
1142      or rename will not work ... */
1143   unlink(file);
1144 #endif
1145   if (rename(file_tmp,file)){
1146       perror (program);
1147       fprintf (stderr, "%s, Rateup Error: Can't rename %s to %s\n", bufftime,file_tmp,file);
1148       exit (1);
1149   }
1150 
1151 
1152 }
1153 
1154 
1155 static double
diff(a,b)1156 diff (a, b)
1157      char *a, *b;
1158 {
1159   char res[MAXL], *a1, *b1, *r1;
1160   int c, x, m;
1161   if (*a == '-' && *b == '-')
1162     {
1163       b1 = b + 1;
1164       b = a + 1;
1165       a = b1;
1166     }
1167 
1168   while (!isdigit ((int) *a))
1169     a++;
1170   while (!isdigit ((int) *b))
1171     b++;
1172   a1 = &a[strlen (a) - 1];
1173   m = max (strlen (a), strlen (b));
1174   r1 = &res[m + 1];
1175   for (b1 = res; b1 <= r1; b1++)
1176     *b1 = ' ';
1177   b1 = &b[strlen (b) - 1];
1178   r1[1] = 0;			/* Null terminate result */
1179   c = 0;
1180   for (x = 0; x < m; x++)
1181     {
1182       /* we want to avoid reading off the edge of the string */
1183       char save_a, save_b;
1184       save_a = (a1 >= a) ? *a1 : '0';
1185       save_b = (b1 >= b) ? *b1 : '0';
1186       *r1 = save_a - save_b - c + '0';
1187       if (*r1 < '0')
1188 	{
1189 	  *r1 += 10;
1190 	  c = 1;
1191 	}
1192       else if (*r1 > '9')
1193 	{			/* 0 - 10 */
1194 	  *r1 -= 10;
1195 	  c = 1;
1196 	}
1197       else
1198 	{
1199 	  c = 0;
1200 	}
1201       a1--;
1202       b1--;
1203       r1--;
1204     }
1205   if (c)
1206     {
1207       r1 = &res[m + 1];
1208       for (x = 0; isdigit ((int) *r1) && x < m; x++, r1--)
1209 	{
1210 	  *r1 = ('9' - *r1 + c) + '0';
1211 	  if (*r1 > '9')
1212 	    {
1213 	      *r1 -= 10;
1214 	      c = 1;
1215 	    }
1216 	  else
1217 	    {
1218 	      c = 0;
1219 	    }
1220 	}
1221       return (-atof (res));
1222     }
1223   else
1224     return (atof (res));
1225 }
1226 
1227 static int
readhist(file)1228 readhist (file)
1229      char *file;
1230 {
1231   FILE *fi;
1232   int x, retcode = 0;
1233   char buf[256], tempform[50];
1234   struct HISTORY *hist;
1235   long long rd[5];
1236   time_t cur;
1237   long lasttime;
1238 
1239   sprintf (tempform, "%%ld %%%ds %%%ds\n", MAXL - 1, MAXL - 1);
1240   if ((fi = fopen (file, "r")) != NULL)
1241     {
1242       if (fscanf (fi, tempform, &lasttime, &last.in[0], &last.out[0]) != 3)
1243 	{
1244 	  fprintf (stderr, "%s, Read Error: File %s lin 1\n", bufftime, file);
1245 	  retcode = 1;
1246 	}
1247       last.time = lasttime;
1248       cur = last.time;
1249       x = histvalid = 0;
1250       hist = history;
1251       while (!feof (fi))
1252 	{
1253 	  fgets (buf, 256, fi);
1254 	  if (sscanf (buf, "" LLD " " LLD " " LLD " " LLD " " LLD "",
1255 		      &rd[0], &rd[1], &rd[2], &rd[3], &rd[4]) < 5)
1256 	    {
1257 	      rd[3] = rd[1];
1258 	      rd[4] = rd[2];
1259 	    }
1260 
1261 /* we are long long now, so don't cut bit 8
1262 	    for (i=0;i<=4;i++){
1263 	      if (rd[i] & 0x80000000)
1264 		rd[i] = 0;
1265 	    }
1266 */
1267 
1268 	  hist->time = rd[0];
1269 	  hist->in = rd[1];
1270 	  hist->out = rd[2];
1271 	  hist->inmax = rd[3];
1272 	  hist->outmax = rd[4];
1273 	  if (hist->inmax < hist->in)
1274 	    hist->inmax = hist->in;
1275 	  if (hist->outmax < hist->out)
1276 	    hist->outmax = hist->out;
1277 	  if (hist->time > cur)
1278 	    {
1279 	      fprintf (stderr,
1280 		       "%s, Rateup ERROR: %s found %s's log file was corrupt\n          or not in sorted order:\ntime: %lu.",
1281 		       bufftime, program, router, (unsigned long) hist->time);
1282 	      retcode = 2;
1283 	      break;
1284 	    }
1285 	  cur = hist->time;
1286 	  if (++x >= Mh)
1287 	    {
1288 	      struct HISTORY *lh;
1289 
1290 	      Mh += MAX_HISTORY;
1291 	      lh = realloc (history, (Mh + 1) * sizeof (struct HISTORY));
1292 	      if (lh == NULL)
1293 		{
1294 		  fprintf (stderr,
1295 			   "%s, Rateup WARNING: (pay attention to this)\nWARNING: %s found %s's log file had too many entries, data discarded\n",
1296 			   bufftime, program, router);
1297 		  break;
1298 		}
1299 	      hist = lh + (hist - history);
1300 	      history = lh;
1301 	    }
1302 	  hist++;
1303 	}
1304       histvalid = x;
1305       fclose (fi);
1306     }
1307   else
1308     {
1309       retcode = 1;
1310     }
1311   return (retcode);
1312 }
1313 
1314 static void
readfile()1315 readfile ()
1316 {
1317   char buf[128];
1318   int err, x;
1319   time_t now;
1320   struct HISTORY *hist;
1321 
1322   sprintf (buf, "%s.log", router);
1323   if ((err = readhist (buf)) != 0)
1324     {				/* Read of log file failed.  Try backup */
1325       fprintf (stderr,
1326 	       "%s, Rateup WARNING: %s could not read the primary log file for %s\n",
1327 	       bufftime, program, router);
1328       sprintf (buf, "%s.old", router);
1329       if ((err = readhist (buf)) != 0)
1330 	{			/* Backup failed too. New file? */
1331 	  fprintf (stderr,
1332 		   "%s, Rateup WARNING: %s The backup log file for %s was invalid as well\n",
1333 		   bufftime, program, router);
1334 	  if (err == 2)
1335 	    exit (1);
1336 
1337 	  /* File does not exist - it must be created */
1338 	  now = NOW - DAY_SAMPLE;
1339 	  hist = history;
1340 	  histvalid = DAY_COUNT + WEEK_COUNT + MONTH_COUNT + YEAR_COUNT - 1;
1341 	  last.time = now;
1342 	  /* calculating a diff does not make sense */
1343 	  last.in[0] = 'x';
1344 	  now /= DAY_SAMPLE;
1345 	  now *= DAY_SAMPLE;
1346 	  for (x = 0; x < DAY_COUNT; x++, hist++)
1347 	    {
1348 	      hist->time = now;
1349 	      hist->in = hist->inmax = hist->out = hist->outmax = 0;
1350 	      now -= DAY_SAMPLE;
1351 	    }
1352 	  now /= WEEK_SAMPLE;
1353 	  now *= WEEK_SAMPLE;
1354 	  for (x = 0; x < WEEK_COUNT; x++, hist++)
1355 	    {
1356 	      hist->time = now;
1357 	      hist->in = hist->inmax = hist->out = hist->outmax = 0;
1358 	      now -= WEEK_SAMPLE;
1359 	    }
1360 	  now /= MONTH_SAMPLE;
1361 	  now *= MONTH_SAMPLE;
1362 	  for (x = 0; x < MONTH_COUNT; x++, hist++)
1363 	    {
1364 	      hist->time = now;
1365 	      hist->in = hist->inmax = hist->out = hist->outmax = 0;
1366 	      now -= MONTH_SAMPLE;
1367 	    }
1368 	  now /= YEAR_SAMPLE;
1369 	  now *= YEAR_SAMPLE;
1370 	  for (x = 0; x < YEAR_COUNT; x++, hist++)
1371 	    {
1372 	      hist->time = now;
1373 	      hist->in = hist->inmax = hist->out = hist->outmax = 0;
1374 	      now -= YEAR_SAMPLE;
1375 	    }
1376 	}
1377     }
1378 }
1379 
1380 static void
update(in,out,abs_max,absupdate)1381 update (in, out, abs_max, absupdate)
1382      char *in, *out;
1383      long long abs_max;
1384      int absupdate;
1385 {
1386   FILE *fo;
1387   char buf[128], buf1[128], buf2[128];
1388   time_t now, nextnow, plannow;
1389   long long inrate, outrate;
1390   long long avc, inmax, outmax;
1391   double period, interval;
1392   int x, n, nout;
1393   struct HISTORY *lhist, *hist;
1394   double inr, outr;
1395 
1396   now = NOW;
1397 
1398   if (now < last.time)
1399     {
1400       fprintf (stderr,
1401 	       "%s, Rateup ERROR: %s found that %s's log file time of %lu was greater than now (%lu)\nERROR: Let's not do the time warp, again. Logfile unchanged.\n",
1402 	       bufftime, program, router, (unsigned long) last.time,
1403 	       (unsigned long) now);
1404       return;
1405     }
1406   sprintf (buf, "%s.tmp_%lu", router,(unsigned long)getpid());
1407   sprintf (buf1, "%s.log", router);
1408   sprintf (buf2, "%s.old", router);
1409   if ((lhist = calloc (1, sizeof (struct HISTORY) * (MAX_HISTORY + 1))) ==
1410       NULL)
1411     {
1412       fprintf (stderr, "%s, Rateup ERROR: Out of memory in update\n", bufftime);
1413       exit (1);
1414     }
1415   hist = lhist;
1416 
1417   period = (now - last.time);
1418   if (period <= 0 || period > (60 * 60) || last.in[0] == 'x')
1419     {				/* if last update is strange */
1420       if (options & OPTION_UNKNASZERO)
1421 	{
1422 	  inrate = 0;		/* sync unknown to zero */
1423 	  outrate = 0;
1424 	}
1425       else
1426 	{
1427 	  inrate = history[0].in;	/* Sync by using last value */
1428 	  outrate = history[0].out;
1429 	}
1430     }
1431   else
1432     {
1433       /* gauge and absolute */
1434       if (strcmp (in, "-1") == 0 ||	/* if current count missing */
1435 	  strcmp (last.in, "-1") == 0)
1436 	{			/* or previous count missing */
1437 	  if (options & OPTION_UNKNASZERO)
1438 	    {			/* then use 0 or last value */
1439 	      inrate = 0;
1440 	    }
1441 	  else
1442 	    {
1443 	      inrate = history[0].in;
1444 	    }
1445 	}
1446       else
1447 	{
1448 	  if ((absupdate != 0) && (absupdate != 3) && (absupdate != 4))
1449 	    {
1450 	      inr = diff (in, "0");
1451 	    }
1452 	  else
1453 	    {
1454 	      inr = diff (in, last.in);
1455 	      if (inr < 0) {
1456                 if (inr > - (long long) 1 << 32) { 	/* wrapped 32-bit counter? */
1457   		    inr += (long long) 1 << 32;
1458                 }
1459                 else {
1460   		    inr = 0;
1461                 }
1462               }
1463 	    }
1464 	  if (absupdate == 2)
1465 	    {
1466 	      inrate = inr + .5;
1467 	    }
1468 	  else if (absupdate == 3)
1469 	    {
1470 	      inrate = inr * (3600.0 / (period * 1.0)) + .5;
1471 	    }
1472 	  else if (absupdate == 4)
1473 	    {
1474 	      inrate = inr * (60.0 / (period * 1.0)) + .5;
1475 	    }
1476 	  else
1477 	    {
1478 	      inrate = inr / period + .5;
1479 	    }
1480 	}
1481       if (strcmp (out, "-1") == 0 ||	/* if current count missing */
1482 	  strcmp (last.out, "-1") == 0)
1483 	{			/* or previous count missing */
1484 	  if (options & OPTION_UNKNASZERO)
1485 	    {			/* then use 0 or last value */
1486 	      outrate = 0;
1487 	    }
1488 	  else
1489 	    {
1490 	      outrate = history[0].out;
1491 	    }
1492 	}
1493       else
1494 	{
1495 	  if ((absupdate != 0) && (absupdate != 3) && (absupdate != 4))
1496 	    {
1497 	      outr = diff (out, "0");
1498 	    }
1499 	  else
1500 	    {
1501 	      outr = diff (out, last.out);
1502 	      if (outr < 0) {	/* wrapped  counter? */
1503                 if (outr > - (long long) 1 << 32) {
1504   		    outr += (long long) 1 << 32;
1505                 }
1506                 else {
1507   		    outr = 0; /* 64bit counters do not wrap usually */
1508                 }
1509               }
1510 	    }
1511 	  if (absupdate == 2)
1512 	    {
1513 	      outrate = outr + .5;
1514 	    }
1515 	  else if (absupdate == 3)
1516 	    {
1517 	      outrate = outr * (3600.0 / (period * 1.0)) + .5;
1518 	    }
1519 	  else if (absupdate == 4)
1520 	    {
1521 	      outrate = outr * (60.0 / (period * 1.0)) + .5;
1522 	    }
1523 	  else
1524 	    {
1525 	      outrate = outr / period + .5;
1526 	    }
1527 	}
1528     }
1529 
1530 
1531 
1532   if (inrate < 0 ||  inrate > abs_max)
1533     {
1534       if (options & OPTION_UNKNASZERO)
1535 	{
1536 	  inrate = 0;		/* sync unknown to zero */
1537 	}
1538       else
1539 	{
1540 	  inrate = history[0].in;	/* Sync by using last value */
1541 	}
1542     }
1543   if (outrate < 0 || outrate > abs_max)
1544     {
1545       if (options & OPTION_UNKNASZERO)
1546 	{
1547 	  outrate = 0;		/* sync unknown to zero */
1548 	}
1549       else
1550 	{
1551 	  outrate = history[0].out;	/* Sync by using last value */
1552 	}
1553     }
1554   if ((fo = fopen (buf, "w")) != NULL)
1555     {
1556       fprintf (fo, "%lu %s %s\n", (unsigned long) now, in, out);
1557       last.time = now;
1558       /* what is this good for? */
1559       /* gauge und absolute */
1560       if ((absupdate != 0) && (absupdate != 3) && (absupdate != 4))
1561 	{
1562 	  strcpy (last.in, "0");
1563 	  strcpy (last.out, "0");
1564 	}
1565       else
1566 	{
1567 	  strncpy (last.in, in, MAXL);
1568 	  last.in[MAXL-1]='\0';
1569 	  strncpy (last.out, out,MAXL);
1570 	  last.out[MAXL-1]='\0';
1571 	}
1572       fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1573 	       (unsigned long) now, inrate, outrate, inrate, outrate);
1574       nout = 1;
1575       hist->time = now;
1576       hist->in = inrate;
1577       hist->out = outrate;
1578       hist->inmax = inrate;
1579       hist->outmax = outrate;
1580       hist++;
1581 
1582       /* just in case we were dead for a long time, don't try to gather
1583          data from non existing log entries  */
1584       now = plannow = history[0].time;
1585 
1586       plannow /= DAY_SAMPLE;
1587       plannow *= DAY_SAMPLE;
1588       n = 0;
1589 
1590       /* gobble up every shred of data we can get ... */
1591       if (plannow < now)
1592 	{
1593 	  NEXT ((unsigned long) (now - plannow));
1594 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1595 		   (unsigned long) now, (long long) inr, (long long) outr,
1596 		   (long long) inmax, (long long) outmax);
1597 	  hist->time = now;
1598 	  hist->in = inr;
1599 	  hist->out = outr;
1600 	  hist->inmax = inmax;
1601 	  hist->outmax = outmax;
1602 	  nout++;
1603 	  hist++;
1604 	  now = plannow;
1605 
1606 	}
1607 
1608       for (x = 1; x < DAY_COUNT; x++)
1609 	{
1610 	  NEXT (DAY_SAMPLE);
1611 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1612 		   (unsigned long) now, (long long) inr, (long long) outr,
1613 		   (long long) inmax, (long long) outmax);
1614 	  hist->time = now;
1615 	  hist->in = inr;
1616 	  hist->out = outr;
1617 	  hist->inmax = inmax;
1618 	  hist->outmax = outmax;
1619 	  nout++;
1620 	  hist++;
1621 	  now -= DAY_SAMPLE;
1622 	}
1623 
1624       plannow = now;
1625       plannow /= WEEK_SAMPLE;
1626       plannow *= WEEK_SAMPLE;
1627 
1628       if (plannow < now)
1629 	{
1630 	  NEXT ((unsigned long) (now - plannow));
1631 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1632 		   (unsigned long) now, (long long) inr, (long long) outr,
1633 		   inmax, outmax);
1634 	  hist->time = now;
1635 	  hist->in = inr;
1636 	  hist->out = outr;
1637 	  hist->inmax = inmax;
1638 	  hist->outmax = outmax;
1639 	  nout++;
1640 	  hist++;
1641 	  now = plannow;
1642 
1643 	}
1644 
1645       for (x = 0; x < WEEK_COUNT; x++)
1646 	{
1647 	  NEXT (WEEK_SAMPLE);
1648 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1649 		   (unsigned long) now, (long long) inr, (long long) outr,
1650 		   inmax, outmax);
1651 	  hist->time = now;
1652 	  hist->in = inr;
1653 	  hist->out = outr;
1654 	  hist->inmax = inmax;
1655 	  hist->outmax = outmax;
1656 	  nout++;
1657 	  hist++;
1658 	  now -= WEEK_SAMPLE;
1659 	}
1660 
1661       plannow = now;
1662       plannow /= MONTH_SAMPLE;
1663       plannow *= MONTH_SAMPLE;
1664 
1665       if (plannow < now)
1666 	{
1667 	  NEXT ((unsigned long) (now - plannow));
1668 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1669 		   (unsigned long) now, (long long) inr, (long long) outr,
1670 		   inmax, outmax);
1671 	  hist->time = now;
1672 	  hist->in = inr;
1673 	  hist->out = outr;
1674 	  hist->inmax = inmax;
1675 	  hist->outmax = outmax;
1676 	  nout++;
1677 	  hist++;
1678 	  now = plannow;
1679 
1680 	}
1681 
1682       for (x = 0; x < MONTH_COUNT; x++)
1683 	{
1684 	  NEXT (MONTH_SAMPLE);
1685 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1686 		   (unsigned long) now, (long long) inr, (long long) outr,
1687 		   inmax, outmax);
1688 	  hist->time = now;
1689 	  hist->in = inr;
1690 	  hist->out = outr;
1691 	  hist->inmax = inmax;
1692 	  hist->outmax = outmax;
1693 	  nout++;
1694 	  hist++;
1695 	  now -= MONTH_SAMPLE;
1696 	}
1697 
1698       plannow = now;
1699       plannow /= YEAR_SAMPLE;
1700       plannow *= YEAR_SAMPLE;
1701 
1702       if (plannow < now)
1703 	{
1704 	  NEXT ((unsigned long) (now - plannow));
1705 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1706 		   (unsigned long) now, (long long) inr, (long long) outr,
1707 		   inmax, outmax);
1708 	  hist->time = now;
1709 	  hist->in = inr;
1710 	  hist->out = outr;
1711 	  hist->inmax = inmax;
1712 	  hist->outmax = outmax;
1713 	  nout++;
1714 	  hist++;
1715 	  now = plannow;
1716 
1717 	}
1718 
1719       for (x = 0; x < YEAR_COUNT; x++)
1720 	{
1721 	  NEXT (YEAR_SAMPLE);
1722 	  fprintf (fo, "%lu " LLD " " LLD " " LLD " " LLD "\n",
1723 		   (unsigned long) now, (long long) inr, (long long) outr,
1724 		   inmax, outmax);
1725 	  hist->time = now;
1726 	  hist->in = inr;
1727 	  hist->out = outr;
1728 	  hist->inmax = inmax;
1729 	  hist->outmax = outmax;
1730 	  nout++;
1731 	  hist++;
1732 	  now -= YEAR_SAMPLE;
1733 	}
1734 
1735       if (ferror (fo) || fclose (fo))
1736 	{
1737 	  perror (program);
1738 	  fprintf (stderr, "%s, Rateup ERROR: Can't write new log file\n", bufftime);
1739 	  exit (1);
1740 	}
1741 #ifdef WIN32
1742       /* another fix to get things working under NT */
1743       if (unlink (buf2))
1744 	{
1745 	  fprintf (stderr,
1746 		   "%s, Rateup WARNING: %s Can't remove %s updating log file\n",
1747 		   bufftime, program, buf2);
1748 	}
1749 #endif
1750       if (rename (buf1, buf2))
1751 	{
1752 	  fprintf (stderr,
1753 		   "%s, Rateup WARNING: %s Can't rename %s to %s updating log file\n",
1754 		   bufftime, program, buf1, buf2);
1755 	}
1756       if (rename (buf, buf1))
1757 	{
1758 	  fprintf (stderr,
1759 		   "%s, Rateup WARNING: %s Can't rename %s to %s updating log file\n",
1760 		   bufftime, program, buf, buf1);
1761 	}
1762       for (n = 0; n < nout && n < MAX_HISTORY; n++)
1763 	{
1764 	  history[n] = lhist[n];
1765 	}
1766     }
1767   else
1768     {
1769       perror (program);
1770       fprintf (stderr, "%s, Rateup ERROR: Can't open %s for write\n", bufftime, buf);
1771       exit (1);
1772     }
1773   free (lhist);
1774 }
1775 
1776 static void
init_colour(int * colmap,int c0,int c1,int c2)1777 init_colour (int *colmap, int c0, int c1, int c2)
1778 {
1779   *colmap++ = c0;
1780   *colmap++ = c1;
1781   *colmap = c2;
1782 }
1783 
1784 /* Constants for readparm option */
1785 #define LENGTH_OF_BUFF  (2048)
1786 #define NUMBER_OF_PARM   (100)
1787 
1788 char buff[LENGTH_OF_BUFF + 1];
1789 char *program;
1790 
1791 static int
readparam(char const * file)1792 readparam (char const *file)
1793 {
1794   FILE *fp = NULL;
1795   int cbuf;
1796 
1797   /* Open the file */
1798   if ((fp = fopen (file, "r")) == NULL)
1799     {
1800       fprintf (stderr, "%s, %s ERROR: Can't open parameters file: %s\n", bufftime, program,
1801 	       file);
1802       return (1);
1803     }
1804   /* Check we actually got something */
1805   if (!(cbuf = fread (buff, 1, LENGTH_OF_BUFF, fp)))
1806     {
1807       fprintf (stderr, "%s, %s ERROR: Parameters file empty\n", bufftime, program);
1808       fclose(fp);
1809       return (1);
1810     }
1811   fclose (fp);
1812   buff[cbuf] = '\0';
1813 
1814 /* #define READPARAM_INFO */
1815 #ifdef READPARAM_INFO
1816 
1817   fprintf (stderr, "%s, %s INFO: Read: %d bytes from File: '%s'\n", bufftime, program, cbuf,
1818 	   file);
1819 
1820 #endif
1821 
1822   return (0);
1823 }
1824 
1825 int
main(argc,argv)1826 main (argc, argv)
1827      int argc;
1828      char **argv;
1829 {
1830   int x, argi, used, initarg;
1831 
1832   program = argv[0];
1833 
1834     /* jpt, april 2006 : 3 lines for date & time logging */
1835     (void) time(&timestamp);
1836     stLocal = localtime(&timestamp);
1837     strftime(bufftime, 32, "%Y-%m-%d %H:%M:%S", stLocal);
1838 
1839   /* Is Argv[1] a path/file to passed parameters? */
1840   if ((argc > 1) && (strncasecmp (argv[1], "-F", 2) == 0))
1841     {
1842       char *b, *c, *l;
1843       if (readparam (argv[2]))
1844 	{
1845 	  return (1);
1846 	}
1847       /* Parse buffer into argv[] */
1848       argv = calloc (NUMBER_OF_PARM + 1, sizeof (char *));
1849       argc = 0;
1850       b = buff;
1851       l = b + strlen (b);
1852       while (b < l)
1853 	{
1854 	  if (b[0] == '"')
1855 	    {
1856 	      b++;
1857 	      c = strstr (b, "\"");
1858 	      if (c != NULL)
1859 		{
1860 		  *c = '\0';
1861 		  argv[argc] = b;
1862 		  argc++;
1863 		  b = c + 2;
1864 		}
1865 	      else
1866 		{
1867 		  fprintf (stderr,
1868 			   "%s, Rateup ERROR: Parameter %d [%s] missing quote\n",
1869 			   bufftime, argc, b);
1870 		  break;
1871 		}
1872 	    }
1873 	  else
1874 	    {
1875 	      c = strstr (b, " ");
1876 	      if (c != NULL)
1877 		{
1878 		  *c = '\0';
1879 		  argv[argc] = b;
1880 		  argc++;
1881 		  b = c + 1;
1882 		}
1883 	      else
1884 		{
1885 		  argv[argc] = b;
1886 		  argc++;
1887 		  b = l;
1888 		}
1889 	    }
1890 	  if (argc == NUMBER_OF_PARM)
1891 	    {
1892 	      break;
1893 	    }
1894 	}
1895       /* Check we didn't fill argv[] */
1896       if (argc == NUMBER_OF_PARM)
1897 	{
1898 	  fprintf (stderr, "%s, Rateup ERROR: Too many parameters read: %d\n",
1899 		   bufftime, argc);
1900 	  return (1);
1901 	}
1902 
1903       /* Check we didn't end early */
1904       if (b < l)
1905 	{
1906 	  return (1);
1907 	}
1908 
1909       /* Mark End of argv[] */
1910       argv[argc] = NULL;
1911 
1912 #ifdef READPARAM_DEBUG
1913 
1914       for (i = 0; i < argc; i++)
1915 	{
1916 	  printf ("ParameterX %2d : '%s'\n", i, argv[i] ? argv[i] : "<null>");
1917 	}
1918 
1919 #endif
1920     }
1921 
1922   if (argc < 3)
1923     {
1924       fprintf (stderr, "%s, %s for MRTG %s\n"
1925 	       "Usage: %s -f <parameter file>\n"
1926 	       "       %s directory basename [sampletime] [t sampletime] "
1927 	       "[-(t)ransparent] [-(b)order]"
1928 	       "[u|a|g|h|m in out abs_max] "
1929 	       "[i/p file maxvi maxvo maxx maxy growright step bits]\n",
1930 	       bufftime, program, VERSION, program, program);
1931       return (1);
1932     }
1933 
1934   routerpath = argv[1];
1935   /* this is for NT compatibility, because it does not seem to like
1936      rename across directories */
1937   if (chdir (routerpath))
1938     {
1939       fprintf (stderr, "%s, Rateup ERROR: Chdir to %s failed ...\n", bufftime, routerpath);
1940       return (1);
1941     }
1942 
1943   /* Initialiase the colour variables  - should be overwritten */
1944   init_colour (&col_in[0], c_in);
1945   init_colour (&col_out[0], c_out);
1946   init_colour (&col_inm[0], c_inm);
1947   init_colour (&col_outm[0], c_outm);
1948   init_colour (&col_outp[0], c_outp);
1949 
1950   if ((history = calloc (1, sizeof (struct HISTORY) * (MAX_HISTORY + 1))) ==
1951       NULL)
1952     {
1953       fprintf (stderr, "%s, Rateup ERROR: Out of memory in main\n", bufftime);
1954       exit (1);
1955     }
1956 #if defined(__WATCOMC__) || defined(NETWARE)
1957   memset (history, 0, sizeof (struct HISTORY) * (MAX_HISTORY + 1));
1958 #endif
1959 
1960   Mh = MAX_HISTORY;
1961 
1962   router = argv[2];
1963   if (strlen(router) > 120)
1964   {
1965       fprintf (stderr, "%s, Rateup ERROR: Too long basename\n", bufftime);
1966       exit (1);
1967   }
1968 
1969   /* from  mrtg-2.x with x>5 rateup calling syntax changed to
1970      to support time properly ... this is for backward compat
1971      we check if now is remotely reasonable ...
1972    */
1973 
1974   if (argc > 3)
1975     {
1976       NOW = atol (argv[3]);
1977       if (NOW > 10 * 365 * 24 * 60 * 60)
1978 	{
1979 	  initarg = 4;
1980 	}
1981       else
1982 	{
1983 	  initarg = 3;
1984 	  time (&NOW);
1985 	}
1986     }
1987   else
1988     {
1989       initarg = 3;
1990       time (&NOW);
1991     }
1992 
1993   readfile ();
1994   used = 1;
1995 
1996   for (argi = initarg; argi < argc; argi += used)
1997     {
1998       switch (argv[argi][0])
1999 	{
2000 	case '-':		/* -options */
2001 	  switch (argv[argi][1])
2002 	    {
2003 	    case 'a':		/* Turn off the direction arrow */
2004 	      options |= OPTION_NOARROW;
2005 	      used = 1;
2006 	      break;
2007 	    case 'A':		/* Turn on the direction arrow */
2008 	      options &= ~OPTION_NOARROW;
2009 	      used = 1;
2010 	      break;
2011 	    case 'b':		/* Turn off the shaded border */
2012 	      options |= OPTION_NOBORDER;
2013 	      used = 1;
2014 	      break;
2015 	    case 'B':		/* Turn on the shaded border */
2016 	      options &= ~OPTION_NOBORDER;
2017 	      used = 1;
2018 	      break;
2019 	    case 'i':		/* Do not graph the I variable */
2020 	      options |= OPTION_NO_I;
2021 	      used = 1;
2022 	      break;
2023 	    case 'I':		/* Graph the I variable */
2024 	      options &= ~OPTION_NO_I;
2025 	      used = 1;
2026 	      break;
2027 	    case 'o':		/* Do not graph the O variable */
2028 	      options |= OPTION_NO_O;
2029 	      used = 1;
2030 	      break;
2031 	    case 'O':		/* Graph the O variable */
2032 	      options &= ~OPTION_NO_O;
2033 	      used = 1;
2034 	      break;
2035 	    case 'l':		/* logarithmic scaling */
2036 	      options |= OPTION_LOGGRAPH;
2037 	      options &= ~OPTION_MEANOVER;
2038 	      options &= ~OPTION_EXPGRAPH;
2039 	      used = 1;
2040 	      break;
2041 	    case 'm':		/* second-mean scaling */
2042 	      options |= OPTION_MEANOVER;
2043 	      options &= ~OPTION_LOGGRAPH;
2044 	      options &= ~OPTION_EXPGRAPH;
2045 	      used = 1;
2046 	      break;
2047 	    case 'p':		/* print router name in image */
2048 	      options |= OPTION_PRINTROUTER;
2049 	      used = 1;
2050 	      break;
2051 	    case 't':		/* Transparent Image */
2052 	      options |= OPTION_TRANSPARENT;
2053 	      used = 1;
2054 	      break;
2055 	    case 'T':		/* non-Transparent Image */
2056 	      options &= ~OPTION_TRANSPARENT;
2057 	      used = 1;
2058 	      break;
2059             case 'x':		/* exponential scaling */
2060 	      options |= OPTION_EXPGRAPH;
2061 	      options &= ~OPTION_MEANOVER;
2062 	      options &= ~OPTION_LOGGRAPH;
2063 	      used = 1;
2064 	      break;
2065 	    case 'z':		/* unknown as zero */
2066 	      options |= OPTION_UNKNASZERO;
2067 	      used = 1;
2068 	      break;
2069 	    case 'Z':		/* repeat last */
2070 	      options &= ~OPTION_UNKNASZERO;
2071 	      used = 1;
2072 	      break;
2073 	    case '0':		/* assume zeroes */
2074 	      options |= OPTION_WITHZEROES;
2075 	      used = 1;
2076 	      break;
2077 	    default:
2078 	      fprintf (stderr, "%s, Rateup ERROR: Unknown option: %s, sorry!\n",
2079 		       bufftime, argv[argi]);
2080 	      return (1);
2081 	    }
2082 	  break;
2083 	case 'i':		/* Create PPM Image record */
2084 	  image (argv[argi + 1],	/* Image */
2085 		 strtoll (argv[argi + 2], NULL, 10),	/* Max Value In */
2086 		 strtoll (argv[argi + 3], NULL, 10),	/* Max Value Out */
2087 		 atol (argv[argi + 4]),	/* xsize maxx */
2088 		 atol (argv[argi + 5]),	/* ysize maxy */
2089 		 atof (argv[argi + 6]),	/* xscale */
2090 		 atof (argv[argi + 7]),	/* yscale */
2091 		 atol (argv[argi + 8]),	/* growright */
2092 		 atol (argv[argi + 9]),	/* step */
2093 		 atol (argv[argi + 10]),	/* bits */
2094 		 atol (argv[argi + 11]),	/* ytics */
2095 		 atof (argv[argi + 12]),	/* yticsfactor */
2096 		 0, argv[argi + 13], atol (argv[argi + 14]));
2097 	  used = 15;
2098 	  break;
2099 	case 'p':		/* Create PPM Image record with Peak values */
2100 	  image (argv[argi + 1], strtoll (argv[argi + 2], NULL, 10),	/* Max Value In */
2101 		 strtoll (argv[argi + 3], NULL, 10),	/* Max Value Out */
2102 		 atol (argv[argi + 4]),	/* xsize maxx */
2103 		 atol (argv[argi + 5]),	/* ysize maxy */
2104 		 atof (argv[argi + 6]),	/* xscale */
2105 		 atof (argv[argi + 7]),	/* yscale */
2106 		 atol (argv[argi + 8]),	/* growright */
2107 		 atol (argv[argi + 9]),	/* step */
2108 		 atol (argv[argi + 10]),	/* bits */
2109 		 atol (argv[argi + 11]),	/* ytics */
2110 		 atof (argv[argi + 12]),	/* yticsfactor */
2111 		 1, argv[argi + 13], atol (argv[argi + 14]));
2112 	  used = 15;
2113 	  break;
2114 	case 'r':		/* Create random records, then update */
2115 	  for (x = 0; x < histvalid; x++)
2116 	    {
2117 	      history[x].in = rand () % atoi (argv[argi + 1]);
2118 	      history[x].out = rand () % atoi (argv[argi + 2]);
2119 	    }
2120 	   /* fallthrough */
2121 	case 'u':		/* Update file */
2122 	  if (argv[argi][1] == 'p')
2123 	    {
2124 	      options |= OPTION_DORELPERCENT;
2125 	    }
2126 	  update (argv[argi + 1], argv[argi + 2],
2127 		  strtoll (argv[argi + 3], NULL, 10), 0);
2128 	  used = 4;
2129 	  break;
2130 	case 'a':		/* Absolute Update file */
2131 	  if (argv[argi][1] == 'p')
2132 	    {
2133 	      options |= OPTION_DORELPERCENT;
2134 	    }
2135 	  update (argv[argi + 1], argv[argi + 2],
2136 		  strtoll (argv[argi + 3], NULL, 10), 1);
2137 	  used = 4;
2138 	  break;
2139 	case 'g':		/* Gauge Update file */
2140 	  if (argv[argi][1] == 'p')
2141 	    {
2142 	      options |= OPTION_DORELPERCENT;
2143 	    }
2144 	  update (argv[argi + 1], argv[argi + 2],
2145 		  strtoll (argv[argi + 3], NULL, 10), 2);
2146 	  used = 4;
2147 	  break;
2148 	case 'h':
2149 	  if (argv[argi][1] == 'p')
2150 	    {
2151 	      options |= OPTION_DORELPERCENT;
2152 	    }
2153 	  update (argv[argi + 1], argv[argi + 2],
2154 		  strtoll (argv[argi + 3], NULL, 10), 3);
2155 	  used = 4;
2156 	  break;
2157 	case 'm':
2158 	  if (argv[argi][1] == 'p')
2159 	    {
2160 	      options |= OPTION_DORELPERCENT;
2161 	    }
2162 	  update (argv[argi + 1], argv[argi + 2],
2163 		  strtoll (argv[argi + 3], NULL, 10), 4);
2164 	  used = 4;
2165 	  break;
2166 	case 'W':		/* Week format */
2167 	  weekformat = argv[argi + 1][0];
2168 	  used = 2;
2169 	  break;
2170 	case 'c':		/* Colour Map */
2171 	  sscanf (argv[argi + 1], "#%2x%2x%2x", &col_in[0], &col_in[1],
2172 		  &col_in[2]);
2173 	  sscanf (argv[argi + 2], "#%2x%2x%2x", &col_out[0], &col_out[1],
2174 		  &col_out[2]);
2175 	  sscanf (argv[argi + 3], "#%2x%2x%2x", &col_inm[0], &col_inm[1],
2176 		  &col_inm[2]);
2177 	  sscanf (argv[argi + 4], "#%2x%2x%2x", &col_outm[0], &col_outm[1],
2178 		  &col_outm[2]);
2179 	  used = 5;
2180 	  break;
2181 	case 'C':		/* Extented Colour Map */
2182 	  sscanf (argv[argi + 1], "#%2x%2x%2x", &col_in[0], &col_in[1],
2183 		  &col_in[2]);
2184 	  sscanf (argv[argi + 2], "#%2x%2x%2x", &col_out[0], &col_out[1],
2185 		  &col_out[2]);
2186 	  sscanf (argv[argi + 3], "#%2x%2x%2x", &col_inm[0], &col_inm[1],
2187 		  &col_inm[2]);
2188 	  sscanf (argv[argi + 4], "#%2x%2x%2x", &col_outm[0], &col_outm[1],
2189 		  &col_outm[2]);
2190 	  sscanf (argv[argi + 5], "#%2x%2x%2x", &col_outp[0], &col_outp[1],
2191 		  &col_outp[2]);
2192 	  used = 6;
2193 	  break;
2194 	case 't':
2195 	  NOW = atol (argv[argi + 1]);
2196 	  used = 2;
2197 	  break;
2198 	case 'k':
2199 	  kilo = atol (argv[argi + 1]);
2200 	  used = 2;
2201 	  break;
2202 	case 'K':
2203 	  kMG = calloc (strlen (argv[argi + 1]) + 1, sizeof (char));
2204 	  strcpy (kMG, argv[argi + 1]);
2205 	  used = 2;
2206 	  break;
2207 	case 'Z':		/* Timezone name */
2208 	  rtimezone = argv[argi + 1];
2209 	  used = 2;
2210 	  break;
2211 	case 'l':		/* YLegend - rewritten by Oliver Haidi, re-rewritten by Jon Barber */
2212 	  {
2213 	    int i, j, k, loop = 1;
2214 	    int start = 1, got_esc = 0, append_ok;
2215 	    char *qstr;
2216 	    longup = (char *) calloc (1, 100);
2217 	    *longup = 0;
2218 	    /* this rather twisty argument scanning is necesary
2219 	       because NT command.coms rather dumb argument
2220 	       passing .... or because we don't know
2221 	       better. Under Unix we just would say.  if
2222 	       ((sscanf(argv[argi+1],"[%[^]]]", longup); */
2223 	    /* at start, check 1st char must be [ */
2224 	    if (argv[argi + 1][0] != '[')
2225 	      {
2226 		fprintf (stderr,
2227 			 "%s, Rateup ERROR: YLegend: Option must be passed with '[' at start and  ']' at end (these will not be printed).\n", bufftime);
2228 		return (1);
2229 	      }
2230 	    for (i = 1; (i < argc) && loop; i++)
2231 	      {			/* check all args until unescaped ']'  */
2232 		qstr = argv[argi + i];
2233 		for (j = start; ((size_t) j < strlen (qstr)) && loop; j++)
2234 		  {
2235 		    start = 0;	/* 1st char in 1st arg already checked */
2236 		    append_ok = 1;	/* OK to append unless we find otherwise */
2237 		    if (qstr[j] == '\\')
2238 		      {
2239 			if (++got_esc == 1)
2240 			  {
2241 			    append_ok = 0;	/* don't append 1st '/' */
2242 			  }
2243 			else
2244 			  {
2245 			    got_esc = 0;	/* 2nd '/' in a row, i.e. '//' */
2246 			  }
2247 		      }
2248 		    if (qstr[j] == ']')
2249 		      {		/* is this the end? */
2250 			if (got_esc == 1)
2251 			  {
2252 			    if (strlen (qstr + j) >= 2)
2253 			      {
2254 				append_ok = 1;
2255 				got_esc = 0;
2256 			      }
2257 			    else
2258 			      {
2259 				fprintf (stderr,
2260 					 "%s, 1a: rateup ERROR: YLegend:  use \"\\]\" for \"]\" or \"\\\\\" for \"\\\".\n", bufftime);
2261 				return (1);
2262 			      }
2263 			  }
2264 			else
2265 			  {
2266 			    if (strlen (qstr + j) >= 2)
2267 			      {
2268 				fprintf (stderr,
2269 					 "%s, 2a: rateup ERROR: YLegend:  use \"\\]\" for \"]\" or \"\\\\\" for \"\\\".\n", bufftime);
2270 				return (1);
2271 			      }
2272 			    loop = 0;
2273 			    append_ok = 0;
2274 			  }
2275 		      }
2276 		    if (append_ok == 1)
2277 		      {
2278 			k = strlen (longup);
2279 			if ((size_t) (k + 1) > 99)
2280 			  {
2281 			    fprintf (stderr,
2282 				     "%s, 3a: rateup ERROR: YLegend too long\n", bufftime);
2283 			    return (1);
2284 			  }
2285 			longup[k] = qstr[j];
2286 			longup[k + 1] = 0;
2287 		      }
2288 		  }		/* for (j=start...)   */
2289 		/* append space */
2290 		k = strlen (longup);
2291 		if ((size_t) (k + 1) > 99)
2292 		  {
2293 		    fprintf (stderr, "%s, 4a: rateup ERROR: YLegend too long\n", bufftime);
2294 		    return (1);
2295 		  }
2296 		longup[k] = ' ';
2297 		longup[k + 1] = 0;
2298 	      }			/* for (i =1 ... */
2299 	    used = i;
2300 	  }
2301 	  /* remove trailing space */
2302 	  longup[max (0, (signed) strlen (longup) - 1)] = 0;
2303 	  shortup = longup;
2304 	  /* fprintf(stderr, "%s, YLegend = \"%s\"\n", bufftime, longup);  */
2305 	  break;
2306 	case 'T':		/* pngTitle - based on YLegend */
2307 	  {
2308 	    int i, j, k, loop = 1;
2309 	    int start = 1, got_esc = 0, append_ok;
2310 	    char *qstr;
2311 	    pngtitle = (char *) calloc (1, 100);
2312 	    *pngtitle = 0;
2313 	    /* this rather twisty argument scanning is necesary
2314 	       because NT command.coms rather dumb argument
2315 	       passing .... or because we don't know
2316 	       better. Under Unix we just would say.  if
2317 	       ((sscanf(argv[argi+1],"[%[^]]]", pngtitle); */
2318 	    /* at start, check 1st char must be [ */
2319 	    if (argv[argi + 1][0] != '[')
2320 	      {
2321 		fprintf (stderr,
2322 			 "%s, Rateup ERROR: YLegend: Option must be passed with '[' at start and  ']' at end (these will not be printed).\n", bufftime);
2323 		return (1);
2324 	      }
2325 	    for (i = 1; (i < argc) && loop; i++)
2326 	      {			/* check all args until unescaped ']'  */
2327 		qstr = argv[argi + i];
2328 		for (j = start; ((size_t) j < strlen (qstr)) && loop; j++)
2329 		  {
2330 		    start = 0;	/* 1st char in 1st arg already checked */
2331 		    append_ok = 1;	/* OK to append unless we find otherwise */
2332 		    if (qstr[j] == '\\')
2333 		      {
2334 			if (++got_esc == 1)
2335 			  {
2336 			    append_ok = 0;	/* don't append 1st '/' */
2337 			  }
2338 			else
2339 			  {
2340 			    got_esc = 0;	/* 2nd '/' in a row, i.e. '//' */
2341 			  }
2342 		      }
2343 		    if (qstr[j] == ']')
2344 		      {		/* is this the end? */
2345 			if (got_esc == 1)
2346 			  {
2347 			    if (strlen (qstr + j) >= 2)
2348 			      {
2349 				append_ok = 1;
2350 				got_esc = 0;
2351 			      }
2352 			    else
2353 			      {
2354 				fprintf (stderr,
2355 					 "%s, 1b: rateup ERROR: YLegend:  use \"\\]\" for \"]\" or \"\\\\\" for \"\\\".\n", bufftime);
2356 				return (1);
2357 			      }
2358 			  }
2359 			else
2360 			  {
2361 			    if (strlen (qstr + j) >= 2)
2362 			      {
2363 				fprintf (stderr,
2364 					 "%s, 2b: rateup ERROR: YLegend:  use \"\\]\" for \"]\" or \"\\\\\" for \"\\\".\n", bufftime);
2365 				return (1);
2366 			      }
2367 			    loop = 0;
2368 			    append_ok = 0;
2369 			  }
2370 		      }
2371 		    if (append_ok == 1)
2372 		      {
2373 			k = strlen (pngtitle);
2374 			if ((size_t) (k + 1) > 99)
2375 			  {
2376 			    fprintf (stderr,
2377 				     "%s, 3b: rateup ERROR: YLegend too long\n", bufftime);
2378 			    return (1);
2379 			  }
2380 			pngtitle[k] = qstr[j];
2381 			pngtitle[k + 1] = 0;
2382 		      }
2383 		  }		/* for (j=start...)   */
2384 		/* append space */
2385 		k = strlen (pngtitle);
2386 		if ((size_t) (k + 1) > 99)
2387 		  {
2388 		    fprintf (stderr, "%s, 4b: rateup ERROR: YLegend too long\n", bufftime);
2389 		    return (1);
2390 		  }
2391 		pngtitle[k] = ' ';
2392 		pngtitle[k + 1] = 0;
2393 	      }			/* for (i =1 ... */
2394 	    used = i;
2395 	  }
2396 	  /* remove trailing space */
2397 	  pngtitle[max (0, (signed) strlen (pngtitle) - 1)] = 0;
2398 	  /* fprintf(stderr, "%s, YLegend = \"%s\"\n", bufftime, pngtitle);  */
2399 	  break;
2400 	default:
2401 	  fprintf (stderr, "%s, Rateup ERROR: Can't cope with %s, sorry!\n",
2402 		   bufftime, argv[argi]);
2403 	  return (1);
2404 	}
2405     }
2406   return (0);
2407 }
2408