示例#1
0
int cook_prompt_if_necessary () {
  char *pre_cooked, *filtered, *uncoloured;
  
  filtered = NULL;
  if (saved_rl_state.cooked_prompt)
    return FALSE;  /* cooked already */
  pre_cooked = mysavestring(saved_rl_state.raw_prompt);
  unbackspace(pre_cooked);
  if ( (prompt_regexp && ! match_regexp(pre_cooked, prompt_regexp, FALSE)) ||  /* raw prompt doesn't match '--only-cook' regexp */
       (strcmp((filtered =  pass_through_filter(TAG_PROMPT, pre_cooked)), "_THIS_CANNOT_BE_A_PROMPT_")== 0)) { /* filter has "refused" the prompt */
    saved_rl_state.cooked_prompt =  (impatient_prompt ? mysavestring(pre_cooked) : mysavestring("")); /* don't cook, eat raw (and eat nothing if patient) */
    free(pre_cooked);
    free(filtered); /* free(NULL) is not an error */
    return FALSE;
  }     
  free(pre_cooked);
  if(substitute_prompt) {
    uncoloured = mysavestring(substitute_prompt); 
    free(filtered);
  } else {
    uncoloured = filtered;
  }     
  if (colour_the_prompt) { 
    saved_rl_state.cooked_prompt =  colourise(uncoloured);
    free(uncoloured);
  } else {
    saved_rl_state.cooked_prompt = uncoloured;
  }
  return TRUE;
}       
示例#2
0
char *
colour_name_to_ansi_code(const char *colour_name) {
  if (colour_name  && *colour_name && isalpha(*colour_name)) {
    char *lc_colour_name = mysavestring(lowercase(colour_name));
    char *bold_code = (isupper(*colour_name) ? "1" : "0");

#define isit(c) (strcmp(c,lc_colour_name)==0)
    char *colour_code =
      isit("black")   ? "30" :
      isit("red")     ? "31" :
      isit("green")   ? "32" :
      isit("yellow")  ? "33" :
      isit("blue")    ? "34" :
      isit("magenta") ? "35" :
      isit("purple")  ? "35" :
      isit("cyan")    ? "36" :
      isit("white")   ? "37" :
      NULL ;
      
#undef isit
    if (colour_code)
      return add3strings(bold_code,";",colour_code);
    else
      myerror("unrecognised colour name '%s'. Use e.g. 'yellow' or 'Blue'.", colour_name);
  }
  return mysavestring(colour_name);
}       
示例#3
0
char *process_new_output(const char* buffer, struct rl_state* UNUSED(state)) {
  char*last_nl, *old_prompt_plus_new_output, *new_prompt, *result;
  
  old_prompt_plus_new_output = append_and_free_old(saved_rl_state.raw_prompt, buffer); 
  last_nl = strrchr(old_prompt_plus_new_output, '\n');
  if (last_nl != NULL) {        /* newline seen, will get new prompt: */
    new_prompt = mysavestring(last_nl +1); /* chop off the part after the last newline -  this will be the new prompt */
    *last_nl = '\0';
    old_prompt_plus_new_output = append_and_free_old (old_prompt_plus_new_output, "\n");
    result = (impatient_prompt ? mysavestring (old_prompt_plus_new_output): pass_through_filter(TAG_OUTPUT, old_prompt_plus_new_output));
    if (remember_for_completion) {
      feed_line_into_completion_list(result); /* feed output into completion list *after* filtering */
    }
  } else {      
    new_prompt = mysavestring(old_prompt_plus_new_output);
    result = mysavestring("");
  }                 
  free(old_prompt_plus_new_output);

  saved_rl_state.raw_prompt = new_prompt;
  if (saved_rl_state.cooked_prompt) {
    free (saved_rl_state.cooked_prompt);
    saved_rl_state.cooked_prompt = NULL; 
  }     
  return result;
}
示例#4
0
void
init_readline(char *UNUSED(prompt))
{
  DPRINTF1(DEBUG_READLINE, "Initialising readline version %x", rl_readline_version);   
  rl_add_defun("rlwrap-accept-line", my_accept_line,-1);
  rl_add_defun("rlwrap-accept-line-and-forget", my_accept_line_and_forget,-1);
  rl_add_defun("rlwrap-call-editor", munge_line_in_editor, -1);
  rl_add_defun("rlwrap-direct-keypress", direct_keypress, -1);  
  rl_add_defun("rlwrap-hotkey", handle_hotkey, -1);
  rl_add_defun("rlwrap-hotkey-ignore-history", handle_hotkey_ignore_history, -1);

  /* only useful while debugging */
  rl_add_defun("rlwrap-dump-all-keybindings", dump_all_keybindings,-1);
  rl_add_defun("rlwrap-debug-ad-hoc", debug_ad_hoc, -1);
  
  /* the old rlwrap bindable function names with underscores are deprecated: */
  rl_add_defun("rlwrap_accept_line_and_forget", please_update_alaf,-1);
  rl_add_defun("rlwrap_call_editor", please_update_ce,-1);
  
  
  rl_variable_bind("blink-matching-paren","on"); /* Shouldn't this be on by default? */

  bindkey('\n', my_accept_line, "emacs-standard; vi-insert; vi-command"); 
  bindkey('\r', my_accept_line, "emacs-standard; vi-insert; vi-command"); 
  bindkey(15, my_accept_line_and_forget, "emacs-standard; vi-insert; vi-command");	/* ascii #15 (Control-O) is unused in readline's emacs and vi keymaps */
  if (multiline_separator) 
    bindkey(30, munge_line_in_editor, "emacs-standard;vi-insert;vi-command");            /* CTRL-^: unused in vi-insert-mode, hardly used in emacs  (doubles arrow-up) */

  
  
  /* rl_variable_bind("gnah","gnerp"); It is not possible to create new readline variables (only functions) */
  rl_catch_signals = 0;
  rl_initialize();		/* This has to happen *before* we set rl_redisplay_function, otherwise
				   readline will not bother to call tgetent(), will be agnostic about terminal
				   capabilities and hence not be able to honour e.g. a set horizontal-scroll-mode off
				   in .inputrc */
  
  using_history();
  rl_redisplay_function = my_redisplay;
  rl_completion_entry_function =
    (rl_compentry_func_t *) & my_completion_function;
  
  rl_catch_signals = FALSE;
  rl_catch_sigwinch = FALSE;
  saved_rl_state.input_buffer = mysavestring(pre_given ? pre_given : ""); /* Even though the pre-given input won't be displayed before the first 
                                                                             cooking takes place, we still want it to be accepted when the user 
                                                                             presses ENTER before that (e.g. because she already knows the 
                                                                             pre-given input and wants to accept that) */
  saved_rl_state.point = strlen(saved_rl_state.input_buffer);
  saved_rl_state.raw_prompt = mysavestring("");
  saved_rl_state.cooked_prompt = NULL;
  
}
示例#5
0
char *
mangle_char_for_debug_log(char c, int quote_me)
{
  char *special = NULL;
  char scrap[10], code, *format;
  char *remainder = "\\]^_"; 
  
  switch (c) {
  case 0: special = "<NUL>"; break;
  case 8: special  = "<BS>";  break;
  case 9: special  = "<TAB>"; break;
  case 10: special = "<NL>";  break;
  case 13: special = "<CR>";  break;
  case 27: special = "<ESC>"; break;
  case 127: special = "<DEL>"; break;
  }
  if (!special) {
    if (c > 0 && c < 27  ) {
      format = "<CTRL-%c>"; code =  c + 64;
    } else if (c > 27 && c < 32) {
      format = "<CTRL-%c>"; code =  remainder[c-28];
    } else {
      format = (quote_me ? "\"%c\"" : "%c"); code = c;
    }   
    snprintf1(scrap, sizeof(scrap), format, code);
  }
  return mysavestring (special ? special : scrap);
}       
示例#6
0
文件: main.c 项目: albfan/rlwrap
/*
 * create pty pair and fork using my_pty_fork; parent returns immediately; child
 * executes the part of rlwrap's command line that remains after
 * read_options_and_command_name() has harvested rlwrap's own options
 */  
