View of xos/usr/lib/readline/output.c


XOS | Parent Directory | View | Download

/* Copyright (C) 2008  Emmanuel Varoquaux
 
   This file is part of XOS.
 
   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.
 
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
 
   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
#include "output.h"
 
#include "tty.h"
#include "terminal.h"
#include "prompt.h"
#include "line.h"
#include "misc.h"
#include "xmalloc.h"
 
#include <readline/rlvars.h>
#include <string.h>
#include <stdlib.h>
 
#define min(a, b) ((a) < (b) ? (a) : (b))
 
/* position dans la ligne de saisie */
struct position_struct {
  int row;
  int col;
};
 
static struct position_struct start_pos; /* position du debut de la saisie */
static struct position_struct cursor_pos; /* position du curseur */
 
/* Tampon */
 
#define OUTBUF_INITIAL_SIZE 1024
 
/* tampon pour les sorties */
static char *outbuf;
static size_t outbuf_size;
static size_t outbuf_pos;
 
/* Agrandit le tampon si necessaire pour obtenir incr cases libres
 * supplementaires.
 * Si le tampon est non vide, la derniere case est toujours libre. */
static void grow_outbuf(size_t incr)
{
  size_t min_size;
  size_t new_size;
 
  if (incr == 0)
    return;
  min_size = outbuf_pos + incr + 1;
  if (min_size <= outbuf_size)
    return;
  new_size = outbuf_size ? : OUTBUF_INITIAL_SIZE;
  while (min_size > new_size)
    new_size *= 2;
  outbuf = xrealloc(outbuf, new_size);
  outbuf_size = new_size;
}
 
static void outbuf_init()
{
  outbuf = NULL;
  outbuf_size = 0;
  outbuf_pos = 0;
}
 
static void outbuf_putc(char c)
{
  grow_outbuf(1);
  outbuf[outbuf_pos++] = c;
}
 
static void outbuf_puts(const char *s)
{
  size_t len;
 
  len = strlen(s);
  grow_outbuf(len);
  memcpy(&outbuf[outbuf_pos], s, len);
  outbuf_pos += len;
}
 
static void outbuf_flush()
{
  if (outbuf_pos == 0) /* en particulier si outbuf == NULL ou outbuf_size == 0 */
    return;
  outbuf[outbuf_pos] = '\0'; /* il reste toujours au moins une case libre */
  fputs(outbuf, rl_outstream);
  fflush(rl_outstream);
  outbuf_pos = 0;
}
 
static void free_outbuf()
{
  free(outbuf);
}
 
/* Fonctions elementaires d'affichage */
 
static void put_bs()
{
  outbuf_putc('\b');
  cursor_pos.col--;
}
 
static void put_lf()
{
  outbuf_putc('\n');
  cursor_pos.row++;
}
 
static void put_cr()
{
  outbuf_putc('\r');
  cursor_pos.col = 0;
}
 
static void put_print(char c)
{
  outbuf_putc(c);
  cursor_pos.col++;
}
 
static void put_cursor_up_seq(int n)
{
  outbuf_puts(cursor_up_seq(n));
  cursor_pos.row -= n;
}
 
static void put_cursor_down_seq(int n)
{
  outbuf_puts(cursor_down_seq(n));
  cursor_pos.row += n;
}
 
static void put_cursor_forward_seq(int n)
{
  outbuf_puts(cursor_forward_seq(n));
  cursor_pos.col += n;
}
 
static void put_cursor_backward_seq(int n)
{
  outbuf_puts(cursor_backward_seq(n));
  cursor_pos.col -= n;
}
 
static void wrap(char c)
{
  outbuf_putc(c);
  cursor_pos.row++;
  put_cr();
}
 
/* Fonctions evoluees d'affichage */
 
static void print_prompt()
{
  if (!local_prompt)
    return;
  if (local_prompt_prefix)
    outbuf_puts(local_prompt_prefix);
  outbuf_puts(local_prompt);
  cursor_pos.row = (prompt_visible_length - 1) / screenwidth;
  cursor_pos.col = (prompt_visible_length - 1) % screenwidth + 1;
}
 
