Home | History | Annotate | Line # | Download | only in dist
      1  1.5  simonb /*	$NetBSD: output.c,v 1.5 2023/10/06 05:49:49 simonb Exp $	*/
      2  1.1    tron 
      3  1.1    tron /*
      4  1.5  simonb  * Copyright (C) 1984-2023  Mark Nudelman
      5  1.1    tron  *
      6  1.1    tron  * You may distribute under the terms of either the GNU General Public
      7  1.1    tron  * License or the Less License, as specified in the README file.
      8  1.1    tron  *
      9  1.4    tron  * For more information, see the README file.
     10  1.1    tron  */
     11  1.1    tron 
     12  1.1    tron 
     13  1.1    tron /*
     14  1.1    tron  * High level routines dealing with the output to the screen.
     15  1.1    tron  */
     16  1.1    tron 
     17  1.1    tron #include "less.h"
     18  1.1    tron #if MSDOS_COMPILER==WIN32C
     19  1.1    tron #include "windows.h"
     20  1.5  simonb #ifndef COMMON_LVB_UNDERSCORE
     21  1.5  simonb #define COMMON_LVB_UNDERSCORE 0x8000
     22  1.5  simonb #endif
     23  1.1    tron #endif
     24  1.1    tron 
     25  1.5  simonb public int errmsgs;    /* Count of messages displayed by error() */
     26  1.1    tron public int need_clr;
     27  1.1    tron public int final_attr;
     28  1.1    tron public int at_prompt;
     29  1.1    tron 
     30  1.1    tron extern int sigs;
     31  1.1    tron extern int sc_width;
     32  1.1    tron extern int so_s_width, so_e_width;
     33  1.1    tron extern int screen_trashed;
     34  1.1    tron extern int is_tty;
     35  1.1    tron extern int oldbot;
     36  1.5  simonb extern char intr_char;
     37  1.1    tron 
     38  1.1    tron #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
     39  1.1    tron extern int ctldisp;
     40  1.1    tron extern int nm_fg_color, nm_bg_color;
     41  1.1    tron extern int bo_fg_color, bo_bg_color;
     42  1.1    tron extern int ul_fg_color, ul_bg_color;
     43  1.1    tron extern int so_fg_color, so_bg_color;
     44  1.1    tron extern int bl_fg_color, bl_bg_color;
     45  1.5  simonb extern int sgr_mode;
     46  1.5  simonb #if MSDOS_COMPILER==WIN32C
     47  1.5  simonb extern int vt_enabled;
     48  1.5  simonb #endif
     49  1.1    tron #endif
     50  1.1    tron 
     51  1.1    tron /*
     52  1.1    tron  * Display the line which is in the line buffer.
     53  1.1    tron  */
     54  1.5  simonb public void put_line(void)
     55  1.1    tron {
     56  1.5  simonb 	int c;
     57  1.5  simonb 	int i;
     58  1.1    tron 	int a;
     59  1.1    tron 
     60  1.1    tron 	if (ABORT_SIGS())
     61  1.1    tron 	{
     62  1.1    tron 		/*
     63  1.1    tron 		 * Don't output if a signal is pending.
     64  1.1    tron 		 */
     65  1.1    tron 		screen_trashed = 1;
     66  1.1    tron 		return;
     67  1.1    tron 	}
     68  1.1    tron 
     69  1.1    tron 	final_attr = AT_NORMAL;
     70  1.1    tron 
     71  1.1    tron 	for (i = 0;  (c = gline(i, &a)) != '\0';  i++)
     72  1.1    tron 	{
     73  1.1    tron 		at_switch(a);
     74  1.1    tron 		final_attr = a;
     75  1.1    tron 		if (c == '\b')
     76  1.1    tron 			putbs();
     77  1.1    tron 		else
     78  1.1    tron 			putchr(c);
     79  1.1    tron 	}
     80  1.1    tron 
     81  1.1    tron 	at_exit();
     82  1.1    tron }
     83  1.1    tron 
     84  1.1    tron static char obuf[OUTBUF_SIZE];
     85  1.1    tron static char *ob = obuf;
     86  1.5  simonb static int outfd = 2; /* stderr */
     87  1.5  simonb 
     88  1.5  simonb #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
     89  1.5  simonb static void win_flush(void)
     90  1.5  simonb {
     91  1.5  simonb 	if (ctldisp != OPT_ONPLUS || (vt_enabled && sgr_mode))
     92  1.5  simonb 		WIN32textout(obuf, ob - obuf);
     93  1.5  simonb 	else
     94  1.5  simonb 	{
     95  1.5  simonb 		/*
     96  1.5  simonb 		 * Look for SGR escape sequences, and convert them
     97  1.5  simonb 		 * to color commands.  Replace bold, underline,
     98  1.5  simonb 		 * and italic escapes into colors specified via
     99  1.5  simonb 		 * the -D command-line option.
    100  1.5  simonb 		 */
    101  1.5  simonb 		char *anchor, *p, *p_next;
    102  1.5  simonb 		static int fg, fgi, bg, bgi;
    103  1.5  simonb 		static int at;
    104  1.5  simonb 		int f, b;
    105  1.5  simonb #if MSDOS_COMPILER==WIN32C
    106  1.5  simonb 		/* Screen colors used by 3x and 4x SGR commands. */
    107  1.5  simonb 		static unsigned char screen_color[] = {
    108  1.5  simonb 			0, /* BLACK */
    109  1.5  simonb 			FOREGROUND_RED,
    110  1.5  simonb 			FOREGROUND_GREEN,
    111  1.5  simonb 			FOREGROUND_RED|FOREGROUND_GREEN,
    112  1.5  simonb 			FOREGROUND_BLUE,
    113  1.5  simonb 			FOREGROUND_BLUE|FOREGROUND_RED,
    114  1.5  simonb 			FOREGROUND_BLUE|FOREGROUND_GREEN,
    115  1.5  simonb 			FOREGROUND_BLUE|FOREGROUND_GREEN|FOREGROUND_RED
    116  1.5  simonb 		};
    117  1.5  simonb #else
    118  1.5  simonb 		static enum COLORS screen_color[] = {
    119  1.5  simonb 			BLACK, RED, GREEN, BROWN,
    120  1.5  simonb 			BLUE, MAGENTA, CYAN, LIGHTGRAY
    121  1.5  simonb 		};
    122  1.5  simonb #endif
    123  1.5  simonb 
    124  1.5  simonb 		if (fg == 0 && bg == 0)
    125  1.5  simonb 		{
    126  1.5  simonb 			fg  = nm_fg_color & 7;
    127  1.5  simonb 			fgi = nm_fg_color & 8;
    128  1.5  simonb 			bg  = nm_bg_color & 7;
    129  1.5  simonb 			bgi = nm_bg_color & 8;
    130  1.5  simonb 		}
    131  1.5  simonb 		for (anchor = p_next = obuf;
    132  1.5  simonb 			 (p_next = memchr(p_next, ESC, ob - p_next)) != NULL; )
    133  1.5  simonb 		{
    134  1.5  simonb 			p = p_next;
    135  1.5  simonb 			if (p[1] == '[')  /* "ESC-[" sequence */
    136  1.5  simonb 			{
    137  1.5  simonb 				if (p > anchor)
    138  1.5  simonb 				{
    139  1.5  simonb 					/*
    140  1.5  simonb 					 * If some chars seen since
    141  1.5  simonb 					 * the last escape sequence,
    142  1.5  simonb 					 * write them out to the screen.
    143  1.5  simonb 					 */
    144  1.5  simonb 					WIN32textout(anchor, p-anchor);
    145  1.5  simonb 					anchor = p;
    146  1.5  simonb 				}
    147  1.5  simonb 				p += 2;  /* Skip the "ESC-[" */
    148  1.5  simonb 				if (is_ansi_end(*p))
    149  1.5  simonb 				{
    150  1.5  simonb 					/*
    151  1.5  simonb 					 * Handle null escape sequence
    152  1.5  simonb 					 * "ESC[m", which restores
    153  1.5  simonb 					 * the normal color.
    154  1.5  simonb 					 */
    155  1.5  simonb 					p++;
    156  1.5  simonb 					anchor = p_next = p;
    157  1.5  simonb 					fg  = nm_fg_color & 7;
    158  1.5  simonb 					fgi = nm_fg_color & 8;
    159  1.5  simonb 					bg  = nm_bg_color & 7;
    160  1.5  simonb 					bgi = nm_bg_color & 8;
    161  1.5  simonb 					at  = 0;
    162  1.5  simonb 					WIN32setcolors(nm_fg_color, nm_bg_color);
    163  1.5  simonb 					continue;
    164  1.5  simonb 				}
    165  1.5  simonb 				p_next = p;
    166  1.5  simonb 				at &= ~32;
    167  1.5  simonb 
    168  1.5  simonb 				/*
    169  1.5  simonb 				 * Select foreground/background colors
    170  1.5  simonb 				 * based on the escape sequence.
    171  1.5  simonb 				 */
    172  1.5  simonb 				while (!is_ansi_end(*p))
    173  1.5  simonb 				{
    174  1.5  simonb 					char *q;
    175  1.5  simonb 					long code = strtol(p, &q, 10);
    176  1.5  simonb 
    177  1.5  simonb 					if (*q == '\0')
    178  1.5  simonb 					{
    179  1.5  simonb 						/*
    180  1.5  simonb 						 * Incomplete sequence.
    181  1.5  simonb 						 * Leave it unprocessed
    182  1.5  simonb 						 * in the buffer.
    183  1.5  simonb 						 */
    184  1.5  simonb 						int slop = (int) (q - anchor);
    185  1.5  simonb 						/* {{ strcpy args overlap! }} */
    186  1.5  simonb 						strcpy(obuf, anchor);
    187  1.5  simonb 						ob = &obuf[slop];
    188  1.5  simonb 						return;
    189  1.5  simonb 					}
    190  1.5  simonb 
    191  1.5  simonb 					if (q == p ||
    192  1.5  simonb 						code > 49 || code < 0 ||
    193  1.5  simonb 						(!is_ansi_end(*q) && *q != ';'))
    194  1.5  simonb 					{
    195  1.5  simonb 						p_next = q;
    196  1.5  simonb 						break;
    197  1.5  simonb 					}
    198  1.5  simonb 					if (*q == ';')
    199  1.5  simonb 					{
    200  1.5  simonb 						q++;
    201  1.5  simonb 						at |= 32;
    202  1.5  simonb 					}
    203  1.5  simonb 
    204  1.5  simonb 					switch (code)
    205  1.5  simonb 					{
    206  1.5  simonb 					default:
    207  1.5  simonb 					/* case 0: all attrs off */
    208  1.5  simonb 						fg = nm_fg_color & 7;
    209  1.5  simonb 						bg = nm_bg_color & 7;
    210  1.5  simonb 						at &= 32;
    211  1.5  simonb 						/*
    212  1.5  simonb 						 * \e[0m use normal
    213  1.5  simonb 						 * intensities, but
    214  1.5  simonb 						 * \e[0;...m resets them
    215  1.5  simonb 						 */
    216  1.5  simonb 						if (at & 32)
    217  1.5  simonb 						{
    218  1.5  simonb 							fgi = 0;
    219  1.5  simonb 							bgi = 0;
    220  1.5  simonb 						} else
    221  1.5  simonb 						{
    222  1.5  simonb 							fgi = nm_fg_color & 8;
    223  1.5  simonb 							bgi = nm_bg_color & 8;
    224  1.5  simonb 						}
    225  1.5  simonb 						break;
    226  1.5  simonb 					case 1: /* bold on */
    227  1.5  simonb 						fgi = 8;
    228  1.5  simonb 						at |= 1;
    229  1.5  simonb 						break;
    230  1.5  simonb 					case 3: /* italic on */
    231  1.5  simonb 					case 7: /* inverse on */
    232  1.5  simonb 						at |= 2;
    233  1.5  simonb 						break;
    234  1.5  simonb 					case 4: /* underline on */
    235  1.5  simonb 						bgi = 8;
    236  1.5  simonb 						at |= 4;
    237  1.5  simonb 						break;
    238  1.5  simonb 					case 5: /* slow blink on */
    239  1.5  simonb 					case 6: /* fast blink on */
    240  1.5  simonb 						bgi = 8;
    241  1.5  simonb 						at |= 8;
    242  1.5  simonb 						break;
    243  1.5  simonb 					case 8: /* concealed on */
    244  1.5  simonb 						at |= 16;
    245  1.5  simonb 						break;
    246  1.5  simonb 					case 22: /* bold off */
    247  1.5  simonb 						fgi = 0;
    248  1.5  simonb 						at &= ~1;
    249  1.5  simonb 						break;
    250  1.5  simonb 					case 23: /* italic off */
    251  1.5  simonb 					case 27: /* inverse off */
    252  1.5  simonb 						at &= ~2;
    253  1.5  simonb 						break;
    254  1.5  simonb 					case 24: /* underline off */
    255  1.5  simonb 						bgi = 0;
    256  1.5  simonb 						at &= ~4;
    257  1.5  simonb 						break;
    258  1.5  simonb 					case 28: /* concealed off */
    259  1.5  simonb 						at &= ~16;
    260  1.5  simonb 						break;
    261  1.5  simonb 					case 30: case 31: case 32:
    262  1.5  simonb 					case 33: case 34: case 35:
    263  1.5  simonb 					case 36: case 37:
    264  1.5  simonb 						fg = screen_color[code - 30];
    265  1.5  simonb 						at |= 32;
    266  1.5  simonb 						break;
    267  1.5  simonb 					case 39: /* default fg */
    268  1.5  simonb 						fg = nm_fg_color & 7;
    269  1.5  simonb 						at |= 32;
    270  1.5  simonb 						break;
    271  1.5  simonb 					case 40: case 41: case 42:
    272  1.5  simonb 					case 43: case 44: case 45:
    273  1.5  simonb 					case 46: case 47:
    274  1.5  simonb 						bg = screen_color[code - 40];
    275  1.5  simonb 						at |= 32;
    276  1.5  simonb 						break;
    277  1.5  simonb 					case 49: /* default bg */
    278  1.5  simonb 						bg = nm_bg_color & 7;
    279  1.5  simonb 						at |= 32;
    280  1.5  simonb 						break;
    281  1.5  simonb 					}
    282  1.5  simonb 					p = q;
    283  1.5  simonb 				}
    284  1.5  simonb 				if (!is_ansi_end(*p) || p == p_next)
    285  1.5  simonb 					break;
    286  1.5  simonb 				/*
    287  1.5  simonb 				 * In SGR mode, the ANSI sequence is
    288  1.5  simonb 				 * always honored; otherwise if an attr
    289  1.5  simonb 				 * is used by itself ("\e[1m" versus
    290  1.5  simonb 				 * "\e[1;33m", for example), set the
    291  1.5  simonb 				 * color assigned to that attribute.
    292  1.5  simonb 				 */
    293  1.5  simonb 				if (sgr_mode || (at & 32))
    294  1.5  simonb 				{
    295  1.5  simonb 					if (at & 2)
    296  1.5  simonb 					{
    297  1.5  simonb 						f = bg | bgi;
    298  1.5  simonb 						b = fg | fgi;
    299  1.5  simonb 					} else
    300  1.5  simonb 					{
    301  1.5  simonb 						f = fg | fgi;
    302  1.5  simonb 						b = bg | bgi;
    303  1.5  simonb 					}
    304  1.5  simonb 				} else
    305  1.5  simonb 				{
    306  1.5  simonb 					if (at & 1)
    307  1.5  simonb 					{
    308  1.5  simonb 						f = bo_fg_color;
    309  1.5  simonb 						b = bo_bg_color;
    310  1.5  simonb 					} else if (at & 2)
    311  1.5  simonb 					{
    312  1.5  simonb 						f = so_fg_color;
    313  1.5  simonb 						b = so_bg_color;
    314  1.5  simonb 					} else if (at & 4)
    315  1.5  simonb 					{
    316  1.5  simonb 						f = ul_fg_color;
    317  1.5  simonb 						b = ul_bg_color;
    318  1.5  simonb 					} else if (at & 8)
    319  1.5  simonb 					{
    320  1.5  simonb 						f = bl_fg_color;
    321  1.5  simonb 						b = bl_bg_color;
    322  1.5  simonb 					} else
    323  1.5  simonb 					{
    324  1.5  simonb 						f = nm_fg_color;
    325  1.5  simonb 						b = nm_bg_color;
    326  1.5  simonb 					}
    327  1.5  simonb 				}
    328  1.5  simonb 				if (at & 16)
    329  1.5  simonb 					f = b ^ 8;
    330  1.5  simonb #if MSDOS_COMPILER==WIN32C
    331  1.5  simonb 				f &= 0xf | COMMON_LVB_UNDERSCORE;
    332  1.5  simonb #else
    333  1.5  simonb 				f &= 0xf;
    334  1.5  simonb #endif
    335  1.5  simonb 				b &= 0xf;
    336  1.5  simonb 				WIN32setcolors(f, b);
    337  1.5  simonb 				p_next = anchor = p + 1;
    338  1.5  simonb 			} else
    339  1.5  simonb 				p_next++;
    340  1.5  simonb 		}
    341  1.5  simonb 
    342  1.5  simonb 		/* Output what's left in the buffer.  */
    343  1.5  simonb 		WIN32textout(anchor, ob - anchor);
    344  1.5  simonb 	}
    345  1.5  simonb 	ob = obuf;
    346  1.5  simonb }
    347  1.5  simonb #endif
    348  1.1    tron 
    349  1.1    tron /*
    350  1.1    tron  * Flush buffered output.
    351  1.1    tron  *
    352  1.1    tron  * If we haven't displayed any file data yet,
    353  1.1    tron  * output messages on error output (file descriptor 2),
    354  1.1    tron  * otherwise output on standard output (file descriptor 1).
    355  1.1    tron  *
    356  1.1    tron  * This has the desirable effect of producing all
    357  1.1    tron  * error messages on error output if standard output
    358  1.1    tron  * is directed to a file.  It also does the same if
    359  1.1    tron  * we never produce any real output; for example, if
    360  1.1    tron  * the input file(s) cannot be opened.  If we do
    361  1.1    tron  * eventually produce output, code in edit() makes
    362  1.1    tron  * sure these messages can be seen before they are
    363  1.1    tron  * overwritten or scrolled away.
    364  1.1    tron  */
    365  1.5  simonb public void flush(void)
    366  1.1    tron {
    367  1.5  simonb 	int n;
    368  1.1    tron 
    369  1.5  simonb 	n = (int) (ob - obuf);
    370  1.1    tron 	if (n == 0)
    371  1.1    tron 		return;
    372  1.5  simonb 	ob = obuf;
    373  1.1    tron 
    374  1.1    tron #if MSDOS_COMPILER==MSOFTC
    375  1.5  simonb 	if (interactive())
    376  1.1    tron 	{
    377  1.5  simonb 		obuf[n] = '\0';
    378  1.1    tron 		_outtext(obuf);
    379  1.1    tron 		return;
    380  1.1    tron 	}
    381  1.1    tron #else
    382  1.1    tron #if MSDOS_COMPILER==WIN32C || MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC
    383  1.5  simonb 	if (interactive())
    384  1.1    tron 	{
    385  1.5  simonb 		ob = obuf + n;
    386  1.1    tron 		*ob = '\0';
    387  1.5  simonb 		win_flush();
    388  1.1    tron 		return;
    389  1.1    tron 	}
    390  1.1    tron #endif
    391  1.1    tron #endif
    392  1.5  simonb 
    393  1.5  simonb 	if (write(outfd, obuf, n) != n)
    394  1.1    tron 		screen_trashed = 1;
    395  1.5  simonb }
    396  1.5  simonb 
    397  1.5  simonb /*
    398  1.5  simonb  * Set the output file descriptor (1=stdout or 2=stderr).
    399  1.5  simonb  */
    400  1.5  simonb public void set_output(int fd)
    401  1.5  simonb {
    402  1.5  simonb 	flush();
    403  1.5  simonb 	outfd = fd;
    404  1.1    tron }
    405  1.1    tron 
    406  1.1    tron /*
    407  1.1    tron  * Output a character.
    408  1.1    tron  */
    409  1.5  simonb public int putchr(int c)
    410  1.1    tron {
    411  1.1    tron #if 0 /* fake UTF-8 output for testing */
    412  1.1    tron 	extern int utf_mode;
    413  1.1    tron 	if (utf_mode)
    414  1.1    tron 	{
    415  1.1    tron 		static char ubuf[MAX_UTF_CHAR_LEN];
    416  1.1    tron 		static int ubuf_len = 0;
    417  1.1    tron 		static int ubuf_index = 0;
    418  1.1    tron 		if (ubuf_len == 0)
    419  1.1    tron 		{
    420  1.1    tron 			ubuf_len = utf_len(c);
    421  1.1    tron 			ubuf_index = 0;
    422  1.1    tron 		}
    423  1.1    tron 		ubuf[ubuf_index++] = c;
    424  1.1    tron 		if (ubuf_index < ubuf_len)
    425  1.1    tron 			return c;
    426  1.1    tron 		c = get_wchar(ubuf) & 0xFF;
    427  1.1    tron 		ubuf_len = 0;
    428  1.1    tron 	}
    429  1.1    tron #endif
    430  1.5  simonb 	clear_bot_if_needed();
    431  1.1    tron #if MSDOS_COMPILER
    432  1.1    tron 	if (c == '\n' && is_tty)
    433  1.1    tron 	{
    434  1.1    tron 		/* remove_top(1); */
    435  1.1    tron 		putchr('\r');
    436  1.1    tron 	}
    437  1.1    tron #else
    438  1.1    tron #ifdef _OSK
    439  1.1    tron 	if (c == '\n' && is_tty)  /* In OS-9, '\n' == 0x0D */
    440  1.1    tron 		putchr(0x0A);
    441  1.1    tron #endif
    442  1.1    tron #endif
    443  1.1    tron 	/*
    444  1.1    tron 	 * Some versions of flush() write to *ob, so we must flush
    445  1.1    tron 	 * when we are still one char from the end of obuf.
    446  1.1    tron 	 */
    447  1.1    tron 	if (ob >= &obuf[sizeof(obuf)-1])
    448  1.1    tron 		flush();
    449  1.1    tron 	*ob++ = c;
    450  1.1    tron 	at_prompt = 0;
    451  1.1    tron 	return (c);
    452  1.1    tron }
    453  1.1    tron 
    454  1.5  simonb public void clear_bot_if_needed(void)
    455  1.5  simonb {
    456  1.5  simonb 	if (!need_clr)
    457  1.5  simonb 		return;
    458  1.5  simonb 	need_clr = 0;
    459  1.5  simonb 	clear_bot();
    460  1.5  simonb }
    461  1.5  simonb 
    462  1.1    tron /*
    463  1.1    tron  * Output a string.
    464  1.1    tron  */
    465  1.5  simonb public void putstr(constant char *s)
    466  1.1    tron {
    467  1.1    tron 	while (*s != '\0')
    468  1.1    tron 		putchr(*s++);
    469  1.1    tron }
    470  1.1    tron 
    471  1.1    tron 
    472  1.1    tron /*
    473  1.1    tron  * Convert an integral type to a string.
    474  1.1    tron  */
    475  1.1    tron #define TYPE_TO_A_FUNC(funcname, type) \
    476  1.5  simonb void funcname(type num, char *buf, int radix) \
    477  1.1    tron { \
    478  1.1    tron 	int neg = (num < 0); \
    479  1.1    tron 	char tbuf[INT_STRLEN_BOUND(num)+2]; \
    480  1.5  simonb 	char *s = tbuf + sizeof(tbuf); \
    481  1.1    tron 	if (neg) num = -num; \
    482  1.1    tron 	*--s = '\0'; \
    483  1.1    tron 	do { \
    484  1.5  simonb 		*--s = "0123456789ABCDEF"[num % radix]; \
    485  1.5  simonb 	} while ((num /= radix) != 0); \
    486  1.1    tron 	if (neg) *--s = '-'; \
    487  1.1    tron 	strcpy(buf, s); \
    488  1.1    tron }
    489  1.1    tron 
    490  1.1    tron TYPE_TO_A_FUNC(postoa, POSITION)
    491  1.1    tron TYPE_TO_A_FUNC(linenumtoa, LINENUM)
    492  1.1    tron TYPE_TO_A_FUNC(inttoa, int)
    493  1.1    tron 
    494  1.1    tron /*
    495  1.5  simonb  * Convert a string to an integral type.  Return ((type) -1) on overflow.
    496  1.1    tron  */
    497  1.5  simonb #define STR_TO_TYPE_FUNC(funcname, type) \
    498  1.5  simonb type funcname(char *buf, char **ebuf, int radix) \
    499  1.5  simonb { \
    500  1.5  simonb 	type val = 0; \
    501  1.5  simonb 	int v = 0; \
    502  1.5  simonb 	for (;; buf++) { \
    503  1.5  simonb 		char c = *buf; \
    504  1.5  simonb 		int digit = (c >= '0' && c <= '9') ? c - '0' : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : -1; \
    505  1.5  simonb 		if (digit < 0 || digit >= radix) break; \
    506  1.5  simonb 		v |= ckd_mul(&val, val, radix); \
    507  1.5  simonb 		v |= ckd_add(&val, val, digit); \
    508  1.5  simonb 	} \
    509  1.5  simonb 	if (ebuf != NULL) *ebuf = buf; \
    510  1.5  simonb 	return v ? -1 : val; \
    511  1.5  simonb }
    512  1.5  simonb 
    513  1.5  simonb STR_TO_TYPE_FUNC(lstrtopos, POSITION)
    514  1.5  simonb STR_TO_TYPE_FUNC(lstrtoi, int)
    515  1.5  simonb STR_TO_TYPE_FUNC(lstrtoul, unsigned long)
    516  1.1    tron 
    517  1.1    tron /*
    518  1.5  simonb  * Print an integral type.
    519  1.1    tron  */
    520  1.5  simonb #define IPRINT_FUNC(funcname, type, typetoa) \
    521  1.5  simonb static int funcname(type num, int radix) \
    522  1.5  simonb { \
    523  1.5  simonb 	char buf[INT_STRLEN_BOUND(num)]; \
    524  1.5  simonb 	typetoa(num, buf, radix); \
    525  1.5  simonb 	putstr(buf); \
    526  1.5  simonb 	return (int) strlen(buf); \
    527  1.5  simonb }
    528  1.1    tron 
    529  1.5  simonb IPRINT_FUNC(iprint_int, int, inttoa)
    530  1.5  simonb IPRINT_FUNC(iprint_linenum, LINENUM, linenumtoa)
    531  1.1    tron 
    532  1.1    tron /*
    533  1.1    tron  * This function implements printf-like functionality
    534  1.1    tron  * using a more portable argument list mechanism than printf's.
    535  1.5  simonb  *
    536  1.5  simonb  * {{ This paranoia about the portability of printf dates from experiences
    537  1.5  simonb  *    with systems in the 1980s and is of course no longer necessary. }}
    538  1.1    tron  */
    539  1.5  simonb public int less_printf(char *fmt, PARG *parg)
    540  1.1    tron {
    541  1.5  simonb 	char *s;
    542  1.5  simonb 	int col;
    543  1.1    tron 
    544  1.1    tron 	col = 0;
    545  1.1    tron 	while (*fmt != '\0')
    546  1.1    tron 	{
    547  1.1    tron 		if (*fmt != '%')
    548  1.1    tron 		{
    549  1.1    tron 			putchr(*fmt++);
    550  1.1    tron 			col++;
    551  1.1    tron 		} else
    552  1.1    tron 		{
    553  1.1    tron 			++fmt;
    554  1.1    tron 			switch (*fmt++)
    555  1.1    tron 			{
    556  1.1    tron 			case 's':
    557  1.1    tron 				s = parg->p_string;
    558  1.1    tron 				parg++;
    559  1.1    tron 				while (*s != '\0')
    560  1.1    tron 				{
    561  1.1    tron 					putchr(*s++);
    562  1.1    tron 					col++;
    563  1.1    tron 				}
    564  1.1    tron 				break;
    565  1.1    tron 			case 'd':
    566  1.5  simonb 				col += iprint_int(parg->p_int, 10);
    567  1.5  simonb 				parg++;
    568  1.5  simonb 				break;
    569  1.5  simonb 			case 'x':
    570  1.5  simonb 				col += iprint_int(parg->p_int, 16);
    571  1.1    tron 				parg++;
    572  1.1    tron 				break;
    573  1.1    tron 			case 'n':
    574  1.5  simonb 				col += iprint_linenum(parg->p_linenum, 10);
    575  1.5  simonb 				parg++;
    576  1.5  simonb 				break;
    577  1.5  simonb 			case 'c':
    578  1.5  simonb 				s = prchar(parg->p_char);
    579  1.1    tron 				parg++;
    580  1.5  simonb 				while (*s != '\0')
    581  1.5  simonb 				{
    582  1.5  simonb 					putchr(*s++);
    583  1.5  simonb 					col++;
    584  1.5  simonb 				}
    585  1.5  simonb 				break;
    586  1.5  simonb 			case '%':
    587  1.5  simonb 				putchr('%');
    588  1.1    tron 				break;
    589  1.1    tron 			}
    590  1.1    tron 		}
    591  1.1    tron 	}
    592  1.1    tron 	return (col);
    593  1.1    tron }
    594  1.1    tron 
    595  1.1    tron /*
    596  1.1    tron  * Get a RETURN.
    597  1.1    tron  * If some other non-trivial char is pressed, unget it, so it will
    598  1.1    tron  * become the next command.
    599  1.1    tron  */
    600  1.5  simonb public void get_return(void)
    601  1.1    tron {
    602  1.1    tron 	int c;
    603  1.1    tron 
    604  1.1    tron #if ONLY_RETURN
    605  1.1    tron 	while ((c = getchr()) != '\n' && c != '\r')
    606  1.1    tron 		bell();
    607  1.1    tron #else
    608  1.1    tron 	c = getchr();
    609  1.1    tron 	if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
    610  1.1    tron 		ungetcc(c);
    611  1.1    tron #endif
    612  1.1    tron }
    613  1.1    tron 
    614  1.1    tron /*
    615  1.1    tron  * Output a message in the lower left corner of the screen
    616  1.1    tron  * and wait for carriage return.
    617  1.1    tron  */
    618  1.5  simonb public void error(char *fmt, PARG *parg)
    619  1.1    tron {
    620  1.1    tron 	int col = 0;
    621  1.1    tron 	static char return_to_continue[] = "  (press RETURN)";
    622  1.1    tron 
    623  1.1    tron 	errmsgs++;
    624  1.1    tron 
    625  1.5  simonb 	if (!interactive())
    626  1.1    tron 	{
    627  1.5  simonb 		less_printf(fmt, parg);
    628  1.1    tron 		putchr('\n');
    629  1.1    tron 		return;
    630  1.1    tron 	}
    631  1.1    tron 
    632  1.5  simonb 	if (!oldbot)
    633  1.5  simonb 		squish_check();
    634  1.5  simonb 	at_exit();
    635  1.5  simonb 	clear_bot();
    636  1.5  simonb 	at_enter(AT_STANDOUT|AT_COLOR_ERROR);
    637  1.5  simonb 	col += so_s_width;
    638  1.5  simonb 	col += less_printf(fmt, parg);
    639  1.1    tron 	putstr(return_to_continue);
    640  1.1    tron 	at_exit();
    641  1.1    tron 	col += sizeof(return_to_continue) + so_e_width;
    642  1.1    tron 
    643  1.1    tron 	get_return();
    644  1.1    tron 	lower_left();
    645  1.5  simonb 	clear_eol();
    646  1.1    tron 
    647  1.1    tron 	if (col >= sc_width)
    648  1.1    tron 		/*
    649  1.1    tron 		 * Printing the message has probably scrolled the screen.
    650  1.1    tron 		 * {{ Unless the terminal doesn't have auto margins,
    651  1.1    tron 		 *    in which case we just hammered on the right margin. }}
    652  1.1    tron 		 */
    653  1.1    tron 		screen_trashed = 1;
    654  1.1    tron 
    655  1.1    tron 	flush();
    656  1.1    tron }
    657  1.1    tron 
    658  1.1    tron /*
    659  1.1    tron  * Output a message in the lower left corner of the screen
    660  1.1    tron  * and don't wait for carriage return.
    661  1.1    tron  * Usually used to warn that we are beginning a potentially
    662  1.1    tron  * time-consuming operation.
    663  1.1    tron  */
    664  1.5  simonb static void ierror_suffix(char *fmt, PARG *parg, char *suffix1, char *suffix2, char *suffix3)
    665  1.1    tron {
    666  1.1    tron 	at_exit();
    667  1.1    tron 	clear_bot();
    668  1.5  simonb 	at_enter(AT_STANDOUT|AT_COLOR_ERROR);
    669  1.1    tron 	(void) less_printf(fmt, parg);
    670  1.5  simonb 	putstr(suffix1);
    671  1.5  simonb 	putstr(suffix2);
    672  1.5  simonb 	putstr(suffix3);
    673  1.1    tron 	at_exit();
    674  1.1    tron 	flush();
    675  1.1    tron 	need_clr = 1;
    676  1.1    tron }
    677  1.1    tron 
    678  1.5  simonb public void ierror(char *fmt, PARG *parg)
    679  1.5  simonb {
    680  1.5  simonb 	ierror_suffix(fmt, parg, "... (interrupt to abort)", "", "");
    681  1.5  simonb }
    682  1.5  simonb 
    683  1.5  simonb public void ixerror(char *fmt, PARG *parg)
    684  1.5  simonb {
    685  1.5  simonb 	if (!supports_ctrl_x())
    686  1.5  simonb 		ierror(fmt, parg);
    687  1.5  simonb 	else
    688  1.5  simonb 		ierror_suffix(fmt, parg,
    689  1.5  simonb 			"... (", prchar(intr_char), " or interrupt to abort)");
    690  1.5  simonb }
    691  1.5  simonb 
    692  1.1    tron /*
    693  1.1    tron  * Output a message in the lower left corner of the screen
    694  1.1    tron  * and return a single-character response.
    695  1.1    tron  */
    696  1.5  simonb public int query(char *fmt, PARG *parg)
    697  1.1    tron {
    698  1.5  simonb 	int c;
    699  1.1    tron 	int col = 0;
    700  1.1    tron 
    701  1.5  simonb 	if (interactive())
    702  1.1    tron 		clear_bot();
    703  1.1    tron 
    704  1.1    tron 	(void) less_printf(fmt, parg);
    705  1.1    tron 	c = getchr();
    706  1.1    tron 
    707  1.5  simonb 	if (interactive())
    708  1.5  simonb 	{
    709  1.5  simonb 		lower_left();
    710  1.5  simonb 		if (col >= sc_width)
    711  1.5  simonb 			screen_trashed = 1;
    712  1.5  simonb 		flush();
    713  1.5  simonb 	} else
    714  1.1    tron 	{
    715  1.1    tron 		putchr('\n');
    716  1.1    tron 	}
    717  1.1    tron 
    718  1.5  simonb 	if (c == 'Q')
    719  1.5  simonb 		quit(QUIT_OK);
    720  1.1    tron 	return (c);
    721  1.1    tron }
    722