source: trunk/ncurses/tack/output.c@ 3003

Last change on this file since 3003 was 2621, checked in by bird, 19 years ago

GNU ncurses 5.5

File size: 14.6 KB
Line 
1/*
2** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3**
4** This file is part of TACK.
5**
6** TACK is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2, or (at your option)
9** any later version.
10**
11** TACK is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with TACK; see the file COPYING. If not, write to
18** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19** Boston, MA 02110-1301, USA
20*/
21/* screen formatting and I/O utility functions */
22
23#include <tack.h>
24#include <time.h>
25
26MODULE_ID("$Id: output.c,v 1.10 2005/09/17 19:49:16 tom Exp $")
27
28/* globals */
29long char_sent; /* number of characters sent */
30int char_count; /* counts characters */
31int line_count; /* counts line feeds */
32int expand_chars; /* length of expand() string */
33int replace_mode; /* used to output replace mode padding */
34int can_go_home; /* TRUE if we can fashion a home command */
35int can_clear_screen; /* TRUE if we can somehow clear the screen */
36int raw_characters_sent; /* Total output characters */
37static int log_count; /* Number of characters on a log line */
38
39/* translate mode default strings */
40#define TM_carriage_return TM_string[0].value
41#define TM_cursor_down TM_string[1].value
42#define TM_scroll_forward TM_string[2].value
43#define TM_newline TM_string[3].value
44#define TM_cursor_left TM_string[4].value
45#define TM_bell TM_string[5].value
46#define TM_form_feed TM_string[6].value
47#define TM_tab TM_string[7].value
48
49struct default_string_list TM_string[TM_last] = {
50 {"cr", "\r", 0},
51 {"cud1", "\n", 0},
52 {"ind", "\n", 0},
53 {"nel", "\r\n", 0},
54 {"cub1", "\b", 0},
55 {"bel", "\007", 0},
56 {"ff", "\f", 0},
57 {"ht", "\t", 0}
58};
59
60static const char *c0[32] = {
61 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK",
62 "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
63 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
64 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
65};
66
67static const char *c1[32] = {
68 "", "", "", "", "IND", "NEL", "SSA", "ESA",
69 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
70 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
71 "", "", "", "CSI", "ST", "OSC", "PM", "APC"
72};
73
74int
75getnext(int mask)
76{ /* get the next character without scan mode
77 conversion */
78 int ch;
79 unsigned char buf;
80
81 tc_putp(req_for_input);
82 fflush(stdout);
83 if (nodelay_read)
84 while (1) {
85 ch = read(fileno(stdin), &buf, 1);
86 if (ch == -1)
87 return EOF;
88 if (ch == 1)
89 return buf;
90 }
91 ch = getchar();
92 if (ch == EOF)
93 return EOF;
94 return ch & mask;
95}
96
97
98int
99getchp(int mask)
100{ /* read a character with scan mode conversion */
101 if (scan_mode) {
102 tc_putp(req_for_input);
103 fflush(stdout);
104 return scan_key();
105 } else
106 return getnext(mask);
107}
108
109/*
110** tc_putch(c)
111**
112** Output one character
113*/
114int
115tc_putch(int c)
116{
117 char_sent++;
118 raw_characters_sent++;
119 putchar(c);
120 if ((raw_characters_sent & 31) == 31) {
121 fflush(stdout);
122 }
123 if (log_fp) {
124 /* terminal output logging */
125 c = UChar(c);
126 if (c < 32) {
127 fprintf(log_fp, "<%s>", c0[c]);
128 log_count += 5;
129 } else
130 if (c < 127) {
131 fprintf(log_fp, "%c", c);
132 log_count += 1;
133 } else {
134 fprintf(log_fp, "<%02x>", c);
135 log_count += 4;
136 }
137 if (c == '\n' || log_count >= 80) {
138 fprintf(log_fp, "\n");
139 log_count = 0;
140 }
141 }
142 return (c);
143}
144
145/*
146** tt_tputs(string, reps)
147**
148** Output a string with tputs() translation.
149** Use this function inside timing tests.
150*/
151void
152tt_tputs(const char *string, int reps)
153{
154 int i;
155
156 if (string) {
157 for (i = 0; i < TT_MAX; i++) {
158 if (i >= ttp) {
159 tt_cap[i] = string;
160 tt_affected[i] = reps;
161 tt_count[i] = 1;
162 tt_delay[i] = msec_cost(string, reps);
163 ttp++;
164 break;
165 }
166 if (string == tt_cap[i] && reps == tt_affected[i]) {
167 tt_count[i]++;
168 tt_delay_used += tt_delay[i];
169 break;
170 }
171 }
172 (void) tputs(string, reps, tc_putch);
173 }
174}
175
176/*
177** tt_putp(string)
178**
179** Output a string with tputs() translation.
180** Use this function inside timing tests.
181*/
182void
183tt_putp(const char *string)
184{
185 tt_tputs(string, 1);
186}
187
188/*
189** tt_putparm(string, reps, arg1, arg2)
190**
191** Send tt_tputs(tparm(string, args1, arg2), reps)
192** Use this function inside timing tests.
193*/
194void
195tt_putparm(
196 NCURSES_CONST char *string,
197 int reps,
198 int arg1,
199 int arg2)
200{
201 int i;
202
203 if (string) {
204 for (i = 0; i < TT_MAX; i++) {
205 if (i >= ttp) {
206 tt_cap[i] = string;
207 tt_affected[i] = reps;
208 tt_count[i] = 1;
209 tt_delay[i] = msec_cost(string, reps);
210 ttp++;
211 break;
212 }
213 if (string == tt_cap[i] && reps == tt_affected[i]) {
214 tt_count[i]++;
215 tt_delay_used += tt_delay[i];
216 break;
217 }
218 }
219 (void) tputs(tparm((NCURSES_CONST char *)string, arg1, arg2), reps, tc_putch);
220 }
221}
222
223/*
224** tc_putp(string)
225**
226** Output a string with tputs() translation.
227** Use this function instead of putp() so we can track
228** the actual number of characters sent.
229*/
230int
231tc_putp(const char *string)
232{
233 return tputs(string, 1, tc_putch);
234}
235
236
237void
238put_this(int c)
239{ /* output one character (with padding) */
240 tc_putch(c);
241 if (char_padding && replace_mode)
242 tt_putp(char_padding);
243}
244
245
246void
247put_cr(void)
248{
249 if (translate_mode && carriage_return) {
250 tt_putp(carriage_return);
251 } else {
252 tt_putp(TM_carriage_return);
253 }
254 char_count = 0;
255}
256
257
258void
259put_lf(void)
260{ /* send a linefeed (only works in RAW or
261 CBREAK mode) */
262 if (translate_mode && cursor_down) {
263 tt_putp(cursor_down);
264 } else {
265 tt_putp(TM_cursor_down);
266 }
267 line_count++;
268}
269
270
271void
272put_ind(void)
273{ /* scroll forward (only works in RAW or
274 CBREAK mode) */
275 if (translate_mode && scroll_forward) {
276 tt_putp(scroll_forward);
277 } else {
278 tt_putp(TM_scroll_forward);
279 }
280 line_count++;
281}
282
283/*
284** put_crlf()
285**
286** Send (nel) or <cr> <lf>
287*/
288void
289put_crlf(void)
290{
291 if (translate_mode && newline) {
292 tt_putp(newline);
293 } else {
294 tt_putp(TM_newline);
295 }
296 char_count = 0;
297 line_count++;
298}
299
300/*
301** put_new_lines(count)
302**
303** Send a number of newlines. (nel)
304*/
305void
306put_newlines(int n)
307{
308 while (n-- > 0) {
309 put_crlf();
310 }
311}
312
313/*
314** putchp(character)
315**
316** Send one character to the terminal.
317** This function does translation of control characters.
318*/
319void
320putchp(int c)
321{
322 switch (c) {
323 case '\b':
324 if (translate_mode && cursor_left) {
325 tt_putp(cursor_left);
326 } else {
327 tt_putp(TM_cursor_left);
328 }
329 char_count--;
330 break;
331 case 7:
332 if (translate_mode && bell) {
333 tt_putp(bell);
334 } else {
335 tt_putp(TM_bell);
336 }
337 break;
338 case '\f':
339 if (translate_mode && form_feed) {
340 tt_putp(form_feed);
341 } else {
342 tt_putp(TM_form_feed);
343 }
344 char_count = 0;
345 line_count++;
346 break;
347 case '\n':
348 put_crlf();
349 break;
350 case '\r':
351 put_cr();
352 break;
353 case '\t':
354 if (translate_mode && tab) {
355 tt_putp(tab);
356 } else {
357 tt_putp(TM_tab);
358 }
359 char_count = ((char_count / 8) + 1) * 8;
360 break;
361 default:
362 put_this(c);
363 char_count++;
364 break;
365 }
366}
367
368
369void
370put_str(const char *s)
371{ /* send the string to the terminal */
372 for (; *s; putchp(*s++));
373}
374
375
376void
377putln(const char *s)
378{ /* output a string followed by a CR LF */
379 for (; *s; putchp(*s++));
380 put_crlf();
381}
382
383
384void
385put_columns(const char *s, int len, int w)
386{ /* put out s in column format */
387 int l;
388
389 if (char_count + w > columns) {
390 put_crlf();
391 }
392 l = char_count % w;
393 if (l) {
394 while (l < w) {
395 putchp(' ');
396 l++;
397 }
398 }
399 if (char_count && char_count + len >= columns) {
400 put_crlf();
401 }
402 l = char_count;
403 put_str(s);
404 char_count = l + len;
405}
406
407
408/*
409** ptext(string)
410**
411** Output a string but do not assume the terminal will wrap to a
412** new line. Break the line at a word boundary then send a CR LF.
413** This is more esthetic on 40 column terminals.
414*/
415void
416ptext(const char *s)
417{
418 const char *t;
419
420 while (*s) {
421 for (t = s + 1; *t > ' '; t++);
422 if ((char_count != 0) && ((t - s) + char_count >= columns)) {
423 put_crlf();
424 while (*s == ' ')
425 s++;
426 }
427 while (s < t) {
428 putchp(*s++);
429 }
430 }
431}
432
433
434void
435put_dec(char *f, int i)
436{ /* print a line with a decimal number in it */
437 char tm[128];
438
439 sprintf(tm, f, i / 10, i % 10);
440 ptext(tm);
441}
442
443
444void
445three_digit(char *tx, int i)
446{ /* convert the decimal number to a string of
447 at least 3 digits */
448 if (i < 1000)
449 sprintf(tx, "%d.%d", i / 10, i % 10);
450 else
451 sprintf(tx, "%d", i / 10);
452}
453
454
455void
456ptextln(const char *s)
457{ /* print the text using ptext() then add a CR
458 LF */
459 ptext(s);
460 put_crlf();
461}
462
463
464static void
465expand_one(int ch, char **v)
466{ /* expand one character */
467 char *t = *v;
468
469 if (ch & 0x80) { /* dump it in octal (yuck) */
470 *t++ = '\\';
471 *t++ = '0' + ((ch >> 6) & 3);
472 *t++ = '0' + ((ch >> 3) & 7);
473 *t++ = '0' + (ch & 7);
474 expand_chars += 4;
475 } else if (ch == 127) { /* DEL */
476 *t++ = '^';
477 *t++ = '?';
478 expand_chars += 2;
479 } else if (ch >= ' ') {
480 *t++ = ch;
481 expand_chars++;
482 } else { /* control characters */
483 *t++ = '^';
484 *t++ = ch + '@';
485 expand_chars += 2;
486 }
487 *v = t;
488}
489
490
491char *
492expand(const char *s)
493{ /* convert the string to printable form */
494 static char buf[4096];
495 char *t, *v;
496 int ch;
497
498 if (magic_cookie_glitch <= 0 && exit_attribute_mode) {
499 v = enter_reverse_mode;
500 } else {
501 v = NULL;
502 }
503 expand_chars = 0;
504 t = buf;
505 if (s) {
506 for (; (ch = *s); s++) {
507 if ((ch & 0x80) && v) { /* print it in reverse video
508 mode */
509 strcpy(t, liberated(tparm(v)));
510 for (; *t; t++);
511 expand_one(ch & 0x7f, &t);
512 strcpy(t, liberated(tparm(exit_attribute_mode)));
513 for (; *t; t++);
514 } else {
515 expand_one(ch, &t);
516 }
517 }
518 }
519 *t = '\0';
520 return buf;
521}
522
523
524char *
525print_expand(char *s)
526{ /* convert the string to 7-bit printable form */
527 static char buf[4096];
528 char *t;
529 int ch;
530
531 expand_chars = 0;
532 t = buf;
533 if (s) {
534 for (; (ch = *s); s++) {
535 expand_one(ch, &t);
536 }
537 }
538 *t = '\0';
539 return buf;
540}
541
542
543char *
544expand_to(char *s, int l)
545{ /* expand s to length l */
546 char *t;
547
548 for (s = t = expand(s); *t; t++);
549 for (; expand_chars < l; expand_chars++) {
550 *t++ = ' ';
551 }
552 *t = '\0';
553 return s;
554}
555
556
557char *
558hex_expand_to(char *s, int l)
559{ /* expand s to length l in hex */
560 static char buf[4096];
561 char *t;
562
563 for (t = buf; *s; s++) {
564 sprintf(t, "%02X ", UChar(*s));
565 t += 3;
566 if (t - buf > (int) sizeof(buf) - 4) {
567 break;
568 }
569 }
570 for (; t - buf < l;) {
571 *t++ = ' ';
572 }
573 *t = '\0';
574 expand_chars = t - buf;
575 return buf;
576}
577
578
579char *
580expand_command(const char *c)
581{ /* expand an ANSI escape sequence */
582 static char buf[256];
583 int i, j, ch;
584 char *s;
585
586 s = buf;
587 for (i = FALSE; (ch = UChar(*c)) != 0; c++) {
588 if (i) {
589 *s++ = ' ';
590 }
591 i = TRUE;
592 if (ch < 32) {
593 j = UChar(c[1]);
594 if (ch == '\033' && j >= '@' && j <= '_') {
595 ch = j - '@';
596 c++;
597 for (j = 0; (*s = c1[ch][j++]); s++);
598 } else
599 for (j = 0; (*s = c0[ch][j++]); s++);
600 } else {
601 *s++ = ch;
602 j = UChar(c[1]);
603 if (ch >= '0' && ch <= '9' &&
604 j >= '0' && j <= '9') {
605 i = FALSE;
606 }
607 }
608 }
609 *s = '\0';
610 return buf;
611}
612
613/*
614** go_home()
615**
616** Move the cursor to the home position
617*/
618void
619go_home(void)
620{
621 int i;
622
623 if (cursor_home)
624 tt_putp(cursor_home);
625 else if (cursor_address)
626 tt_putparm(cursor_address, lines, 0, 0);
627 else if (row_address) { /* use (vpa) */
628 put_cr();
629 tt_putparm(row_address, 1, 0, 0);
630 } else if (cursor_up && cursor_to_ll) {
631 tt_putp(cursor_to_ll);
632 for (i = 1; i < lines; i++) {
633 tt_putp(cursor_up);
634 }
635 } else {
636 can_go_home = FALSE;
637 return;
638 }
639 char_count = line_count = 0;
640 can_go_home = TRUE;
641}
642
643
644void
645home_down(void)
646{ /* move the cursor to the lower left hand
647 corner */
648 int i;
649
650 if (cursor_to_ll)
651 tt_putp(cursor_to_ll);
652 else if (cursor_address)
653 tt_putparm(cursor_address, lines, lines - 1, 0);
654 else if (row_address) { /* use (vpa) */
655 put_cr();
656 tt_putparm(row_address, 1, lines - 1, 0);
657 } else if (cursor_down && cursor_home) {
658 tt_putp(cursor_home);
659 for (i = 1; i < lines; i++)
660 tt_putp(cursor_down);
661 } else
662 return;
663 char_count = 0;
664 line_count = lines - 1;
665}
666
667
668void
669put_clear(void)
670{ /* clear the screen */
671 int i;
672
673 if (clear_screen)
674 tt_tputs(clear_screen, lines);
675 else if (clr_eos && can_go_home) {
676 go_home();
677 tt_tputs(clr_eos, lines);
678 } else if (scroll_forward && !over_strike && (can_go_home || cursor_up)) {
679 /* clear the screen by scrolling */
680 put_cr();
681 if (cursor_to_ll) {
682 tt_putp(cursor_to_ll);
683 } else if (cursor_address) {
684 tt_putparm(cursor_address, lines, lines - 1, 0);
685 } else if (row_address) {
686 tt_putparm(row_address, 1, lines - 1, 0);
687 } else {
688 for (i = 1; i < lines; i++) {
689 tt_putp(scroll_forward);
690 }
691 }
692 for (i = 1; i < lines; i++) {
693 tt_putp(scroll_forward);
694 }
695 if (can_go_home) {
696 go_home();
697 } else {
698 for (i = 1; i < lines; i++) {
699 tt_putp(cursor_up);
700 }
701 }
702 } else {
703 can_clear_screen = FALSE;
704 return;
705 }
706 char_count = line_count = 0;
707 can_clear_screen = TRUE;
708}
709
710/*
711** wait_here()
712**
713** read one character from the input stream
714** If the terminal is not in RAW mode then this function will
715** wait for a <cr> or <lf>.
716*/
717int
718wait_here(void)
719{
720 char ch, cc[64];
721 char message[16];
722 int i, j;
723
724 for (i = 0; i < (int) sizeof(cc); i++) {
725 cc[i] = ch = getchp(STRIP_PARITY);
726 if (ch == '\r' || ch == '\n') {
727 put_crlf();
728 char_sent = 0;
729 return cc[i ? i - 1 : 0];
730 }
731 if (ch >= ' ') {
732 if (stty_query(TTY_CHAR_MODE)) {
733 put_crlf();
734 char_sent = 0;
735 return ch;
736 }
737 continue;
738 }
739 if (ch == 023) { /* Control S */
740 /* ignore control S, but tell me about it */
741 while (ch == 023 || ch == 021) {
742 ch = getchp(STRIP_PARITY);
743 if (i < (int) sizeof(cc))
744 cc[++i] = ch;
745 }
746 put_str("\nThe terminal sent a ^S -");
747 for (j = 0; j <= i; j++) {
748 sprintf(message, " %02X", cc[j] & 0xFF);
749 put_str(message);
750 }
751 put_crlf();
752 i = -1;
753 } else if (ch != 021) { /* Not Control Q */
754 /* could be abort character */
755 spin_flush();
756 if (tty_can_sync == SYNC_TESTED) {
757 (void) tty_sync_error();
758 } else {
759 put_str("\n? ");
760 }
761 }
762 }
763 return '?';
764}
765
766
767/*
768** read_string(buffer, length)
769**
770** Read a string of characters from the input stream.
771*/
772void
773read_string(
774 char *buf,
775 int length)
776{
777 int ch, i;
778
779 for (i = 0; i < length - 1; ) {
780 ch = getchp(STRIP_PARITY);
781 if (ch == '\r' || ch == '\n') {
782 break;
783 }
784 if (ch == '\b' || ch == 127) {
785 if (i) {
786 putchp('\b');
787 putchp(' ');
788 putchp('\b');
789 i--;
790 }
791 } else {
792 buf[i++] = ch;
793 putchp(ch);
794 }
795 }
796 buf[i] = '\0';
797 put_crlf();
798 char_sent = 0;
799}
800
801/*
802** maybe_wait(lines)
803**
804** wait if near the end of the screen, then clear screen
805*/
806void
807maybe_wait(int n)
808{
809 if (line_count + n >= lines) {
810 if (char_sent != 0) {
811 ptext("Go? ");
812 (void) wait_here();
813 }
814 put_clear();
815 } else {
816 put_crlf();
817 }
818}
Note: See TracBrowser for help on using the repository browser.