"Fossies" - the Fresh Open Source Software Archive

Member "vnstat-2.9/src/image.c" (2 Jan 2022, 37235 Bytes) of package /linux/misc/vnstat-2.9.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.


    1 #include "common.h"
    2 #include "dbsql.h"
    3 #include "misc.h"
    4 #include "image.h"
    5 #include "image_support.h"
    6 #include "vnstati.h"
    7 
    8 void initimagecontent(IMAGECONTENT *ic)
    9 {
   10     ic->im = NULL;
   11     ic->font = gdFontGetSmall();
   12     ic->lineheight = 12;
   13     ic->large = 0;
   14     ic->showheader = 1;
   15     ic->showedge = 1;
   16     ic->showlegend = 1;
   17     ic->altdate = 0;
   18     ic->headertext[0] = '\0';
   19     ic->databegin[0] = '\0';
   20     ic->dataend[0] = '\0';
   21     ic->interface.name[0] = '\0';
   22     ic->interface.alias[0] = '\0';
   23 }
   24 
   25 void drawimage(IMAGECONTENT *ic)
   26 {
   27     switch (cfg.qmode) {
   28         case 1:
   29             drawlist(ic, "day");
   30             break;
   31         case 2:
   32             drawlist(ic, "month");
   33             break;
   34         case 3:
   35             drawlist(ic, "top");
   36             break;
   37         case 4:
   38             drawlist(ic, "year");
   39             break;
   40         case 5:
   41             drawsummary(ic, 0, 0);
   42             break;
   43         case 51:
   44             drawsummary(ic, 1, cfg.hourlyrate); // horizontal
   45             break;
   46         case 52:
   47             drawsummary(ic, 2, cfg.hourlyrate); // vertical
   48             break;
   49         case 7:
   50             drawhourly(ic, cfg.hourlyrate);
   51             break;
   52         case 8:
   53             drawlist(ic, "hour");
   54             break;
   55         case 9:
   56             drawlist(ic, "fiveminute");
   57             break;
   58         case 10:
   59             drawfivegraph(ic, cfg.hourlyrate, cfg.fivegresultcount, cfg.fivegheight);
   60             break;
   61         default:
   62             printf("Error: No such query mode: %d\n", cfg.qmode);
   63             exit(EXIT_FAILURE);
   64     }
   65 
   66     /* enable background transparency if needed */
   67     if (cfg.transbg) {
   68         gdImageColorTransparent(ic->im, ic->cbackground);
   69     }
   70 }
   71 
   72 #if HAVE_DECL_GD_NEAREST_NEIGHBOUR
   73 void scaleimage(IMAGECONTENT *ic)
   74 {
   75     gdImagePtr im_scaled;
   76     unsigned int width = 0, height = 0;
   77 
   78     if (cfg.imagescale == 100 || ic->im == NULL) {
   79         return;
   80     }
   81 
   82     width = (unsigned int)((float)gdImageSX(ic->im) * ((float)cfg.imagescale / (float)100));
   83     height = (unsigned int)((float)gdImageSY(ic->im) * ((float)cfg.imagescale / (float)100));
   84 
   85     if (width < 100 || height < 100) {
   86         return;
   87     }
   88 
   89     if (width > 5000 || height > 5000) {
   90         return;
   91     }
   92 
   93     /* keep output sharp when percent is an exact multiplier */
   94     if (cfg.imagescale % 100 == 0) {
   95         gdImageSetInterpolationMethod(ic->im, GD_NEAREST_NEIGHBOUR);
   96     }
   97 
   98     im_scaled = gdImageScale(ic->im, width, height);
   99     if (im_scaled == NULL) {
  100         return;
  101     }
  102 
  103     gdImageDestroy(ic->im);
  104     ic->im = im_scaled;
  105 }
  106 #endif
  107 
  108 int drawhours(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate)
  109 {
  110     int i, tmax = 0, s = 0, step, prev = 0, diff = 0, chour;
  111     int x = xpos, y = ypos, extrax = 0, extray = 0, xt = 0;
  112     double ratediv;
  113     uint64_t max = 1, scaleunit = 0;
  114     char buffer[32];
  115     struct tm *d;
  116     dbdatalist *datalist = NULL, *datalist_i = NULL;
  117     dbdatalistinfo datainfo;
  118     HOURDATA hourdata[24];
  119     gdFontPtr font;
  120 
  121     if (ic->large) {
  122         font = gdFontGetSmall();
  123     } else {
  124         font = gdFontGetTiny();
  125     }
  126 
  127     for (i = 0; i < 24; i++) {
  128         hourdata[i].rx = hourdata[i].tx = 0;
  129         hourdata[i].date = 0;
  130     }
  131 
  132     if (cfg.hourlygmode == 0) {
  133         buffer[0] = '\0';
  134     } else {
  135         snprintf(buffer, 32, "today");
  136     }
  137 
  138     if (!db_getdata_range(&datalist, &datainfo, ic->interface.name, "hour", 24, buffer, "") || datainfo.count == 0) {
  139         gdImageString(ic->im, ic->font, x + (32 * ic->font->w), y + 54, (unsigned char *)"no data available", ic->ctext);
  140         return 0;
  141     }
  142 
  143     datalist_i = datalist;
  144 
  145     while (datalist_i != NULL) {
  146         d = localtime(&datalist_i->timestamp);
  147         if (hourdata[d->tm_hour].date != 0 || ic->interface.updated - datalist_i->timestamp > 86400) {
  148             datalist_i = datalist_i->next;
  149             continue;
  150         }
  151         hourdata[d->tm_hour].rx = datalist_i->rx;
  152         hourdata[d->tm_hour].tx = datalist_i->tx;
  153         hourdata[d->tm_hour].date = datalist_i->timestamp;
  154         datalist_i = datalist_i->next;
  155     }
  156     dbdatalistfree(&datalist);
  157 
  158     ic->current = ic->interface.updated;
  159     chour = localtime(&ic->current)->tm_hour;
  160 
  161     if (cfg.rateunit) {
  162         ratediv = 450; /* x * 8 / 3600 */
  163     } else {
  164         ratediv = 3600;
  165     }
  166 
  167     /* tmax (time max) = current hour */
  168     /* max = transfer max */
  169 
  170     for (i = 0; i < 24; i++) {
  171         /* convert hourly transfer to hourly rate if needed */
  172         if (rate) {
  173             if ((ic->current - hourdata[i].date) > 3600) {
  174                 hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv);
  175                 hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv);
  176             } else {
  177                 /* scale ongoing hour properly */
  178                 if (chour != i) {
  179                     hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / ratediv);
  180                     hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / ratediv);
  181                 } else {
  182                     d = localtime(&ic->current);
  183                     diff = d->tm_min * 60;
  184                     if (!diff) {
  185                         diff = 60;
  186                     }
  187                     if (cfg.rateunit == 1) {
  188                         hourdata[i].rx *= 8;
  189                         hourdata[i].tx *= 8;
  190                     }
  191                     hourdata[i].rx = (uint64_t)((double)hourdata[i].rx / (double)diff);
  192                     hourdata[i].tx = (uint64_t)((double)hourdata[i].tx / (double)diff);
  193                 }
  194             }
  195         }
  196 
  197         if (hourdata[i].date >= hourdata[tmax].date) {
  198             tmax = i;
  199         }
  200         if (hourdata[i].rx >= max) {
  201             max = hourdata[i].rx;
  202         }
  203         if (hourdata[i].tx >= max) {
  204             max = hourdata[i].tx;
  205         }
  206     }
  207 
  208     if (ic->large) {
  209         x += 14;
  210         extrax = 145;
  211         extray = 35;
  212     }
  213 
  214     /* scale values */
  215     scaleunit = getscale(max, rate);
  216 
  217     s = (int)lrint(((double)scaleunit / (double)max) * (124 + extray));
  218     if (s < SCALEMINPIXELS) {
  219         step = 2;
  220     } else {
  221         step = 1;
  222     }
  223 
  224     xt = x + 36;
  225 
  226     for (i = step; i * s <= (124 + extray + 4); i = i + step) {
  227         gdImageDashedLine(ic->im, xt, y + 124 - (i * s), xt + 424 + extrax, y + 124 - (i * s), ic->cline);
  228         gdImageDashedLine(ic->im, xt, y + 124 - prev - (step * s) / 2, xt + 424 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel);
  229         gdImageString(ic->im, font, x + 16 - (ic->large * 3), y + 121 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
  230         prev = i * s;
  231     }
  232     if ((prev + (step * s) / 2) <= (124 + extray + 4)) {
  233         gdImageDashedLine(ic->im, xt, y + 124 - prev - (step * s) / 2, xt + 424 + extrax, y + 124 - prev - (step * s) / 2, ic->clinel);
  234     }
  235 
  236     /* scale text */
  237     gdImageStringUp(ic->im, font, x - 2 - (ic->large * 14), y + 58 + (rate * 10) - (extray / 2), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext);
  238 
  239     /* axis */
  240     gdImageLine(ic->im, xt - 4, y + 124, xt + 430 + extrax, y + 124, ic->ctext);
  241     gdImageLine(ic->im, xt, y - 10 - extray, xt, y + 124 + 4, ic->ctext);
  242 
  243     /* arrows */
  244     drawarrowup(ic, xt, y - 9 - extray);
  245     drawarrowright(ic, xt + 429 + extrax, y + 124);
  246 
  247     xt = x + 440 + extrax;
  248 
  249     /* keep alignment when midnight line isn't shown s*/
  250     if (cfg.hourlygmode || tmax - 23 == 0) {
  251         xt--;
  252     }
  253 
  254     /* x-axis values and poles */
  255     for (i = 0; i < 24; i++) {
  256         if (cfg.hourlygmode == 0) {
  257             s = tmax - i;
  258             if (s < 0) {
  259                 s += 24;
  260             }
  261         } else {
  262             s = 23 - i;
  263         }
  264         snprintf(buffer, 32, "%02d ", s);
  265         if (hourdata[s].date == 0) {
  266             chour = ic->cline;
  267         } else {
  268             chour = ic->ctext;
  269         }
  270         gdImageString(ic->im, font, xt, y + 128, (unsigned char *)buffer, chour);
  271         drawpoles(ic, xt - 2, y - extray, 124 + extray, hourdata[s].rx, hourdata[s].tx, max);
  272         gdImageLine(ic->im, xt - 4 - (ic->large * 3), y + 124, xt + 12 + (ic->large * 3), y + 124, chour);
  273         if (s == 0 && i != 23) {
  274             /* midnight line */
  275             gdImageLine(ic->im, xt - 5 - (ic->large * 3), y - 5 - extray, xt - 5 - (ic->large * 3), y + 124 - 1, ic->clinel);
  276             xt--;
  277         }
  278         xt = xt - (17 + ic->large * 6);
  279     }
  280 
  281     return 1;
  282 }
  283 
  284 void drawhourly(IMAGECONTENT *ic, const int rate)
  285 {
  286     int width, height, headermod = 0;
  287 
  288     width = 500 + (ic->large * 168);
  289     height = 200 + (ic->large * 48);
  290 
  291     if (!ic->showheader) {
  292         headermod = 26;
  293         height -= 22;
  294     }
  295 
  296     imageinit(ic, width, height);
  297     layoutinit(ic, " / hourly", width, height);
  298 
  299     if (drawhours(ic, 12, 46 - headermod + (ic->large * 40), rate)) {
  300         drawlegend(ic, width / 2 - (ic->large * 10), 183 - headermod + (ic->large * 46), 0);
  301     }
  302 }
  303 
  304 void drawlist(IMAGECONTENT *ic, const char *listname)
  305 {
  306     ListType listtype = LT_None;
  307     int textx, texty, offsetx = 0;
  308     int width, height, headermod, i = 1, rowcount = 0;
  309     int estimateavailable = 0, estimatevisible = 0;
  310     int32_t limit;
  311     uint64_t e_rx = 0, e_tx = 0, e_secs = 0;
  312     char buffer[512], datebuff[16], daybuff[16];
  313     char stampformat[64], titlename[16], colname[8];
  314     struct tm *d;
  315     time_t current;
  316     dbdatalist *datalist = NULL, *datalist_i = NULL;
  317     dbdatalistinfo datainfo;
  318 
  319     if (strcmp(listname, "day") == 0) {
  320         listtype = LT_Day;
  321         strncpy_nt(colname, listname, 8);
  322         snprintf(titlename, 16, "daily");
  323         strncpy_nt(stampformat, cfg.dformat, 64);
  324         limit = cfg.listdays;
  325     } else if (strcmp(listname, "month") == 0) {
  326         listtype = LT_Month;
  327         strncpy_nt(colname, listname, 8);
  328         snprintf(titlename, 16, "monthly");
  329         strncpy_nt(stampformat, cfg.mformat, 64);
  330         limit = cfg.listmonths;
  331     } else if (strcmp(listname, "year") == 0) {
  332         listtype = LT_Year;
  333         strncpy_nt(colname, listname, 8);
  334         snprintf(titlename, 16, "yearly");
  335         strncpy_nt(stampformat, "%Y", 64);
  336         limit = cfg.listyears;
  337     } else if (strcmp(listname, "top") == 0) {
  338         listtype = LT_Top;
  339         snprintf(colname, 8, "day");
  340         strncpy_nt(stampformat, cfg.tformat, 64);
  341         limit = cfg.listtop;
  342         offsetx = 5 * ic->font->w;
  343     } else if (strcmp(listname, "hour") == 0) {
  344         listtype = LT_Hour;
  345         strncpy_nt(colname, listname, 8);
  346         snprintf(titlename, 16, "hourly");
  347         strncpy_nt(stampformat, "%H:%M", 64);
  348         limit = cfg.listhours;
  349     } else if (strcmp(listname, "fiveminute") == 0) {
  350         listtype = LT_5min;
  351         strncpy_nt(colname, "time", 8);
  352         snprintf(titlename, 16, "5 minute");
  353         strncpy_nt(stampformat, "%H:%M", 64);
  354         limit = cfg.listfivemins;
  355     } else {
  356         return;
  357     }
  358 
  359     if (limit < 0) {
  360         limit = 0;
  361     }
  362 
  363     daybuff[0] = '\0';
  364 
  365     db_getdata_range(&datalist, &datainfo, ic->interface.name, listname, (uint32_t)limit, ic->databegin, ic->dataend);
  366 
  367     datalist_i = datalist;
  368 
  369     if (strlen(ic->dataend) == 0 && datainfo.count > 0 && listtype != LT_Top) {
  370         getestimates(&e_rx, &e_tx, listtype, ic->interface.updated, &datalist);
  371         if ((cfg.estimatestyle > 0 || cfg.barshowsrate > 0) && e_rx + e_tx > datainfo.max) {
  372             datainfo.max = e_rx + e_tx;
  373         }
  374         estimateavailable = 1;
  375         if (listtype == LT_Day || listtype == LT_Month || listtype == LT_Year) {
  376             estimatevisible = 1;
  377         }
  378     }
  379 
  380     if (listtype == LT_Top) {
  381         if (limit > 0 && datainfo.count < (uint32_t)limit) {
  382             limit = (int32_t)datainfo.count;
  383         }
  384         if (limit <= 0 || datainfo.count > 999) {
  385             snprintf(titlename, 16, "top");
  386         } else {
  387             snprintf(titlename, 16, "top %d", limit);
  388         }
  389     }
  390 
  391     if (listtype == LT_Hour || listtype == LT_5min) {
  392         while (datalist_i != NULL) {
  393             d = localtime(&datalist_i->timestamp);
  394             strftime(datebuff, 16, cfg.dformat, d);
  395             if (strcmp(daybuff, datebuff) != 0) {
  396                 rowcount += 1;
  397                 strcpy(daybuff, datebuff);
  398             }
  399             datalist_i = datalist_i->next;
  400         }
  401         datalist_i = datalist;
  402         daybuff[0] = '\0';
  403     }
  404     rowcount += datainfo.count;
  405 
  406     width = 83 * ic->font->w + 2 + (ic->large * 2);
  407     height = 62 + 3 * ic->lineheight;
  408 
  409     // less space needed when no estimate or sum is shown (Top, 5min and Hour never have estimate)
  410     if ((!estimatevisible && datainfo.count < 2) || (listtype == LT_Top || listtype == LT_Hour || listtype == LT_5min)) {
  411         height = 62 + 2 * ic->lineheight;
  412     }
  413 
  414     // exception for 5min and Hour when having sum shown
  415     if ((listtype == LT_5min || listtype == LT_Hour) && datainfo.count > 1 && strlen(ic->dataend) > 0) {
  416         height = 62 + 3 * ic->lineheight;
  417     }
  418 
  419     height += (ic->lineheight + cfg.linespaceadjust) * rowcount - cfg.linespaceadjust;
  420 
  421     // "no data available"
  422     if (!datainfo.count) {
  423         height = 98 + (ic->large * 12);
  424     }
  425 
  426     if (!ic->showheader) {
  427         headermod = 26;
  428         height -= 22;
  429     } else {
  430         headermod = 0;
  431     }
  432 
  433     snprintf(buffer, 512, " / %s", titlename);
  434 
  435     imageinit(ic, width, height);
  436     layoutinit(ic, buffer, width, height);
  437 
  438     if (datainfo.count) {
  439         if (listtype == LT_Top) {
  440             if (cfg.ostyle <= 2) {
  441                 drawlegend(ic, 66 * ic->font->w + 2, 40 - headermod, 0);
  442             }
  443             current = time(NULL);
  444             d = localtime(&current);
  445             strftime(daybuff, 16, stampformat, d);
  446         } else { // everything else
  447             if (cfg.ostyle > 2) {
  448                 if (estimateavailable && cfg.barshowsrate) {
  449                     drawlegend(ic, 72 * ic->font->w, 40 - headermod, 1);
  450                 } else {
  451                     drawlegend(ic, 72 * ic->font->w, 40 - headermod, 0);
  452                 }
  453             } else {
  454                 drawlegend(ic, 64 * ic->font->w + 1, 40 - headermod, 0);
  455             }
  456         }
  457     }
  458 
  459     textx = 10;
  460     texty = 40 - headermod;
  461 
  462     if (listtype == LT_Top) { // top
  463         snprintf(buffer, 512, "   #      day        rx           tx          total");
  464     } else { // everything else
  465         snprintf(buffer, 512, " %8s       rx           tx          total", colname);
  466     }
  467     if (cfg.ostyle > 2) {
  468         strcat(buffer, "       avg. rate");
  469         gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
  470         gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (65 * ic->font->w) + offsetx + 2, texty + ic->lineheight + 4, ic->cline);
  471     } else {
  472         gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
  473         gdImageLine(ic->im, textx + 2, texty + ic->lineheight + 4, textx + (50 * ic->font->w) + offsetx - 4, texty + ic->lineheight + 4, ic->cline);
  474     }
  475 
  476     texty += ic->lineheight + 8;
  477 
  478     if (datainfo.count) {
  479         gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
  480         gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
  481         if (cfg.ostyle > 2) {
  482             gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty + ((ic->lineheight + cfg.linespaceadjust) * rowcount) - cfg.linespaceadjust + 5 - (ic->large * 2), ic->cline);
  483         }
  484     } else {
  485         gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (24 * ic->font->w) + offsetx, texty - 4, ic->cline);
  486         gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (37 * ic->font->w) + offsetx, texty - 4, ic->cline);
  487         if (cfg.ostyle > 2) {
  488             gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6 - ic->lineheight, textx + (50 * ic->font->w) + offsetx, texty - 4, ic->cline);
  489         }
  490     }
  491 
  492     while (datalist_i != NULL) {
  493         d = localtime(&datalist_i->timestamp);
  494 
  495         if (listtype == LT_5min || listtype == LT_Hour) {
  496             strftime(datebuff, 16, cfg.dformat, d);
  497             if (strcmp(daybuff, datebuff) != 0) {
  498                 snprintf(buffer, 32, " %s", datebuff);
  499                 gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
  500                 texty += ic->lineheight + cfg.linespaceadjust;
  501                 strcpy(daybuff, datebuff);
  502             }
  503         }
  504 
  505         if (listtype == LT_Top) {
  506             if (strftime(datebuff, 16, stampformat, d) <= 8) {
  507                 snprintf(buffer, 32, "  %2d   %*s", i, getpadding(8, datebuff), datebuff);
  508                 strcat(buffer, "   ");
  509             } else {
  510                 snprintf(buffer, 32, "  %2d  %-*s ", i, getpadding(11, datebuff), datebuff);
  511             }
  512             if (strcmp(datebuff, daybuff) == 0) {
  513                 if (cfg.ostyle > 2) {
  514                     gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (65 * ic->font->w) + offsetx + 2, texty + 11 + (ic->large * 3), ic->cbgoffset);
  515                 } else {
  516                     gdImageFilledRectangle(ic->im, textx + 2, texty + 2 - (ic->large * 1), textx + (50 * ic->font->w) + offsetx - 4, texty + 11 + (ic->large * 3), ic->cbgoffset);
  517                 }
  518             }
  519         } else {
  520             if (strftime(datebuff, 16, stampformat, d) <= 8) {
  521                 snprintf(buffer, 32, "  %*s", getpadding(8, datebuff), datebuff);
  522                 strcat(buffer, "   ");
  523             } else {
  524                 snprintf(buffer, 32, " %-*s ", getpadding(11, datebuff), datebuff);
  525             }
  526         }
  527         strncat(buffer, getvalue(datalist_i->rx, 10, RT_Normal), 32);
  528         strcat(buffer, "   ");
  529         strncat(buffer, getvalue(datalist_i->tx, 10, RT_Normal), 32);
  530         strcat(buffer, "   ");
  531         strncat(buffer, getvalue(datalist_i->rx + datalist_i->tx, 10, RT_Normal), 32);
  532         if (cfg.ostyle > 2) {
  533             strcat(buffer, "  ");
  534             if (datalist_i->next == NULL && issametimeslot(listtype, datalist_i->timestamp, ic->interface.updated)) {
  535                 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 1);
  536             } else {
  537                 e_secs = getperiodseconds(listtype, datalist_i->timestamp, ic->interface.updated, 0);
  538             }
  539             strncat(buffer, gettrafficrate(datalist_i->rx + datalist_i->tx, (time_t)e_secs, 14), 32);
  540         }
  541         gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
  542         if (listtype == LT_Top) {
  543             if (cfg.ostyle > 2) {
  544                 drawbar(ic, textx + (71 * ic->font->w) + 2, texty + 4, 9 * ic->font->w - 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  545             } else {
  546                 drawbar(ic, textx + (56 * ic->font->w), texty + 4, 23 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  547             }
  548         } else { // everything else
  549             if (cfg.ostyle > 2) {
  550                 if (datalist_i->next == NULL && estimateavailable && cfg.barshowsrate) {
  551                     drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 0);
  552                 } else {
  553                     drawbar(ic, textx + (67 * ic->font->w) - 2, texty + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  554                 }
  555             } else {
  556                 drawbar(ic, textx + (51 * ic->font->w) - 2, texty + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  557             }
  558         }
  559         texty += ic->lineheight + cfg.linespaceadjust;
  560         if (datalist_i->next == NULL) {
  561             texty -= cfg.linespaceadjust;
  562             break;
  563         }
  564         datalist_i = datalist_i->next;
  565         i++;
  566     }
  567 
  568     if (!datainfo.count) {
  569         i = 17 * ic->font->w;
  570         if (cfg.ostyle > 2) {
  571             i += 8 * ic->font->w;
  572         }
  573         gdImageString(ic->im, ic->font, textx + i, texty, (unsigned char *)"no data available", ic->ctext);
  574         texty += ic->lineheight;
  575     }
  576 
  577     if (cfg.ostyle > 2) {
  578         gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (65 * ic->font->w) + offsetx + 2, texty + 5 - (ic->large * 2), ic->cline);
  579     } else {
  580         gdImageLine(ic->im, textx + 2, texty + 5 - (ic->large * 2), textx + (50 * ic->font->w) + offsetx - 4, texty + 5 - (ic->large * 2), ic->cline);
  581     }
  582 
  583     buffer[0] = '\0';
  584 
  585     /* estimate visible */
  586     if (estimatevisible) {
  587         snprintf(buffer, 32, " estimated   ");
  588         strncat(buffer, getvalue(e_rx, 10, RT_Estimate), 32);
  589         strcat(buffer, "   ");
  590         strncat(buffer, getvalue(e_tx, 10, RT_Estimate), 32);
  591         strcat(buffer, "   ");
  592         strncat(buffer, getvalue(e_rx + e_tx, 10, RT_Estimate), 32);
  593 
  594         if (cfg.estimatestyle) {
  595             if (cfg.ostyle > 2) {
  596                 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, e_rx, e_tx, datainfo.max, 1);
  597                 drawbar(ic, textx + (67 * ic->font->w) - 2, texty - ic->lineheight + 4, 13 * ic->font->w + 1, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  598             } else {
  599                 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, e_rx, e_tx, datainfo.max, 1);
  600                 drawbar(ic, textx + (51 * ic->font->w) - 2, texty - ic->lineheight + 4, 28 * ic->font->w + 3, datalist_i->rx, datalist_i->tx, datainfo.max, 0);
  601             }
  602         }
  603 
  604     /* sum visible */
  605     } else if (strlen(ic->dataend) > 0 && datainfo.count > 1 && listtype != LT_Top) {
  606         if (datainfo.count < 100) {
  607             snprintf(datebuff, 16, "sum of %" PRIu32 "", datainfo.count);
  608         } else {
  609             snprintf(datebuff, 16, "sum");
  610         }
  611         snprintf(buffer, 32, " %9s   ", datebuff);
  612         strncat(buffer, getvalue(datainfo.sumrx, 10, RT_Normal), 32);
  613         strcat(buffer, "   ");
  614         strncat(buffer, getvalue(datainfo.sumtx, 10, RT_Normal), 32);
  615         strcat(buffer, "   ");
  616         strncat(buffer, getvalue(datainfo.sumrx + datainfo.sumtx, 10, RT_Normal), 32);
  617     }
  618 
  619     if (strlen(buffer) > 0) {
  620         texty += 8;
  621         gdImageString(ic->im, ic->font, textx, texty, (unsigned char *)buffer, ic->ctext);
  622 
  623         gdImageLine(ic->im, textx + (24 * ic->font->w) + offsetx, texty - 6, textx + (24 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
  624         gdImageLine(ic->im, textx + (37 * ic->font->w) + offsetx, texty - 6, textx + (37 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
  625         if (cfg.ostyle > 2) {
  626             gdImageLine(ic->im, textx + (50 * ic->font->w) + offsetx, texty - 6, textx + (50 * ic->font->w) + offsetx, texty + ic->lineheight - (ic->large * 2), ic->cline);
  627         }
  628     }
  629 
  630     dbdatalistfree(&datalist);
  631 }
  632 
  633 void drawsummary(IMAGECONTENT *ic, const int layout, const int rate)
  634 {
  635     int width, height, headermod;
  636 
  637     switch (layout) {
  638         // horizontal
  639         case 1:
  640             width = 163 * ic->font->w + 2 + (ic->large * 2);
  641             height = 56 + 12 * ic->lineheight;
  642             break;
  643         // vertical
  644         case 2:
  645             width = 83 * ic->font->w + 2 + (ic->large * 2);
  646             height = 370 + (ic->large * 90);
  647             break;
  648         // no hours
  649         default:
  650             width = 83 * ic->font->w + 2 + (ic->large * 2);
  651             height = 56 + 12 * ic->lineheight;
  652             break;
  653     }
  654 
  655     if (!ic->showheader) {
  656         headermod = 26;
  657         height -= 22;
  658     } else {
  659         headermod = 0;
  660     }
  661 
  662     imageinit(ic, width, height);
  663     layoutinit(ic, "", width, height);
  664 
  665     if (ic->interface.rxtotal == 0 && ic->interface.txtotal == 0) {
  666         gdImageString(ic->im, ic->font, 33 * ic->font->w, 100, (unsigned char *)"no data available", ic->ctext);
  667         return;
  668     }
  669 
  670     drawsummary_alltime(ic, 385 + (ic->large * 125), 57 - headermod + (ic->large * 10));
  671     drawlegend(ic, 410 + (ic->large * 132), 155 - headermod + (ic->large * 40), 0);
  672 
  673     drawsummary_digest(ic, 100, 30 - headermod, "day");
  674     drawsummary_digest(ic, 100, 29 + 7 * ic->lineheight - headermod, "month");
  675 
  676     switch (layout) {
  677         // horizontal
  678         case 1:
  679             if (cfg.summarygraph == 1) {
  680                 drawfiveminutes(ic, 496 + (ic->large * 174), height - 30 - (ic->large * 8), rate, 422 + (ic->large * 154), height - 68 + headermod - (ic->large * 8));
  681             } else {
  682                 drawhours(ic, 500 + (ic->large * 160), 46 + (ic->large * 40) - headermod, rate);
  683             }
  684             break;
  685         // vertical
  686         case 2:
  687             if (cfg.summarygraph == 1) {
  688                 drawfiveminutes(ic, 8 + (ic->large * 14), height - 31 - (ic->large * 6), rate, 422 + (ic->large * 154), 132 + (ic->large * 35));
  689             } else {
  690                 drawhours(ic, 12, 215 + (ic->large * 84) - headermod, rate);
  691             }
  692             break;
  693         default:
  694             break;
  695     }
  696 }
  697 
  698 void drawsummary_alltime(IMAGECONTENT *ic, const int x, const int y)
  699 {
  700     struct tm *d;
  701     char buffer[512], datebuff[16], daytemp[32];
  702     gdFontPtr titlefont;
  703 
  704     if (ic->large) {
  705         titlefont = gdFontGetGiant();
  706     } else {
  707         titlefont = gdFontGetLarge();
  708     }
  709 
  710     gdImageString(ic->im, titlefont, x + 12 + (ic->large * 10), y, (unsigned char *)"all time", ic->ctext);
  711     snprintf(buffer, 4, "rx ");
  712     strncat(buffer, getvalue(ic->interface.rxtotal, 12, RT_Normal), 32);
  713     gdImageString(ic->im, ic->font, x, y + (2 * ic->lineheight), (unsigned char *)buffer, ic->ctext);
  714     snprintf(buffer, 4, "tx ");
  715     strncat(buffer, getvalue(ic->interface.txtotal, 12, RT_Normal), 32);
  716     gdImageString(ic->im, ic->font, x, y + (3 * ic->lineheight), (unsigned char *)buffer, ic->ctext);
  717     snprintf(buffer, 4, " = ");
  718     strncat(buffer, getvalue(ic->interface.rxtotal + ic->interface.txtotal, 12, RT_Normal), 32);
  719     gdImageString(ic->im, ic->font, x, y + (4 * ic->lineheight) + 2 + (ic->large * 4), (unsigned char *)buffer, ic->ctext);
  720     d = localtime(&ic->interface.created);
  721     strftime(datebuff, 16, cfg.tformat, d);
  722     snprintf(daytemp, 24, "since %s", datebuff);
  723     snprintf(buffer, 32, "%23s", daytemp);
  724     gdImageString(ic->im, ic->font, x - 8 * ic->font->w, y + (5 * ic->lineheight) + 10 + (ic->large * 4), (unsigned char *)buffer, ic->ctext);
  725 }
  726 
  727 void drawsummary_digest(IMAGECONTENT *ic, const int x, const int y, const char *mode)
  728 {
  729     int textx, texty, offset = 0;
  730     double rxp, txp, mod;
  731     char buffer[512], datebuff[16], daytemp[32];
  732     time_t yesterday;
  733     struct tm *d = NULL;
  734     dbdatalist *datalist = NULL;
  735     dbdatalist *data_current = NULL, *data_previous = NULL;
  736     dbdatalistinfo datainfo;
  737     gdFontPtr titlefont;
  738 
  739     if (ic->large) {
  740         titlefont = gdFontGetGiant();
  741     } else {
  742         titlefont = gdFontGetLarge();
  743     }
  744 
  745     yesterday = ic->current - 86400;
  746 
  747     switch(mode[0]) {
  748         case 'd':
  749             break;
  750         case 'm':
  751             break;
  752         default:
  753             printf("Error: Unsupported mode %s for summary digest\n", mode);
  754             return;
  755     }
  756 
  757     if (!db_getdata(&datalist, &datainfo, ic->interface.name, mode, 2) || datalist == NULL) {
  758         gdImageString(ic->im, ic->font, 25 * ic->font->w, y + 30, (unsigned char *)"no data available", ic->ctext);
  759         return;
  760     } else if (datalist->next == NULL) {
  761         data_current = datalist;
  762     } else {
  763         data_previous = datalist;
  764         data_current = datalist->next;
  765     }
  766 
  767     /* latest entry */
  768     if (data_current->rx + data_current->tx == 0) {
  769         rxp = txp = 0;
  770     } else {
  771         rxp = (double)data_current->rx / (double)(data_current->rx + data_current->tx) * 100;
  772         txp = (double)100 - rxp;
  773     }
  774 
  775     /* do scaling if needed */
  776     if (data_previous != NULL && (data_current->rx + data_current->tx) < (data_previous->rx + data_previous->tx)) {
  777         mod = (double)(data_current->rx + data_current->tx) / (double)(data_previous->rx + data_previous->tx);
  778         rxp = rxp * mod;
  779         txp = txp * mod;
  780     }
  781 
  782     /* move graph to center if there's only one to draw for this line */
  783     if (data_previous == NULL) {
  784         offset = 85 + (ic->large * 25);
  785     }
  786 
  787     textx = x + offset;
  788     texty = y;
  789 
  790     drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3));
  791 
  792     if (mode[0] == 'd') {
  793         /* get formatted date for today */
  794         d = localtime(&ic->current);
  795         strftime(datebuff, 16, cfg.dformat, d);
  796 
  797         /* get formatted date for current day in database */
  798         d = localtime(&data_current->timestamp);
  799         strftime(daytemp, 16, cfg.dformat, d);
  800 
  801         /* change daytemp to today if formatted days match */
  802         if (strcmp(datebuff, daytemp) == 0) {
  803             strncpy_nt(daytemp, "today", 32);
  804         }
  805     } else if (mode[0] == 'm') {
  806         d = localtime(&data_current->timestamp);
  807         strftime(daytemp, 16, cfg.mformat, d);
  808     }
  809 
  810     snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp);
  811     gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext);
  812 
  813     if (cfg.summaryrate) {
  814         d = localtime(&ic->interface.updated);
  815         if (mode[0] == 'd') {
  816             snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, d->tm_sec + (d->tm_min * 60) + (d->tm_hour * 3600), 15));
  817         } else if (mode[0] == 'm') {
  818             snprintf(buffer, 16, "%15s", gettrafficrate(data_current->rx + data_current->tx, mosecs(data_current->timestamp, ic->interface.updated), 15));
  819         }
  820         gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext);
  821     } else {
  822         texty += 7;
  823     }
  824 
  825     snprintf(buffer, 4, "rx ");
  826     strncat(buffer, getvalue(data_current->rx, 12, RT_Normal), 32);
  827     gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
  828     snprintf(buffer, 4, "tx ");
  829     strncat(buffer, getvalue(data_current->tx, 12, RT_Normal), 32);
  830     gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
  831     snprintf(buffer, 4, " = ");
  832     strncat(buffer, getvalue(data_current->rx + data_current->tx, 12, RT_Normal), 32);
  833     gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext);
  834 
  835     /* previous entry */
  836     if (data_previous != NULL) {
  837         if (data_previous->rx + data_previous->tx == 0) {
  838             rxp = txp = 0;
  839         } else {
  840             rxp = (double)data_previous->rx / (double)(data_previous->rx + data_previous->tx) * 100;
  841             txp = (double)100 - rxp;
  842         }
  843 
  844         /* do scaling if needed */
  845         if ((data_previous->rx + data_previous->tx) < (data_current->rx + data_current->tx)) {
  846             mod = (double)(data_previous->rx + data_previous->tx) / (double)(data_current->rx + data_current->tx);
  847             rxp = rxp * mod;
  848             txp = txp * mod;
  849         }
  850 
  851         textx += 180 + (ic->large * 60);
  852 
  853         drawdonut(ic, textx + 50 + (ic->large * 40), texty + 45 + (ic->large * 10), (float)rxp, (float)txp, 49 + (ic->large * 10), 15 + (ic->large * 3));
  854 
  855         if (mode[0] == 'd') {
  856             /* get formatted date for yesterday */
  857             d = localtime(&yesterday);
  858             strftime(datebuff, 16, cfg.dformat, d);
  859 
  860             /* get formatted date for previous day in database */
  861             d = localtime(&data_previous->timestamp);
  862             strftime(daytemp, 16, cfg.dformat, d);
  863 
  864             /* change daytemp to yesterday if formatted days match */
  865             if (strcmp(datebuff, daytemp) == 0) {
  866                 strncpy_nt(daytemp, "yesterday", 32);
  867             }
  868         } else if (mode[0] == 'm') {
  869             d = localtime(&data_previous->timestamp);
  870             strftime(daytemp, 16, cfg.mformat, d);
  871         }
  872 
  873         snprintf(buffer, 32, "%*s", getpadding(12, daytemp), daytemp);
  874         gdImageString(ic->im, titlefont, textx - 54 + (ic->large * (ic->font->w * 3 - 4)), texty - 1, (unsigned char *)buffer, ic->ctext);
  875 
  876         if (cfg.summaryrate) {
  877             if (mode[0] == 'd') {
  878                 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, 86400, 15));
  879             } else if (mode[0] == 'm') {
  880                 snprintf(buffer, 16, "%15s", gettrafficrate(data_previous->rx + data_previous->tx, dmonth(d->tm_mon) * 86400, 15));
  881             }
  882             gdImageString(ic->im, ic->font, textx - 74, texty + 4 * ic->lineheight + 10, (unsigned char *)buffer, ic->ctext);
  883         } else {
  884             texty += 7;
  885         }
  886 
  887         snprintf(buffer, 4, "rx ");
  888         strncat(buffer, getvalue(data_previous->rx, 12, RT_Normal), 32);
  889         gdImageString(ic->im, ic->font, textx - 74, texty + ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
  890         snprintf(buffer, 4, "tx ");
  891         strncat(buffer, getvalue(data_previous->tx, 12, RT_Normal), 32);
  892         gdImageString(ic->im, ic->font, textx - 74, texty + 2 * ic->lineheight + 6, (unsigned char *)buffer, ic->ctext);
  893         snprintf(buffer, 4, " = ");
  894         strncat(buffer, getvalue(data_previous->rx + data_previous->tx, 12, RT_Normal), 32);
  895         gdImageString(ic->im, ic->font, textx - 74, texty + 3 * ic->lineheight + 8, (unsigned char *)buffer, ic->ctext);
  896     }
  897 
  898     data_current = NULL;
  899     data_previous = NULL;
  900     dbdatalistfree(&datalist);
  901 }
  902 
  903 void drawfivegraph(IMAGECONTENT *ic, const int rate, const int resultcount, const int height)
  904 {
  905     int imagewidth, imageheight = height, headermod = 0;
  906 
  907     imagewidth = resultcount + FIVEMINEXTRASPACE + (ic->large * 14);
  908 
  909     if (!ic->showheader) {
  910         headermod = 22;
  911     }
  912 
  913     imageinit(ic, imagewidth, imageheight);
  914     layoutinit(ic, " / 5 minute", imagewidth, imageheight);
  915 
  916     if (drawfiveminutes(ic, 8 + (ic->large * 14), imageheight - 30 - (ic->large * 8), rate, resultcount, imageheight - 68 + headermod - (ic->large * 8))) {
  917         drawlegend(ic, imagewidth / 2 - (ic->large * 10), imageheight - 17 - (ic->large * 2), 0);
  918     }
  919 }
  920 
  921 int drawfiveminutes(IMAGECONTENT *ic, const int xpos, const int ypos, const int rate, const int resultcount, const int height)
  922 {
  923     int x = xpos, y = ypos, i = 0, t = 0, rxh = 0, txh = 0, step = 0, s = 0, prev = 0;
  924     uint64_t scaleunit, max;
  925     time_t timestamp;
  926     double ratediv, e;
  927     char buffer[32];
  928     struct tm *d;
  929     dbdatalist *datalist = NULL, *datalist_i = NULL;
  930     dbdatalistinfo datainfo;
  931     gdFontPtr font;
  932 
  933     if (ic->large) {
  934         font = gdFontGetSmall();
  935     } else {
  936         font = gdFontGetTiny();
  937     }
  938 
  939     if (!db_getdata(&datalist, &datainfo, ic->interface.name, "fiveminute", (uint32_t)resultcount) || datainfo.count == 0) {
  940         x = (resultcount + FIVEMINEXTRASPACE + (ic->large * 14)) / 2 - (8 * ic->font->w + ic->font->w / 2);
  941         gdImageString(ic->im, ic->font, x, y - (height / 2) - ic->font->h, (unsigned char *)"no data available", ic->ctext);
  942         return 0;
  943     }
  944 
  945     datalist_i = datalist;
  946 
  947     if (cfg.rateunit) {
  948         ratediv = 37.5; /* x * 8 / 300 */
  949     } else {
  950         ratediv = 300;
  951     }
  952 
  953     /* axis */
  954     x += 36;
  955     gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHFULLPADDING), y, ic->ctext);
  956     gdImageLine(ic->im, x + 4, y + 4, x + 4, y - height, ic->ctext);
  957 
  958     /* arrows */
  959     drawarrowup(ic, x + 4, y - 1 - height);
  960     drawarrowright(ic, x + 1 + (resultcount + FIVEMINWIDTHFULLPADDING), y);
  961 
  962     max = datainfo.maxrx + datainfo.maxtx;
  963 
  964     if (datainfo.maxrx == datainfo.maxtx) {
  965         txh = (int)((height - FIVEMINHEIGHTOFFSET * 2) / 2);
  966         rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh;
  967         max = (uint64_t)((double)datainfo.maxrx / ratediv);
  968         t = rxh;
  969     } else if (datainfo.maxrx > datainfo.maxtx) {
  970         txh = (int)lrint(((double)datainfo.maxtx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2));
  971         rxh = height - FIVEMINHEIGHTOFFSET * 2 - txh;
  972         max = (uint64_t)((double)datainfo.maxrx / ratediv);
  973         t = rxh;
  974     } else {
  975         rxh = (int)lrint(((double)datainfo.maxrx / (double)max) * (height - FIVEMINHEIGHTOFFSET * 2));
  976         txh = height - FIVEMINHEIGHTOFFSET * 2 - rxh;
  977         max = (uint64_t)((double)datainfo.maxtx / ratediv);
  978         t = txh;
  979     }
  980 
  981     /* center line */
  982     x += 5;
  983     y -= txh + FIVEMINHEIGHTOFFSET;
  984     gdImageLine(ic->im, x, y, x + (resultcount + FIVEMINWIDTHPADDING), y, ic->ctext);
  985     gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 4 - (ic->large * 3), (unsigned char *)"  0", ic->ctext);
  986 
  987     /* scale values */
  988     scaleunit = getscale(max, rate);
  989 
  990     s = (int)lrint(((double)scaleunit / (double)max) * t);
  991     if (s == 0) {
  992         s = 1; // force to show something when there's not much or any traffic, scale is likely to be wrong in this case
  993     }
  994     while (s * step < SCALEMINPIXELS) {
  995         step++;
  996     }
  997 
  998     if (debug) {
  999         printf("maxrx: %" PRIu64 "\n", datainfo.maxrx);
 1000         printf("maxtx: %" PRIu64 "\n", datainfo.maxtx);
 1001         printf("rxh: %d     txh: %d\n", rxh, txh);
 1002         printf("max divided: %" PRIu64 "\n", max);
 1003         printf("scaleunit:   %" PRIu64 "\nstep: %d\n", scaleunit, step);
 1004         printf("pixels per step: %d\n", s);
 1005         printf("mintime: %" PRIu64 "\nmaxtime: %" PRIu64 "\n", (uint64_t)datainfo.mintime, (uint64_t)datainfo.maxtime);
 1006         printf("count: %u\n", datainfo.count);
 1007     }
 1008 
 1009     /* upper part scale values */
 1010     y--; // adjust to start above center line
 1011     for (i = step; i * s <= rxh; i = i + step) {
 1012         gdImageDashedLine(ic->im, x, y - (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y - (i * s), ic->cline);
 1013         gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel);
 1014         gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 - (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
 1015         prev = i * s;
 1016     }
 1017     if ((prev + (step * s) / 2) <= rxh) {
 1018         gdImageDashedLine(ic->im, x, y - prev - (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y - prev - (step * s) / 2, ic->clinel);
 1019     }
 1020 
 1021     y += 2; // adjust to start below center line
 1022     prev = 0;
 1023 
 1024     /* lower part scale values */
 1025     for (i = step; i * s <= txh; i = i + step) {
 1026         gdImageDashedLine(ic->im, x, y + (i * s), x + (resultcount + FIVEMINWIDTHPADDING), y + (i * s), ic->cline);
 1027         gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel);
 1028         gdImageString(ic->im, font, x - 21 - (ic->large * 3), y - 3 + (i * s) - (ic->large * 3), (unsigned char *)getimagevalue(scaleunit * (unsigned int)i, 3, rate), ic->ctext);
 1029         prev = i * s;
 1030     }
 1031     if ((prev + (step * s) / 2) <= txh) {
 1032         gdImageDashedLine(ic->im, x, y + prev + (step * s) / 2, x + (resultcount + FIVEMINWIDTHPADDING), y + prev + (step * s) / 2, ic->clinel);
 1033     }
 1034 
 1035     y--; // y is now back on center line
 1036 
 1037     /* scale text */
 1038     gdImageStringUp(ic->im, font, x - 39 - (ic->large * 14), ypos - height / 2 + (rate * 10), (unsigned char *)getimagescale(scaleunit * (unsigned int)step, rate), ic->ctext);
 1039 
 1040     timestamp = datainfo.maxtime - (resultcount * 300);
 1041 
 1042     while (datalist_i != NULL && datalist_i->timestamp < timestamp + 300) {
 1043         if (debug) {
 1044             printf("Skip data, %" PRIu64 " < %" PRIu64 "\n", (uint64_t)datalist_i->timestamp, (uint64_t)timestamp + 300);
 1045         }
 1046         datalist_i = datalist_i->next;
 1047     }
 1048 
 1049     for (i = 0; i < resultcount; i++) {
 1050 
 1051         if (datalist_i == NULL) {
 1052             break;
 1053         }
 1054 
 1055         timestamp += 300;
 1056         d = localtime(&timestamp);
 1057 
 1058         if (d->tm_min == 0 && i > 2) {
 1059             if (d->tm_hour % 2 == 0) {
 1060                 if (d->tm_hour == 0) {
 1061                     gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cline);
 1062                 } else {
 1063                     gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset);
 1064                 }
 1065 
 1066                 if (i > font->w) {
 1067                     snprintf(buffer, 32, "%02d", d->tm_hour);
 1068                     if (datalist_i->timestamp > timestamp) {
 1069                         gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->cline);
 1070                     } else {
 1071                         gdImageString(ic->im, font, x + i - font->w + 1, y + txh + font->h - (ic->large * 5), (unsigned char *)buffer, ic->ctext);
 1072                     }
 1073                 }
 1074             } else {
 1075                 gdImageLine(ic->im, x + i, y + txh - 1 + FIVEMINHEIGHTOFFSET, x + i, y - rxh - 1, ic->cbgoffset);
 1076             }
 1077             gdImageSetPixel(ic->im, x + i, y, ic->ctext);
 1078         }
 1079 
 1080         if (datalist_i->timestamp > timestamp) {
 1081             gdImageSetPixel(ic->im, x + i, y, ic->cline);
 1082             gdImageSetPixel(ic->im, x + i, y + txh + FIVEMINHEIGHTOFFSET, ic->cline);
 1083             continue;
 1084         }
 1085 
 1086         /* only the last entry can be the currently ongoing period that may need scaling */
 1087         if (datalist_i->next == NULL && issametimeslot(LT_5min, datalist_i->timestamp, ic->interface.updated)) {
 1088             e = (double)(ic->interface.updated - datalist_i->timestamp) / (double)300;
 1089             if (e < 0.01) {
 1090                 e = 1;
 1091             }
 1092         } else {
 1093             e = 1;
 1094         }
 1095 
 1096         t = (int)lrint(((double)datalist_i->rx / e / (double)datainfo.maxrx) * rxh);
 1097         if (t > rxh) {
 1098             t = rxh;
 1099         }
 1100         drawpole(ic, x + i, y - 1, t, 1, ic->crx);
 1101 
 1102         t = (int)lrint(((double)datalist_i->tx / e / (double)datainfo.maxtx) * txh);
 1103         if (t > txh) {
 1104             t = txh;
 1105         }
 1106         drawpole(ic, x + i, y + 1, t, 2, ic->ctx);
 1107 
 1108         datalist_i = datalist_i->next;
 1109     }
 1110 
 1111     dbdatalistfree(&datalist);
 1112 
 1113     return 1;
 1114 }