[BACK]Return to progressbar.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / ftp

Annotation of src/usr.bin/ftp/progressbar.c, Revision 1.9

1.9     ! he          1: /*     $NetBSD: progressbar.c,v 1.8 2005/05/19 03:05:04 lukem Exp $    */
1.1       jhawk       2:
                      3: /*-
                      4:  * Copyright (c) 1997-2003 The NetBSD Foundation, Inc.
                      5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Luke Mewburn.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  * 3. All advertising materials mentioning features or use of this software
                     19:  *    must display the following acknowledgement:
                     20:  *     This product includes software developed by the NetBSD
                     21:  *     Foundation, Inc. and its contributors.
                     22:  * 4. Neither the name of The NetBSD Foundation nor the names of its
                     23:  *    contributors may be used to endorse or promote products derived
                     24:  *    from this software without specific prior written permission.
                     25:  *
                     26:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     27:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     28:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     29:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     30:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     31:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     32:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     33:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     34:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     35:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     36:  * POSSIBILITY OF SUCH DAMAGE.
                     37:  */
                     38:
                     39: #include <sys/cdefs.h>
                     40: #ifndef lint
1.9     ! he         41: __RCSID("$NetBSD: progressbar.c,v 1.8 2005/05/19 03:05:04 lukem Exp $");
1.1       jhawk      42: #endif /* not lint */
                     43:
                     44: /*
                     45:  * FTP User Program -- Misc support routines
                     46:  */
                     47: #include <sys/types.h>
                     48: #include <sys/param.h>
                     49:
                     50: #include <err.h>
                     51: #include <errno.h>
                     52: #include <signal.h>
                     53: #include <stdio.h>
                     54: #include <stdlib.h>
1.5       hubertf    55: #include <string.h>
1.1       jhawk      56: #include <time.h>
                     57: #include <tzfile.h>
                     58: #include <unistd.h>
                     59:
                     60: #include "progressbar.h"
                     61:
1.2       grant      62: #if !defined(NO_PROGRESS)
1.1       jhawk      63: /*
                     64:  * return non-zero if we're the current foreground process
                     65:  */
                     66: int
                     67: foregroundproc(void)
                     68: {
                     69:        static pid_t pgrp = -1;
                     70:
                     71:        if (pgrp == -1)
                     72:                pgrp = getpgrp();
                     73:
                     74:        return (tcgetpgrp(fileno(ttyout)) == pgrp);
                     75: }
