tin  2.4.5
About: TIN is a threaded NNTP and spool based UseNet newsreader.
  Fossies Dox: tin-2.4.5.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 : 2020-06-23
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
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  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
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();
342  MoveCursor(line, mark_offset);
343  EndInverse();
344  my_fputwc((wint_t) wtmp[mark_offset], stdout);
345  }
346  free(wtmp);
347  }
348 #else
349  StartInverse();
350  my_fputs(s, stdout);
351  EndInverse();
353  MoveCursor(line, mark_offset);
354  EndInverse();
355  my_fputc(s[mark_offset], stdout);
356  }
357 #endif /* MULTIBYTE_ABLE && !NO_LOCALE */
358  }
359  stow_cursor();
360 }
361 
362 
363 void
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);
396  MoveCursor(line, mark_offset);
397  StartInverse();
398  my_fputwc((wint_t) wtmp[mark_offset], stdout);
399  EndInverse();
400  }
401  free(wtmp);
402  }
403 #else
404  my_fputs(s, stdout);
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
418  const char *title)
419 {
420  int col;
421 
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 */
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
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
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
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 = (this_time.tv_sec - last_time.tv_sec) * 1000000;
550  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 += (time_diff / count_diff);
569  average = sum / 20;
570  } else {
571  sum += (time_diff / count_diff);
572  average = sum / ++samples;
573  }
574 
575  if (average >= 1000000)
576  secs_left = (total - count) * (average / 1000000);
577  else
578  secs_left = ((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 }
unsigned t_bool
Definition: bool.h:77
#define TRUE
Definition: bool.h:74
#define FALSE
Definition: bool.h:70
char mailbox[PATH_LEN]
Definition: init.c:91
t_bool cmd_line
Definition: init.c:128
constext txt_you_have_mail[]
Definition: lang.c:992
t_menu * currmenu
Definition: init.c:165
constext txt_type_h_for_help[]
Definition: lang.c:895
int cCOLS
Definition: curses.c:53
int cLINES
Definition: curses.c:52
struct t_config tinrc
Definition: init.c:191
t_bool batch_mode
Definition: init.c:126
static char buf[16]
Definition: langinfo.c:50
char * strerror(int n)
Definition: pcregrep.c:477
static uschar * buffer
Definition: pcretest.c:154
int errno
char * sized_message(char **result, const char *format, const char *subject)
Definition: prompt.c:646
char * my_strdup(const char *str)
Definition: string.c:139
void StartInverse(void)
Definition: curses.c:540
int RawState(void)
Definition: curses.c:604
void cursoroff(void)
Definition: curses.c:721
t_bool mail_check(const char *mailbox_name)
Definition: misc.c:928
int tin_gettime(struct t_tintime *tt)
Definition: misc.c:4185
void MoveCursor(int row, int col)
Definition: curses.c:441
char * strunc(const char *message, int len)
Definition: string.c:1075
int strwidth(const char *str)
Definition: string.c:1049
void cursoron(void)
Definition: curses.c:712
void CleartoEOLN(void)
Definition: curses.c:458
void EndInverse(void)
Definition: curses.c:564
void draw_arrow_mark(int line)
Definition: screen.c:312
void center_line(int line, t_bool inverse, const char *str)
Definition: screen.c:258
char * fmt_message(const char *fmt, va_list ap)
Definition: screen.c:73
void error_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:184
void perror_message(const char *fmt,...)
Definition: screen.c:220
void info_message(const char *fmt,...)
Definition: screen.c:102
void spin_cursor(void)
Definition: screen.c:457
void erase_arrow(void)
Definition: screen.c:364
void ring_bell(void)
Definition: screen.c:440
int mark_offset
Definition: screen.c:48
void clear_message(void)
Definition: screen.c:243
#define DISPLAY_FMT
Definition: screen.c:483
void wait_message(unsigned int sdelay, const char *fmt,...)
Definition: screen.c:133
void stow_cursor(void)
Definition: screen.c:59
void show_title(const char *title)
Definition: screen.c:417
struct t_screen * screen
Definition: screen.c:51
void show_progress(const char *txt, t_artnum count, t_artnum total)
Definition: screen.c:489
t_bool draw_arrow
Definition: tinrc.h:212
char art_marked_selected
Definition: tinrc.h:62
int curr
Definition: tin.h:2017
int first
Definition: tin.h:2019
int max
Definition: tin.h:2018
Definition: tin.h:1940
char * col
Definition: tin.h:1941
time_t tv_sec
Definition: tin.h:2046
long tv_nsec
Definition: tin.h:2047
#define my_flush()
Definition: tcurses.h:171
#define my_fflush(stream)
Definition: tcurses.h:172
#define my_fputs(str, stream)
Definition: tcurses.h:153
#define my_printf
Definition: tcurses.h:169
#define my_fputc(ch, stream)
Definition: tcurses.h:152
#define vsnprintf
Definition: tin.h:2435
#define LEN
Definition: tin.h:857
long t_artnum
Definition: tin.h:229
#define IS_LOCAL_CHARSET(c)
Definition: tin.h:779
#define STRCPY(dst, src)
Definition: tin.h:817
#define my_malloc(size)
Definition: tin.h:2207
#define end_va_copy(dst)
Definition: tin.h:2424
#define _(Text)
Definition: tin.h:94
#define snprintf
Definition: tin.h:2432
#define begin_va_copy(dst, src)
Definition: tin.h:2423
#define INDEX_TOP
Definition: tin.h:1011
#define my_realloc(ptr, size)
Definition: tin.h:2209