static void print_tab()
{
  int newcol;
 
  if (cursor_pos.col == screenwidth)
    wrap(' ');
  newcol = (cursor_pos.col | 7) + 1;
  do
    put_print(' ');
  while (cursor_pos.col != screenwidth && cursor_pos.col != newcol);
}
 
static void print_linefeed()
{
  if (cursor_pos.col == screenwidth)
    wrap('\n');
  outbuf_puts(erase_end_of_line_seq);
  put_lf();
  put_cr();
}
 
static void print_print(char c)
{
  if (cursor_pos.col == screenwidth)
    wrap(c);
  put_print(c);
}
 
static void print_char(char c)
{
  if (c == '\t')
    print_tab();
  else if (c == '\n')
    print_linefeed();
  else if (ctrl_char(c) || c == RUBOUT) {
    print_print('^');
    print_print(ctrl_char(c) ? unctrl(c) : '?');
  }
  else
    print_print(c);
}
 
static void print_string(const char *s)
{
  while (*s)
    print_char(*s++);
}
 
static void erase(int n)
{
  while (n) {
    print_char(' ');
    n--;
  }
}
 
static void wrap_if_needed()
{
  if (cursor_pos.col == screenwidth)
    wrap(' ');
}
 
static void move_cursor_to(struct position_struct pos)
{
  if (cursor_pos.row < pos.row)
    put_cursor_down_seq(pos.row - cursor_pos.row);
  else if (cursor_pos.row > pos.row)
    put_cursor_up_seq(cursor_pos.row - pos.row);
  if (cursor_pos.col < pos.col)
    put_cursor_forward_seq(pos.col - cursor_pos.col);
  else if (cursor_pos.col > pos.col) {
    if (pos.col == 0)
      put_cr();
    else if (pos.col == cursor_pos.col - 1)
      put_bs();
    else
      put_cursor_backward_seq(cursor_pos.col - pos.col);
  }
}
 
static void move_cursor_to_origin()
{
  if (cursor_pos.row > 0)
    put_cursor_up_seq(cursor_pos.row);
  put_cr();
}
 
/* Fonctions sur les positions */
 
static void pos_wrap(struct position_struct *pos)
{
  pos->row++;
  pos->col = 0;
}
 
static void pos_move_forward_tab(struct position_struct *pos)
{
  if (pos->col == screenwidth)
    pos_wrap(pos);
  pos->col = min((pos->col | 7) + 1, screenwidth);
}
 
static void pos_move_forward_linefeed(struct position_struct *pos)
{
  if (pos->col == screenwidth)
    pos_wrap(pos);
  pos->row++;
  pos->col = 0;
}
 
static void pos_move_forward_print(struct position_struct *pos)
{
  if (pos->col == screenwidth)
    pos_wrap(pos);
  pos->col++;
}
 
static void pos_move_forward_char(struct position_struct *pos, char c)
{
  if (c == '\t')
    pos_move_forward_tab(pos);
  else if (c == '\n')
    pos_move_forward_linefeed(pos);
  else if (ctrl_char(c) || c == RUBOUT) {
    pos_move_forward_print(pos);
    pos_move_forward_print(pos);
  }
  else
    pos_move_forward_print(pos);
}
 
static void pos_move_forward_string(struct position_struct *pos, const char *s)
{
  while (*s)
    pos_move_forward_char(pos, *s++);
}
 
static void pos_move_forward_region(struct position_struct *pos, const char *start, const char *end)
{
  while (start < end)
    pos_move_forward_char(pos, *start++);
}
 
static void pos_wrap_if_needed(struct position_struct *pos)
{
  if (pos->col == screenwidth)
    pos_wrap(pos);
}
 
static int pos_coincide(struct position_struct pos1, struct position_struct pos2)
{
  pos_wrap_if_needed(&pos1);
  pos_wrap_if_needed(&pos2);
  return pos1.row == pos2.row && pos1.col == pos2.col;
}
 
/* Commandes d'affichage */
 
static void print_line()
{
  struct position_struct pos;
 
  print_prompt();
  print_string(line);
  wrap_if_needed();
  pos = start_pos;
  pos_move_forward_region(&pos, line, line + point);
  pos_wrap_if_needed(&pos);
  move_cursor_to(pos);
}
 
