"Fossies" - the Fresh Open Source Software Archive

Member "vnstat-2.9/src/image_support.c" (15 Dec 2021, 16238 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. For more information about "image_support.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.8_vs_2.9.

    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 imageinit(IMAGECONTENT *ic, const int width, const int height)
    9 {
   10     int rgb[3];
   11 
   12     ic->im = gdImageCreate(width, height);
   13 
   14     /* text, edge and header colors */
   15     hextorgb(cfg.ctext, rgb);
   16     ic->ctext = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   17     colorinitcheck("ctext", ic->ctext, cfg.ctext, rgb);
   18     hextorgb(cfg.cedge, rgb);
   19     ic->cedge = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   20     colorinitcheck("cedge", ic->cedge, cfg.cedge, rgb);
   21     hextorgb(cfg.cheader, rgb);
   22     ic->cheader = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   23     colorinitcheck("cheader", ic->cheader, cfg.cheader, rgb);
   24     hextorgb(cfg.cheadertitle, rgb);
   25     ic->cheadertitle = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   26     colorinitcheck("cheadertitle", ic->cheadertitle, cfg.cheadertitle, rgb);
   27     hextorgb(cfg.cheaderdate, rgb);
   28     ic->cheaderdate = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   29     colorinitcheck("cheaderdate", ic->cheaderdate, cfg.cheaderdate, rgb);
   30 
   31     /* lines */
   32     hextorgb(cfg.cline, rgb);
   33     ic->cline = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   34     colorinitcheck("cline", ic->cline, cfg.cline, rgb);
   35     if (cfg.clinel[0] == '-') {
   36         modcolor(rgb, 50, 1);
   37     } else {
   38         hextorgb(cfg.clinel, rgb);
   39     }
   40     ic->clinel = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   41     colorinitcheck("clinel", ic->clinel, cfg.clinel, rgb);
   42 
   43     /* background */
   44     hextorgb(cfg.cbg, rgb);
   45     ic->cbackground = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   46     colorinitcheck("cbackground", ic->cbackground, cfg.cbg, rgb);
   47     modcolor(rgb, -35, 0);
   48     ic->cvnstat = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   49     colorinitcheck("cvnstat", ic->cvnstat, cfg.cbg, rgb);
   50     hextorgb(cfg.cbg, rgb);
   51     modcolor(rgb, -15, 0);
   52     ic->cbgoffset = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   53     colorinitcheck("cbgoffset", ic->cbgoffset, cfg.cbg, rgb);
   54     hextorgb(cfg.cbg, rgb);
   55     modcolor(rgb, -40, 0);
   56     ic->cbgoffsetmore = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   57     colorinitcheck("cbgoffsetmore", ic->cbgoffsetmore, cfg.cbg, rgb);
   58 
   59     /* rx */
   60     hextorgb(cfg.crx, rgb);
   61     ic->crx = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   62     colorinitcheck("crx", ic->crx, cfg.crx, rgb);
   63     if (cfg.crxd[0] == '-') {
   64         modcolor(rgb, -50, 1);
   65     } else {
   66         hextorgb(cfg.crxd, rgb);
   67     }
   68     ic->crxd = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   69     colorinitcheck("crxd", ic->crxd, cfg.crxd, rgb);
   70 
   71     /* tx */
   72     hextorgb(cfg.ctx, rgb);
   73     ic->ctx = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   74     colorinitcheck("ctx", ic->ctx, cfg.ctx, rgb);
   75     if (cfg.ctxd[0] == '-') {
   76         modcolor(rgb, -50, 1);
   77     } else {
   78         hextorgb(cfg.ctxd, rgb);
   79     }
   80     ic->ctxd = gdImageColorAllocate(ic->im, rgb[0], rgb[1], rgb[2]);
   81     colorinitcheck("ctxd", ic->ctxd, cfg.ctxd, rgb);
   82 }
   83 
   84 void colorinitcheck(const char *color, const int value, const char *cfgtext, const int *rgb)
   85 {
   86     if (value == -1) {
   87         printf("Error: ImageColorAllocate failed.\n");
   88         printf("       C: \"%s\" T: \"%s\" RGB: %d/%d/%d\n", color, cfgtext, rgb[0], rgb[1], rgb[2]);
   89         exit(EXIT_FAILURE);
   90     }
   91 }
   92 
   93 void layoutinit(IMAGECONTENT *ic, const char *title, const int width, const int height)
   94 {
   95     struct tm *d;
   96     char datestring[64], buffer[512];
   97     gdFontPtr datefont;
   98 
   99     if (ic->large) {
  100         datefont = gdFontGetSmall();
  101     } else {
  102         datefont = gdFontGetTiny();
  103     }
  104 
  105     /* get time in given format */
  106     d = localtime(&ic->interface.updated);
  107     strftime(datestring, 64, cfg.hformat, d);
  108 
  109     /* background, edges */
  110     gdImageFill(ic->im, 0, 0, ic->cbackground);
  111     if (ic->showedge) {
  112         gdImageRectangle(ic->im, 0, 0, width - 1, height - 1, ic->cedge);
  113     }
  114 
  115     /* titlebox with title */
  116     if (ic->showheader) {
  117 
  118         if (strlen(ic->headertext)) {
  119             strncpy_nt(buffer, ic->headertext, 65);
  120         } else {
  121             if (strcmp(ic->interface.name, ic->interface.alias) == 0 || strlen(ic->interface.alias) == 0) {
  122                 snprintf(buffer, 512, "%s%s", ic->interface.name, title);
  123             } else {
  124                 snprintf(buffer, 512, "%s (%s)%s", ic->interface.alias, ic->interface.name, title);
  125             }
  126         }
  127 
  128         gdImageFilledRectangle(ic->im, 2 + ic->showedge, 2 + ic->showedge, width - 3 - ic->showedge, 24, ic->cheader);
  129         gdImageString(ic->im, gdFontGetGiant(), 12, 5 + ic->showedge, (unsigned char *)buffer, ic->cheadertitle);
  130     }
  131 
  132     /* date */
  133     if (!ic->showheader || ic->altdate) {
  134         gdImageString(ic->im, datefont, 5 + ic->showedge, height - 12 - ic->showedge - (ic->large * 3), (unsigned char *)datestring, ic->cvnstat);
  135     } else {
  136         gdImageString(ic->im, datefont, width - (((int)strlen(datestring)) * datefont->w + 12), 9 + ic->showedge - (ic->large * 3), (unsigned char *)datestring, ic->cheaderdate);
  137     }
  138 
  139     /* generator */
  140     gdImageString(ic->im, gdFontGetTiny(), width - 114 - ic->showedge, height - 12 - ic->showedge, (unsigned char *)"vnStat / Teemu Toivola", ic->cvnstat);
  141 }
  142 
  143 void drawlegend(IMAGECONTENT *ic, const int x, const int y, const short israte)
  144 {
  145     if (!ic->showlegend) {
  146         return;
  147     }
  148 
  149     if (!israte) {
  150         gdImageString(ic->im, ic->font, x, y, (unsigned char *)"rx     tx", ic->ctext);
  151 
  152         gdImageFilledRectangle(ic->im, x - 12 - (ic->large * 2), y + 4, x - 12 + ic->font->w - (ic->large * 2), y + 4 + ic->font->w, ic->crx);
  153         gdImageRectangle(ic->im, x - 12 - (ic->large * 2), y + 4, x - 12 + ic->font->w - (ic->large * 2), y + 4 + ic->font->w, ic->ctext);
  154 
  155         gdImageFilledRectangle(ic->im, x + 30 + (ic->large * 12), y + 4, x + 30 + ic->font->w + (ic->large * 12), y + 4 + ic->font->w, ic->ctx);
  156         gdImageRectangle(ic->im, x + 30 + (ic->large * 12), y + 4, x + 30 + ic->font->w + (ic->large * 12), y + 4 + ic->font->w, ic->ctext);
  157     } else {
  158         gdImageString(ic->im, ic->font, x - 12, y, (unsigned char *)"rx   tx rate", ic->ctext);
  159 
  160         gdImageFilledRectangle(ic->im, x - 22 - (ic->large * 3), y + 4, x - 22 + ic->font->w - (ic->large * 3), y + 4 + ic->font->w, ic->crx);
  161         gdImageRectangle(ic->im, x - 22 - (ic->large * 3), y + 4, x - 22 + ic->font->w - (ic->large * 3), y + 4 + ic->font->w, ic->ctext);
  162 
  163         gdImageFilledRectangle(ic->im, x + 8 + (ic->large * 7), y + 4, x + 8 + ic->font->w + (ic->large * 7), y + 4 + ic->font->w, ic->ctx);
  164         gdImageRectangle(ic->im, x + 8 + (ic->large * 7), y + 4, x + 8 + ic->font->w + (ic->large * 7), y + 4 + ic->font->w, ic->ctext);
  165     }
  166 }
  167 
  168 void drawbar(IMAGECONTENT *ic, const int x, const int y, const int len, const uint64_t rx, const uint64_t tx, const uint64_t max, const short isestimate)
  169 {
  170     int rxl, txl, width = len, overlap = 0;
  171     int crx = ic->crx, ctx = ic->ctx, crxd = ic->crxd, ctxd = ic->ctxd;
  172     int ybeginoffset = YBEGINOFFSET, yendoffset = YBEGINOFFSET + ic->font->h - 6 - ic->large;
  173 
  174     if (isestimate) {
  175 
  176         switch (cfg.estimatestyle) {
  177             case 0:
  178                 return;
  179             case 1:
  180                 crx = ic->cbgoffsetmore;
  181                 ctx = ic->cbgoffsetmore;
  182                 crxd = ic->cbgoffsetmore;
  183                 ctxd = ic->cbgoffsetmore;
  184                 break;
  185             case 2:
  186                 ybeginoffset += 19;
  187                 yendoffset += 19;
  188                 crxd = ic->crx;
  189                 ctxd = ic->ctx;
  190                 crx = ic->cbgoffset;
  191                 ctx = ic->cbgoffset;
  192                 break;
  193             default:
  194                 return;
  195         }
  196     }
  197 
  198     if ((rx + tx) < max) {
  199         width = (int)lrint(((double)(rx + tx) / (double)max) * len);
  200     } else if ((rx + tx) > max || max == 0) {
  201         if (debug && (rx + tx) > max) {
  202             printf("Warning: Bar rx + tx sum exceeds given maximum, no bar shown\n");
  203         }
  204         return;
  205     }
  206 
  207     if (width <= 0) {
  208         return;
  209     }
  210 
  211     if (tx > rx) {
  212         rxl = (int)lrint(((double)rx / (double)(rx + tx) * width));
  213         txl = width - rxl;
  214     } else {
  215         txl = (int)lrint(((double)tx / (double)(rx + tx) * width));
  216         rxl = width - txl;
  217     }
  218 
  219     if (rxl) {
  220         if (txl > 0) {
  221             overlap = 1;
  222         }
  223         gdImageFilledRectangle(ic->im, x, y + ybeginoffset, x + rxl - 1 + overlap, y + yendoffset, crx);
  224         gdImageRectangle(ic->im, x, y + ybeginoffset, x + rxl - 1 + overlap, y + yendoffset, crxd);
  225     }
  226 
  227     if (txl) {
  228         gdImageFilledRectangle(ic->im, x + rxl, y + ybeginoffset, x + rxl + txl - 1, y + yendoffset, ctx);
  229         gdImageRectangle(ic->im, x + rxl, y + ybeginoffset, x + rxl + txl - 1, y + yendoffset, ctxd);
  230     }
  231 }
  232 
  233 void drawpoles(IMAGECONTENT *ic, const int x, const int y, const int len, const uint64_t rx, const uint64_t tx, const uint64_t max)
  234 {
  235     int l;
  236 
  237     if (rx > 0) {
  238         l = (int)lrint(((double)rx / (double)max) * len);
  239         if (l > 0) {
  240             gdImageFilledRectangle(ic->im, x - (ic->large * 2), y + (len - l), x + 7 + (ic->large * 0), y + len, ic->crx);
  241         }
  242     }
  243 
  244     if (tx > 0) {
  245         l = (int)lrint(((double)tx / (double)max) * len);
  246         if (l > 0) {
  247             gdImageFilledRectangle(ic->im, x + 5 - (ic->large * 0), y + (len - l), x + 12 + (ic->large * 2), y + len, ic->ctx);
  248         }
  249     }
  250 }
  251 
  252 void drawdonut(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp, const int size, const int holesize)
  253 {
  254     // libgd versions 2.2.3 - 2.2.5 have bug in gdImageFilledArc() https://github.com/libgd/libgd/issues/351
  255     // so workaround needs to be used, 2.2 version series ends with 2.2.5 and the bug is fixed starting from 2.3.0
  256     if (GD_MAJOR_VERSION == 2 && GD_MINOR_VERSION == 2 && GD_RELEASE_VERSION >= 3) {
  257         drawdonut_libgd_bug_workaround(ic, x, y, rxp, txp, size, holesize);
  258     } else {
  259         drawdonut_libgd_native(ic, x, y, rxp, txp, size, holesize);
  260     }
  261 }
  262 
  263 void drawdonut_libgd_bug_workaround(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp, const int size, const int holesize)
  264 {
  265     int rxarc = 0, txarc = 0;
  266 
  267     if ((int)(rxp + txp) > 0) {
  268         rxarc = (int)lrintf(360 * (rxp / (float)100));
  269         if ((int)(rxp + txp) == 100) {
  270             txarc = 360 - rxarc;
  271         } else {
  272             txarc = (int)lrintf(360 * (txp / (float)100));
  273         }
  274     }
  275 
  276     // background filled circle
  277     gdImageFilledArc(ic->im, x, y, size, size, 0, 360, ic->cbgoffset, 0);
  278 
  279     if (txarc) {
  280         gdImageFilledArc(ic->im, x, y, size, size, 270, 270 + txarc, ic->ctxd, gdEdged | gdNoFill);
  281         if (txarc >= 5) {
  282             gdImageFill(ic->im, x + 1, y - (size / 2 - 3), ic->ctx);
  283         }
  284         gdImageFilledArc(ic->im, x, y, holesize, holesize, 270, 270 + txarc, ic->ctxd, gdEdged | gdNoFill);
  285     }
  286 
  287     if (rxarc) {
  288         gdImageFilledArc(ic->im, x, y, size, size, 270 + txarc, 270 + txarc + rxarc, ic->crxd, gdEdged | gdNoFill);
  289         if (rxarc >= 5) {
  290             gdImageFill(ic->im, (int)(x + (size / 2 - 3) * cos((int)((270 * 2 + 2 * txarc + rxarc) / 2) * M_PI / 180)), (int)(y + (size / 2 - 3) * sin((int)((270 * 2 + 2 * txarc + rxarc) / 2) * M_PI / 180)), ic->crx);
  291         }
  292         gdImageFilledArc(ic->im, x, y, holesize, holesize, 270 + txarc, 270 + txarc + rxarc, ic->crxd, gdEdged | gdNoFill);
  293     }
  294 
  295     // remove center from background filled circle, making it a donut
  296     gdImageFilledArc(ic->im, x, y, holesize - 2, holesize - 2, 0, 360, ic->cbackground, 0);
  297 }
  298 
  299 void drawdonut_libgd_native(IMAGECONTENT *ic, const int x, const int y, const float rxp, const float txp, const int size, const int holesize)
  300 {
  301     int rxarc = 0, txarc = 0;
  302 
  303     if ((int)(rxp + txp) > 0) {
  304         rxarc = (int)(360 * (rxp / (float)100));
  305         if ((int)(rxp + txp) == 100) {
  306             txarc = 360 - rxarc;
  307         } else {
  308             txarc = (int)(360 * (txp / (float)100));
  309         }
  310     }
  311 
  312     // background filled circle
  313     gdImageFilledArc(ic->im, x, y, size, size, 0, 360, ic->cbgoffset, 0);
  314 
  315     if (txarc) {
  316         gdImageFilledArc(ic->im, x, y, size, size, 270, 270 + txarc, ic->ctx, 0);
  317         gdImageFilledArc(ic->im, x, y, size, size, 270, 270 + txarc, ic->ctxd, gdEdged | gdNoFill);
  318         gdImageFilledArc(ic->im, x, y, holesize, holesize, 270, 270 + txarc, ic->ctxd, gdEdged | gdNoFill);
  319     }
  320 
  321     if (rxarc) {
  322         gdImageFilledArc(ic->im, x, y, size, size, 270 + txarc, 270 + txarc + rxarc, ic->crx, 0);
  323         gdImageFilledArc(ic->im, x, y, size, size, 270 + txarc, 270 + txarc + rxarc, ic->crxd, gdEdged | gdNoFill);
  324         gdImageFilledArc(ic->im, x, y, holesize, holesize, 270 + txarc, 270 + txarc + rxarc, ic->crxd, gdEdged | gdNoFill);
  325     }
  326 
  327     // remove center from background filled circle, making it a donut
  328     gdImageFilledArc(ic->im, x, y, holesize - 2, holesize - 2, 0, 360, ic->cbackground, 0);
  329 }
  330 
  331 void drawpole(IMAGECONTENT *ic, const int x, const int y, const int length, const int direction, const int color)
  332 {
  333     int len = length - 1;
  334 
  335     if (length > 0) {
  336         switch (direction) {
  337             case 1:
  338                 gdImageLine(ic->im, x, y, x, y - len, color);
  339                 break;
  340             case 2:
  341                 gdImageLine(ic->im, x, y, x, y + len, color);
  342                 break;
  343             default:
  344                 break;
  345         }
  346     }
  347 }
  348 
  349 void drawarrowup(IMAGECONTENT *ic, const int x, const int y)
  350 {
  351     gdImageLine(ic->im, x, y, x + 2, y + 3, ic->ctext);
  352     gdImageLine(ic->im, x, y, x - 2, y + 3, ic->ctext);
  353     gdImageLine(ic->im, x - 2, y + 3, x + 2, y + 3, ic->ctext);
  354     gdImageLine(ic->im, x, y + 1, x, y - 1, ic->ctext);
  355 }
  356 
  357 void drawarrowright(IMAGECONTENT *ic, const int x, const int y)
  358 {
  359     gdImageLine(ic->im, x, y, x - 3, y - 2, ic->ctext);
  360     gdImageLine(ic->im, x, y, x - 3, y + 2, ic->ctext);
  361     gdImageLine(ic->im, x - 3, y - 2, x - 3, y + 2, ic->ctext);
  362     gdImageLine(ic->im, x + 1, y, x - 1, y, ic->ctext);
  363 }
  364 
  365 void hextorgb(const char *input, int *rgb)
  366 {
  367     int offset;
  368     char hex[3], dec[4];
  369 
  370     if (input[0] == '#') {
  371         offset = 1;
  372     } else {
  373         offset = 0;
  374     }
  375 
  376     snprintf(hex, 3, "%c%c", input[(0 + offset)], input[(1 + offset)]);
  377     snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16));
  378     rgb[0] = atoi(dec);
  379     snprintf(hex, 3, "%c%c", input[(2 + offset)], input[(3 + offset)]);
  380     snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16));
  381     rgb[1] = atoi(dec);
  382     snprintf(hex, 3, "%c%c", input[(4 + offset)], input[(5 + offset)]);
  383     snprintf(dec, 4, "%d", (int)strtol(hex, NULL, 16));
  384     rgb[2] = atoi(dec);
  385 
  386     if (debug) {
  387         printf("%s -> %d, %d, %d\n", input, rgb[0], rgb[1], rgb[2]);
  388     }
  389 }
  390 
  391 void modcolor(int *rgb, const int offset, const int force)
  392 {
  393     int i, overflow = 0;
  394 
  395     if (debug) {
  396         printf("m%d (%d): %d, %d, %d -> ", offset, force, rgb[0], rgb[1], rgb[2]);
  397     }
  398 
  399     for (i = 0; i < 3; i++) {
  400         if ((rgb[i] + offset) > 255 || (rgb[i] + offset) < 0) {
  401             overflow++;
  402         }
  403     }
  404 
  405     /* positive offset gives lighter color, negative darker if forced */
  406     /* otherwise the direction is changed depending on possible overflows */
  407     for (i = 0; i < 3; i++) {
  408         if (overflow < 2 || force) {
  409             if ((rgb[i] + offset) > 255) {
  410                 rgb[i] = 255;
  411             } else if ((rgb[i] + offset) < 0) {
  412                 rgb[i] = 0;
  413             } else {
  414                 rgb[i] += offset;
  415             }
  416         } else {
  417             if ((rgb[i] - offset) < 0) {
  418                 rgb[i] = 0;
  419             } else if ((rgb[i] - offset) > 255) {
  420                 rgb[i] = 255;
  421             } else {
  422                 rgb[i] -= offset;
  423             }
  424         }
  425     }
  426 
  427     if (debug) {
  428         printf("%d, %d, %d\n", rgb[0], rgb[1], rgb[2]);
  429     }
  430 }
  431 
  432 char *getimagevalue(const uint64_t b, const int len, const int rate)
  433 {
  434     static char buffer[64];
  435     int i, declen = 0, unit = 0, p = 1024;
  436     uint64_t limit;
  437 
  438     if (b == 0) {
  439         snprintf(buffer, 64, "%*s", len, "--");
  440     } else {
  441         if (rate && (getunit() == 2 || getunit() == 4)) {
  442             p = 1000;
  443             unit = getunit();
  444         }
  445         for (i = UNITPREFIXCOUNT - 1; i > 0; i--) {
  446             limit = (uint64_t)(pow(p, i - 1)) * 1000;
  447             if (b >= limit) {
  448                 snprintf(buffer, 64, "%*.*f", len, declen, (double)b / (double)(getunitdivisor(unit, i + 1)));
  449                 return buffer;
  450             }
  451         }
  452         snprintf(buffer, 64, "%*" PRIu64 "", len, b);
  453     }
  454 
  455     return buffer;
  456 }
  457 
  458 char *getimagescale(const uint64_t b, const int rate)
  459 {
  460     static char buffer[8];
  461     int unit, div = 1, p = 1024;
  462 
  463     unit = getunit();
  464 
  465     if (b == 0) {
  466         snprintf(buffer, 8, "--");
  467     } else {
  468         if (rate) {
  469             if (unit == 2 || unit == 4) {
  470                 p = 1000;
  471             }
  472             while (div < UNITPREFIXCOUNT && (double)b >= (pow(p, div - 1) * 1000)) {
  473                 div++;
  474             }
  475             snprintf(buffer, 8, "%s", getrateunitprefix(unit, div));
  476         } else {
  477             while (div < UNITPREFIXCOUNT && (double)b >= (pow(p, div - 1) * 1000)) {
  478                 div++;
  479             }
  480             snprintf(buffer, 8, "%s", getunitprefix(div));
  481         }
  482     }
  483     return buffer;
  484 }
  485 
  486 uint64_t getscale(const uint64_t input, const int rate)
  487 {
  488     int i, unit;
  489     unsigned int div = 1024;
  490     uint64_t result = input;
  491 
  492     unit = getunit();
  493 
  494     if (rate && (unit == 2 || unit == 4)) {
  495         div = 1000;
  496     }
  497 
  498     /* get unit */
  499     for (i = 0; result >= div; i++) {
  500         result = result / div;
  501     }
  502 
  503     /* round result depending of scale */
  504     if (result >= 300) {
  505         result = result / 4 + (100 - ((result / 4) % 100));
  506     } else if (result > 20) {
  507         result = result / 4 + (10 - ((result / 4) % 10));
  508     } else {
  509         result = result / 4;
  510     }
  511 
  512     /* put unit back */
  513     if (i) {
  514         result = result * (uint64_t)(pow(div, i));
  515     }
  516 
  517     /* make sure result isn't zero */
  518     if (!result) {
  519         if (i) {
  520             result = (uint64_t)(pow(div, i));
  521         } else {
  522             result = 1;
  523         }
  524     }
  525 
  526     return result;
  527 }