"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.3/src/screen.c" (4 Dec 2018, 13598 Bytes) of package /linux/misc/tin-2.4.3.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.2_vs_2.4.3.

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