static void move_cursor_to_point(int to_point)
{
  int from_point;
  struct position_struct pos;
 
  if (to_point >= point) {
    from_point = point;
    pos = cursor_pos;
  }
  else {
    from_point = 0;
    pos = start_pos;
  }
  pos_move_forward_region(&pos, line + from_point, line + to_point);
  pos_wrap_if_needed(&pos);
  move_cursor_to(pos);
}
 
/* Deplace le curseur au point start et positionne pos a la position du point
 * end. */
static void select_region(int start, int end, struct position_struct *pos)
{
  if (start != point)
    move_cursor_to_point(start);
  *pos = cursor_pos;
  if (end != start)
    pos_move_forward_region(pos, line + start, line + end);
}
 
/* Reecrit la ligne a partir du point start a la position du curseur. oldpos
 * est la position ou la ligne a partir du point start etait precedemment
 * ecrite. Le curseur est en suite deplace a la position pos. */
static void reprint(int start, struct position_struct oldpos, struct position_struct pos)
{
  char c;
 
  while ((c = line[start]) && !pos_coincide(cursor_pos, oldpos)) {
    print_char(c);
    pos_move_forward_char(&oldpos, c);
    start++;
  }
  wrap_if_needed();
  pos_wrap_if_needed(&oldpos);
  while (cursor_pos.row < oldpos.row) {
    outbuf_puts(erase_end_of_line_seq);
    put_lf();
    put_cr();
  }
  if (cursor_pos.row == oldpos.row)
    while (cursor_pos.col < oldpos.col)
      print_print(' ');
  pos_wrap_if_needed(&pos);
  move_cursor_to(pos);
}
 
void begin_output(const char *prompt)
{
  set_prompt(prompt);
  outbuf_init();
  print_prompt();
  if (tty_echo)
    wrap_if_needed();
  outbuf_flush();
  start_pos = cursor_pos;
}
 
void output_replace_region_with_char(int start, int end, char c)
{
  struct position_struct pos;
 
  if (!tty_echo)
    return;
  select_region(start, end, &pos);
  print_char(c);
  reprint(end, pos, cursor_pos);
  outbuf_flush();
}
 
void output_replace_region_with_text(int start, int end, const char *text)
{
  struct position_struct pos;
 
  if (!tty_echo)
    return;
  select_region(start, end, &pos);
  print_string(text);
  reprint(end, pos, cursor_pos);
  outbuf_flush();
}
 
void output_erase_region(int start, int end)
{
  struct position_struct pos1, pos2;
 
  if (!tty_echo)
    return;
  select_region(start, end, &pos1);
  pos2 = cursor_pos;
  erase(end - start);
  reprint(end, pos1, pos2);
  outbuf_flush();
}
 
void output_delete_region(int start, int end)
{
  struct position_struct pos;
 
  if (!tty_echo)
    return;
  select_region(start, end, &pos);
  reprint(end, pos, cursor_pos);
  outbuf_flush();
}
 
void output_move_cursor(int newpoint)
{
  if (!tty_echo)
    return;
  move_cursor_to_point(newpoint);
  outbuf_flush();
}
 
void output_clear_line_and_reprint()
{
  if (tty_echo)
    move_cursor_to_origin();
  outbuf_puts(erase_end_of_line_seq);
  outbuf_flush();
  if (!tty_echo)
    return;
  print_line();
  outbuf_flush();
}
 
void output_clear_screen_and_reprint()
{
  outbuf_puts(cursor_home_seq);
  outbuf_puts(erase_screen_seq);
  outbuf_flush();
  if (!tty_echo)
    return;
  print_line();
  outbuf_flush();
}
 
void output_new_line()
{
  struct position_struct pos;
 
  if (!tty_echo)
    return;
  pos = cursor_pos;
  pos_move_forward_string(&pos, line + point);
  pos_wrap_if_needed(&pos);
  pos.col = cursor_pos.col; /* mouvement uniquement vertical */
  move_cursor_to(pos);
  outbuf_puts("\r\n");
  outbuf_flush();
}
 
void end_output()
{
  free_outbuf();
  free_prompt();
}
 
void ding()
{
  if (!tty_echo)
    return;
  ring_bell();
}