1.2       grant      76: #endif /* !defined(NO_PROGRESS) */
1.1       jhawk      77:
                     78:
                     79: static void updateprogressmeter(int);
                     80:
                     81: /*
                     82:  * SIGALRM handler to update the progress meter
                     83:  */
                     84: static void
                     85: updateprogressmeter(int dummy)
                     86: {
                     87:        int oerrno = errno;
                     88:
                     89:        progressmeter(0);
                     90:        errno = oerrno;
                     91: }
                     92:
                     93: /*
                     94:  * List of order of magnitude prefixes.
                     95:  * The last is `P', as 2^64 = 16384 Petabytes
                     96:  */
                     97: static const char prefixes[] = " KMGTP";
                     98:
                     99: /*
                    100:  * Display a transfer progress bar if progress is non-zero.
                    101:  * SIGALRM is hijacked for use by this function.
                    102:  * - Before the transfer, set filesize to size of file (or -1 if unknown),
                    103:  *   and call with flag = -1. This starts the once per second timer,
                    104:  *   and a call to updateprogressmeter() upon SIGALRM.
                    105:  * - During the transfer, updateprogressmeter will call progressmeter
                    106:  *   with flag = 0
                    107:  * - After the transfer, call with flag = 1
                    108:  */
                    109: static struct timeval start;
                    110: static struct timeval lastupdate;
                    111:
                    112: #define        BUFLEFT (sizeof(buf) - len)
                    113:
                    114: void
                    115: progressmeter(int flag)
                    116: {
                    117:        static off_t lastsize;
                    118:        off_t cursize;
                    119:        struct timeval now, wait;
                    120: #ifndef NO_PROGRESS
                    121:        struct timeval td;
                    122:        off_t abbrevsize, bytespersec;
                    123:        double elapsed;
1.8       lukem     124:        int ratio, i, remaining;
                    125:        size_t barlength;
1.1       jhawk     126:
                    127:                        /*
                    128:                         * Work variables for progress bar.
                    129:                         *
                    130:                         * XXX: if the format of the progress bar changes
                    131:                         *      (especially the number of characters in the
                    132:                         *      `static' portion of it), be sure to update
                    133:                         *      these appropriately.
                    134:                         */
1.6       jmc       135: #endif
1.8       lukem     136:        size_t          len;
1.1       jhawk     137:        char            buf[256];       /* workspace for progress bar */
1.6       jmc       138: #ifndef NO_PROGRESS
1.1       jhawk     139: #define        BAROVERHEAD     43              /* non `*' portion of progress bar */
                    140:                                        /*
                    141:                                         * stars should contain at least
                    142:                                         * sizeof(buf) - BAROVERHEAD entries
                    143:                                         */
                    144:        static const char       stars[] =
                    145: "*****************************************************************************"
                    146: "*****************************************************************************"
                    147: "*****************************************************************************";
                    148:
                    149: #endif
                    150:
                    151:        if (flag == -1) {
                    152:                (void)gettimeofday(&start, NULL);
                    153:                lastupdate = start;
                    154:                lastsize = restart_point;
                    155:        }
                    156:
                    157:        (void)gettimeofday(&now, NULL);
                    158:        cursize = bytes + restart_point;
                    159:        timersub(&now, &lastupdate, &wait);
                    160:        if (cursize > lastsize) {
                    161:                lastupdate = now;
                    162:                lastsize = cursize;
                    163:                wait.tv_sec = 0;
                    164:        } else {
                    165: #ifndef STANDALONE_PROGRESS
                    166:                if (quit_time > 0 && wait.tv_sec > quit_time) {
                    167:                        len = snprintf(buf, sizeof(buf), "\r\n%s: "
                    168:                            "transfer aborted because stalled for %lu sec.\r\n",
                    169:                            getprogname(), (unsigned long)wait.tv_sec);
                    170:                        (void)write(fileno(ttyout), buf, len);
                    171:                        (void)xsignal(SIGALRM, SIG_DFL);
                    172:                        alarmtimer(0);
                    173:                        siglongjmp(toplevel, 1);
                    174:                }
                    175: #endif /* !STANDALONE_PROGRESS */
                    176:        }
                    177:        /*
                    178:         * Always set the handler even if we are not the foreground process.
                    179:         */
                    180: #ifdef STANDALONE_PROGRESS
                    181:        if (progress) {
                    182: #else
                    183:        if (quit_time > 0 || progress) {
                    184: #endif /* !STANDALONE_PROGRESS */
                    185:                if (flag == -1) {
                    186:                        (void)xsignal_restart(SIGALRM, updateprogressmeter, 1);
                    187:                        alarmtimer(1);          /* set alarm timer for 1 Hz */
                    188:                } else if (flag == 1) {
                    189:                        (void)xsignal(SIGALRM, SIG_DFL);
                    190:                        alarmtimer(0);
                    191:                }
                    192:        }
                    193: #ifndef NO_PROGRESS
                    194:        if (!progress)
                    195:                return;
                    196:        len = 0;
                    197:
                    198:        /*
                    199:         * print progress bar only if we are foreground process.
                    200:         */
                    201:        if (! foregroundproc())
                    202:                return;
                    203:
                    204:        len += snprintf(buf + len, BUFLEFT, "\r");
1.5       hubertf   205:        if (prefix)
                    206:          len += snprintf(buf + len, BUFLEFT, "%s", prefix);
1.1       jhawk     207:        if (filesize > 0) {
                    208:                ratio = (int)((double)cursize * 100.0 / (double)filesize);
                    209:                ratio = MAX(ratio, 0);
                    210:                ratio = MIN(ratio, 100);
                    211:                len += snprintf(buf + len, BUFLEFT, "%3d%% ", ratio);
                    212:
                    213:                        /*
                    214:                         * calculate the length of the `*' bar, ensuring that
1.7       lukem     215:                         * the number of stars won't exceed the buffer size
1.1       jhawk     216:                         */
                    217:                barlength = MIN(sizeof(buf) - 1, ttywidth) - BAROVERHEAD;
1.5       hubertf   218:                if (prefix)
1.7       lukem     219:                        barlength -= strlen(prefix);
1.1       jhawk     220:                if (barlength > 0) {
                    221:                        i = barlength * ratio / 100;
                    222:                        len += snprintf(buf + len, BUFLEFT,
1.9     ! he        223:                            "|%.*s%*s|", i, stars, (int)(barlength - i), "");
1.1       jhawk     224:                }
                    225:        }
                    226:
                    227:        abbrevsize = cursize;
                    228:        for (i = 0; abbrevsize >= 100000 && i < sizeof(prefixes); i++)
                    229:                abbrevsize >>= 10;
                    230:        len += snprintf(buf + len, BUFLEFT, " " LLFP("5") " %c%c ",
                    231:            (LLT)abbrevsize,
                    232:            prefixes[i],
                    233:            i == 0 ? ' ' : 'B');
                    234:
                    235:        timersub(&now, &start, &td);
                    236:        elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
                    237:
                    238:        bytespersec = 0;
                    239:        if (bytes > 0) {
                    240:                bytespersec = bytes;
                    241:                if (elapsed > 0.0)
                    242:                        bytespersec /= elapsed;
                    243:        }
                    244:        for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
                    245:                bytespersec >>= 10;
                    246:        len += snprintf(buf + len, BUFLEFT,
                    247:            " " LLFP("3") ".%02d %cB/s ",
                    248:            (LLT)(bytespersec / 1024),
                    249:            (int)((bytespersec % 1024) * 100 / 1024),
                    250:            prefixes[i]);
                    251:
                    252:        if (filesize > 0) {
                    253:                if (bytes <= 0 || elapsed <= 0.0 || cursize > filesize) {
                    254:                        len += snprintf(buf + len, BUFLEFT, "   --:-- ETA");
                    255:                } else if (wait.tv_sec >= STALLTIME) {
                    256:                        len += snprintf(buf + len, BUFLEFT, " - stalled -");
                    257:                } else {
                    258:                        remaining = (int)
                    259:                            ((filesize - restart_point) / (bytes / elapsed) -
                    260:                            elapsed);
                    261:                        if (remaining >= 100 * SECSPERHOUR)
                    262:                                len += snprintf(buf + len, BUFLEFT,
                    263:                                    "   --:-- ETA");
                    264:                        else {
                    265:                                i = remaining / SECSPERHOUR;
                    266:                                if (i)
                    267:                                        len += snprintf(buf + len, BUFLEFT,
                    268:                                            "%2d:", i);
                    269:                                else
                    270:                                        len += snprintf(buf + len, BUFLEFT,
                    271:                                            "   ");
                    272:                                i = remaining % SECSPERHOUR;
                    273:                                len += snprintf(buf + len, BUFLEFT,
                    274:                                    "%02d:%02d ETA", i / 60, i % 60);
                    275:                        }
                    276:                }
                    277:        }
                    278:        if (flag == 1)
                    279:                len += snprintf(buf + len, BUFLEFT, "\n");
                    280:        (void)write(fileno(ttyout), buf, len);
                    281:
                    282: #endif /* !NO_PROGRESS */
                    283: }
                    284:
                    285: #ifndef STANDALONE_PROGRESS
                    286: /*
                    287:  * Display transfer statistics.
                    288:  * Requires start to be initialised by progressmeter(-1),
                    289:  * direction to be defined by xfer routines, and filesize and bytes
                    290:  * to be updated by xfer routines
                    291:  * If siginfo is nonzero, an ETA is displayed, and the output goes to stderr
                    292:  * instead of ttyout.
                    293:  */
                    294: void
                    295: ptransfer(int siginfo)
                    296: {
                    297:        struct timeval now, td, wait;
                    298:        double elapsed;
                    299:        off_t bytespersec;
1.8       lukem     300:        int remaining, hh, i;
                    301:        size_t len;
1.1       jhawk     302:
                    303:        char buf[256];          /* Work variable for transfer status. */
                    304:
                    305:        if (!verbose && !progress && !siginfo)
                    306:                return;
                    307:
                    308:        (void)gettimeofday(&now, NULL);
                    309:        timersub(&now, &start, &td);
                    310:        elapsed = td.tv_sec + (td.tv_usec / 1000000.0);
                    311:        bytespersec = 0;
                    312:        if (bytes > 0) {
                    313:                bytespersec = bytes;
                    314:                if (elapsed > 0.0)
                    315:                        bytespersec /= elapsed;
                    316:        }
                    317:        len = 0;
                    318:        len += snprintf(buf + len, BUFLEFT, LLF " byte%s %s in ",
                    319:            (LLT)bytes, bytes == 1 ? "" : "s", direction);
                    320:        remaining = (int)elapsed;
                    321:        if (remaining > SECSPERDAY) {
                    322:                int days;
                    323:
                    324:                days = remaining / SECSPERDAY;
                    325:                remaining %= SECSPERDAY;
                    326:                len += snprintf(buf + len, BUFLEFT,
                    327:                    "%d day%s ", days, days == 1 ? "" : "s");
                    328:        }
                    329:        hh = remaining / SECSPERHOUR;
                    330:        remaining %= SECSPERHOUR;
                    331:        if (hh)
                    332:                len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
                    333:        len += snprintf(buf + len, BUFLEFT,
                    334:            "%02d:%02d ", remaining / 60, remaining % 60);
                    335:
                    336:        for (i = 1; bytespersec >= 1024000 && i < sizeof(prefixes); i++)
                    337:                bytespersec >>= 10;
                    338:        len += snprintf(buf + len, BUFLEFT, "(" LLF ".%02d %cB/s)",
                    339:            (LLT)(bytespersec / 1024),
                    340:            (int)((bytespersec % 1024) * 100 / 1024),
                    341:            prefixes[i]);
                    342:
                    343:        if (siginfo && bytes > 0 && elapsed > 0.0 && filesize >= 0
                    344:            && bytes + restart_point <= filesize) {
                    345:                remaining = (int)((filesize - restart_point) /
                    346:                                  (bytes / elapsed) - elapsed);
                    347:                hh = remaining / SECSPERHOUR;
                    348:                remaining %= SECSPERHOUR;
                    349:                len += snprintf(buf + len, BUFLEFT, "  ETA: ");
                    350:                if (hh)
                    351:                        len += snprintf(buf + len, BUFLEFT, "%2d:", hh);
                    352:                len += snprintf(buf + len, BUFLEFT, "%02d:%02d",
                    353:                    remaining / 60, remaining % 60);
                    354:                timersub(&now, &lastupdate, &wait);
                    355:                if (wait.tv_sec >= STALLTIME)
                    356:                        len += snprintf(buf + len, BUFLEFT, "  (stalled)");
                    357:        }
                    358:        len += snprintf(buf + len, BUFLEFT, "\n");
                    359:        (void)write(siginfo ? STDERR_FILENO : fileno(ttyout), buf, len);
                    360: }
                    361:
                    362: /*
                    363:  * SIG{INFO,QUIT} handler to print transfer stats if a transfer is in progress
                    364:  */
                    365: void
                    366: psummary(int notused)
                    367: {
                    368:        int oerrno = errno;
                    369:
                    370:        if (bytes > 0) {
                    371:                if (fromatty)
                    372:                        write(fileno(ttyout), "\n", 1);
                    373:                ptransfer(1);
                    374:        }
                    375:        errno = oerrno;
                    376: }
                    377: #endif /* !STANDALONE_PROGRESS */
                    378:
                    379:
                    380: /*
                    381:  * Set the SIGALRM interval timer for wait seconds, 0 to disable.
                    382:  */
                    383: void
                    384: alarmtimer(int wait)
                    385: {
                    386:        struct itimerval itv;
                    387:
                    388:        itv.it_value.tv_sec = wait;
                    389:        itv.it_value.tv_usec = 0;
                    390:        itv.it_interval = itv.it_value;
                    391:        setitimer(ITIMER_REAL, &itv, NULL);
                    392: }
                    393:
                    394:
                    395: /*
                    396:  * Install a POSIX signal handler, allowing the invoker to set whether
                    397:  * the signal should be restartable or not
                    398:  */
                    399: sigfunc
                    400: xsignal_restart(int sig, sigfunc func, int restartable)
                    401: {
                    402:        struct sigaction act, oact;
                    403:        act.sa_handler = func;
                    404:
                    405:        sigemptyset(&act.sa_mask);
                    406: #if defined(SA_RESTART)                        /* 4.4BSD, Posix(?), SVR4 */
                    407:        act.sa_flags = restartable ? SA_RESTART : 0;
                    408: #elif defined(SA_INTERRUPT)            /* SunOS 4.x */
                    409:        act.sa_flags = restartable ? 0 : SA_INTERRUPT;
                    410: #else
                    411: #error "system must have SA_RESTART or SA_INTERRUPT"
                    412: #endif
                    413:        if (sigaction(sig, &act, &oact) < 0)
                    414:                return (SIG_ERR);
                    415:        return (oact.sa_handler);
                    416: }
                    417:
                    418: /*
                    419:  * Install a signal handler with the `restartable' flag set dependent upon
                    420:  * which signal is being set. (This is a wrapper to xsignal_restart())
                    421:  */
                    422: sigfunc
                    423: xsignal(int sig, sigfunc func)
                    424: {
                    425:        int restartable;
                    426:
                    427:        /*
                    428:         * Some signals print output or change the state of the process.
                    429:         * There should be restartable, so that reads and writes are
                    430:         * not affected.  Some signals should cause program flow to change;
                    431:         * these signals should not be restartable, so that the system call
                    432:         * will return with EINTR, and the program will go do something
                    433:         * different.  If the signal handler calls longjmp() or siglongjmp(),
                    434:         * it doesn't matter if it's restartable.
                    435:         */
                    436:
                    437:        switch(sig) {
                    438: #ifdef SIGINFO
                    439:        case SIGINFO:
                    440: #endif
                    441:        case SIGQUIT:
                    442:        case SIGUSR1:
                    443:        case SIGUSR2:
                    444:        case SIGWINCH:
                    445:                restartable = 1;
                    446:                break;
                    447:
                    448:        case SIGALRM:
                    449:        case SIGINT:
                    450:        case SIGPIPE:
                    451:                restartable = 0;
                    452:                break;
                    453:
                    454:        default:
                    455:                /*
                    456:                 * This is unpleasant, but I don't know what would be better.
                    457:                 * Right now, this "can't happen"
                    458:                 */
                    459:                errx(1, "xsignal_restart called with signal %d", sig);
                    460:        }
                    461:
                    462:        return(xsignal_restart(sig, func, restartable));
                    463: }

CVSweb <webmaster@jp.NetBSD.org>