static void
fork_child(char *command_name, char **argv)
{
  char *arg = argv[optind], *p, **argp;
  int pid;

  command_line = mysavestring(arg);
  for (argp = argv + optind + 1; *argp; argp++) {
    command_line = append_and_free_old (command_line, " ");
    command_line = append_and_free_old (command_line, *argp);
  }

  pid = my_pty_fork(&master_pty_fd, &saved_terminal_settings, &winsize);
  if (pid > 0)			/* parent: */
    return;
  else {			/* child: */
    DPRINTF1(DEBUG_TERMIO, "preparing to execute %s", arg);
    close_open_files_without_writing_buffers();
    
    if (client_term_name)
      mysetenv("TERM", client_term_name);   
    if (execvp(argv[optind], &argv[optind]) < 0) {
      if (last_opt > 0 && last_option_didnt_have_optional_argument) { /* e.g. 'rlwrap -a Password: sqlpus' will try to exec 'Password:'******'; !(){}"; *p; p++) /* does arg need shell quoting? */ 
	  if (strchr(arg,*p)) { 
            arg = add3strings("'", arg,"'"); /* quote it  */
            break;
	  }	
	fprintf(stderr, "Did you mean '%s' to be an option argument?\nThen you should write -%c%s, without the space(s)\n",
                argv[optind], last_opt, arg); 
      }
      myerror("Cannot execute %s", argv[optind]);   	/* stillborn child, parent will live on and display child's last gasps */
    }
  }
}
示例#7
0
/* lowercase(str) returns lowercased copy of str */
char *
lowercase(const char *str) {
  char *result, *p;
  result = mysavestring(str);
  for (p=result; *p; p++)
    *p = tolower(*p);
  return result;
}       
示例#8
0
/* mystrtok: saner version of strtok that doesn't overwrite its first argument */
char *mystrtok(const char *s, const char *delim) {
  static char *scratchpad = NULL;
  if (s) {
    if (scratchpad)
      free(scratchpad);
    scratchpad = mysavestring(s);
  }    
  return strtok(s ? scratchpad : NULL, delim);
}       
示例#9
0
void test_unbackspace (const char *input, const char *expected_result) {
  char *scrap = mysavestring(input);
  unbackspace(scrap);
  if (strcmp(scrap, expected_result) != 0)
      myerror("unbackspace %s yielded %s, expected %s",
              mangle_string_for_debug_log(input,0),
              mangle_string_for_debug_log(scrap,0),
              expected_result);
}
示例#10
0
void
init_readline(char *prompt)
{
  DPRINTF1(DEBUG_READLINE, "Initialising readline version %x", rl_readline_version);   
  rl_add_defun("rlwrap-accept-line", my_accept_line,-1);
  rl_add_defun("rlwrap-accept-line-and-forget", my_accept_line_and_forget,-1);
  rl_add_defun("rlwrap-dump-all-keybindings", dump_all_keybindings,-1);
  rl_add_defun("rlwrap-call-editor", munge_line_in_editor, -1);

  /* rlwrap bindable function names with underscores are deprecated: */
  rl_add_defun("rlwrap_accept_line_and_forget", please_update_alaf,-1);
  rl_add_defun("rlwrap_call_editor", please_update_ce,-1);
  
  rl_variable_bind("blink-matching-paren","on"); /* Shouldn't this be on by default? */

  bindkey('\n', my_accept_line, "emacs-standard; vi-insert; vi-command"); 
  bindkey('\r', my_accept_line, "emacs-standard; vi-insert; vi-command"); 
  bindkey(15, my_accept_line_and_forget, "emacs-standard; vi-insert; vi-command");	/* ascii #15 (Control-O) is unused in readline's emacs and vi keymaps */
  if (multiline_separator) 
    bindkey(30, munge_line_in_editor, "emacs-standard;vi-insert;vi-command");            /* CTRL-^: unused in vi-insert-mode, hardly used in emacs  (doubles arrow-up) */
  if (debug)
    bindkey(7, dump_all_keybindings,"emacs-standard; vi-insert; vi-move; vi-command" /* "emacs-ctlx; emacs-meta" */); /* CTRL-G */
  

  /* rl_variable_bind("gnah","gnerp"); It is not possible to create new readline variables (only functions) */
  rl_catch_signals = 0;
  rl_initialize();		/* This has to happen *before* we set rl_redisplay_function, otherwise
				   readline will not bother to call tgetent(), will be agnostic about terminal
				   capabilities and hence not be able to honour e.g. a set horizontal-scroll-mode off
				   in .inputrc */
  
  using_history();
  rl_redisplay_function = my_redisplay;
  rl_completion_entry_function =
    (rl_compentry_func_t *) & my_completion_function;
  
  rl_catch_signals = FALSE;
  rl_catch_sigwinch = FALSE;
  saved_rl_state.input_buffer = mysavestring("");
  saved_rl_state.raw_prompt = mysavestring("");
  saved_rl_state.cooked_prompt = NULL;
  
}
示例#11
0
/* returns a colourised copy of prompt, trailing space is not colourised */
char*
colourise (const char *prompt)
{
  char *prompt_copy, *trailing_space, *colour_end_with_space, *result, *p;
  prompt_copy = mysavestring(prompt);
  if (strchr(prompt_copy, '\033') || strchr(prompt_copy, RL_PROMPT_START_IGNORE) ) {     /* prompt contains escape codes? */
    DPRINTF1(DEBUG_READLINE, "colourise %s: left as-is", prompt);
    return prompt_copy; /* if so, leave prompt alone  */
  }
  for (p = prompt_copy + strlen(prompt_copy); p > prompt_copy && *(p-1) == ' '; p--)
    ; /* skip back over trailing space */
  trailing_space = mysavestring(p); /* p now points at first trailing space, or else the final NULL */
  *p = '\0';
  colour_end_with_space = add2strings(colour_end, trailing_space);
  result = add3strings(colour_start, prompt_copy, colour_end_with_space);
  free (prompt_copy); free(trailing_space); free(colour_end_with_space);
  DPRINTF1(DEBUG_READLINE, "colourise %s: colour added ", prompt);
  return result;
}
示例#12
0
文件: filter.c 项目: rubicks/rlwrap
char *pass_through_filter(int tag, const char *buffer) {
    char *filtered;
    DPRINTF3(DEBUG_FILTERING, "to filter (%s, %d bytes) %s", tag2description(tag), (int) strlen(buffer), mangle_string_for_debug_log(buffer, MANGLE_LENGTH));
    if (filter_pid ==0)
        return mysavestring(buffer);
    write_to_filter((expected_tag = tag), buffer);
    filtered = read_from_filter(tag);
    DPRINTF2(DEBUG_FILTERING, "from filter (%d bytes) %s", (int) strlen(filtered), mangle_string_for_debug_log(filtered, MANGLE_LENGTH));
    return filtered;
}
示例#13
0
static int
direct_keypress(int UNUSED(count), int key)
{
  char *key_as_str = mysavestring("?");
  /* put the key in the output queue    */
  *key_as_str = key;
  DPRINTF1(DEBUG_READLINE,"direct keypress: %s", mangle_char_for_debug_log(key, TRUE));
  put_in_output_queue(key_as_str);
  free(key_as_str);
  return 0;
}
示例#14
0
char *
append_and_free_old(char *str1, const char *str2)
{
  if (!str1)
    return mysavestring(str2); /* if str1 == NULL there is no need to "free the old str1" */
  else {
    char *result = add2strings(str1,str2);
    free (str1);
    return result;
  }     
}
示例#15
0
/*
  returns TRUE if 'string' matches the 'regexp' (or is a superstring
  of it, when we don't HAVE_REGEX_H). The regexp is recompiled with
  every call, which doesn't really hurt as this function is not called
  often: at most twice for every prompt.  'string' and 'regexp' may be
  NULL (in which case FALSE is returned)

  Only used for the --forget-regexp and the --prompt-regexp options
*/
int match_regexp (const char *string, const char *regexp, int case_insensitive) {
  int result;
  
  if (!regexp || !string)
    return FALSE;
  
#ifndef HAVE_REGEX_H
  {
    static int been_warned = 0;
    char *metachars = "*()+?";
    char *lc_string = (case_insensitive ? lowercase(string) : mysavestring(string));
    char *lc_regexp = (case_insensitive  ? lowercase(regexp) : mysavestring(regexp));

    if (scan_metacharacters(regexp, metachars) && !been_warned++) /* warn only once if the user specifies a metacharacter */
      mywarn("one of the regexp metacharacters \"%s\" occurs in regexp(?) \"%s\"\n"
             "  ...but on your platform, regexp matching is not supported!", metachars, regexp);        
    
    result = mystrstr(lc_string, lc_regexp);
    free(lc_string);
    free(lc_regexp);    
  }
#else
  {
    regex_t *compiled_regexp = mymalloc(sizeof(regex_t));
    int compile_error = regcomp(compiled_regexp, regexp, REG_EXTENDED|REG_NOSUB|(case_insensitive ? REG_ICASE : 0));
     
    if (compile_error) {
      int size = regerror(compile_error, compiled_regexp, NULL, 0);
      char *error_message =  mymalloc(size);
      regerror(compile_error, compiled_regexp, error_message, size);
      errno=0; myerror("in regexp \"%s\": %s", regexp, error_message);  
    } else {
      result = !regexec(compiled_regexp, string, 0, NULL, 0);
      free(compiled_regexp);
    }
  }
#endif

  
  return result;        
}
示例#16
0
文件: pty.c 项目: jsarenik/rlwrap
void
write_EOF_to_master_pty()
{
  struct termios *pterm_slave = my_tcgetattr(slave_pty_fd, "slave pty");
  char *sent_EOF = mysavestring("?");

  *sent_EOF = (pterm_slave && pterm_slave->c_cc[VEOF]  ? pterm_slave->c_cc[VEOF] : 4) ; /*@@@ HL shouldn't we directly mysavestring(pterm_slave->c_cc[VEOF]) ??*/
  DPRINTF1(DEBUG_TERMIO, "Sending %s", mangle_string_for_debug_log(sent_EOF, MANGLE_LENGTH));
  put_in_output_queue(sent_EOF);
  myfree(pterm_slave);
  free(sent_EOF);
}
示例#17
0
char *
mangle_string_for_debug_log(const char *string, int maxlen)
{
  int total_length;
  char *mangled_char, *result;
  const char *p; /* good old K&R-style *p. I have become a fossil... */

  if (!string)
    return mysavestring("(null)");
  result = mysavestring("");
  for(p = string, total_length = 0; *p; p++) {
    mangled_char = mangle_char_for_debug_log(*p, FALSE);
    total_length +=  strlen(mangled_char);
    if (maxlen && (total_length > maxlen)) {
      result = append_and_free_old(result, "...");      
      break;  /* break *before* we reach maxlen */
    }
    result = append_and_free_old(result, mangled_char);
    free(mangled_char);
  }
  return result;
}
示例#18
0
char *
get_last_screenline(char *long_line, int termwidth)
{
  int line_length, removed;
  char *line_copy, *last_screenline;

  line_copy = copy_and_unbackspace(long_line);
  line_length = strlen(line_copy);
  
  if (termwidth == 0 ||              /* this may be the case on some weird systems */
      line_length <=  termwidth)  {  /* line doesn't extend beyond right margin
                                        @@@ are there terminals that put the cursor on the
                                        next line if line_length == termwidth?? */
    return line_copy; 
  } else if (strchr(long_line, '\033')) { /* <ESC> found, give up */
    free (line_copy);
    return mysavestring("Ehhmm..? > ");
  } else {      
    removed = (line_length / termwidth) * termwidth;   /* integer arithmetic: 33/10 = 3 */
    last_screenline  = mysavestring(line_copy + removed);
    free(line_copy);
    return last_screenline;
  }
}
示例#19
0
static char* entire_history_as_one_string(void) {
  HIST_ENTRY **the_list = history_list(), **entryp;
  if (!the_list) /* i.e. if there is no history */
    return mysavestring("");
  char *big_string = mymalloc(history_total_bytes() + history_length + 1);
  char * stringp =  big_string;
  for (entryp = the_list; *entryp; entryp++) {
    int length = strlen((*entryp)->line);
    strncpy(stringp, (*entryp)->line, length); /* copy line, without closing NULL byte; */
    stringp +=length;
    *stringp++ = '\n';
  }
  *stringp = '\0';
  DPRINTF1(DEBUG_READLINE, "stringified %d bytes of history", (int) strlen(big_string));
  return big_string;
}       
示例#20
0
char *
search_and_replace(char *patt, char *repl, const char *string, int cursorpos,
                   int *line, int *col)
{
  int i, j, k;
  int pattlen = strlen(patt);
  int replen = strlen(repl);
  int stringlen = strlen(string);
  int cursor_found = FALSE;
  int current_line = 1;
  int current_column = 0;
  size_t scratchsize;
  char *scratchpad, *result;

  assert(patt && repl && string);
  DPRINTF2(DEBUG_READLINE, "string=%s, cursorpos=%d",
           mangle_string_for_debug_log(string, 40), cursorpos);
  scratchsize = max(stringlen, (stringlen * replen) / pattlen) + 1;     /* worst case : repleng > pattlen and string consists of only <patt> */
  DPRINTF1(DEBUG_READLINE, "Allocating %d bytes for scratchpad", (int) scratchsize);
  scratchpad = mymalloc(scratchsize);


  for (i = j = 0; i < stringlen;  ) {
    if (line && col &&                           /* if col and line are BOTH non-NULL, and .. */
        i >= cursorpos && !cursor_found) {       /*  ... for the first time, i >= cursorpos: */
      cursor_found = TRUE;                       /* flag that we're done here */                              
      *line = current_line;                      /* update *line */ 
      *col = current_column;                     /* update *column */
    }
    if (strncmp(patt, string + i, pattlen) == 0) { /* found match */
      i += pattlen;                                /* update i ("jump over" patt (and, maybe, cursorpos)) */  
      for (k = 0; k < replen; k++)                 /* append repl to scratchpad */
        scratchpad[j++] = repl[k];
      current_line++;                              /* update line # (assuming that repl = "\n") */
      current_column = 0;                          /* reset column */
    } else {
      scratchpad[j++] = string[i++];               /* or else just copy things */
      current_column++;
    }
  }
  if (line && col)
    DPRINTF2(DEBUG_READLINE, "line=%d, col=%d", *line, *col);
  scratchpad[j] = '\0';
  result = mysavestring(scratchpad);
  free(scratchpad);
  return (result);
}
示例#21
0
/* save readline internal state in rl_state, redisplay the prompt
   (so that client output gets printed at the right place) */
