"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.6.0/src/screen.c" (4 Jul 2021, 14560 Bytes) of package /linux/misc/tin-2.6.0.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.4.5_vs_2.6.0.

    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-07-02
    7  *  Notes     :
    8  *
    9  * Copyright (c) 1991-2021 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 
  173     (void) sleep(sdelay);
  174 /*  clear_message(); would be nice, but tin doesn't expect this yet */
  175     va_end(ap);
  176 }
  177 
  178 
  179 /*
  180  * Print a formatted message to stderr, no colour is added.
  181  * Interesting - this function implicitly clears 'errno'
  182  */
  183 void
  184 error_message(
  185     unsigned int sdelay,
  186     const char *fmt,
  187     ...)
  188 {
  189     char *buf;
  190     va_list ap;
  191 
  192     va_start(ap, fmt);
  193 
  194     errno = 0;
  195     clear_message();
  196 
  197     buf = fmt_message(fmt, ap);
  198     my_fputs(buf, stderr);  /* don't use my_fprintf() here due to %format chars */
  199     my_fflush(stderr);
  200     free(buf);
  201 
  202     if (cmd_line) {
  203         my_fputc('\n', stderr);
  204         fflush(stderr);
  205     } else {
  206         stow_cursor();
  207         (void) sleep(sdelay);
  208         clear_message();
  209     }
  210 
  211     va_end(ap);
  212 }
  213 
  214 
  215 /*
  216  * Print a formatted error message to stderr, no colour is added.
  217  * This function implicitly clears 'errno'
  218  */
  219 void
  220 perror_message(
  221     const char *fmt,
  222     ...)
  223 {
  224     char *buf;
  225     int err;
  226     va_list ap;
  227 
  228     err = errno;
  229     va_start(ap, fmt);
  230 
  231     clear_message();
  232 
  233     if ((buf = fmt_message(fmt, ap)) != NULL) {
  234         error_message(2, "%s: Error: %s", buf, strerror(err));
  235         free(buf);
  236     }
  237 
  238     va_end(ap);
  239 }
  240 
  241 
  242 void
  243 clear_message(
  244     void)
  245 {
  246     if (!cmd_line) {
  247         MoveCursor(cLINES, 0);
  248         CleartoEOLN();
  249         cursoroff();
  250 #ifndef USE_CURSES
  251         my_flush();
  252 #endif /* !USE_CURSES */
  253     }
  254 }
  255 
  256 
  257 void
  258 center_line(
  259     int line,
  260     t_bool inverse,
  261     const char *str)
  262 {
  263     char *ln;
  264     int pos;
  265     int len;
  266 
  267     len = strwidth(str);
  268 
  269 #if defined(HAVE_LIBICUUC) && defined(MULTIBYTE_ABLE) && defined(HAVE_UNICODE_UBIDI_H) && !defined(NO_LOCALE)
  270     if (tinrc.render_bidi && IS_LOCAL_CHARSET("UTF-8") && len > 1) {
  271         t_bool is_rtl;
  272 
  273         if ((ln = render_bidi(str, &is_rtl)) == NULL)
  274             ln = my_strdup(str);
  275     } else
  276 #endif /* HAVE_LIBICUUC && MULTIBYTE_ABLE && HAVE_UNICODE_UBIDI_H && !NO_LOCALE */
  277         ln = my_strdup(str);
  278 
  279     if (!cmd_line) {
  280         if (cCOLS >= len)
  281             pos = (cCOLS - len) / 2;
  282         else
  283             pos = 1;
  284 
  285         MoveCursor(line, pos);
  286         if (inverse) {
  287             StartInverse();
  288             my_flush();
  289         }
  290     }
  291 
  292     if (len >= cCOLS) {
  293         char *buffer;
  294 
  295         buffer = strunc(ln, cCOLS - 2);
  296         my_fputs(buffer, stdout);
  297         free(buffer);
  298     } else
  299         my_fputs(ln, stdout);
  300 
  301     if (cmd_line)
  302         my_flush();
  303     else {
  304         if (inverse)
  305             EndInverse();
  306     }
  307     free(ln);
  308 }
  309 
  310 
  311 void
  312 draw_arrow_mark(
  313     int line)
  314 {
  315 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  316     wchar_t *wtmp;
  317 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  318 
  319     MoveCursor(line, 0);
  320 
  321     if (tinrc.draw_arrow) {
  322 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  323         if (tinrc.utf8_graphics) {
  324             my_fputwc(CURSOR_HORIZ, stdout);
  325             my_fputwc(CURSOR_ARROW, stdout);
  326         } else
  327 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  328             my_fputs("->", stdout);
  329     } else {
  330 #ifdef USE_CURSES
  331         char buffer[BUFSIZ];
  332         char *s = screen_contents(line, 0, buffer);
  333 #else
  334         char *s = screen[line - INDEX_TOP].col;
  335 #endif /* USE_CURSES */
  336 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  337         if ((wtmp = char2wchar_t(s)) != NULL) {
  338             StartInverse();
  339             my_fputws(wtmp, stdout);
  340             EndInverse();
  341             if (mark_offset && wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))] == tinrc.art_marked_selected) {
  342                 MoveCursor(line, mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected)));
  343 /*              EndInverse(); */ /* needed? */
  344                 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
  345             }
  346             free(wtmp);
  347         }
  348 #else
  349         StartInverse();
  350         my_fputs(s, stdout);
  351         EndInverse();
  352         if (mark_offset && s[mark_offset] == tinrc.art_marked_selected) {
  353             MoveCursor(line, mark_offset);
  354 /*          EndInverse(); */ /* needed? */
  355             my_fputc(s[mark_offset], stdout);
  356         }
  357 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  358     }
  359     stow_cursor();
  360 }
  361 
  362 
  363 void
  364 erase_arrow(
  365     void)
  366 {
  367     int line = INDEX_TOP + currmenu->curr - currmenu->first;
  368 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  369     wchar_t *wtmp;
  370 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
  371 
  372     if (!currmenu->max)
  373         return;
  374 
  375     MoveCursor(line, 0);
  376 
  377     if (tinrc.draw_arrow)
  378         my_fputs("  ", stdout);
  379     else {
  380 #ifdef USE_CURSES
  381         char buffer[BUFSIZ];
  382         char *s = screen_contents(line, 0, buffer);
  383 #else
  384         char *s;
  385 
  386         if (line - INDEX_TOP < 0) /* avoid underruns */
  387             line = INDEX_TOP;
  388 
  389         s = screen[line - INDEX_TOP].col;
  390 #endif /* USE_CURSES */
  391         EndInverse();
  392 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
  393         if ((wtmp = char2wchar_t(s)) != NULL) {
  394             my_fputws(wtmp, stdout);
  395             if (mark_offset && wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))] == tinrc.art_marked_selected) {
  396                 MoveCursor(line, mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected)));
  397                 StartInverse();
  398                 my_fputwc((wint_t) wtmp[mark_offset + (art_mark_width - wcwidth(tinrc.art_marked_selected))], stdout);
  399                 EndInverse();
  400             }
  401             free(wtmp);
  402         }
  403 #else
  404         my_fputs(s, stdout);
  405         if (mark_offset && s[mark_offset] == tinrc.art_marked_selected) {
  406             MoveCursor(line, mark_offset);
  407             StartInverse();
  408             my_fputc(s[mark_offset], stdout);
  409             EndInverse();
  410         }
  411 #endif /* MULTIBYTE_ABLE && !NOLOCALE */
  412     }
  413 }
  414 
  415 
  416 void
  417 show_title(
  418     const char *title)
  419 {
  420     int col;
  421 
  422     col = cCOLS - strwidth(_(txt_type_h_for_help));
  423     if (col > 0) {
  424         MoveCursor(0, col);
  425 #ifdef HAVE_COLOR
  426         fcol(tinrc.col_title);
  427 #endif /* HAVE_COLOR */
  428         /* you have mail message in */
  429         my_fputs((mail_check(mailbox) ? _(txt_you_have_mail) : _(txt_type_h_for_help)), stdout);
  430 
  431 #ifdef HAVE_COLOR
  432         fcol(tinrc.col_normal);
  433 #endif /* HAVE_COLOR */
  434     }
  435     center_line(0, TRUE, title); /* wastes some space on the left */
  436 }
  437 
  438 
  439 void
  440 ring_bell(
  441     void)
  442 {
  443 #ifdef USE_CURSES
  444     if (!cmd_line)
  445         beep();
  446     else {
  447 #endif /* USE_CURSES */
  448     my_fputc('\007', stdout);
  449     my_flush();
  450 #ifdef USE_CURSES
  451     }
  452 #endif /* USE_CURSES */
  453 }
  454 
  455 
  456 void
  457 spin_cursor(
  458     void)
  459 {
  460     static const char buf[] = "|/-\\|/-\\ "; /* don't remove the tailing space! */
  461     static unsigned short int i = 0;
  462 
  463     if (batch_mode)
  464         return;
  465 
  466     if (i > 7)
  467         i = 0;
  468 
  469 #ifdef HAVE_COLOR
  470     fcol(tinrc.col_message);
  471 #endif /* HAVE_COLOR */
  472     my_printf("\b%c", buf[i++]);
  473     my_flush();
  474 #ifdef HAVE_COLOR
  475     fcol(tinrc.col_normal);
  476 #endif /* HAVE_COLOR */
  477 }
  478 
  479 
  480 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  481 #   define DISPLAY_FMT "%s %3d%% "
  482 #else
  483 #   define DISPLAY_FMT "%s %3d%%"
  484 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  485 /*
  486  * progressmeter in %
  487  */
  488 void
  489 show_progress(
  490     const char *txt,
  491     t_artnum count,
  492     t_artnum total)
  493 {
  494     char display[LEN];
  495     int ratio;
  496     time_t curr_time;
  497     static char last_display[LEN];
  498     static int last_ratio;
  499     static t_artnum last_count;
  500     static t_artnum last_total;
  501     static time_t last_update;
  502 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  503     static int average;
  504     static int samples;
  505     static int last_secs_left;
  506     static int sum;
  507     char *display_format;
  508     int time_diff;
  509     int secs_left;
  510     t_artnum count_diff;
  511     static struct t_tintime last_time;
  512     static struct t_tintime this_time;
  513 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  514 
  515     if (batch_mode || count <= 0 || total <= 1)
  516         return;
  517 
  518     /* If this is a new progress meter, start recalculating */
  519     if ((last_total != total) || (count <= last_count)) {
  520         last_ratio = -1;
  521         last_display[0] = '\0';
  522         last_update = time(NULL) - 2;
  523     }
  524 
  525     curr_time = time(NULL);
  526     ratio = (int) ((count * 100) / total);
  527     if ((ratio == last_ratio) && (curr_time - last_update < 2))
  528         /*
  529          * return if ratio did not change and less than
  530          * 2 seconds since last update to reduce output
  531          */
  532         return;
  533 
  534     last_update = curr_time;
  535 
  536 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
  537     display_format = my_malloc(strlen(DISPLAY_FMT) + strlen(_(txt_remaining)) + 1);
  538     strcpy(display_format, DISPLAY_FMT);
  539 
  540     if (last_ratio == -1) {
  541         /* Don't print the time remaining */
  542         snprintf(display, sizeof(display), display_format, txt, ratio);
  543 
  544         /* Reset the variables */
  545         sum = average = samples = last_secs_left = 0;
  546     } else {
  547         /* Get the current time */
  548         tin_gettime(&this_time);
  549         time_diff = (int) ((this_time.tv_sec - last_time.tv_sec) * 1000000);
  550         time_diff = (int) (time_diff + ((this_time.tv_nsec - last_time.tv_nsec) / 1000));
  551         count_diff = (count - last_count);
  552 
  553         if (!count_diff) /* avoid div by zero */
  554             count_diff++;
  555 
  556         /*
  557          * Calculate a running average based on the last 20 samples. For the
  558          * first 19 samples just add all and divide by the number of samples.
  559          * From the 20th sample on use only the last 20 samples to calculate
  560          * the running averave. To make things easier we don't want to store
  561          * and keep track of all of them, so we assume that the first sample
  562          * was close to the current average and subtract it from sum. Then,
  563          * the new sample is added to the sum and the sum is divided by 20 to
  564          * get the new average.
  565          */
  566         if (samples == 20) {
  567             sum -= average;
  568             sum = (int) (sum + (time_diff / count_diff));
  569             average = sum / 20;
  570         } else {
  571             sum = (int) (sum + (time_diff / count_diff));
  572             average = sum / ++samples;
  573         }
  574 
  575         if (average >= 1000000)
  576             secs_left = (int) ((total - count) * (average / 1000000));
  577         else
  578             secs_left = (int) (((total - count) * average) / 1000000);
  579 
  580         if (secs_left < 0)
  581             secs_left = 0;
  582 
  583         if ((secs_left > 0) && (last_secs_left == 0))
  584             last_secs_left = secs_left;
  585 
  586         if (samples < 5)
  587             /* Don't print the time remaining */
  588             snprintf(display, sizeof(display), display_format, txt, ratio);
  589         else {
  590             /* Don't allow time remaining to increase by 1 or 2 seconds */
  591             if ((secs_left == last_secs_left + 1) || (secs_left == last_secs_left + 2))
  592                 secs_left = last_secs_left;
  593             else if (secs_left < last_secs_left)
  594                 last_secs_left = secs_left;
  595             strcat(display_format, _(txt_remaining));
  596             snprintf(display, sizeof(display), display_format, txt, ratio, secs_left / 60, secs_left % 60);
  597         }
  598     }
  599     free(display_format);
  600 
  601     tin_gettime(&last_time);
  602 #else
  603     snprintf(display, sizeof(display), DISPLAY_FMT, txt, ratio);
  604 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
  605 
  606     /* Only display text if it changed from last time */
  607     if (strcmp(display, last_display)) {
  608         char *tmp;
  609 
  610         if (RawState()) {
  611             clear_message();
  612             MoveCursor(cLINES, 0);
  613         } else {
  614             my_printf("\r");
  615             my_flush();
  616             CleartoEOLN();
  617         }
  618 
  619 #ifdef HAVE_COLOR
  620         if (RawState())
  621             fcol(tinrc.col_message);
  622 #endif /* HAVE_COLOR */
  623 
  624         /*
  625          * TODO: depending on the length of the newsgroup name
  626          * it's possible to cut away a great part of the progress meter
  627          * perhaps we should shorten the newsgroup name instead?
  628          */
  629         my_printf("%s", sized_message(&tmp, "%s", display));
  630         free(tmp);
  631 
  632 #ifdef HAVE_COLOR
  633         if (RawState())
  634             fcol(tinrc.col_normal);
  635 #endif /* HAVE_COLOR */
  636 
  637 #ifndef USE_CURSES
  638         if (!RawState())
  639             MoveCursor(cLINES, 0);
  640 #endif /* !USE_CURSES */
  641 
  642         my_flush();
  643         STRCPY(last_display, display);
  644     }
  645 
  646     last_count = count;
  647     last_total = total;
  648     last_ratio = ratio;
  649 }