busybox/libbb/progress.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Progress bar code.
   4 */
   5/* Original copyright notice which applies to the CONFIG_FEATURE_WGET_STATUSBAR stuff,
   6 * much of which was blatantly stolen from openssh.
   7 */
   8/*-
   9 * Copyright (c) 1992, 1993
  10 * The Regents of the University of California.  All rights reserved.
  11 *
  12 * Redistribution and use in source and binary forms, with or without
  13 * modification, are permitted provided that the following conditions
  14 * are met:
  15 * 1. Redistributions of source code must retain the above copyright
  16 *    notice, this list of conditions and the following disclaimer.
  17 * 2. Redistributions in binary form must reproduce the above copyright
  18 *    notice, this list of conditions and the following disclaimer in the
  19 *    documentation and/or other materials provided with the distribution.
  20 *
  21 * 3. BSD Advertising Clause omitted per the July 22, 1999 licensing change
  22 *    ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change
  23 *
  24 * 4. Neither the name of the University nor the names of its contributors
  25 *    may be used to endorse or promote products derived from this software
  26 *    without specific prior written permission.
  27 *
  28 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  29 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  30 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  31 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  32 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  37 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  38 * SUCH DAMAGE.
  39 */
  40#include "libbb.h"
  41#include "unicode.h"
  42
  43enum {
  44        /* Seconds when xfer considered "stalled" */
  45        STALLTIME = 5
  46};
  47
  48void FAST_FUNC bb_progress_init(bb_progress_t *p, const char *curfile)
  49{
  50#if ENABLE_UNICODE_SUPPORT
  51        init_unicode();
  52        p->curfile = unicode_conv_to_printable_fixedwidth(/*NULL,*/ curfile, 20);
  53#else
  54        p->curfile = curfile;
  55#endif
  56        p->start_sec = monotonic_sec();
  57        p->last_update_sec = p->start_sec;
  58        p->last_change_sec = p->start_sec;
  59        p->last_size = 0;
  60}
  61
  62/* File already had beg_size bytes.
  63 * Then we started downloading.
  64 * We downloaded "transferred" bytes so far.
  65 * Download is expected to stop when total size (beg_size + transferred)
  66 * will be "totalsize" bytes.
  67 * If totalsize == 0, then it is unknown.
  68 */
  69void FAST_FUNC bb_progress_update(bb_progress_t *p,
  70                uoff_t beg_size,
  71                uoff_t transferred,
  72                uoff_t totalsize)
  73{
  74        uoff_t beg_and_transferred;
  75        unsigned since_last_update, elapsed;
  76        int notty;
  77        int kiloscale;
  78
  79        //transferred = 1234; /* use for stall detection testing */
  80        //totalsize = 0; /* use for unknown size download testing */
  81
  82        elapsed = monotonic_sec();
  83        since_last_update = elapsed - p->last_update_sec;
  84        p->last_update_sec = elapsed;
  85
  86        if (totalsize != 0 && transferred >= totalsize - beg_size) {
  87                /* Last call. Do not skip this update */
  88                transferred = totalsize - beg_size; /* sanitize just in case */
  89        }
  90        else if (since_last_update == 0) {
  91                /*
  92                 * Do not update on every call
  93                 * (we can be called on every network read!)
  94                 */
  95                return;
  96        }
  97
  98        kiloscale = 0;
  99        /*
 100         * Scale sizes down if they are close to overflowing.
 101         * This allows calculations like (100 * transferred / totalsize)
 102         * without risking overflow: we guarantee 10 highest bits to be 0.
 103         * Introduced error is less than 1 / 2^12 ~= 0.025%
 104         */
 105        if (ULONG_MAX > 0xffffffff || sizeof(off_t) == 4 || sizeof(off_t) != 8) {
 106                /*
 107                 * 64-bit CPU || small off_t: in either case,
 108                 * >> is cheap, single-word operation.
 109                 * ... || strange off_t: also use this code
 110                 * (it is safe, just suboptimal wrt code size),
 111                 * because 32/64 optimized one works only for 64-bit off_t.
 112                 */
 113                if (totalsize >= (1 << 22)) {
 114                        totalsize >>= 10;
 115                        beg_size >>= 10;
 116                        transferred >>= 10;
 117                        kiloscale = 1;
 118                }
 119        } else {
 120                /* 32-bit CPU and 64-bit off_t.
 121                 * Use a 40-bit shift, it is easier to do on 32-bit CPU.
 122                 */
 123/* ONE suppresses "warning: shift count >= width of type" */
 124#define ONE (sizeof(off_t) > 4)
 125                if (totalsize >= (uoff_t)(1ULL << 54*ONE)) {
 126                        totalsize = (uint32_t)(totalsize >> 32*ONE) >> 8;
 127                        beg_size = (uint32_t)(beg_size >> 32*ONE) >> 8;
 128                        transferred = (uint32_t)(transferred >> 32*ONE) >> 8;
 129                        kiloscale = 4;
 130                }
 131        }
 132
 133        notty = !isatty(STDERR_FILENO);
 134
 135        if (ENABLE_UNICODE_SUPPORT)
 136                fprintf(stderr, "\r%s" + notty, p->curfile);
 137        else
 138                fprintf(stderr, "\r%-20.20s" + notty, p->curfile);
 139
 140        beg_and_transferred = beg_size + transferred;
 141
 142        if (totalsize != 0) {
 143                int barlength;
 144                unsigned ratio = 100 * beg_and_transferred / totalsize;
 145                fprintf(stderr, "%4u%%", ratio);
 146
 147                barlength = get_terminal_width(2) - 49;
 148                if (barlength > 0) {
 149                        /* god bless gcc for variable arrays :) */
 150                        char buf[barlength + 1];
 151                        unsigned stars = (unsigned)barlength * beg_and_transferred / totalsize;
 152                        memset(buf, ' ', barlength);
 153                        buf[barlength] = '\0';
 154                        memset(buf, '*', stars);
 155                        fprintf(stderr, " |%s|", buf);
 156                }
 157        }
 158
 159        while (beg_and_transferred >= 100000) {
 160                beg_and_transferred >>= 10;
 161                kiloscale++;
 162        }
 163        /* see http://en.wikipedia.org/wiki/Tera */
 164        fprintf(stderr, "%6u%c", (unsigned)beg_and_transferred, " kMGTPEZY"[kiloscale]);
 165#define beg_and_transferred dont_use_beg_and_transferred_below()
 166
 167        since_last_update = elapsed - p->last_change_sec;
 168        if ((unsigned)transferred != p->last_size) {
 169                p->last_change_sec = elapsed;
 170                p->last_size = (unsigned)transferred;
 171                if (since_last_update >= STALLTIME) {
 172                        /* We "cut out" these seconds from elapsed time
 173                         * by adjusting start time */
 174                        p->start_sec += since_last_update;
 175                }
 176                since_last_update = 0; /* we are un-stalled now */
 177        }
 178
 179        elapsed -= p->start_sec; /* now it's "elapsed since start" */
 180
 181        if (since_last_update >= STALLTIME) {
 182                fprintf(stderr, "  - stalled -");
 183        } else if (!totalsize || !transferred || (int)elapsed < 0) {
 184                fprintf(stderr, " --:--:-- ETA");
 185        } else {
 186                unsigned eta, secs, hours;
 187
 188                totalsize -= beg_size; /* now it's "total to upload" */
 189
 190                /* Estimated remaining time =
 191                 * estimated_sec_to_dl_totalsize_bytes - elapsed_sec =
 192                 * totalsize / average_bytes_sec_so_far - elapsed =
 193                 * totalsize / (transferred/elapsed) - elapsed =
 194                 * totalsize * elapsed / transferred - elapsed
 195                 */
 196                eta = totalsize * elapsed / transferred - elapsed;
 197                if (eta >= 1000*60*60)
 198                        eta = 1000*60*60 - 1;
 199                secs = eta % 3600;
 200                hours = eta / 3600;
 201                fprintf(stderr, "%3u:%02u:%02u ETA", hours, secs / 60, secs % 60);
 202        }
 203        if (notty)
 204                fputc('\n', stderr);
 205}
 206