]>
git.ipfire.org Git - thirdparty/sarg.git/blob - grepday.c
2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
27 #include "include/conf.h"
28 #include "include/defs.h"
32 #if defined(HAVE_ICONV_H) && defined(gdFTEX_Unicode)
37 struct GraphDataStruct
47 //! The color of the top side of a graph bar.
49 //! The color of the right side of a graph bar.
51 //! The color of the front side of a graph bar.
53 //! The libgd image we are drawing on.
55 //! An allocated buffer to convert the string into UTF-8.
57 //! The number of bytes allocated for the string buffer.
59 //! The bottom border of the graph.
61 //! The top border of the graph.
63 //! The left border of the graph.
65 //! The right border of the graph.
67 //! The height at which the bottom depth border of the graph extends.
69 //! The distance between two ticks on the horizontal axis.
71 //! The distance between two ticks on the vertical axis.
73 //! The exterior length of a tick on the scales.
75 //! The distance, in pixels, between two ticks along the Y axis.
88 //! The data points to plot.
89 long long int *datapoints
;
90 //! The number of points to plot.
92 //! The minimum data to plot along the Y axis.
94 //! The maximum data to plot along the Y axis.
96 //! The type of Y axis to draw.
98 //! The label to write on the Y axis.
116 //! The iconv object to convert the text from the locale character set to UTF-8.
117 iconv_t localtoutf
=(iconv_t
)-1;
120 static void Sarg_gdImageStringFT (struct GraphDataStruct
*gdata
, int fg
, char *fontlist
,
121 double ptsize
, double angle
, int x
, int y
, const char *string
,enum TextRefPos RefPos
)
126 int minx
,miny
,maxx
,maxy
;
130 if (localtoutf
!=(iconv_t
)-1) {
135 slen
= strlen(string
) + 1; // We must include string termination character
136 sslen
= slen
* 3; // We assume that the UTF8 string will not be bigger than 3 times the original size.
137 if (sslen
>gdata
->string_size
) {
138 sstring
= (char *)realloc(gdata
->string
,sslen
);
140 debuga(_("realloc error (%zu bytes required)\n"),sslen
);
143 gdata
->string
=(char *)sstring
;
144 gdata
->string_size
=sslen
;
146 sstring
=gdata
->string
;
147 sslen
=gdata
->string_size
;
152 if (iconv (localtoutf
, (ICONV_CONST
char **)&str
, &slen
, &sstr
, &sslen
)==-1) {
153 debuga(_("(grepday) iconv failed to convert string \"%s\" from %s to UTF-8 - %s\n"),string
,CharSet
,strerror(errno
));
154 sstring
=(char *)string
; //show something sensible on the graph
157 sstring
=(char *)string
; //show something sensible on the graph
160 sstring
=(char *)string
;
163 if (RefPos
!=TRP_BottomLeft
) {
164 retval
= gdImageStringFTEx (NULL
, brect
, fg
, fontlist
, ptsize
, angle
, 0, 0, sstring
, gdFTEX_Unicode
);
166 debuga(_("libgd failed to calculate the bounding box of the text \"%s\": %s\n"),sstring
,retval
);
170 From libgd documentation, brect contains this without taking into account the angle:
171 0 lower left corner, X position
172 1 lower left corner, Y position
173 2 lower right corner, X position
174 3 lower right corner, Y position
175 4 upper right corner, X position
176 5 upper right corner, Y position
177 6 upper left corner, X position
178 7 upper left corner, Y position
182 for (i
=2 ; i
<7 ; i
+=2) {
183 if (minx
>brect
[i
]) minx
=brect
[i
];
184 if (maxx
<brect
[i
]) maxx
=brect
[i
];
185 if (miny
>brect
[i
+1]) miny
=brect
[i
+1];
186 if (maxy
<brect
[i
+1]) maxy
=brect
[i
+1];
209 case TRP_BottomCenter
:
213 case TRP_BottomRight
:
226 case TRP_CenterRight
:
231 retval
= gdImageStringFTEx (gdata
->im
, brect
, fg
, fontlist
, ptsize
, angle
, x
, y
, sstring
, gdFTEX_Unicode
);
233 debuga(_("libgd failed to render the text \"%s\": %s\n"),sstring
,retval
);
238 static void bar(struct GraphDataStruct
*gdata
,int x1
,double y
,const char *label
)
244 val
=gdata
->BottomGraph
+5-(int)(y
*gdata
->YScale
+0.5);
246 width2
=(int)(gdata
->XScale
/4.);
247 gdImageFilledRectangle(gdata
->im
, x1
-width2
, val
, x1
+width2
, gdata
->BottomDepth
, gdata
->color3
);
249 points
[0].x
= x1
-width2
+5;
251 points
[1].x
= x1
-width2
;
253 points
[2].x
= x1
+width2
;
255 points
[3].x
= x1
+width2
+5;
257 gdImageFilledPolygon(gdata
->im
, points
, 4, gdata
->color1
);
259 gdImageLine(gdata
->im
, x1
+2, val
-2, x1
+2, val
-10, gdata
->dimgray
);
260 gdImageFilledRectangle(gdata
->im
, x1
-8, val
-20, x1
+12, val
-10, gdata
->goldenrod
);
261 gdImageRectangle(gdata
->im
, x1
-8, val
-20, x1
+12, val
-10, gdata
->goldenrod2
);
263 Sarg_gdImageStringFT(gdata
,gdata
->black
,GraphFont
,6,0.0,x1
+2,val
-12,label
,TRP_BottomCenter
);
265 points
[0].x
= x1
+width2
+5;
267 points
[1].x
= x1
+width2
;
269 points
[2].x
= x1
+width2
;
270 points
[2].y
= gdata
->BottomDepth
;
271 points
[3].x
= x1
+width2
+5;
272 points
[3].y
= gdata
->BottomGraph
;
273 gdImageFilledPolygon(gdata
->im
, points
, 4, gdata
->color2
);
278 static int greport_compute_yaxis(struct PlotStruct
*pdata
,struct GraphDataStruct
*gdata
)
285 if (pdata
->ymin
<0.) {
286 debuga(_("Minimum for Y scale of the graph is out of range: %lld\n"),pdata
->ymin
);
289 if (pdata
->ymax
<=0.) {
290 debuga(_("Maximum for Y scale of the graph is out of range: %lld\n"),pdata
->ymax
);
297 symin
=(double)pdata
->ymin
;
298 symax
=(double)pdata
->ymax
;
304 symin
=log(pdata
->ymin
)/log(1024);
307 symax
=log(pdata
->ymax
)/log(1024);
312 symin
=(double)pdata
->ymin
;
313 symax
=(double)pdata
->ymax
;
318 debuga(_("Unknown type %d for Y axis scale\n"),pdata
->ytype
);
321 gdata
->YTickSpace
=10;
324 yscale
=(double)(gdata
->BottomGraph
-gdata
->TopGraph
)/range
;
325 gdata
->YScale
=yscale
;
329 static void greport_formatbin(double yval
,int ndigits
,char *string
,int slen
)
332 char schar
[]={'\0','k','M','G','T','P'};
335 for (scount
=0 ; scount
<sizeof(schar
)/sizeof(*schar
) && yval
>=1024. ; scount
++)
338 len
=snprintf(string
,slen
,"%.*lg",ndigits
,yval
);
340 len
=snprintf(string
,slen
,"%.0lf",yval
);
341 if (schar
[scount
] && len
<slen
) {
342 string
[len
++]=schar
[scount
];
347 static void greport_draw_yaxis(struct PlotStruct
*pdata
,struct GraphDataStruct
*gdata
)
358 y0
=gdata
->BottomGraph
;
359 yt
=gdata
->BottomDepth
-gdata
->BottomGraph
;
360 xexterior
=gdata
->LeftGraph
-10;
361 xinterior
=gdata
->LeftGraph
;
362 xtick
=gdata
->LeftGraph
-10-gdata
->TickLength
;
363 for(y
=y0
-gdata
->YTickSpace
; y
>=gdata
->TopGraph
; y
-=gdata
->YTickSpace
) {
364 gdImageLine(gdata
->im
, xtick
, y
+yt
, xexterior
, y
+yt
, gdata
->dimgray
);
365 gdImageLine(gdata
->im
, xexterior
, y
+yt
, xinterior
, y
, gdata
->dimgray
);
366 gdImageLine(gdata
->im
, xinterior
, y
, gdata
->RightGraph
, y
, gdata
->dimgray
);
367 switch (pdata
->ytype
)
370 yval
=(double)(y0
-y
)/gdata
->YScale
+(double)pdata
->ymin
;
371 greport_formatbin(yval
,0,YLabel
,sizeof(YLabel
));
375 yval
=pow(1024.,(double)(y0
-y
)/gdata
->YScale
+log(pdata
->ymin
)/log(1024));
376 greport_formatbin(yval
,0,YLabel
,sizeof(YLabel
));
383 yval
=(double)(y0
-y
)/gdata
->YScale
+(double)pdata
->ymin
;
384 t
=(int)(yval
/60000.);
385 snprintf(YLabel
,sizeof(YLabel
),"%02d:%02d",t
/60,t
%60);
389 Sarg_gdImageStringFT(gdata
,gdata
->dimgray
,GraphFont
,7,0.0,xtick
,y
+yt
,YLabel
,TRP_CenterRight
);
393 static void greport_plot(const struct userinfostruct
*uinfo
,struct PlotStruct
*pdata
)
405 struct GraphDataStruct gdata
;
406 const int ImgXSize
=720;
407 const int ImgYSize
=480;
408 const int LeftMargin
=60;
409 const int RightMargin
=20;
410 const int TopMargin
=60;
411 const int BottomMargin
=60;
412 const int TickLength
=3;
413 const int ZTickLength
=5;
417 memset(&gdata
,0,sizeof(gdata
));
419 gdata
.im
= gdImageCreate(ImgXSize
, ImgYSize
);
420 gdata
.BottomGraph
=ImgYSize
-BottomMargin
;
421 gdata
.LeftGraph
=LeftMargin
;
422 gdata
.RightGraph
=ImgXSize
-RightMargin
;
423 gdata
.TopGraph
=TopMargin
;
424 gdata
.BottomDepth
=gdata
.BottomGraph
+5;
425 gdata
.XScale
=(double)(gdata
.RightGraph
-gdata
.LeftGraph
)/(pdata
->npoints
+1);
426 if (greport_compute_yaxis(pdata
,&gdata
)<0) return;
427 gdata
.TickLength
=TickLength
;
429 // first allocated color is the background
430 gdata
.lavender
= gdImageColorAllocate(gdata
.im
, 230, 230, 250);
431 gdata
.gray
= gdImageColorAllocate(gdata
.im
, 192, 192, 192);
432 gdata
.silver
= gdImageColorAllocate(gdata
.im
, 211, 211, 211);
433 gdata
.black
= gdImageColorAllocate(gdata
.im
, 0, 0, 0);
434 gdata
.dimgray
= gdImageColorAllocate(gdata
.im
, 105, 105, 105);
435 gdata
.darkblue
= gdImageColorAllocate(gdata
.im
, 0, 0, 139);
436 gdata
.goldenrod
= gdImageColorAllocate(gdata
.im
, 234, 234, 174);
437 gdata
.goldenrod2
= gdImageColorAllocate(gdata
.im
, 207, 181, 59);
439 if(strcmp(GraphDaysBytesBarColor
,"orange") == 0) {
440 gdata
.color1
= gdImageColorAllocate(gdata
.im
, 255, 233, 142);
441 gdata
.color2
= gdImageColorAllocate(gdata
.im
, 220, 163, 72);
442 gdata
.color3
= gdImageColorAllocate(gdata
.im
, 255, 198, 107);
444 else if(strcmp(GraphDaysBytesBarColor
,"blue") == 0) {
445 gdata
.color1
= gdImageColorAllocate(gdata
.im
, 62, 80, 167);
446 gdata
.color2
= gdImageColorAllocate(gdata
.im
, 40, 51, 101);
447 gdata
.color3
= gdImageColorAllocate(gdata
.im
, 57, 73, 150);
449 else if(strcmp(GraphDaysBytesBarColor
,"green") == 0) {
450 gdata
.color1
= gdImageColorAllocate(gdata
.im
,120,166,129);
451 gdata
.color2
= gdImageColorAllocate(gdata
.im
,84,113,82);
452 gdata
.color3
= gdImageColorAllocate(gdata
.im
,158,223,167);
454 else if(strcmp(GraphDaysBytesBarColor
,"yellow") == 0) {
455 gdata
.color1
= gdImageColorAllocate(gdata
.im
,185,185,10);
456 gdata
.color2
= gdImageColorAllocate(gdata
.im
,111,111,10);
457 gdata
.color3
= gdImageColorAllocate(gdata
.im
,166,166,10);
459 else if(strcmp(GraphDaysBytesBarColor
,"brown") == 0) {
460 gdata
.color1
= gdImageColorAllocate(gdata
.im
,97,45,27);
461 gdata
.color2
= gdImageColorAllocate(gdata
.im
,60,30,20);
462 gdata
.color3
= gdImageColorAllocate(gdata
.im
,88,41,26);
464 else if(strcmp(GraphDaysBytesBarColor
,"red") == 0){
465 gdata
.color1
= gdImageColorAllocate(gdata
.im
,185,10,10);
466 gdata
.color2
= gdImageColorAllocate(gdata
.im
,111,10,10);
467 gdata
.color3
= gdImageColorAllocate(gdata
.im
,166,10,10);
469 debuga(_("Unknown color \"%s\" requested for the graph. Using orange instead\n"),GraphDaysBytesBarColor
);
470 gdata
.color1
= gdImageColorAllocate(gdata
.im
, 255, 233, 142);
471 gdata
.color2
= gdImageColorAllocate(gdata
.im
, 220, 163, 72);
472 gdata
.color3
= gdImageColorAllocate(gdata
.im
, 255, 198, 107);
475 // rectangle around the image
476 gdImageRectangle(gdata
.im
, 0, 0, ImgXSize
-1, ImgYSize
-1, gdata
.dimgray
);
477 // backtround of the graph
478 gdImageFilledRectangle(gdata
.im
, LeftMargin
, gdata
.TopGraph
, gdata
.RightGraph
, gdata
.BottomGraph
, gdata
.silver
);
480 // depth of the left Y axis
481 points
[0].x
= gdata
.LeftGraph
-10;
482 points
[0].y
= gdata
.TopGraph
+5;
483 points
[1].x
= gdata
.LeftGraph
-10;
484 points
[1].y
= gdata
.BottomDepth
;
485 points
[2].x
= gdata
.LeftGraph
;
486 points
[2].y
= gdata
.BottomGraph
;
487 points
[3].x
= gdata
.LeftGraph
;
488 points
[3].y
= gdata
.TopGraph
;
489 gdImageFilledPolygon(gdata
.im
, points
, 4, gdata
.gray
);
491 // depth of the bottom X axis
492 points
[0].x
= gdata
.LeftGraph
;
493 points
[0].y
= gdata
.BottomGraph
;
494 points
[1].x
= gdata
.LeftGraph
-10;
495 points
[1].y
= gdata
.BottomDepth
;
496 points
[2].x
= gdata
.RightGraph
-10;
497 points
[2].y
= gdata
.BottomDepth
;
498 points
[3].x
= gdata
.RightGraph
;
499 points
[3].y
= gdata
.BottomGraph
;
500 gdImageFilledPolygon(gdata
.im
, points
, 4, gdata
.gray
);
502 // vertical exterior line of the depth
503 gdImageLine(gdata
.im
, LeftMargin
-10, TopMargin
+5, LeftMargin
-10, gdata
.BottomDepth
+ZTickLength
, gdata
.black
);
504 // horizontal exterior line of the depth
505 gdImageLine(gdata
.im
, LeftMargin
-10-ZTickLength
, gdata
.BottomDepth
, gdata
.RightGraph
-10, gdata
.BottomDepth
, gdata
.black
);
506 // diagonal line between the two depths
507 gdImageLine(gdata
.im
, LeftMargin
-10, gdata
.BottomDepth
, LeftMargin
, gdata
.BottomGraph
, gdata
.black
);
508 // vertical left line of the graph
509 gdImageLine(gdata
.im
, LeftMargin
, gdata
.BottomGraph
, LeftMargin
, gdata
.TopGraph
, gdata
.black
);
510 // horizontal bottom line of the graph
511 gdImageLine(gdata
.im
, LeftMargin
, gdata
.BottomGraph
, gdata
.RightGraph
, gdata
.BottomGraph
, gdata
.black
);
512 // vertical right line of the graph
513 gdImageLine(gdata
.im
, gdata
.RightGraph
, gdata
.TopGraph
, gdata
.RightGraph
, gdata
.BottomGraph
, gdata
.black
);
514 // diagonal line to close the right of the bottom depth
515 gdImageLine(gdata
.im
, gdata
.RightGraph
-10, gdata
.BottomDepth
, gdata
.RightGraph
, gdata
.BottomGraph
, gdata
.black
);
518 greport_draw_yaxis(pdata
,&gdata
);
520 // X axis ticks and labels
521 for(y
=1; y
<=pdata
->npoints
; y
++) {
522 x
=gdata
.LeftGraph
-10+(int)((double)y
*gdata
.XScale
+0.5);
523 gdImageLine(gdata
.im
, x
, gdata
.BottomDepth
, x
, gdata
.BottomDepth
+TickLength
, gdata
.dimgray
);
525 Sarg_gdImageStringFT(&gdata
,gdata
.dimgray
,GraphFont
,7,0.0,x
,gdata
.BottomDepth
+TickLength
+1,s
,TRP_TopCenter
);
529 local
= localtime(&t
);
530 if(DateFormat
[0]=='u')
531 strftime(ftime
, sizeof(ftime
), "%b/%d/%Y %H:%M", local
);
532 if(DateFormat
[0]=='e')
533 strftime(ftime
, sizeof(ftime
), "%d/%b/%Y-%H:%M", local
);
536 Sarg_gdImageStringFT(&gdata
,gdata
.darkblue
,GraphFont
,7,0.0,ImgXSize
-10,ImgYSize
-10,ftime
,TRP_BottomRight
);
537 if(ShowSargInfo
) Sarg_gdImageStringFT(&gdata
,gdata
.darkblue
,GraphFont
,10,0.0,x
,15,_("SARG, "),TRP_BottomRight
);
538 Sarg_gdImageStringFT(&gdata
,gdata
.darkblue
,GraphFont
,10,0.0,x
,15,Title
,TRP_BottomLeft
);
539 sprintf(warea
,_("Period: %s"),period
.text
);
540 Sarg_gdImageStringFT(&gdata
,gdata
.darkblue
,GraphFont
,9,0.0,x
,27,warea
,TRP_BottomLeft
);
541 sprintf(warea
,_("User: %s"),uinfo
->label
);
542 Sarg_gdImageStringFT(&gdata
,gdata
.darkblue
,GraphFont
,9,0.0,x
,38,warea
,TRP_BottomLeft
);
544 Sarg_gdImageStringFT(&gdata
,gdata
.black
,GraphFont
,10,3.141592/2,20,ImgYSize
/2,pdata
->YLabel
,TRP_CenterLeft
);
545 Sarg_gdImageStringFT(&gdata
,gdata
.black
,GraphFont
,10,0.0,ImgXSize
/2,ImgYSize
-20,_("DAYS"),TRP_BottomCenter
);
547 for (day
=0 ; day
<pdata
->npoints
; day
++) {
548 if (pdata
->datapoints
[day
]>0) {
549 x1
=gdata
.LeftGraph
-10+(int)((double)(day
+1)*gdata
.XScale
+0.5);
550 switch (pdata
->ytype
)
553 yval
=(double)pdata
->datapoints
[day
];
554 if (yval
<pdata
->ymin
)
556 else if (yval
>pdata
->ymax
)
560 //strcpy(blabel,fixnum(pdata->datapoints[day],0));
561 greport_formatbin(pdata
->datapoints
[day
],2,blabel
,sizeof(blabel
));
564 yval
=(double)pdata
->datapoints
[day
];
565 if (yval
<=pdata
->ymin
)
567 else if (yval
>pdata
->ymax
)
568 yval
=log(pdata
->ymax
)/log(1024)-log(pdata
->ymin
)/log(1024);
570 yval
=log(yval
)/log(1024)-log(pdata
->ymin
)/log(1024);
571 greport_formatbin(pdata
->datapoints
[day
],2,blabel
,sizeof(blabel
));
572 //strcpy(blabel,fixnum(pdata->datapoints[day],0));
578 yval
=(double)pdata
->datapoints
[day
];
579 if (yval
<pdata
->ymin
)
581 else if (yval
>pdata
->ymax
)
585 t
=(int)(pdata
->datapoints
[day
]/60000.);
586 snprintf(blabel
,sizeof(blabel
),"%d:%02d",t
/60,t
%60);
590 bar(&gdata
,x1
,yval
,blabel
);
594 if (snprintf(graph
,sizeof(graph
),"%s/%s/graph_day.png",outdirname
,uinfo
->filename
)>=sizeof(graph
)) {
595 debuga(_("user name too long for %s/%s/graph_day.png\n"),outdirname
,uinfo
->filename
);
598 if((pngout
=fopen(graph
,"wb"))==NULL
) {
599 debuga(_("(grepday) Cannot open log file %s\n"),graph
);
602 gdImagePng(gdata
.im
, pngout
);
604 gdImageDestroy(gdata
.im
);
606 if (gdata
.string
) free(gdata
.string
);
611 void greport_prepare(void)
614 if(access(GraphFont
, R_OK
) != 0) {
615 debuga(_("(grepday) Fontname %s not found\n"),GraphFont
);
620 localtoutf
= iconv_open ("UTF-8", CharSet
);
621 if (localtoutf
==(iconv_t
)-1) {
622 debuga(_("(grepday) iconv cannot convert from %s to UTF-8 - %s\n"),CharSet
,strerror(errno
));
629 void greport_day(const struct userinfostruct
*uinfo
)
633 char wdirname
[MAXLEN
];
638 long long int datapoints
[31];
639 struct getwordstruct gwarea
;
640 struct PlotStruct pdata
;
642 if (snprintf(wdirname
,sizeof(wdirname
),"%s/%s.day",tmp
,uinfo
->filename
)>=sizeof(wdirname
)) {
643 debuga(_("user name too long for %s/%s.day\n"),tmp
,uinfo
->filename
);
646 if(access(wdirname
, R_OK
) != 0) {
649 if(!Graphs
|| GraphFont
[0]=='\0') {
654 if((fp_in
=fopen(wdirname
,"r"))==NULL
) {
655 debuga(_("(grepday) Cannot open log file %s\n"),wdirname
);
659 memset(datapoints
,0,sizeof(datapoints
));
660 while(fgets(buf
,sizeof(buf
),fp_in
)!=NULL
) {
662 getword_start(&gwarea
,buf
);
663 if (getword_atoll(&llday
,&gwarea
,'/')<0) {
664 debuga(_("Maybe you have a broken record or garbage in your %s file\n"),wdirname
);
667 if(DateFormat
[0]=='u') {
668 if (getword_atoll(&llday
,&gwarea
,'/')<0) {
669 debuga(_("Maybe you have a broken record or garbage in your %s file\n"),wdirname
);
674 if (day
<1 || day
>31) continue;
675 if (getword_skip(20,&gwarea
,'\t')<0 || getword_skip(20,&gwarea
,'\t')<0 || getword_atoll(&bytes
,&gwarea
,'\t')<0) {
676 debuga(_("Maybe you have a broken record or garbage in your %s file\n"),wdirname
);
679 datapoints
[day
-1]+=bytes
;
684 memset(&pdata
,0,sizeof(pdata
));
685 pdata
.datapoints
=datapoints
;
687 if(datetimeby
==DATETIME_BYTE
) {
688 pdata
.ymin
=20LL*1024LL;
689 pdata
.ymax
=5LL*1024LL*1024LL*1024LL;
690 pdata
.ytype
=PTG_LogBin
;
691 pdata
.YLabel
=_("BYTES");
695 pdata
.ytype
=PTG_Time
;
696 pdata
.YLabel
=_("ELAPSED TIME");
698 greport_plot(uinfo
,&pdata
);
704 void greport_cleanup(void)
707 gdFontCacheShutdown();
710 if (localtoutf
!=(iconv_t
)-1)
711 iconv_close (localtoutf
);