tin  2.4.4
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.4.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

screen.c
Go to the documentation of this file.
1 /*
2  * Project : tin - a Usenet reader
3  * Module : screen.c
4  * Author : I. Lea & R. Skrenta
5  * Created : 1991-04-01
6  * Updated : 2019-01-17
7  * Notes :
8  *
9  * Copyright (c) 1991-2020 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
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 *
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
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
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
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
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
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
259  int line,
260  t_bool inverse,
261  const char *str)
262 {
263  int pos;
264  int len;
265 
266  len = strwidth(str);
267 
268  if (!cmd_line) {
269  if (cCOLS >= len)
270  pos = (cCOLS - len) / 2;
271  else
272  pos = 1;
273 
274  MoveCursor(line, pos);
275  if (inverse) {
276  StartInverse();
277  my_flush();
278  }
279  }
280 
281  if (len >= cCOLS) {
282  char *buffer;
283 
284  buffer = strunc(str, cCOLS - 2);
285  my_fputs(buffer, stdout);
286  free(buffer);
287  } else
288  my_fputs(str, stdout);
289 
290  if (cmd_line)
291  my_flush();
292  else {
293  if (inverse)
294  EndInverse();
295  }
296 }
297 
298 
299 void
301  int line)
302 {
303 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
304  wchar_t *wtmp;
305 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
306 
307  MoveCursor(line, 0);
308 
309  if (tinrc.draw_arrow) {
310 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
311  if (tinrc.utf8_graphics) {
312  my_fputwc(CURSOR_HORIZ, stdout);
313  my_fputwc(CURSOR_ARROW, stdout);
314  } else
315 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
316  my_fputs("->", stdout);
317  } else {
318 #ifdef USE_CURSES
319  char buffer[BUFSIZ];
320  char *s = screen_contents(line, 0, buffer);
321 #else
322  char *s = screen[line - INDEX_TOP].col;
323 #endif /* USE_CURSES */
324 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
325  if ((wtmp = char2wchar_t(s)) != NULL) {
326  StartInverse();
327  my_fputws(wtmp, stdout);
328  EndInverse();
330  MoveCursor(line, mark_offset);
331  EndInverse();
332  my_fputwc((wint_t) wtmp[mark_offset], stdout);
333  }
334  free(wtmp);
335  }
336 #else
337  StartInverse();
338  my_fputs(s, stdout);
339  EndInverse();
341  MoveCursor(line, mark_offset);
342  EndInverse();
343  my_fputc(s[mark_offset], stdout);
344  }
345 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
346  }
347  stow_cursor();
348 }
349 
350 
351 void
353  void)
354 {
355  int line = INDEX_TOP + currmenu->curr - currmenu->first;
356 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
357  wchar_t *wtmp;
358 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
359 
360  if (!currmenu->max)
361  return;
362 
363  MoveCursor(line, 0);
364 
365  if (tinrc.draw_arrow)
366  my_fputs(" ", stdout);
367  else {
368 #ifdef USE_CURSES
369  char buffer[BUFSIZ];
370  char *s = screen_contents(line, 0, buffer);
371 #else
372  char *s;
373 
374  if (line - INDEX_TOP < 0) /* avoid underruns */
375  line = INDEX_TOP;
376 
377  s = screen[line - INDEX_TOP].col;
378 #endif /* USE_CURSES */
379  EndInverse();
380 #if defined(MULTIBYTE_ABLE) && !defined(NO_LOCALE)
381  if ((wtmp = char2wchar_t(s)) != NULL) {
382  my_fputws(wtmp, stdout);
384  MoveCursor(line, mark_offset);
385  StartInverse();
386  my_fputwc((wint_t) wtmp[mark_offset], stdout);
387  EndInverse();
388  }
389  free(wtmp);
390  }
391 #else
392  my_fputs(s, stdout);
394  MoveCursor(line, mark_offset);
395  StartInverse();
396  my_fputc(s[mark_offset], stdout);
397  EndInverse();
398  }
399 #endif /* MULTIBYTE_ABLE && !NOLOCALE */
400  }
401 }
402 
403 
404 void
406  const char *title)
407 {
408  int col;
409 
411  if (col > 0) {
412  MoveCursor(0, col);
413 #ifdef HAVE_COLOR
414  fcol(tinrc.col_title);
415 #endif /* HAVE_COLOR */
416  /* you have mail message in */
418 
419 #ifdef HAVE_COLOR
420  fcol(tinrc.col_normal);
421 #endif /* HAVE_COLOR */
422  }
423  center_line(0, TRUE, title); /* wastes some space on the left */
424 }
425 
426 
427 void
429  void)
430 {
431 #ifdef USE_CURSES
432  if (!cmd_line)
433  beep();
434  else {
435 #endif /* USE_CURSES */
436  my_fputc('\007', stdout);
437  my_flush();
438 #ifdef USE_CURSES
439  }
440 #endif /* USE_CURSES */
441 }
442 
443 
444 void
446  void)
447 {
448  static const char buf[] = "|/-\\|/-\\ "; /* don't remove the tailing space! */
449  static unsigned short int i = 0;
450 
451  if (batch_mode)
452  return;
453 
454  if (i > 7)
455  i = 0;
456 
457 #ifdef HAVE_COLOR
458  fcol(tinrc.col_message);
459 #endif /* HAVE_COLOR */
460  my_printf("\b%c", buf[i++]);
461  my_flush();
462 #ifdef HAVE_COLOR
463  fcol(tinrc.col_normal);
464 #endif /* HAVE_COLOR */
465 }
466 
467 
468 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
469 # define DISPLAY_FMT "%s %3d%% "
470 #else
471 # define DISPLAY_FMT "%s %3d%%"
472 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
473 /*
474  * progressmeter in %
475  */
476 void
478  const char *txt,
479  t_artnum count,
480  t_artnum total)
481 {
482  char display[LEN];
483  int ratio;
484  time_t curr_time;
485  static char last_display[LEN];
486  static int last_ratio;
487  static t_artnum last_total;
488  static time_t last_update;
489 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
490  static t_artnum last_count;
491  static int average;
492  static int samples;
493  static int last_secs_left;
494  static int sum;
495  char *display_format;
496  int time_diff;
497  int secs_left;
498  t_artnum count_diff;
499  static struct t_tintime last_time;
500  static struct t_tintime this_time;
501 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
502 
503  if (batch_mode || count <= 0 || total <= 1)
504  return;
505 
506  /* If this is a new progress meter, start recalculating */
507  if ((last_total != total) || (count == 1)) {
508  last_ratio = -1;
509  last_display[0] = '\0';
510  last_update = time(NULL) - 2;
511  }
512 
513  curr_time = time(NULL);
514  ratio = (int) ((count * 100) / total);
515  if ((ratio == last_ratio) && (curr_time - last_update < 2))
516  /*
517  * return if ratio did not change and less than
518  * 2 seconds since last update to reduce output
519  */
520  return;
521 
522  last_update = curr_time;
523 
524 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
525  display_format = my_malloc(strlen(DISPLAY_FMT) + strlen(_(txt_remaining)) + 1);
526  strcpy(display_format, DISPLAY_FMT);
527 
528  if (last_ratio == -1) {
529  /* Don't print the time remaining */
530  snprintf(display, sizeof(display), display_format, txt, ratio);
531 
532  /* Reset the variables */
533  sum = average = samples = last_secs_left = 0;
534  } else {
535  /* Get the current time */
536  tin_gettime(&this_time);
537  time_diff = (this_time.tv_sec - last_time.tv_sec) * 1000000;
538  time_diff += ((this_time.tv_nsec - last_time.tv_nsec) / 1000);
539  count_diff = (count - last_count);
540 
541  if (!count_diff) /* avoid div by zero */
542  count_diff++;
543 
544  /*
545  * Calculate a running average based on the last 20 samples. For the
546  * first 19 samples just add all and divide by the number of samples.
547  * From the 20th sample on use only the last 20 samples to calculate
548  * the running averave. To make things easier we don't want to store
549  * and keep track of all of them, so we assume that the first sample
550  * was close to the current average and subtract it from sum. Then,
551  * the new sample is added to the sum and the sum is divided by 20 to
552  * get the new average.
553  */
554  if (samples == 20) {
555  sum -= average;
556  sum += (time_diff / count_diff);
557  average = sum / 20;
558  } else {
559  sum += (time_diff / count_diff);
560  average = sum / ++samples;
561  }
562 
563  if (average >= 1000000)
564  secs_left = (total - count) * (average / 1000000);
565  else
566  secs_left = ((total - count) * average) / 1000000;
567 
568  if (secs_left < 0)
569  secs_left = 0;
570 
571  if ((secs_left > 0) && (last_secs_left == 0))
572  last_secs_left = secs_left;
573 
574  if (samples < 5)
575  /* Don't print the time remaining */
576  snprintf(display, sizeof(display), display_format, txt, ratio);
577  else {
578  /* Don't allow time remaining to increase by 1 or 2 seconds */
579  if ((secs_left == last_secs_left + 1) || (secs_left == last_secs_left + 2))
580  secs_left = last_secs_left;
581  else if (secs_left < last_secs_left)
582  last_secs_left = secs_left;
583  strcat(display_format, _(txt_remaining));
584  snprintf(display, sizeof(display), display_format, txt, ratio, secs_left / 60, secs_left % 60);
585  }
586  }
587  free(display_format);
588 
589  last_count = count;
590  tin_gettime(&last_time);
591 #else
592  snprintf(display, sizeof(display), DISPLAY_FMT, txt, ratio);
593 #endif /* HAVE_CLOCK_GETTIME || HAVE_GETTIMEOFDAY */
594 
595  /* Only display text if it changed from last time */
596  if (strcmp(display, last_display)) {
597  char *tmp;
598 
599  if (RawState()) {
600  clear_message();
601  MoveCursor(cLINES, 0);
602  } else {
603  my_printf("\r");
604  my_flush();
605  CleartoEOLN();
606  }
607 
608 #ifdef HAVE_COLOR
609  if (RawState())
610  fcol(tinrc.col_message);
611 #endif /* HAVE_COLOR */
612 
613  /*
614  * TODO: depending on the length of the newsgroup name
615  * it's possible to cut away a great part of the progress meter
616  * perhaps we should shorten the newsgroup name instead?
617  */
618  my_printf("%s", sized_message(&tmp, "%s", display));
619  free(tmp);
620 
621 #ifdef HAVE_COLOR
622  if (RawState())
623  fcol(tinrc.col_normal);
624 #endif /* HAVE_COLOR */
625 
626 #ifndef USE_CURSES
627  if (!RawState())
628  MoveCursor(cLINES, 0);
629 #endif /* !USE_CURSES */
630 
631  my_flush();
632  STRCPY(last_display, display);
633  }
634 
635  last_total = total;
636  last_ratio = ratio;
637 }
RawState
int RawState(void)
Definition: curses.c:604
_
#define _(Text)
Definition: tin.h:94
my_realloc
#define my_realloc(ptr, size)
Definition: tin.h:2198
INDEX_TOP
#define INDEX_TOP
Definition: tin.h:1008
center_line
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:258
t_tintime
Definition: tin.h:2034
mailbox
char mailbox[PATH_LEN]
Definition: init.c:91
cLINES
int cLINES
Definition: curses.c:52
ring_bell
void ring_bell(void)
Definition: screen.c:428
info_message
void info_message(const char *fmt,...)
Definition: screen.c:102
my_flush
#define my_flush()
Definition: tcurses.h:171
end_va_copy
#define end_va_copy(dst)
Definition: tin.h:2409
tinrc
struct t_config tinrc
Definition: init.c:191
my_fputc
#define my_fputc(ch, stream)
Definition: tcurses.h:152
t_config::art_marked_selected
char art_marked_selected
Definition: tinrc.h:62
MoveCursor
void MoveCursor(int row, int col)
Definition: curses.c:441
tcurses.h
t_menu::max
int max
Definition: tin.h:2007
tin.h
clear_message
void clear_message(void)
Definition: screen.c:243
CleartoEOLN
void CleartoEOLN(void)
Definition: curses.c:458
stow_cursor
void stow_cursor(void)
Definition: screen.c:59
EndInverse
void EndInverse(void)
Definition: curses.c:564
error_message
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:184
fmt_message
char * fmt_message(const char *fmt, va_list ap)
Definition: screen.c:73
strunc
char * strunc(const char *message, int len)
Definition: string.c:1069
t_screen
Definition: tin.h:1929
vsnprintf
#define vsnprintf
Definition: tin.h:2420
wait_message
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
draw_arrow_mark
void draw_arrow_mark(int line)
Definition: screen.c:300
strwidth
int strwidth(const char *str)
Definition: string.c:1043
sized_message
char * sized_message(char **result, const char *format, const char *subject)
Definition: prompt.c:646
cmd_line
t_bool cmd_line
Definition: init.c:128
buf
static char buf[16]
Definition: langinfo.c:50
cCOLS
int cCOLS
Definition: curses.c:53
LEN
#define LEN
Definition: tin.h:854
show_title
void show_title(const char *title)
Definition: screen.c:405
mail_check
t_bool mail_check(const char *mailbox_name)
Definition: misc.c:911
mark_offset
int mark_offset
Definition: screen.c:48
t_tintime::tv_nsec
long tv_nsec
Definition: tin.h:2036
t_tintime::tv_sec
time_t tv_sec
Definition: tin.h:2035
currmenu
t_menu * currmenu
Definition: init.c:165
cursoroff
void cursoroff(void)
Definition: curses.c:721
cursoron
void cursoron(void)
Definition: curses.c:712
my_fputs
#define my_fputs(str, stream)
Definition: tcurses.h:153
buffer
static uschar * buffer
Definition: pcretest.c:154
batch_mode
t_bool batch_mode
Definition: init.c:126
my_printf
#define my_printf
Definition: tcurses.h:169
t_menu::curr
int curr
Definition: tin.h:2006
begin_va_copy
#define begin_va_copy(dst, src)
Definition: tin.h:2408
t_screen::col
char * col
Definition: tin.h:1930
spin_cursor
void spin_cursor(void)
Definition: screen.c:445
FALSE
#define FALSE
Definition: bool.h:70
STRCPY
#define STRCPY(dst, src)
Definition: tin.h:814
snprintf
#define snprintf
Definition: tin.h:2417
erase_arrow
void erase_arrow(void)
Definition: screen.c:352
t_artnum
long t_artnum
Definition: tin.h:226
t_menu::first
int first
Definition: tin.h:2008
StartInverse
void StartInverse(void)
Definition: curses.c:540
show_progress
void show_progress(const char *txt, t_artnum count, t_artnum total)
Definition: screen.c:477
txt_you_have_mail
constext txt_you_have_mail[]
Definition: lang.c:992
strerror
char * strerror(int n)
Definition: pcregrep.c:477
t_bool
unsigned t_bool
Definition: bool.h:77
errno
int errno
perror_message
void perror_message(const char *fmt,...)
Definition: screen.c:220
TRUE
#define TRUE
Definition: bool.h:74
my_fflush
#define my_fflush(stream)
Definition: tcurses.h:172
screen
struct t_screen * screen
Definition: screen.c:51
t_config::draw_arrow
t_bool draw_arrow
Definition: tinrc.h:212
tin_gettime
int tin_gettime(struct t_tintime *tt)
Definition: misc.c:4134
txt_type_h_for_help
constext txt_type_h_for_help[]
Definition: lang.c:895
DISPLAY_FMT
#define DISPLAY_FMT
Definition: screen.c:471
my_malloc
#define my_malloc(size)
Definition: tin.h:2196