"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.1/src/screen.c" (22 Dec 2021, 15339 Bytes) of package /linux/misc/tin-2.6.1.tar.xz:


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 "screen.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.6.0_vs_2.6.1.

    1 /*
    2  *  Project   : tin - a Usenet reader
    3  *  Module    : screen.c
    4  *  Author    : I. Lea & R. Skrenta
    5  *  Created   : 1991-04-01
    6  *  Updated   : 2021-09-28
    7  *  Notes     :
    8  *
    9  * Copyright (c) 1991-2022 Iain Lea <iain@bricbrac.de>, Rich Skrenta <skrenta@pbm.com>
   10  * 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  *
   16  * 1. Redistributions of source code must retain the above copyright notice,
   17  *    this list of conditions and the following disclaimer.
   18  *
   19  * 2. Redistributions in binary form must reproduce the above copyright
   20  *    notice, this list of conditions and the following disclaimer in the
   21  *    documentation and/or other materials provided with the distribution.
   22  *
   23  * 3. Neither the name of the copyright holder nor the names of its
   24  *    contributors may be used to endorse or promote products derived from
   25  *    this software without specific prior written permission.
   26  *
   27  * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   28  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   30  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
   31  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   37  * POSSIBILITY OF SUCH DAMAGE.
   38  */
   39 
   40 
   41 #ifndef TIN_H
   42 #   include "tin.h"
   43 #endif /* !TIN_H */
   44 #ifndef TCURSES_H
   45 #   include "tcurses.h"
   46 #endif /* !TCURSES_H */
   47 
   48 int mark_offset = 0;
   49 
   50 #ifndef USE_CURSES
   51     struct t_screen *screen;
   52 #endif /* !USE_CURSES */
   53 
   54 
   55 /*
   56  * Move the cursor to the lower-left of the screen, where it won't be annoying
   57  */
   58 void
   59 stow_cursor(
   60     void)
   61 {
   62     if (!cmd_line)
   63         MoveCursor(cLINES, 0);
   64 }
   65 
   66 
   67 /*
   68  * helper for the various *_message() functions
   69  * returns a pointer to an allocated buffer with the formatted message
   70  * must be freed if not needed anymore
   71  */
   72 char *
   73 fmt_message(
   74     const char *fmt,
   75     va_list ap)
   76 {
   77     size_t size = LEN;
   78     char *msg = my_malloc(size);
   79     int used;
   80     va_list aq;
   81 
   82     while (1) {
   83         begin_va_copy(aq, ap);
   84         used = vsnprintf(msg, size, fmt, aq);
   85         end_va_copy(aq);
   86 
   87         if (used >= 0 && used < (int) size)
   88             break;
   89 
   90         size <<= 1;
   91         msg = my_realloc(msg, size);
   92     }
   93 
   94     return msg;
   95 }
   96 
   97 
   98 /*
   99  * Centre a formatted colour message at the bottom of the screen
  100  */
  101 void
  102 info_message(
  103     const char *fmt,
  104     ...)
  105 {
  106     char *buf;
  107     va_list ap;
  108 
  109     va_start(ap, fmt);
  110 
  111     clear_message();
  112 #ifdef HAVE_COLOR
  113     fcol(tinrc.col_message);
  114 #endif /* HAVE_COLOR */
  115 
  116     buf = fmt_message(fmt, ap);
  117     center_line(cLINES, FALSE, buf);    /* center the message at screen bottom */
  118     free(buf);
  119 
  120 #ifdef HAVE_COLOR
  121     fcol(tinrc.col_normal);
  122 #endif /* HAVE_COLOR */
  123     stow_cursor();
  124 
  125     va_end(ap);
  126 }
  127 
  128 
  129 /*
  130  * Print a formatted colour message at the bottom of the screen, wait a while
  131  */
  132 void
  133 wait_message(
  134     unsigned int sdelay,
  135     const char *fmt,
  136     ...)
  137 {
  138     char *buf, *msg;
  139     va_list ap;
  140 
  141     va_start(ap, fmt);
  142 
  143     clear_message();
  144 #ifdef HAVE_COLOR
  145     fcol(tinrc.col_message);
  146 #endif /* HAVE_COLOR */
  147 
  148     buf = fmt_message(fmt, ap);
  149     /* test for multiline messages */
  150     if (strrchr(buf, '\n')) {
  151         char *from, *to;
  152 
  153         for (from = buf; *from && (to = strchr(from, '\n')); from = ++to) {
  154             *to = '\0';
  155             msg = strunc(from, cCOLS - 1);
  156             my_fputs(msg, stdout);
  157             my_fputc('\n', stdout);
  158             free(msg);
  159         }
  160     } else {
  161         msg = strunc(buf, cCOLS - 1);
  162         my_fputs(msg, stdout);
  163         free(msg);
  164     }
  165     free(buf);
  166 
  167 #ifdef HAVE_COLOR
  168     fcol(tinrc.col_normal);
  169 #endif /* HAVE_COLOR */
  170     cursoron();
  171     my_flush();
  172     va_end(ap);
  173 
  174 #ifdef HAVE_SELECT
  175     /* allow to skip sleep time via key-press */
  176     {
  177         int nfds;
  178         fd_set readfds;
  179         struct timeval tv;
  180 
  181         forever {
  182             FD_ZERO(&readfds);
  183             FD_SET(STDIN_FILENO, &readfds);
  184             tv.tv_sec = sdelay;
  185             tv.tv_usec = 0;
  186 
  187 #   ifdef HAVE_SELECT_INTP
  188             nfds = select(STDIN_FILENO + 1, (int *) &readfds, NULL, NULL, &tv);
  189 #   else
  190             nfds = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
  191 #   endif /* HAVE_SELECT_INTP */
  192             if (nfds == -1) {
  193 
  194                 if (errno != EINTR) {
  195                     perror_message("wait_message(select()) failed");
  196                     free(tin_progname);
  197                     giveup();
  198                 } else
  199                     return;
  200             } else
  201                 break;
  202         }
  203 
  204         if (nfds > 0) {
  205             if (FD_ISSET(STDIN_FILENO, &readfds))
  206 #   if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  207                 ReadWch();
  208 #   else
  209                 ReadCh();
  210 #   endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  211         }
  212     }
  213 #else
  214     (void) sleep(sdelay);
  215 #endif /* HAVE_SELECT */
  216 }
  217 
  218 
  219 /*
  220  * Print a formatted message to stderr, no colour is added.
  221  * Interesting - this function implicitly clears 'errno'
  222  */
  223 void
  224 error_message(
  225     unsigned int sdelay,
  226     const char *fmt,
  227     ...)
  228 {
  229     char *buf;
  230     va_list ap;
  231 
  232     va_start(ap, fmt);
  233 
  234     errno = 0;
  235     clear_message();
  236 
  237     buf = fmt_message(fmt, ap);
  238     my_fputs(buf, stderr);  /* don't use my_fprintf() here due to %format chars */
  239     my_fflush(stderr);
  240     free(buf);
  241 
  242     if (cmd_line) {
  243         my_fputc('\n', stderr);
  244         fflush(stderr);
  245     } else {
  246         stow_cursor();
  247         (void) sleep(sdelay);
  248         clear_message();
  249     }
  250 
  251     va_end(ap);
  252 }
  253 
  254 
  255 /*
  256  * Print a formatted error message to stderr, no colour is added.
  257  * This function implicitly clears 'errno'
  258  */
  259 void
  260 perror_message(
  261     const char *fmt,
  262     ...)
  263 {
  264     char *buf;
  265     int err;
  266     va_list ap;
  267 
  268     err = errno;
  269     va_start(ap, fmt);
  270 
  271     clear_message();
  272 
  273     if ((buf = fmt_message(fmt, ap)) != NULL) {
  274         error_message(2, "%s: Error: %s", buf, strerror(err));
  275         free(buf);
  276     }
  277 
  278     va_end(ap);
  279 }
  280 
  281 
  282 void
  283 clear_message(
  284     void)
  285 {
  286     if (!cmd_line) {
  287         MoveCursor(cLINES, 0);
  288         CleartoEOLN();
  289         cursoroff();
  290 #ifndef USE_CURSES
  291         my_flush();
  292 #endif /* !USE_CURSES */
  293     }
  294 }
  295 
  296 
  297 void
  298 center_line(
  299     int line,
  300     t_bool inverse,
  301     const char *str)
  302 {
  303     char *ln;
  304     int pos;
  305     int len;
  306 
  307     len = strwidth(str);
  308 
  309 #if defined(HAVE_LIBICUUC) && defined(MULTIBYTE_ABLE) && defined(HAVE_UNICODE_UBIDI_H) && !defined(NO_LOCALE)
  310     if (tinrc.render_bidi && IS_LOCAL_CHARSET("UTF-8") && len > 1) {
  311         t_bool is_rtl;
  312 
  313         if ((ln = render_bidi(str, &is_rtl)) == NULL)
  314             ln = my_strdup(str);
  315     } else
  316 #endif /* HAVE_LIBICUUC && MULTIBYTE_ABLE && HAVE_UNICODE_UBIDI_H && !NO_LOCALE */
  317         ln = my_strdup(str);
  318 
  319     if (!cmd_line) {
  320         if (cCOLS >= len)
  321             pos = (cCOLS - len) / 2;
  322         else
  323             pos = 1;
  324 
  325         MoveCursor(line, pos);
  326         if (inverse) {
  327             StartInverse();
  328             my_flush();
  329         }
  330     }
  331 
  332     if (len >= cCOLS) {
  333         char *buffer;
  334 
  335         buffer = strunc(ln, cCOLS - 2);
  336         my_fputs(buffer, stdout);
  337         free(buffer);
  338     } else
  339         my_fputs(ln, stdout);
  340 
  341     if (cmd_line)
  342         my_flush();
  343     else {
  344         if (inverse)
  345             EndInverse();
  346     }
  347     free(ln);
  348 }
  349 
  350 
  351 void
  352 draw_arrow_mark(
  353     int line)
  354 {
  355 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  356     wchar_t *wtmp;
  357 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  358 
  359     MoveCursor(line, 0);
  360 
  361     if (tinrc.draw_arrow) {
  362 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  363         if (tinrc.utf8_graphics) {
  364             my_fputwc(CURSOR_HORIZ, stdout);
  365             my_fputwc(CURSOR_ARROW, stdout);
  366         } else
  367 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  368             my_fputs("->", stdout);
  369     } else {
  370 #ifdef USE_CURSES
  371         char buffer[BUFSIZ];
  372         char *s = screen_contents(line, 0, buffer);
  373 #else
  374         char *s = screen[line - INDEX_TOP].col;
  375 #endif /* USE_CURSES */
  376 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  377         if ((wtmp = char2wchar_t(s)) != NULL) {
  378             StartInverse();
  379             my_fputws(wtmp, stdout);
  380             EndInverse();
  381             if (mark_offset && wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))] == tinrc.art_marked_selected) {
  382                 MoveCursor(line, mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected)));
  383 /*              EndInverse(); */ /* needed? */
  384                 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
  385             }
  386             free(wtmp);
  387         }
  388 #else
  389         StartInverse();
  390         my_fputs(s, stdout);
  391         EndInverse();
  392         if (mark_offset && s[mark_offset] == tinrc.art_marked_selected) {
  393             MoveCursor(line, mark_offset);
  394 /*          EndInverse(); */ /* needed? */
  395             my_fputc(s[mark_offset], stdout);
  396         }
  397 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  398     }
  399     stow_cursor();
  400 }
  401 
  402 
  403 void
  404 erase_arrow(
  405     void)
  406 {
  407     int line = INDEX_TOP + currmenu->curr - currmenu->first;
  408 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  409     wchar_t *wtmp;
  410 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  411 
  412     if (!currmenu->max)
  413         return;
  414 
  415     MoveCursor(line, 0);
  416 
  417     if (tinrc.draw_arrow)
  418         my_fputs("  ", stdout);
  419     else {
  420 #ifdef USE_CURSES
  421         char buffer[BUFSIZ];
  422         char *s = screen_contents(line, 0, buffer);
  423 #else
  424         char *s;
  425 
  426         if (line - INDEX_TOP < 0) /* avoid underruns */
  427             line = INDEX_TOP;
  428 
  429         s = screen[line - INDEX_TOP].col;
  430 #endif /* USE_CURSES */
  431         EndInverse();
  432 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  433         if ((wtmp = char2wchar_t(s)) != NULL) {
  434             my_fputws(wtmp, stdout);
  435             if (mark_offset && wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))] == tinrc.art_marked_selected) {
  436                 MoveCursor(line, mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected)));
  437                 StartInverse();
  438                 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
  439                 EndInverse();
  440             }
  441             free(wtmp);
  442         }
  443 #else
  444         my_fputs(s, stdout);
  445         if (mark_offset && s[mark_offset] == tinrc.art_marked_selected) {
  446             MoveCursor(line, mark_offset);
  447             StartInverse();
  448             my_fputc(s[mark_offset], stdout);
  449             EndInverse();
  450         }
  451 #endif /* MULTIBYTE_ABLE && !NOLOCALE */
  452     }
  453 }
  454 
  455 
  456 void
  457 show_title(
  458     const char *title)
  459 {
  460     int col;
  461 
  462     col = cCOLS - strwidth(_(txt_type_h_for_help));
  463     if (col > 0) {
  464         MoveCursor(0, col);
  465 #ifdef HAVE_COLOR
  466         fcol(tinrc.col_title);
  467 #endif /* HAVE_COLOR */
  468         /* you have mail message in */
  469         my_fputs((mail_check(mailbox) ? _(txt_you_have_mail) : _(txt_type_h_for_help)), stdout);
  470 
  471 #ifdef HAVE_COLOR
  472         fcol(tinrc.col_normal);
  473 #endif /* HAVE_COLOR */
  474     }
  475     center_line(0, TRUE, title); /* wastes some space on the left */
  476 }
  477 
  478 
  479 void
  480 ring_bell(
  481     void)
  482 {
  483 #ifdef USE_CURSES
  484     if (!cmd_line)
  485         beep();
  486     else {
  487 #endif /* USE_CURSES */
  488     my_fputc('\007', stdout);
  489     my_flush();
  490 #ifdef USE_CURSES
  491     }
  492 #endif /* USE_CURSES */
  493 }
  494 
  495 
  496 void
  497 spin_cursor(
  498     void)
  499 {
  500     static const char buf[] = "|/-\\|/-\\ "; /* don't remove the tailing space! */
  501     static unsigned short int i = 0;
  502 
  503     if (batch_mode)
  504         return;
  505 
  506     if (i > 7)
  507         i = 0;
  508 
  509 #ifdef HAVE_COLOR
  510     fcol(tinrc.col_message);
  511 #endif /* HAVE_COLOR */
  512     my_printf("\b%c", buf[i++]);
  513     my_flush();
  514 #ifdef HAVE_COLOR
  515     fcol(tinrc.col_normal);
  516 #endif /* HAVE_COLOR */
  517 }
  518 
  519 
  520 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  521 #   define DISPLAY_FMT "%s %3d%% "
  522 #else
  523 #   define DISPLAY_FMT "%s %3d%%"
  524 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  525 /*
  526  * progressmeter in %
  527  */
  528 void
  529 show_progress(
  530     const char *txt,
  531     t_artnum count,
  532     t_artnum total)
  533 {
  534     char display[LEN];
  535     int ratio;
  536     time_t curr_time;
  537     static char last_display[LEN];
  538     static int last_ratio;
  539     static t_artnum last_count;
  540     static t_artnum last_total;
  541     static time_t last_update;
  542 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  543     static int average;
  544     static int samples;
  545     static int last_secs_left;
  546     static int sum;
  547     char *display_format;
  548     int time_diff;
  549     int secs_left;
  550     t_artnum count_diff;
  551     static struct t_tintime last_time;
  552     static struct t_tintime this_time;
  553 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  554 
  555     if (batch_mode || count <= 0 || total <= 1)
  556         return;
  557 
  558     /* If this is a new progress meter, start recalculating */
  559     if ((last_total != total) || (count <= last_count)) {
  560         last_ratio = -1;
  561         last_display[0] = '\0';
  562         last_update = time(NULL) - 2;
  563     }
  564 
  565     curr_time = time(NULL);
  566     ratio = (int) ((count * 100) / total);
  567     if ((ratio == last_ratio) && (curr_time - last_update < 2))
  568         /*
  569          * return if ratio did not change and less than
  570          * 2 seconds since last update to reduce output
  571          */
  572         return;
  573 
  574     last_update = curr_time;
  575 
  576 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  577     display_format = my_malloc(strlen(DISPLAY_FMT) + strlen(_(txt_remaining)) + 1);
  578     strcpy(display_format, DISPLAY_FMT);
  579 
  580     if (last_ratio == -1) {
  581         /* Don't print the time remaining */
  582         snprintf(display, sizeof(display), display_format, txt, ratio);
  583 
  584         /* Reset the variables */
  585         sum = average = samples = last_secs_left = 0;
  586     } else {
  587         /* Get the current time */
  588         tin_gettime(&this_time);
  589         time_diff = (int) ((this_time.tv_sec - last_time.tv_sec) * 1000000);
  590         time_diff = (int) (time_diff + ((this_time.tv_nsec - last_time.tv_nsec) / 1000));
  591         count_diff = (count - last_count);
  592 
  593         if (!count_diff) /* avoid div by zero */
  594             count_diff++;
  595 
  596         /*
  597          * Calculate a running average based on the last 20 samples. For the
  598          * first 19 samples just add all and divide by the number of samples.
  599          * From the 20th sample on use only the last 20 samples to calculate
  600          * the running averave. To make things easier we don't want to store
  601          * and keep track of all of them, so we assume that the first sample
  602          * was close to the current average and subtract it from sum. Then,
  603          * the new sample is added to the sum and the sum is divided by 20 to
  604          * get the new average.
  605          */
  606         if (samples == 20) {
  607             sum -= average;
  608             sum = (int) (sum + (time_diff / count_diff));
  609             average = sum / 20;
  610         } else {
  611             sum = (int) (sum + (time_diff / count_diff));
  612             average = sum / ++samples;
  613         }
  614 
  615         if (average >= 1000000)
  616             secs_left = (int) ((total - count) * (average / 1000000));
  617         else
  618             secs_left = (int) (((total - count) * average) / 1000000);
  619 
  620         if (secs_left < 0)
  621             secs_left = 0;
  622 
  623         if ((secs_left > 0) && (last_secs_left == 0))
  624             last_secs_left = secs_left;
  625 
  626         if (samples < 5)
  627             /* Don't print the time remaining */
  628             snprintf(display, sizeof(display), display_format, txt, ratio);
  629         else {
  630             /* Don't allow time remaining to increase by 1 or 2 seconds */
  631             if ((secs_left == last_secs_left + 1) || (secs_left == last_secs_left + 2))
  632                 secs_left = last_secs_left;
  633             else if (secs_left < last_secs_left)
  634                 last_secs_left = secs_left;
  635             strcat(display_format, _(txt_remaining));
  636             snprintf(display, sizeof(display), display_format, txt, ratio, secs_left / 60, secs_left % 60);
  637         }
  638     }
  639     free(display_format);
  640 
  641     tin_gettime(&last_time);
  642 #else
  643     snprintf(display, sizeof(display), DISPLAY_FMT, txt, ratio);
  644 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  645 
  646     /* Only display text if it changed from last time */
  647     if (strcmp(display, last_display)) {
  648         char *tmp;
  649 
  650         if (RawState()) {
  651             clear_message();
  652             MoveCursor(cLINES, 0);
  653         } else {
  654             my_printf("\r");
  655             my_flush();
  656             CleartoEOLN();
  657         }
  658 
  659 #ifdef HAVE_COLOR
  660         if (RawState())
  661             fcol(tinrc.col_message);
  662 #endif /* HAVE_COLOR */
  663 
  664         /*
  665          * TODO: depending on the length of the newsgroup name
  666          * it's possible to cut away a great part of the progress meter
  667          * perhaps we should shorten the newsgroup name instead?
  668          */
  669         my_printf("%s", sized_message(&tmp, "%s", display));
  670         free(tmp);
  671 
  672 #ifdef HAVE_COLOR
  673         if (RawState())
  674             fcol(tinrc.col_normal);
  675 #endif /* HAVE_COLOR */
  676 
  677 #ifndef USE_CURSES
  678         if (!RawState())
  679             MoveCursor(cLINES, 0);
  680 #endif /* !USE_CURSES */
  681 
  682         my_flush();
  683         STRCPY(last_display, display);
  684     }
  685 
  686     last_count = count;
  687     last_total = total;
  688     last_ratio = ratio;
  689 }