void
save_rl_state()
{
  free(saved_rl_state.input_buffer); /* free(saved_rl_state.raw_prompt) */;
  saved_rl_state.input_buffer = mysavestring(rl_line_buffer);
  /* saved_rl_state.raw_prompt = mysavestring(rl_prompt); */
 
  saved_rl_state.point = rl_point;      /* and point    */
  rl_line_buffer[0] = '\0';
  if (saved_rl_state.already_saved)
    return;
  saved_rl_state.already_saved = 1;
  rl_delete_text(0, rl_end);    /* clear line  (after prompt) */
  rl_point = 0;
  my_redisplay();               /* and redisplay (this time without user input, cf the comments for the line_handler() function below) */
  rl_callback_handler_remove(); /* restore original terminal settings */
  rl_deprep_terminal();

}
示例#22
0
char *
mark_invisible(const char *buf)
{
  int padsize =  (assert(buf != NULL), (3 * strlen(buf) + 1)); /* worst case: every char in buf gets surrounded by RL_PROMPT_{START,END}_IGNORE */
  char *scratchpad = mymalloc (padsize);
  char *result = scratchpad;
  const char **original = &buf;
  char **copy = &scratchpad;

  if (strchr(buf, RL_PROMPT_START_IGNORE))
    return mysavestring(buf); /* "invisible" parts already marked */
    
  while (**original) {
    copy_ordinary_char_or_ESC_sequence(original, copy); 
    assert(*copy - scratchpad < padsize);
  }
  **copy = '\0';
  return(result);       
}       
示例#23
0
char *
copy_and_unbackspace(const char *original)
{
  char *copy = mysavestring(original);
#if 0  
  char *copy_start = copy;
  for( ; *original; original++) {
    if(*original == BACKSPACE)
      copy = (copy > copy_start ? copy - 1 : copy_start);
    else if (*original == CARRIAGE_RETURN)
      copy = copy_start;
    else
      *copy++ = *original;
  }     
  *copy = '\0';
  return copystart;
#else
  return copy;
#endif
}
示例#24
0
文件: main.c 项目: albfan/rlwrap
void
flush_output_queue()
{
  int nwritten, queuelen, how_much;
  char *old_queue = output_queue;
  char *nl;

  if (!output_queue)
    return;
  queuelen = strlen(output_queue);
  nl       = strchr(output_queue, '\n');
  how_much = min(BUFFSIZE, nl ? 1+ nl - output_queue : queuelen); /* never write more than one line, and never more than BUFFSIZE in one go */
  nwritten = write(master_pty_fd, output_queue, how_much);

  assert(nwritten <= strlen(output_queue));
  if (debug) {
    char scratch = output_queue[nwritten];
    output_queue[nwritten] = '\0'; /* temporarily replace the last written byte + 1 by a '\0' */
    DPRINTF3(DEBUG_TERMIO,"flushed %d of %d bytes from output queue to pty: %s",
	     nwritten, queuelen, mangle_string_for_debug_log(output_queue, MANGLE_LENGTH));
    output_queue[nwritten] =  scratch;
  }	
  
  if (nwritten < 0) {
    switch (nwritten) {
    case EINTR:
    case EAGAIN:
      return;
    default:
      myerror("write to master pty failed");
    }
  }

  if (!output_queue[nwritten]) /* nothing left in queue */
    output_queue = NULL;
  else
    output_queue = mysavestring(output_queue + nwritten);	/* this much is left to be written */

  free(old_queue);
}
示例#25
0
文件: pty.c 项目: jsarenik/rlwrap
/* @@@ The next fuction is probably superfluous */
void
write_EOL_to_master_pty(char *received_eol)
{
  struct termios *pterm_slave = my_tcgetattr(slave_pty_fd, "slave pty");
  char *sent_eol = mysavestring("?");

  *sent_eol = *received_eol;
  if (pterm_slave) { 
    switch (*received_eol) {
    case '\n':
      if (pterm_slave->c_iflag & INLCR)
        *sent_eol = '\r';
      break;
    case '\r':
      if (pterm_slave->c_iflag & IGNCR)
        return;
      if (pterm_slave->c_iflag & ICRNL)
        *sent_eol = '\n';
    }
  } 
  put_in_output_queue(sent_eol);
  myfree(pterm_slave);
  free(sent_eol);
}
示例#26
0
int cook_prompt_if_necessary () {
  char *pre_cooked, *rubbish_from_alternate_screen,  *filtered, *uncoloured, *cooked, *p, *non_rubbish = NULL;
  static char **term_ctrl_seqs[] 
    = {&term_rmcup, &term_rmkx, NULL}; /* (NULL-terminated) list of (pointers to) term control sequences that may be
                                       used by clients to return from an 'alternate screen'. If we spot one of those,
                                       assume that it, and anything before it, is rubbish and better left untouched */

  char ***tcptr;
  filtered = NULL;

  DPRINTF2(DEBUG_READLINE, "Prompt <%s>: %s", saved_rl_state.raw_prompt, prompt_is_still_uncooked ? "still raw" : "cooked already");

  if (saved_rl_state.cooked_prompt)    /* if (!prompt_is_still_uncooked) bombs with multi-line paste. Apparently
                                        prompt_is_still_uncooked can be FALSE while saved_rl_state.cooked_prompt = NULL. Ouch!@@@! */
    return FALSE;  /* cooked already */
  
  pre_cooked = mysavestring(saved_rl_state.raw_prompt);

  
  for (tcptr = term_ctrl_seqs; *tcptr; tcptr++) { 
    /* find last occurence of one of term_ctrl_seq */
    if (**tcptr && (p = mystrstr(pre_cooked, **tcptr))) {
      p += strlen(**tcptr);  /* p now points 1 char past term control sequence */ 
      if (p > non_rubbish) 
        non_rubbish = p; 
    }   
  }     
  /* non_rubbish now points 1 past the last 'alternate screen terminating' control char in prompt */
  if (non_rubbish) { 
    rubbish_from_alternate_screen = pre_cooked;
    pre_cooked = mysavestring(non_rubbish);
    *non_rubbish = '\0'; /* 0-terminate rubbish_from_alternate_screen */ 
  } else { 
    rubbish_from_alternate_screen = mysavestring("");
  }
  

  unbackspace(pre_cooked); /* programs that display a running counter would otherwise make rlwrap keep prompts
                              like " 1%\r 2%\r 3%\ ......" */

  if ( /* raw prompt doesn't match '--only-cook' regexp */
      (prompt_regexp && ! match_regexp(pre_cooked, prompt_regexp, FALSE)) ||
       /* now filter it, but filter may "refuse" the prompt */
      (strcmp((filtered =  pass_through_filter(TAG_PROMPT, pre_cooked)), "_THIS_CANNOT_BE_A_PROMPT_")== 0)) { 
    /* don't cook, eat raw (and eat nothing if patient) */       
    saved_rl_state.cooked_prompt =  (impatient_prompt ? mysavestring(pre_cooked) : mysavestring("")); 
    /* NB: if impatient, the rubbish_from_alternate_screen has been output already, no need to send it again */  
    free(pre_cooked);
    free(filtered); /* free(NULL) is never an error */
    return FALSE;
  }     
  free(pre_cooked);
  if(substitute_prompt) {
    uncoloured = mysavestring(substitute_prompt); 
    free(filtered);
  } else {
    uncoloured = filtered;
  }     
  if (colour_the_prompt) { 
    cooked =  colourise(uncoloured);
    free(uncoloured);
  } else {
    cooked = uncoloured;
  }
  if (! impatient_prompt)  /* in this case our rubbish hasn't been output yet. Output it now, but don't store
                              it in the prompt, as this may be re-printed e.g. after resuming a suspended rlwrap */
                              
    write_patiently(STDOUT_FILENO,rubbish_from_alternate_screen, strlen(rubbish_from_alternate_screen), "to stdout");
  saved_rl_state.cooked_prompt = cooked;
  return TRUE;
}       
示例#27
0
文件: main.c 项目: albfan/rlwrap
char *
read_options_and_command_name(int argc, char **argv)
{
  int c;
  char *opt_C = NULL;
  int option_count = 0;
  int opt_b = FALSE;
  int opt_f = FALSE;
  int remaining = -1; /* remaining number of arguments on command line */
  int longindex = -1; /* index of current option in longopts[], set by getopt_long */
  
  
  full_program_name = mysavestring(argv[0]);
  program_name = mybasename(full_program_name);	/* normally "rlwrap"; needed by myerror() */
  rl_basic_word_break_characters = " \t\n\r(){}[],+-=&^%$#@\";|\\";

  opterr = 0;			/* we do our own error reporting */

  while (1) {
#ifdef HAVE_GETOPT_LONG
    c = getopt_long(argc, argv, optstring, longopts, &longindex);
#else
    c = getopt(argc, argv, optstring);
#endif

    if (c == EOF)
      break;
    option_count++;
    last_option_didnt_have_optional_argument = FALSE;
    remaining = argc - optind;
    last_opt = c;    

    switch (c) {
    case 'a':
      always_readline = TRUE;
      if (check_optarg('a', remaining))
        password_prompt_search_string = mysavestring(optarg);
      break;
    case 'A':	ansi_colour_aware = TRUE; break;
    case 'b':
      rl_basic_word_break_characters = add3strings("\r\n \t", optarg, "");
      opt_b = TRUE;
      break;
    case 'c':	complete_filenames = TRUE; break;
    case 'C':	opt_C = mysavestring(optarg); break;
    case 'd':
#ifdef DEBUG
      if (option_count > 1)
        myerror("-d or --debug option has to be the *first* rlwrap option");
      if (check_optarg('d', remaining))
        debug = atoi(optarg);
      else
        debug = DEBUG_DEFAULT;
#else
      myerror
        ("To use -d( for debugging), configure %s with --enable-debug and rebuild",
         program_name);
#endif
      break;

    case 'D': 
      history_duplicate_avoidance_policy=atoi(optarg);
      if (history_duplicate_avoidance_policy < 0 || history_duplicate_avoidance_policy > 2)
        myerror("%s option with illegal value %d, should be 0, 1 or 2",
                current_option('D', longindex), history_duplicate_avoidance_policy);
      break;
    case 'f':
      if (strncmp(optarg, ".", 10) == 0)
        feed_history_into_completion_list =  TRUE;
      else
        feed_file_into_completion_list(optarg);
      opt_f = TRUE;
      break;
    case 'F': myerror("The -F (--history-format) option is obsolete. Use -z \"history_format '%s'\" instead", optarg);
    case 'g': forget_regexp = mysavestring(optarg); match_regexp("just testing", forget_regexp, 1); break;
    case 'h':	usage(EXIT_SUCCESS);		/* will call exit() */
    case 'H':	history_filename = mysavestring(optarg); break;
    case 'i': 
      if (opt_f)
        myerror("-i option has to precede -f options");
      completion_is_case_sensitive = FALSE;
      break;
    case 'I':	pass_on_sigINT_as_sigTERM = TRUE; break;
    case 'l':	open_logfile(optarg); break;
    case 'n':	nowarn = TRUE; break;
    case 'm':
#ifndef HAVE_SYSTEM
      mywarn("the -m option doesn't work on this system");
#endif
      multiline_separator =
        (check_optarg('m', remaining) ? mysavestring(optarg) : " \\ ");
      break;
    case 'N': commands_children_not_wrapped = TRUE; break;
    case 'o': 
      one_shot_rlwrap = TRUE;
      impatient_prompt = FALSE;
      wait_before_prompt = 200;
      break;
    case 'O': prompt_regexp = mysavestring(optarg); match_regexp("just testing", prompt_regexp, 1); break;
    case 'p':
      colour_the_prompt = TRUE;
      initialise_colour_codes(check_optarg('p', remaining) ?
                              colour_name_to_ansi_code(optarg) :
                              colour_name_to_ansi_code("Red"));
      break;
    case 'P':
      pre_given = mysavestring(optarg);
      always_readline = TRUE; /* pre_given does not work well with transparent mode */
	
      break;
    case 'q': rl_basic_quote_characters = mysavestring(optarg); break;
    case 'r':	remember_for_completion = TRUE;	break;
    case 'R': renice = TRUE;	break;
    case 's':
      histsize = atoi(optarg);
      if (histsize < 0) {
        write_histfile = 0;
        histsize = -histsize;
      }
      break;
    case 'S': substitute_prompt =  mysavestring(optarg);break;
    case 't':	client_term_name=mysavestring(optarg);break;
#ifdef DEBUG
    case 'T':	test_terminal(); exit(EXIT_SUCCESS);
#endif
    case 'v':	printf("rlwrap %s\n",  VERSION); exit(EXIT_SUCCESS);
    case 'w':
      wait_before_prompt = atoi(optarg);
      if (wait_before_prompt < 0) {
        wait_before_prompt *= -1;
        impatient_prompt =  FALSE;
      }
      break;
    case 'z': filter_command = mysavestring(optarg);	break;
    case '?':
      assert(optind > 0);
      myerror("unrecognised option %s\ntry '%s --help' for more information",
              argv[optind-1], full_program_name);
    case ':':
      assert(optind > 0);
      myerror
        ("option %s requires an argument \ntry '%s --help' for more information",
         argv[optind-1], full_program_name);

    default:
      usage(EXIT_FAILURE);
    }
  }

  if (!complete_filenames && !opt_b) {	/* use / and . as default breaking characters whenever we don't complete filenames */
    rl_basic_word_break_characters =
      add2strings(rl_basic_word_break_characters, "/.");
  }

  
  if (optind >= argc) { /* rlwrap -a -b -c with no command specified */
    if (filter_command) { /* rlwrap -z filter with no command specified */
      mysignal(SIGALRM, &handle_sigALRM); /* needed for read_patiently2 */
      spawn_filter(filter_command);
      pass_through_filter(TAG_OUTPUT,""); /* ignore result but allow TAG_OUTPUT_OUT_OF_BAND */
      cleanup_rlwrap_and_exit(EXIT_SUCCESS);
    } else {
      usage(EXIT_FAILURE); 
    }
  }
  if (opt_C) {
    int countback = atoi(opt_C);	/* investigate whether -C option argument is numeric */

    if (countback > 0) {	/* e.g -C 1 or -C 12 */
      if (argc - countback < optind)	/* -C 666 */
	myerror("when using -C %d you need at least %d non-option arguments",
		countback, countback);
      else if (argv[argc - countback][0] == '-')	/* -C 2 perl -d blah.pl */
	myerror("the last argument minus %d appears to be an option!",
		countback);
      else {			/* -C 1 perl test.cgi */
	command_name = mysavestring(mybasename(argv[argc - countback]));

      }
    } else if (countback == 0) {	/* -C name1 name2 or -C 0 */
      if (opt_C[0] == '0' && opt_C[1] == '\0')	/* -C 0 */
	myerror("-C 0 makes no sense");
      else if (strlen(mybasename(opt_C)) != strlen(opt_C))	/* -C dir/name */
	myerror("-C option argument should not contain directory components");
      else if (opt_C[0] == '-')	/* -C -d  (?) */
	myerror("-C option needs argument");
      else			/* -C name */
	command_name = opt_C;
    } else {			/* -C -2 */
      myerror
	("-C option needs string or positive number as argument, perhaps you meant -C %d?",
	 -countback);
    }
  } else {			/* no -C option given, use command name */
    command_name = mysavestring(mybasename(argv[optind]));
  }
  assert(command_name != NULL);
  return command_name;
}
示例#28
0
static int
handle_hotkey2(int UNUSED(count), int hotkey, int ignore_history)
{
  char *prefix, *postfix, *history,  *histpos_as_string, *executing_keyseq;
  char *new_prefix, *new_postfix, *new_history, *new_histpos_as_string, *message; 
  char *filter_food, *filtered, **fragments,  *new_rl_line_buffer;
  int length, new_histpos;
  unsigned long int hash;

  static const unsigned int MAX_HISTPOS_DIGITS = 6; /* one million history items should suffice */

  
#ifdef HAVE_RL_EXECUTING_KEYSEQ /* i.e. if readline version is >= 6.3 */
  executing_keyseq = mysavestring(rl_executing_keyseq);
#else
  executing_keyseq = mysavestring("?");
  *executing_keyseq = hotkey; /* The filter will only get the *last* byte of the key sequence that triggered rl_handle_hotkey */ 
#endif

    
  DPRINTF3(DEBUG_READLINE, "hotkey press (ignore_history == %d): %x (%s)", ignore_history, hotkey, mangle_string_for_debug_log(executing_keyseq, MANGLE_LENGTH));

  if (hotkey == '\t') /* this would go horribly wrong with all the splitting on '\t' going on.... @@@ or pass key as a string e.g. "009" */
    myerror(FATAL | NOERRNO, "Sorry, you cannot use TAB as an hotkey in rlwrap");


  prefix = mysavestring(rl_line_buffer);
  prefix[rl_point] = '\0';                                     /* chop off just before cursor */
  postfix = mysavestring(rl_line_buffer + rl_point);

  if (ignore_history) {
    histpos_as_string = mysavestring("0");
    history = mysavestring("");
  } else {
    histpos_as_string = as_string(where_history());
    assert(strlen(histpos_as_string) <= MAX_HISTPOS_DIGITS);
    history = entire_history_as_one_string();
    hash = hash_multiple(2, history, histpos_as_string);
  }     

  /* filter_food = key + tab + prefix + tab + postfix + tab + history + tab + histpos  + '\0' */
  length = strlen(rl_line_buffer) + strlen(history) + MAX_HISTPOS_DIGITS + 5; 
  filter_food = mymalloc(length);   
  sprintf(filter_food, "%s\t%s\t%s\t%s\t%s", executing_keyseq, prefix, postfix, history, histpos_as_string); /* this is the format that the filter expects */

  /* let the filter filter ...! */
  filtered= pass_through_filter(TAG_HOTKEY, filter_food);
  
  /* OK, we now have to read back everything. There should be exactly 5 TAB-separated components*/
  fragments = split_on_single_char(filtered, '\t', 5);
  message               = fragments[0];
  new_prefix            = fragments[1];
  new_postfix           = fragments[2];
  new_history           = fragments[3];
  new_histpos_as_string = fragments[4];

  if (!ignore_history && hash_multiple(2, new_history, new_histpos_as_string) != hash) { /* history has been rewritten */
    char **linep, **history_lines = split_on_single_char(new_history, '\n', 0);
    DPRINTF3(DEBUG_READLINE, "hash=%lx, new_history is %d bytes long, histpos <%s>", hash, (int) strlen(new_history), new_histpos_as_string);
    clear_history();
    for (linep = history_lines; *linep; linep++) 
      add_history(*linep);
    new_histpos = my_atoi(new_histpos_as_string);
    history_set_pos(new_histpos);
    free_splitlist(history_lines);
  }
  new_rl_line_buffer = add2strings(new_prefix, new_postfix);

  if ( (length = strlen(new_rl_line_buffer)) > 0  &&  new_rl_line_buffer[length - 1] == '\n') {
    new_rl_line_buffer[length - 1] = '\0';
    rl_done = TRUE;
    return_key = (char) '\n';
    assert(strchr(new_rl_line_buffer, '\n') == NULL);
  }

  rl_delete_text(0, strlen(rl_line_buffer));
  rl_point = 0;
  rl_insert_text(new_rl_line_buffer);
  rl_point = strlen(new_rl_line_buffer);

  
  
  if (*message && *message != hotkey) {                          /* if message has been set (i.e. != hotkey) , and isn't empty: */ 
    message = append_and_free_old(mysavestring(message), " ");   /* put space (for readability) between the message and the input line .. */
    message_in_echo_area(message);                          /* .. then write it to echo area */
  }     

  clear_line();
  rl_on_new_line();
  rl_redisplay();
 

  free_splitlist(fragments);                                   /* this will free all the fragments (and the list itself) in one go  */
  free_multiple(prefix, postfix, filter_food, executing_keyseq, filtered, new_rl_line_buffer, history, histpos_as_string, FMEND);
  return 0;
}
示例#29
0
static void
line_handler(char *line)
{
  char *rewritten_line, *filtered_line;
  
  if (line == NULL) {           /* EOF on input, forward it  */
    DPRINTF1(DEBUG_READLINE, "EOF detected, writing character %d", term_eof);
    /* colour_the_prompt = FALSE; don't mess with the cruft that may come out of dying command @@@ but command may not die!*/
    write_EOF_to_master_pty();
  } else {
    if (*line &&                 /* forget empty lines  */
        redisplay &&             /* forget passwords    */
        !forget_line &&          /* forget lines entered by CTRL-O */
        !match_regexp(line, forget_regexp, TRUE)) {     /* forget lines (case-inseitively) matching -g option regexp */ 
      my_add_history(line);
    }
    forget_line = FALSE; /* until CTRL-O is used again */

    /* Time for a design decision: when we send the accepted line to the client command, it will most probably be echoed
       back. We have two choices: we leave the entered line on screen and suppress just enough of the clients output (I
       believe this is what rlfe does), or we remove the entered input (but not the prompt!) and let it be replaced by
       the echo.

       This is what we do; as a bonus, if the program doesn't echo, e.g. at a password prompt, the **** which has been
       put there by our homegrown_redisplay function will be removed (@@@is this what we want?)

       I think this method is more natural for multi-line input as well, (we will actually see our multi-line input as
       multiple lines) but not everyone will agree with that.
          
       O.K, we know for sure that cursor is at start of line. When clients output arrives, it will be printed at
       just the right place - but first we 'll erase the user input (as it may be about to be changed by the filter) */
    
    rl_delete_text(0, rl_end);  /* clear line  (after prompt) */
    rl_point = 0;
    my_redisplay();             /* and redisplay (this time without user input, cf the comments for the line_handler() function below) */

    rewritten_line =
      (multiline_separator ? 
       search_and_replace(multiline_separator, "\n", line, 0, NULL,
                          NULL) : mysavestring(line));


    
      
    if (redisplay)
      filtered_line = pass_through_filter(TAG_INPUT, rewritten_line);
    else { /* password, none of filters business */
      pass_through_filter(TAG_INPUT, "***"); /* filter some input anyway, to keep filter in sync (result is discarded).  */
      filtered_line = mysavestring(rewritten_line);
    }   
    free(rewritten_line);
        
    
    /* do we have to adapt clients winzsize now? */
    if (deferred_adapt_commands_window_size) {
      adapt_tty_winsize(STDIN_FILENO, master_pty_fd);
      kill(-command_pid, SIGWINCH);
      deferred_adapt_commands_window_size = FALSE;
    }


    
    /*OK, now feed line to underlying command and wait for the echo to come back */
    put_in_output_queue(filtered_line);
    DPRINTF2(DEBUG_READLINE, "putting %d bytes %s in output queue", (int) strlen(rewritten_line),
             mangle_string_for_debug_log(rewritten_line, 40));
    write_EOL_to_master_pty(return_key ? &return_key : "\n");

    accepted_lines++;
    free_foreign(line);         /* free_foreign because line was malloc'ed by readline, not by rlwrap */
    free(filtered_line);        /* we're done with them  */
        
    return_key = 0;
    within_line_edit = FALSE;
    if(!RL_ISSTATE(RL_STATE_MACROINPUT)) /* when called during playback of a multi-line macro, line_handler() will be called more 
                                            than once whithout re-entering main_loop(). If we'd remove it here, the second call
                                            would crash  */ 
       rl_callback_handler_remove();
    set_echo(FALSE);
    free(saved_rl_state.input_buffer);
    free(saved_rl_state.raw_prompt);
    free(saved_rl_state.cooked_prompt); 
    
    saved_rl_state.input_buffer = mysavestring("");
    saved_rl_state.raw_prompt = mysavestring("");
    saved_rl_state.cooked_prompt = NULL;
    saved_rl_state.point = 0;
    saved_rl_state.already_saved = TRUE;
    redisplay  = TRUE;

    if (one_shot_rlwrap)
      write_EOF_to_master_pty();
  }
}
示例#30
0
static void
my_homegrown_redisplay(int hide_passwords)
{
  static int line_start = 0;    /* at which position of prompt_plus_line does the printed line start? */
  static int line_extends_right = 0;
  static int line_extends_left = 0;
  static char *previous_line = NULL;
  
  
  int width = winsize.ws_col;
  int skip = max(1, min(width / 5, 10));        /* jumpscroll this many positions when cursor reaches edge of terminal */
  
  char *prompt_without_ignore_markers;
  int colourless_promptlen = colourless_strlen(rl_prompt, &prompt_without_ignore_markers,0);
  int promptlen = strlen(prompt_without_ignore_markers);
  int invisible_chars_in_prompt = promptlen - colourless_promptlen;
  char *prompt_plus_line = add2strings(prompt_without_ignore_markers, rl_line_buffer);
  char *new_line;
  int total_length = strlen(prompt_plus_line);
  int curpos = promptlen + rl_point; /* cursor position within prompt_plus_line */
  int i, printed_length,
    new_curpos,                    /* cursor position on screen */
    keep_old_line, vlinestart, printwidth, last_column;
  DPRINTF3(DEBUG_AD_HOC,"rl_prompt: <%s>, prompt_without_ignore_markers: <%s>,  prompt_plus_line: <%s>", rl_prompt, prompt_without_ignore_markers, prompt_plus_line);   

  /* In order to handle prompt with colour we either print the whole prompt, or start past it:
     starting in the middle is too difficult (i.e. I am too lazy) to get it right.
     We use a "virtual line start" vlinestart, which is the number of invisible chars in prompt in the former case, or
     linestart in the latter (which then must be >= strlen(prompt))

     At all times (before redisplay and after) the following is true:
     - the cursor is at column (curpos - vlinestart) (may be < 0 or > width)
     - the character under the cursor is prompt_plus_line[curpos]
     - the character at column 0 is prompt_plus_line[linestart]
     - the last column is at <number of printed visible or invisible chars> - vlinestart
     
     the goal of this function is to display (part of) prompt_plus_line such
     that the cursor is visible again */
     
  
  if (hide_passwords)
    for (i = promptlen; i < total_length; i++)
      prompt_plus_line[i] = '*';        /* hide a pasword by making user input unreadable  */


  if (rl_point == 0)            /* (re)set  at program start and after accept_line (where rl_point is zeroed) */
    line_start = 0;
  assert(line_start == 0 || line_start >= promptlen); /* the line *never* starts in the middle of the prompt (too complicated to handle)*/
  vlinestart = (line_start > promptlen ? line_start : invisible_chars_in_prompt); 
  

  if (curpos - vlinestart > width - line_extends_right) /* cursor falls off right edge ?   */
    vlinestart = (curpos - width + line_extends_right) + skip;  /* jumpscroll left                 */

  else if (curpos < vlinestart + line_extends_left) {   /* cursor falls off left edge ?    */
    if (curpos == total_length) /* .. but still at end of line?    */
      vlinestart = max(0, total_length - width);        /* .. try to display entire line   */
    else                        /* in not at end of line ..        */
      vlinestart = curpos - line_extends_left - skip; /* ... jumpscroll right ..         */
  }     
  if (vlinestart <= invisible_chars_in_prompt) {
    line_start = 0;             /* ... but not past start of line! */
    vlinestart = invisible_chars_in_prompt;
  } else if (vlinestart > invisible_chars_in_prompt && vlinestart <= promptlen) {
    line_start = vlinestart = promptlen;
  } else {
    line_start = vlinestart;
  }

  printwidth = (line_start > 0 ? width : width + invisible_chars_in_prompt);
  printed_length = min(printwidth, total_length - line_start);  /* never print more than width     */
  last_column = printed_length - vlinestart;


  /* some invariants :     0 <= line_start <= curpos <= line_start + printed_length <= total_length */
  /* these are interesting:   ^                                                      ^              */

  assert(0 <= line_start);
  assert(line_start <= curpos);
  assert(curpos <= line_start + printed_length);        /* <=, rather than <, as cursor may be past eol   */
  assert(line_start + printed_length <= total_length);


  new_line = prompt_plus_line + line_start;
  new_line[printed_length] = '\0';
  new_curpos = curpos - vlinestart;

  /* indicate whether line extends past right or left edge  (i.e. whether the "interesting
     inequalities marked ^ above are really unequal) */

  line_extends_left = (line_start > 0 ? 1 : 0);
  line_extends_right = (total_length - vlinestart > width ? 1 : 0);
  if (line_extends_left)
    new_line[0] = '<';
  if (line_extends_right)
    new_line[printwidth - 1] = '>';

  

  keep_old_line = FALSE;
  if (term_cursor_hpos) {
    if (previous_line && strcmp(new_line, previous_line) == 0) {
      keep_old_line = TRUE;
    } else {
      if (previous_line)
        free(previous_line);
      previous_line = mysavestring(new_line);
    }
  }
  /* DPRINTF2(DEBUG_AD_HOC, "keep_old_line=%d, new_line=<%s>", keep_old_line, new_line); */
  /* keep_old_line = TRUE; */
  if (!keep_old_line) {
    clear_line();
    cr();
    write_patiently(STDOUT_FILENO, new_line, printed_length, "to stdout");
  }
  
  assert(term_cursor_hpos || !keep_old_line);   /* if we cannot position cursor, we must have reprinted ... */

  if (term_cursor_hpos)
    cursor_hpos(new_curpos);
  else                          /* ... so we know we're 1 past last position on line */
    backspace(last_column - new_curpos);
  free(prompt_plus_line);
  free(prompt_without_ignore_markers);
}