"Fossies" - the Fresh Open Source Software Archive

Member "tin-2.4.1/src/screen.c" (12 Oct 2016, 13252 Bytes) of archive /linux/misc/tin-2.4.1.tar.gz:


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