X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Ftui%2Ftui-io.c;h=c7df50a4f24b8a7c4f2c9241f9ae0e856bcf105d;hb=e04caa70901ed44eb9537ccdbd286fe9b0a46ce2;hp=92da76e790452c077cfb0c904bbb45ca3b7fcc81;hpb=369003559a4526b4d504d17da211f63b20713a00;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/tui/tui-io.c b/gdb/tui/tui-io.c index 92da76e790..c7df50a4f2 100644 --- a/gdb/tui/tui-io.c +++ b/gdb/tui/tui-io.c @@ -1,7 +1,6 @@ /* TUI support I/O functions. - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2007, 2008, 2009, - 2010 Free Software Foundation, Inc. + Copyright (C) 1998-2018 Free Software Foundation, Inc. Contributed by Hewlett-Packard Company. @@ -33,12 +32,13 @@ #include "tui/tui-win.h" #include "tui/tui-wingeneral.h" #include "tui/tui-file.h" +#include "tui/tui-out.h" #include "ui-out.h" #include "cli-out.h" #include #include -#include - +#include "filestuff.h" +#include "completer.h" #include "gdb_curses.h" /* This redefines CTRL if it is not already defined, so it must come @@ -64,17 +64,6 @@ key_is_backspace (int ch) return (ch == 8); } -int -key_is_command_char (int ch) -{ - return ((ch == KEY_NPAGE) || (ch == KEY_PPAGE) - || (ch == KEY_LEFT) || (ch == KEY_RIGHT) - || (ch == KEY_UP) || (ch == KEY_DOWN) - || (ch == KEY_SF) || (ch == KEY_SR) - || (ch == (int)'\f') - || key_is_start_sequence (ch)); -} - /* Use definition from readline 4.3. */ #undef CTRL_CHAR #define CTRL_CHAR(c) \ @@ -126,14 +115,15 @@ struct ui_out *tui_out; /* GDB output files in non-curses mode. */ static struct ui_file *tui_old_stdout; static struct ui_file *tui_old_stderr; -struct ui_out *tui_old_uiout; +cli_ui_out *tui_old_uiout; /* Readline previous hooks. */ -static Function *tui_old_rl_getc_function; -static VFunction *tui_old_rl_redisplay_function; -static VFunction *tui_old_rl_prep_terminal; -static VFunction *tui_old_rl_deprep_terminal; -static int tui_old_readline_echoing_p; +static rl_getc_func_t *tui_old_rl_getc_function; +static rl_voidfunc_t *tui_old_rl_redisplay_function; +static rl_vintfunc_t *tui_old_rl_prep_terminal; +static rl_voidfunc_t *tui_old_rl_deprep_terminal; +static rl_compdisp_func_t *tui_old_rl_display_matches_hook; +static int tui_old_rl_echoing_p; /* Readline output stream. Should be removed when readline is clean. */ @@ -147,50 +137,94 @@ static int tui_readline_pipe[2]; This may be the main gdb prompt or a secondary prompt. */ static char *tui_rl_saved_prompt; -static unsigned int tui_handle_resize_during_io (unsigned int); +/* Print a character in the curses command window. The output is + buffered. It is up to the caller to refresh the screen if + necessary. */ + +static void +do_tui_putc (WINDOW *w, char c) +{ + static int tui_skip_line = -1; + + /* Catch annotation and discard them. We need two \032 and discard + until a \n is seen. */ + if (c == '\032') + { + tui_skip_line++; + } + else if (tui_skip_line != 1) + { + tui_skip_line = -1; + /* Expand TABs, since ncurses on MS-Windows doesn't. */ + if (c == '\t') + { + int col; + + col = getcurx (w); + do + { + waddch (w, ' '); + col++; + } + while ((col % 8) != 0); + } + else + waddch (w, c); + } + else if (c == '\n') + tui_skip_line = -1; +} + +/* Update the cached value of the command window's start line based on + the window's current Y coordinate. */ + +static void +update_cmdwin_start_line () +{ + TUI_CMD_WIN->detail.command_info.start_line + = getcury (TUI_CMD_WIN->generic.handle); +} + +/* Print a character in the curses command window. The output is + buffered. It is up to the caller to refresh the screen if + necessary. */ static void tui_putc (char c) { - char buf[2]; + WINDOW *w = TUI_CMD_WIN->generic.handle; - buf[0] = c; - buf[1] = 0; - tui_puts (buf); + do_tui_putc (w, c); + update_cmdwin_start_line (); } -/* Print the string in the curses command window. */ +/* Print LENGTH characters from the buffer pointed to by BUF to the + curses command window. The output is buffered. It is up to the + caller to refresh the screen if necessary. */ + +void +tui_write (const char *buf, size_t length) +{ + WINDOW *w = TUI_CMD_WIN->generic.handle; + + for (size_t i = 0; i < length; i++) + do_tui_putc (w, buf[i]); + update_cmdwin_start_line (); +} + +/* Print a string in the curses command window. The output is + buffered. It is up to the caller to refresh the screen if + necessary. */ + void tui_puts (const char *string) { - static int tui_skip_line = -1; + WINDOW *w = TUI_CMD_WIN->generic.handle; char c; - WINDOW *w; - w = TUI_CMD_WIN->generic.handle; while ((c = *string++) != 0) - { - /* Catch annotation and discard them. We need two \032 and - discard until a \n is seen. */ - if (c == '\032') - { - tui_skip_line++; - } - else if (tui_skip_line != 1) - { - tui_skip_line = -1; - waddch (w, c); - } - else if (c == '\n') - tui_skip_line = -1; - } - getyx (w, TUI_CMD_WIN->detail.command_info.cur_line, - TUI_CMD_WIN->detail.command_info.curch); - TUI_CMD_WIN->detail.command_info.start_line = TUI_CMD_WIN->detail.command_info.cur_line; - - /* We could defer the following. */ - wrefresh (w); - fflush (stdout); + do_tui_putc (w, c); + update_cmdwin_start_line (); } /* Readline callback. @@ -201,18 +235,21 @@ tui_redisplay_readline (void) { int prev_col; int height; - int col, line; + int col; int c_pos; int c_line; int in; WINDOW *w; - char *prompt; + const char *prompt; int start_line; /* Detect when we temporarily left SingleKey and now the readline edit buffer is empty, automatically restore the SingleKey - mode. */ - if (tui_current_key_mode == TUI_ONE_COMMAND_MODE && rl_end == 0) + mode. The restore must only be done if the command has finished. + The command could call prompt_for_continue and we must not + restore SingleKey so that the prompt and normal keymap are used. */ + if (tui_current_key_mode == TUI_ONE_COMMAND_MODE && rl_end == 0 + && !gdb_in_secondary_prompt_p (current_ui)) tui_set_key_mode (TUI_SINGLE_KEY_MODE); if (tui_current_key_mode == TUI_SINGLE_KEY_MODE) @@ -230,49 +267,54 @@ tui_redisplay_readline (void) for (in = 0; prompt && prompt[in]; in++) { waddch (w, prompt[in]); - getyx (w, line, col); - if (col < prev_col) + col = getcurx (w); + if (col <= prev_col) height++; prev_col = col; } - for (in = 0; in < rl_end; in++) + for (in = 0; in <= rl_end; in++) { unsigned char c; - c = (unsigned char) rl_line_buffer[in]; if (in == rl_point) { getyx (w, c_line, c_pos); } + if (in == rl_end) + break; + + c = (unsigned char) rl_line_buffer[in]; if (CTRL_CHAR (c) || c == RUBOUT) { waddch (w, '^'); waddch (w, CTRL_CHAR (c) ? UNCTRL (c) : '?'); } + else if (c == '\t') + { + /* Expand TABs, since ncurses on MS-Windows doesn't. */ + col = getcurx (w); + do + { + waddch (w, ' '); + col++; + } while ((col % 8) != 0); + } else { waddch (w, c); } if (c == '\n') - { - getyx (w, TUI_CMD_WIN->detail.command_info.start_line, - TUI_CMD_WIN->detail.command_info.curch); - } - getyx (w, line, col); + TUI_CMD_WIN->detail.command_info.start_line = getcury (w); + col = getcurx (w); if (col < prev_col) height++; prev_col = col; } wclrtobot (w); - getyx (w, TUI_CMD_WIN->detail.command_info.start_line, - TUI_CMD_WIN->detail.command_info.curch); + TUI_CMD_WIN->detail.command_info.start_line = getcury (w); if (c_line >= 0) - { - wmove (w, c_line, c_pos); - TUI_CMD_WIN->detail.command_info.cur_line = c_line; - TUI_CMD_WIN->detail.command_info.curch = c_pos; - } + wmove (w, c_line, c_pos); TUI_CMD_WIN->detail.command_info.start_line -= height - 1; wrefresh (w); @@ -289,7 +331,7 @@ tui_prep_terminal (int notused1) (we can't use gdb_prompt() due to secondary prompts and can't use rl_prompt because it points to an alloca buffer). */ xfree (tui_rl_saved_prompt); - tui_rl_saved_prompt = xstrdup (rl_prompt); + tui_rl_saved_prompt = rl_prompt != NULL ? xstrdup (rl_prompt) : NULL; } /* Readline callback to restore the terminal. It is called once each @@ -317,190 +359,105 @@ tui_readline_output (int error, gdb_client_data data) } #endif -/* Return the portion of PATHNAME that should be output when listing - possible completions. If we are hacking filename completion, we - are only interested in the basename, the portion following the - final slash. Otherwise, we return what we were passed. +/* TUI version of displayer.crlf. */ - Comes from readline/complete.c. */ -static char * -printable_part (char *pathname) +static void +tui_mld_crlf (const struct match_list_displayer *displayer) { - char *temp; - - temp = rl_filename_completion_desired ? strrchr (pathname, '/') : (char *)NULL; -#if defined (__MSDOS__) - if (rl_filename_completion_desired - && temp == 0 && isalpha (pathname[0]) - && pathname[1] == ':') - temp = pathname + 1; -#endif - return (temp ? ++temp : pathname); + tui_putc ('\n'); } -/* Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and - we are using it, check for and output a single character for - `special' filenames. Return the number of characters we - output. */ - -#define PUTX(c) \ - do { \ - if (CTRL_CHAR (c)) \ - { \ - tui_puts ("^"); \ - tui_putc (UNCTRL (c)); \ - printed_len += 2; \ - } \ - else if (c == RUBOUT) \ - { \ - tui_puts ("^?"); \ - printed_len += 2; \ - } \ - else \ - { \ - tui_putc (c); \ - printed_len++; \ - } \ - } while (0) +/* TUI version of displayer.putch. */ -static int -print_filename (char *to_print, char *full_pathname) +static void +tui_mld_putch (const struct match_list_displayer *displayer, int ch) { - int printed_len = 0; - char *s; - - for (s = to_print; *s; s++) - { - PUTX (*s); - } - return printed_len; + tui_putc (ch); } -/* The user must press "y" or "n". Non-zero return means "y" pressed. - Comes from readline/complete.c. */ -static int -get_y_or_n (void) +/* TUI version of displayer.puts. */ + +static void +tui_mld_puts (const struct match_list_displayer *displayer, const char *s) { - extern int _rl_abort_internal (); - int c; + tui_puts (s); +} - for (;;) - { - c = rl_read_key (); - if (c == 'y' || c == 'Y' || c == ' ') - return (1); - if (c == 'n' || c == 'N' || c == RUBOUT) - return (0); - if (c == ABORT_CHAR) - _rl_abort_internal (); - beep (); - } +/* TUI version of displayer.flush. */ + +static void +tui_mld_flush (const struct match_list_displayer *displayer) +{ + wrefresh (TUI_CMD_WIN->generic.handle); } -/* A convenience function for displaying a list of strings in - columnar format on readline's output stream. MATCHES is the list - of strings, in argv format, LEN is the number of strings in MATCHES, - and MAX is the length of the longest string in MATCHES. +/* TUI version of displayer.erase_entire_line. */ - Comes from readline/complete.c and modified to write in - the TUI command window using tui_putc/tui_puts. */ static void -tui_rl_display_match_list (char **matches, int len, int max) +tui_mld_erase_entire_line (const struct match_list_displayer *displayer) { - typedef int QSFUNC (const void *, const void *); - extern int _rl_qsort_string_compare (const void *, - const void *); - extern int _rl_print_completions_horizontally; - - int count, limit, printed_len; - int i, j, k, l; - char *temp; + WINDOW *w = TUI_CMD_WIN->generic.handle; + int cur_y = getcury (w); - /* Screen dimension correspond to the TUI command window. */ - int screenwidth = TUI_CMD_WIN->generic.width; + wmove (w, cur_y, 0); + wclrtoeol (w); + wmove (w, cur_y, 0); +} - /* If there are many items, then ask the user if she really wants to - see them all. */ - if (len >= rl_completion_query_items) - { - char msg[256]; +/* TUI version of displayer.beep. */ - sprintf (msg, "\nDisplay all %d possibilities? (y or n)", len); - tui_puts (msg); - if (get_y_or_n () == 0) - { - tui_puts ("\n"); - return; - } - } +static void +tui_mld_beep (const struct match_list_displayer *displayer) +{ + beep (); +} - /* How many items of MAX length can we fit in the screen window? */ - max += 2; - limit = screenwidth / max; - if (limit != 1 && (limit * max == screenwidth)) - limit--; +/* Helper function for tui_mld_read_key. + This temporarily replaces tui_getc for use during tab-completion + match list display. */ - /* Avoid a possible floating exception. If max > screenwidth, limit - will be 0 and a divide-by-zero fault will result. */ - if (limit == 0) - limit = 1; +static int +tui_mld_getc (FILE *fp) +{ + WINDOW *w = TUI_CMD_WIN->generic.handle; + int c = wgetch (w); - /* How many iterations of the printing loop? */ - count = (len + (limit - 1)) / limit; + return c; +} - /* Watch out for special case. If LEN is less than LIMIT, then - just do the inner printing loop. - 0 < len <= limit implies count = 1. */ +/* TUI version of displayer.read_key. */ - /* Sort the items if they are not already sorted. */ - if (rl_ignore_completion_duplicates == 0) - qsort (matches + 1, len, sizeof (char *), - (QSFUNC *)_rl_qsort_string_compare); +static int +tui_mld_read_key (const struct match_list_displayer *displayer) +{ + rl_getc_func_t *prev = rl_getc_function; + int c; - tui_putc ('\n'); + /* We can't use tui_getc as we need NEWLINE to not get emitted. */ + rl_getc_function = tui_mld_getc; + c = rl_read_key (); + rl_getc_function = prev; + return c; +} - if (_rl_print_completions_horizontally == 0) - { - /* Print the sorted items, up-and-down alphabetically, like ls. */ - for (i = 1; i <= count; i++) - { - for (j = 0, l = i; j < limit; j++) - { - if (l > len || matches[l] == 0) - break; - else - { - temp = printable_part (matches[l]); - printed_len = print_filename (temp, matches[l]); - - if (j + 1 < limit) - for (k = 0; k < max - printed_len; k++) - tui_putc (' '); - } - l += count; - } - tui_putc ('\n'); - } - } - else - { - /* Print the sorted items, across alphabetically, like ls -x. */ - for (i = 1; matches[i]; i++) - { - temp = printable_part (matches[i]); - printed_len = print_filename (temp, matches[i]); - /* Have we reached the end of this line? */ - if (matches[i+1]) - { - if (i && (limit > 1) && (i % limit) == 0) - tui_putc ('\n'); - else - for (k = 0; k < max - printed_len; k++) - tui_putc (' '); - } - } - tui_putc ('\n'); - } +/* TUI version of rl_completion_display_matches_hook. + See gdb_display_match_list for a description of the arguments. */ + +static void +tui_rl_display_match_list (char **matches, int len, int max) +{ + struct match_list_displayer displayer; + + rl_get_screen_size (&displayer.height, &displayer.width); + displayer.crlf = tui_mld_crlf; + displayer.putch = tui_mld_putch; + displayer.puts = tui_mld_puts; + displayer.flush = tui_mld_flush; + displayer.erase_entire_line = tui_mld_erase_entire_line; + displayer.beep = tui_mld_beep; + displayer.read_key = tui_mld_read_key; + + gdb_display_match_list (matches, len, max, &displayer); } /* Setup the IO for curses or non-curses mode. @@ -514,8 +471,8 @@ tui_rl_display_match_list (char **matches, int len, int max) void tui_setup_io (int mode) { - extern int readline_echoing_p; - + extern int _rl_echoing_p; + if (mode) { /* Redirect readline to TUI. */ @@ -523,13 +480,14 @@ tui_setup_io (int mode) tui_old_rl_deprep_terminal = rl_deprep_term_function; tui_old_rl_prep_terminal = rl_prep_term_function; tui_old_rl_getc_function = rl_getc_function; + tui_old_rl_display_matches_hook = rl_completion_display_matches_hook; tui_old_rl_outstream = rl_outstream; - tui_old_readline_echoing_p = readline_echoing_p; + tui_old_rl_echoing_p = _rl_echoing_p; rl_redisplay_function = tui_redisplay_readline; rl_deprep_term_function = tui_deprep_terminal; rl_prep_term_function = tui_prep_terminal; rl_getc_function = tui_getc; - readline_echoing_p = 0; + _rl_echoing_p = 0; rl_outstream = tui_rl_outstream; rl_prompt = 0; rl_completion_display_matches_hook = tui_rl_display_match_list; @@ -538,14 +496,16 @@ tui_setup_io (int mode) /* Keep track of previous gdb output. */ tui_old_stdout = gdb_stdout; tui_old_stderr = gdb_stderr; - tui_old_uiout = uiout; + tui_old_uiout = dynamic_cast (current_uiout); + gdb_assert (tui_old_uiout != nullptr); /* Reconfigure gdb output. */ gdb_stdout = tui_stdout; gdb_stderr = tui_stderr; gdb_stdlog = gdb_stdout; /* for moment */ gdb_stdtarg = gdb_stderr; /* for moment */ - uiout = tui_out; + gdb_stdtargerr = gdb_stderr; /* for moment */ + current_uiout = tui_out; /* Save tty for SIGCONT. */ savetty (); @@ -557,16 +517,17 @@ tui_setup_io (int mode) gdb_stderr = tui_old_stderr; gdb_stdlog = gdb_stdout; /* for moment */ gdb_stdtarg = gdb_stderr; /* for moment */ - uiout = tui_old_uiout; + gdb_stdtargerr = gdb_stderr; /* for moment */ + current_uiout = tui_old_uiout; /* Restore readline. */ rl_redisplay_function = tui_old_rl_redisplay_function; rl_deprep_term_function = tui_old_rl_deprep_terminal; rl_prep_term_function = tui_old_rl_prep_terminal; rl_getc_function = tui_old_rl_getc_function; + rl_completion_display_matches_hook = tui_old_rl_display_matches_hook; rl_outstream = tui_old_rl_outstream; - rl_completion_display_matches_hook = 0; - readline_echoing_p = tui_old_readline_echoing_p; + _rl_echoing_p = tui_old_rl_echoing_p; rl_already_prompted = 0; /* Save tty for SIGCONT. */ @@ -588,10 +549,6 @@ tui_cont_sig (int sig) /* Force a refresh of the screen. */ tui_refresh_all_win (); - /* Update cursor position on the screen. */ - wmove (TUI_CMD_WIN->generic.handle, - TUI_CMD_WIN->detail.command_info.start_line, - TUI_CMD_WIN->detail.command_info.curch); wrefresh (TUI_CMD_WIN->generic.handle); } signal (sig, tui_cont_sig); @@ -607,29 +564,24 @@ tui_initialize_io (void) #endif /* Create tui output streams. */ - tui_stdout = tui_fileopen (stdout); - tui_stderr = tui_fileopen (stderr); + tui_stdout = new tui_file (stdout); + tui_stderr = new tui_file (stderr); tui_out = tui_out_new (tui_stdout); - /* Create the default UI. It is not created because we installed a - deprecated_init_ui_hook. */ - tui_old_uiout = uiout = cli_out_new (gdb_stdout); + /* Create the default UI. */ + tui_old_uiout = cli_out_new (gdb_stdout); #ifdef TUI_USE_PIPE_FOR_READLINE /* Temporary solution for readline writing to stdout: redirect readline output in a pipe, read that pipe and output the content in the curses command window. */ - if (pipe (tui_readline_pipe) != 0) - { - fprintf_unfiltered (gdb_stderr, "Cannot create pipe for readline"); - exit (1); - } + if (gdb_pipe_cloexec (tui_readline_pipe) != 0) + error (_("Cannot create pipe for readline")); + tui_rl_outstream = fdopen (tui_readline_pipe[1], "w"); if (tui_rl_outstream == 0) - { - fprintf_unfiltered (gdb_stderr, "Cannot redirect readline output"); - exit (1); - } + error (_("Cannot redirect readline output")); + setvbuf (tui_rl_outstream, (char*) NULL, _IOLBF, 0); #ifdef O_NONBLOCK @@ -661,7 +613,6 @@ tui_getc (FILE *fp) #endif ch = wgetch (w); - ch = tui_handle_resize_during_io (ch); /* The \n must be echoed because it will not be printed by readline. */ @@ -672,9 +623,9 @@ tui_getc (FILE *fp) with empty lines with gdb prompt at beginning. Instead of that, stay on the same line but provide a visual effect to show the user we recognized the command. */ - if (rl_end == 0) + if (rl_end == 0 && !gdb_in_secondary_prompt_p (current_ui)) { - wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, 0); + wmove (w, getcury (w), 0); /* Clear the line. This will blink the gdb prompt since it will be redrawn at the same line. */ @@ -684,39 +635,104 @@ tui_getc (FILE *fp) } else { - wmove (w, TUI_CMD_WIN->detail.command_info.cur_line, - TUI_CMD_WIN->detail.command_info.curch); - waddch (w, ch); + /* Move cursor to the end of the command line before emitting the + newline. We need to do so because when ncurses outputs a newline + it truncates any text that appears past the end of the cursor. */ + int px, py; + getyx (w, py, px); + px += rl_end - rl_point; + py += px / TUI_CMD_WIN->generic.width; + px %= TUI_CMD_WIN->generic.width; + wmove (w, py, px); + tui_putc ('\n'); } } - if (key_is_command_char (ch)) - { /* Handle prev/next/up/down here. */ - ch = tui_dispatch_ctrl_char (ch); - } + /* Handle prev/next/up/down here. */ + ch = tui_dispatch_ctrl_char (ch); - if (ch == '\n' || ch == '\r' || ch == '\f') - TUI_CMD_WIN->detail.command_info.curch = 0; if (ch == KEY_BACKSPACE) return '\b'; - + + if (current_ui->command_editing && key_is_start_sequence (ch)) + { + int ch_pending; + + nodelay (w, TRUE); + ch_pending = wgetch (w); + nodelay (w, FALSE); + + /* If we have pending input following a start sequence, call the stdin + event handler again because ncurses may have already read and stored + the input into its internal buffer, meaning that we won't get an stdin + event for it. If we don't compensate for this missed stdin event, key + sequences as Alt_F (^[f) will not behave promptly. + + (We only compensates for the missed 2nd byte of a key sequence because + 2-byte sequences are by far the most commonly used. ncurses may have + buffered a larger, 3+-byte key sequence though it remains to be seen + whether it is useful to compensate for all the bytes of such + sequences.) */ + if (ch_pending != ERR) + { + ungetch (ch_pending); + call_stdin_event_handler_again_p = 1; + } + } + return ch; } +/* Utility function to expand TABs in a STRING into spaces. STRING + will be displayed starting at column COL, and is assumed to include + no newlines. The returned expanded string is malloc'ed. */ -/* Cleanup when a resize has occured. - Returns the character that must be processed. */ -static unsigned int -tui_handle_resize_during_io (unsigned int original_ch) +char * +tui_expand_tabs (const char *string, int col) { - if (tui_win_resized ()) + int n_adjust, ncol; + const char *s; + char *ret, *q; + + /* 1. How many additional characters do we need? */ + for (ncol = col, n_adjust = 0, s = string; s; ) { - tui_resize_all (); - tui_refresh_all_win (); - dont_repeat (); - tui_set_win_resized_to (FALSE); - return '\n'; + s = strpbrk (s, "\t"); + if (s) + { + ncol += (s - string) + n_adjust; + /* Adjustment for the next tab stop, minus one for the TAB + we replace with spaces. */ + n_adjust += 8 - (ncol % 8) - 1; + s++; + } } - else - return original_ch; + + /* Allocate the copy. */ + ret = q = (char *) xmalloc (strlen (string) + n_adjust + 1); + + /* 2. Copy the original string while replacing TABs with spaces. */ + for (ncol = col, s = string; s; ) + { + const char *s1 = strpbrk (s, "\t"); + if (s1) + { + if (s1 > s) + { + strncpy (q, s, s1 - s); + q += s1 - s; + ncol += s1 - s; + } + do { + *q++ = ' '; + ncol++; + } while ((ncol % 8) != 0); + s1++; + } + else + strcpy (q, s); + s = s1; + } + + return ret; }