Blame buffer.c

Packit Bot a3ac83
/* buffer.c: scratch-file buffer routines for the ed line editor. */
Packit Bot a3ac83
/*  GNU ed - The GNU line editor.
Packit Bot a3ac83
    Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
Packit Bot a3ac83
    Copyright (C) 2006-2017 Antonio Diaz Diaz.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is free software: you can redistribute it and/or modify
Packit Bot a3ac83
    it under the terms of the GNU General Public License as published by
Packit Bot a3ac83
    the Free Software Foundation, either version 2 of the License, or
Packit Bot a3ac83
    (at your option) any later version.
Packit Bot a3ac83
Packit Bot a3ac83
    This program is distributed in the hope that it will be useful,
Packit Bot a3ac83
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Bot a3ac83
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Bot a3ac83
    GNU General Public License for more details.
Packit Bot a3ac83
Packit Bot a3ac83
    You should have received a copy of the GNU General Public License
Packit Bot a3ac83
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Bot a3ac83
*/
Packit Bot a3ac83
Packit Bot a3ac83
#include <errno.h>
Packit Bot a3ac83
#include <limits.h>
Packit Bot a3ac83
#include <stdio.h>
Packit Bot a3ac83
#include <stdlib.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
#include <unistd.h>
Packit Bot a3ac83
#include <sys/file.h>
Packit Bot a3ac83
#include <sys/stat.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "ed.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static int current_addr_ = 0;	/* current address in editor buffer */
Packit Bot a3ac83
static int last_addr_ = 0;	/* last address in editor buffer */
Packit Bot a3ac83
static bool isbinary_ = false;	/* if set, buffer contains ASCII NULs */
Packit Bot a3ac83
static bool modified_ = false;	/* if set, buffer modified since last write */
Packit Bot a3ac83
Packit Bot a3ac83
static bool seek_write = false;	/* seek before writing */
Packit Bot a3ac83
static FILE * sfp = 0;		/* scratch file pointer */
Packit Bot a3ac83
static long sfpos = 0;		/* scratch file position */
Packit Bot a3ac83
static line_t buffer_head;	/* editor buffer ( linked list of line_t )*/
Packit Bot a3ac83
static line_t yank_buffer_head;
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int current_addr( void ) { return current_addr_; }
Packit Bot a3ac83
int inc_current_addr( void )
Packit Bot a3ac83
  { if( ++current_addr_ > last_addr_ ) current_addr_ = last_addr_;
Packit Bot a3ac83
    return current_addr_; }
Packit Bot a3ac83
void set_current_addr( const int addr ) { current_addr_ = addr; }
Packit Bot a3ac83
Packit Bot a3ac83
int last_addr( void ) { return last_addr_; }
Packit Bot a3ac83
Packit Bot a3ac83
bool isbinary( void ) { return isbinary_; }
Packit Bot a3ac83
void set_binary( void ) { isbinary_ = true; }
Packit Bot a3ac83
Packit Bot a3ac83
bool modified( void ) { return modified_; }
Packit Bot a3ac83
void set_modified( const bool m ) { modified_ = m; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int inc_addr( int addr )
Packit Bot a3ac83
  { if( ++addr > last_addr_ ) addr = 0; return addr; }
Packit Bot a3ac83
Packit Bot a3ac83
int dec_addr( int addr )
Packit Bot a3ac83
  { if( --addr < 0 ) addr = last_addr_; return addr; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* link next and previous nodes */
Packit Bot a3ac83
static void link_nodes( line_t * const prev, line_t * const next )
Packit Bot a3ac83
  { prev->q_forw = next; next->q_back = prev; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* insert line node into circular queue after previous */
Packit Bot a3ac83
static void insert_node( line_t * const lp, line_t * const prev )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  link_nodes( lp, prev->q_forw );
Packit Bot a3ac83
  link_nodes( prev, lp );
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* add a line node in the editor buffer after the given line */
Packit Bot a3ac83
static void add_line_node( line_t * const lp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * const prev = search_line_node( current_addr_ );
Packit Bot a3ac83
  insert_node( lp, prev );
Packit Bot a3ac83
  ++current_addr_;
Packit Bot a3ac83
  ++last_addr_;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return a pointer to a copy of a line node, or to a new node if lp == 0 */
Packit Bot a3ac83
static line_t * dup_line_node( line_t * const lp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * const p = (line_t *) malloc( sizeof (line_t) );
Packit Bot a3ac83
  if( !p )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( 0, errno );
Packit Bot a3ac83
    set_error_msg( "Memory exhausted" );
Packit Bot a3ac83
    return 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( lp ) { p->pos = lp->pos; p->len = lp->len; }
Packit Bot a3ac83
  return p;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Insert text from stdin (or from command buffer if global) to after
Packit Bot a3ac83
   line n; stop when either a single period is read or EOF.
Packit Bot a3ac83
   Returns false if insertion fails. */
Packit Bot a3ac83
bool append_lines( const char ** const ibufpp, const int addr,
Packit Bot a3ac83
                   bool insert, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int size = 0;
Packit Bot a3ac83
  undo_t * up = 0;
Packit Bot a3ac83
  current_addr_ = addr;
Packit Bot a3ac83
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( !isglobal )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      *ibufpp = get_stdin_line( &size );
Packit Bot a3ac83
      if( !*ibufpp ) return false;			/* error */
Packit Bot a3ac83
      if( size <= 0 ) return true;			/* EOF */
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( !**ibufpp ) return true;
Packit Bot a3ac83
      for( size = 0; (*ibufpp)[size++] != '\n'; ) ;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    if( size == 2 && **ibufpp == '.' ) { *ibufpp += size; return true; }
Packit Bot a3ac83
    disable_interrupts();
Packit Bot a3ac83
    if( insert ) { insert = false; if( current_addr_ > 0 ) --current_addr_; }
Packit Bot a3ac83
    if( !put_sbuf_line( *ibufpp, size ) )
Packit Bot a3ac83
      { enable_interrupts(); return false; }
Packit Bot a3ac83
    if( up ) up->tail = search_line_node( current_addr_ );
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      up = push_undo_atom( UADD, current_addr_, current_addr_ );
Packit Bot a3ac83
      if( !up ) { enable_interrupts(); return false; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    *ibufpp += size;
Packit Bot a3ac83
    modified_ = true;
Packit Bot a3ac83
    enable_interrupts();
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static void clear_yank_buffer( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * lp = yank_buffer_head.q_forw;
Packit Bot a3ac83
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  while( lp != &yank_buffer_head )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    line_t * const p = lp->q_forw;
Packit Bot a3ac83
    link_nodes( lp->q_back, lp->q_forw );
Packit Bot a3ac83
    free( lp );
Packit Bot a3ac83
    lp = p;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* close scratch file */
Packit Bot a3ac83
bool close_sbuf( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  clear_yank_buffer();
Packit Bot a3ac83
  clear_undo_stack();
Packit Bot a3ac83
  if( sfp )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( fclose( sfp ) != 0 )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      show_strerror( 0, errno );
Packit Bot a3ac83
      set_error_msg( "Cannot close temp file" );
Packit Bot a3ac83
      return false;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    sfp = 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  sfpos = 0;
Packit Bot a3ac83
  seek_write = false;
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* copy a range of lines; return false if error */
Packit Bot a3ac83
bool copy_lines( const int first_addr, const int second_addr, const int addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t *lp, *np = search_line_node( first_addr );
Packit Bot a3ac83
  undo_t * up = 0;
Packit Bot a3ac83
  int n = second_addr - first_addr + 1;
Packit Bot a3ac83
  int m = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  current_addr_ = addr;
Packit Bot a3ac83
  if( addr >= first_addr && addr < second_addr )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    n = addr - first_addr + 1;
Packit Bot a3ac83
    m = second_addr - addr;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  for( ; n > 0; n = m, m = 0, np = search_line_node( current_addr_ + 1 ) )
Packit Bot a3ac83
    for( ; n-- > 0; np = np->q_forw )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      disable_interrupts();
Packit Bot a3ac83
      lp = dup_line_node( np );
Packit Bot a3ac83
      if( !lp ) { enable_interrupts(); return false; }
Packit Bot a3ac83
      add_line_node( lp );
Packit Bot a3ac83
      if( up ) up->tail = lp;
Packit Bot a3ac83
      else
Packit Bot a3ac83
        {
Packit Bot a3ac83
        up = push_undo_atom( UADD, current_addr_, current_addr_ );
Packit Bot a3ac83
        if( !up ) { enable_interrupts(); return false; }
Packit Bot a3ac83
        }
Packit Bot a3ac83
      modified_ = true;
Packit Bot a3ac83
      enable_interrupts();
Packit Bot a3ac83
      }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* delete a range of lines */
Packit Bot a3ac83
bool delete_lines( const int from, const int to, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t *n, *p;
Packit Bot a3ac83
Packit Bot a3ac83
  if( !yank_lines( from, to ) ) return false;
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  if( !push_undo_atom( UDEL, from, to ) )
Packit Bot a3ac83
    { enable_interrupts(); return false; }
Packit Bot a3ac83
  n = search_line_node( inc_addr( to ) );
Packit Bot a3ac83
  p = search_line_node( from - 1 );	/* this search_line_node last! */
Packit Bot a3ac83
  if( isglobal ) unset_active_nodes( p->q_forw, n );
Packit Bot a3ac83
  link_nodes( p, n );
Packit Bot a3ac83
  last_addr_ -= to - from + 1;
Packit Bot a3ac83
  current_addr_ = min( from, last_addr_ );
Packit Bot a3ac83
  modified_ = true;
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return line number of pointer */
Packit Bot a3ac83
int get_line_node_addr( const line_t * const lp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const line_t * p = &buffer_head;
Packit Bot a3ac83
  int addr = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  while( p != lp && ( p = p->q_forw ) != &buffer_head ) ++addr;
Packit Bot a3ac83
  if( addr && p == &buffer_head )
Packit Bot a3ac83
    { set_error_msg( "Invalid address" ); return -1; }
Packit Bot a3ac83
  return addr;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* get a line of text from the scratch file; return pointer to the text */
Packit Bot a3ac83
char * get_sbuf_line( const line_t * const lp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  int len;
Packit Bot a3ac83
Packit Bot a3ac83
  if( lp == &buffer_head ) return 0;
Packit Bot a3ac83
  seek_write = true;			/* force seek on write */
Packit Bot a3ac83
  /* out of position */
Packit Bot a3ac83
  if( sfpos != lp->pos )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    sfpos = lp->pos;
Packit Bot a3ac83
    if( fseek( sfp, sfpos, SEEK_SET ) != 0 )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      show_strerror( 0, errno );
Packit Bot a3ac83
      set_error_msg( "Cannot seek temp file" );
Packit Bot a3ac83
      return 0;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  len = lp->len;
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
Packit Bot a3ac83
  if( (int)fread( buf, 1, len, sfp ) != len )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( 0, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot read temp file" );
Packit Bot a3ac83
    return 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  sfpos += len;		/* update file position */
Packit Bot a3ac83
  buf[len] = 0;
Packit Bot a3ac83
  return buf;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* open scratch buffer; initialize line queue */
Packit Bot a3ac83
bool init_buffers( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  /* Read stdin one character at a time to avoid i/o contention
Packit Bot a3ac83
     with shell escapes invoked by nonterminal input, e.g.,
Packit Bot a3ac83
     ed - <
Packit Bot a3ac83
     !cat
Packit Bot a3ac83
     hello, world
Packit Bot a3ac83
     EOF */
Packit Bot a3ac83
  setvbuf( stdin, 0, _IONBF, 0 );
Packit Bot a3ac83
  if( !open_sbuf() ) return false;
Packit Bot a3ac83
  link_nodes( &buffer_head, &buffer_head );
Packit Bot a3ac83
  link_nodes( &yank_buffer_head, &yank_buffer_head );
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* replace a range of lines with the joined text of those lines */
Packit Bot a3ac83
bool join_lines( const int from, const int to, const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  int size = 0;
Packit Bot a3ac83
  line_t * const ep = search_line_node( inc_addr( to ) );
Packit Bot a3ac83
  line_t * bp = search_line_node( from );
Packit Bot a3ac83
Packit Bot a3ac83
  while( bp != ep )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const char * const s = get_sbuf_line( bp );
Packit Bot a3ac83
    if( !s || !resize_buffer( &buf, &bufsz, size + bp->len ) ) return false;
Packit Bot a3ac83
    memcpy( buf + size, s, bp->len );
Packit Bot a3ac83
    size += bp->len;
Packit Bot a3ac83
    bp = bp->q_forw;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, size + 2 ) ) return false;
Packit Bot a3ac83
  buf[size++] = '\n';
Packit Bot a3ac83
  buf[size++] = 0;
Packit Bot a3ac83
  if( !delete_lines( from, to, isglobal ) ) return false;
Packit Bot a3ac83
  current_addr_ = from - 1;
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  if( !put_sbuf_line( buf, size ) ||
Packit Bot a3ac83
      !push_undo_atom( UADD, current_addr_, current_addr_ ) )
Packit Bot a3ac83
    { enable_interrupts(); return false; }
Packit Bot a3ac83
  modified_ = true;
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* move a range of lines */
Packit Bot a3ac83
bool move_lines( const int first_addr, const int second_addr, const int addr,
Packit Bot a3ac83
                 const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t *b1, *a1, *b2, *a2;
Packit Bot a3ac83
  int n = inc_addr( second_addr );
Packit Bot a3ac83
  int p = first_addr - 1;
Packit Bot a3ac83
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  if( addr == first_addr - 1 || addr == second_addr )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    a2 = search_line_node( n );
Packit Bot a3ac83
    b2 = search_line_node( p );
Packit Bot a3ac83
    current_addr_ = second_addr;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  else if( !push_undo_atom( UMOV, p, n ) ||
Packit Bot a3ac83
           !push_undo_atom( UMOV, addr, inc_addr( addr ) ) )
Packit Bot a3ac83
    { enable_interrupts(); return false; }
Packit Bot a3ac83
  else
Packit Bot a3ac83
    {
Packit Bot a3ac83
    a1 = search_line_node( n );
Packit Bot a3ac83
    if( addr < first_addr )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      b1 = search_line_node( p );
Packit Bot a3ac83
      b2 = search_line_node( addr );	/* this search_line_node last! */
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      b2 = search_line_node( addr );
Packit Bot a3ac83
      b1 = search_line_node( p );	/* this search_line_node last! */
Packit Bot a3ac83
      }
Packit Bot a3ac83
    a2 = b2->q_forw;
Packit Bot a3ac83
    link_nodes( b2, b1->q_forw );
Packit Bot a3ac83
    link_nodes( a1->q_back, a2 );
Packit Bot a3ac83
    link_nodes( b1, a1 );
Packit Bot a3ac83
    current_addr_ = addr + ( ( addr < first_addr ) ?
Packit Bot a3ac83
                           second_addr - first_addr + 1 : 0 );
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( isglobal ) unset_active_nodes( b2->q_forw, a2 );
Packit Bot a3ac83
  modified_ = true;
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* open scratch file */
Packit Bot a3ac83
bool open_sbuf( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  isbinary_ = false; reset_unterminated_line();
Packit Bot a3ac83
  sfp = tmpfile();
Packit Bot a3ac83
  if( !sfp )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( 0, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot open temp file" );
Packit Bot a3ac83
    return false;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
int path_max( const char * filename )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  long result;
Packit Bot a3ac83
  if( !filename ) filename = "/";
Packit Bot a3ac83
  errno = 0;
Packit Bot a3ac83
  result = pathconf( filename, _PC_PATH_MAX );
Packit Bot a3ac83
  if( result < 0 ) { if( errno ) result = 256; else result = 1024; }
Packit Bot a3ac83
  else if( result < 256 ) result = 256;
Packit Bot a3ac83
  return result;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* append lines from the yank buffer */
Packit Bot a3ac83
bool put_lines( const int addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  undo_t * up = 0;
Packit Bot a3ac83
  line_t *p, *lp = yank_buffer_head.q_forw;
Packit Bot a3ac83
Packit Bot a3ac83
  if( lp == &yank_buffer_head )
Packit Bot a3ac83
    { set_error_msg( "Nothing to put" ); return false; }
Packit Bot a3ac83
  current_addr_ = addr;
Packit Bot a3ac83
  while( lp != &yank_buffer_head )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    disable_interrupts();
Packit Bot a3ac83
    p = dup_line_node( lp );
Packit Bot a3ac83
    if( !p ) { enable_interrupts(); return false; }
Packit Bot a3ac83
    add_line_node( p );
Packit Bot a3ac83
    if( up ) up->tail = p;
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      up = push_undo_atom( UADD, current_addr_, current_addr_ );
Packit Bot a3ac83
      if( !up ) { enable_interrupts(); return false; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    modified_ = true;
Packit Bot a3ac83
    lp = lp->q_forw;
Packit Bot a3ac83
    enable_interrupts();
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* write a line of text to the scratch file and add a line node to the
Packit Bot a3ac83
   editor buffer; return a pointer to the end of the text, or 0 if error */
Packit Bot a3ac83
const char * put_sbuf_line( const char * const buf, const int size )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const char * const p = (const char *) memchr( buf, '\n', size );
Packit Bot a3ac83
  line_t * lp;
Packit Bot a3ac83
  int len;
Packit Bot a3ac83
Packit Bot a3ac83
  if( !p ) { set_error_msg( "Line too long" ); return 0; }
Packit Bot a3ac83
  len = p - buf;
Packit Bot a3ac83
  /* out of position */
Packit Bot a3ac83
  if( seek_write )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( fseek( sfp, 0L, SEEK_END ) != 0 )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      show_strerror( 0, errno );
Packit Bot a3ac83
      set_error_msg( "Cannot seek temp file" );
Packit Bot a3ac83
      return 0;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    sfpos = ftell( sfp );
Packit Bot a3ac83
    seek_write = false;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( (int)fwrite( buf, 1, len, sfp ) != len )	/* assert: interrupts disabled */
Packit Bot a3ac83
    {
Packit Bot a3ac83
    sfpos = -1;
Packit Bot a3ac83
    show_strerror( 0, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot write temp file" );
Packit Bot a3ac83
    return 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  lp = dup_line_node( 0 );
Packit Bot a3ac83
  if( !lp ) return 0;
Packit Bot a3ac83
  lp->pos = sfpos; lp->len = len;
Packit Bot a3ac83
  add_line_node( lp );
Packit Bot a3ac83
  sfpos += len;				/* update file position */
Packit Bot a3ac83
  return p + 1;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return pointer to a line node in the editor buffer */
Packit Bot a3ac83
line_t * search_line_node( const int addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static line_t * lp = &buffer_head;
Packit Bot a3ac83
  static int o_addr = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  if( o_addr < addr )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( o_addr + last_addr_ >= 2 * addr )
Packit Bot a3ac83
      while( o_addr < addr ) { ++o_addr; lp = lp->q_forw; }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      lp = buffer_head.q_back; o_addr = last_addr_;
Packit Bot a3ac83
      while( o_addr > addr ) { --o_addr; lp = lp->q_back; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  else if( o_addr <= 2 * addr )
Packit Bot a3ac83
    while( o_addr > addr ) { --o_addr; lp = lp->q_back; }
Packit Bot a3ac83
  else
Packit Bot a3ac83
    { lp = &buffer_head; o_addr = 0;
Packit Bot a3ac83
      while( o_addr < addr ) { ++o_addr; lp = lp->q_forw; } }
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return lp;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* copy a range of lines to the cut buffer */
Packit Bot a3ac83
bool yank_lines( const int from, const int to )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * const ep = search_line_node( inc_addr( to ) );
Packit Bot a3ac83
  line_t * bp = search_line_node( from );
Packit Bot a3ac83
  line_t * lp = &yank_buffer_head;
Packit Bot a3ac83
  line_t * p;
Packit Bot a3ac83
Packit Bot a3ac83
  clear_yank_buffer();
Packit Bot a3ac83
  while( bp != ep )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    disable_interrupts();
Packit Bot a3ac83
    p = dup_line_node( bp );
Packit Bot a3ac83
    if( !p ) { enable_interrupts(); return false; }
Packit Bot a3ac83
    insert_node( p, lp );
Packit Bot a3ac83
    bp = bp->q_forw; lp = p;
Packit Bot a3ac83
    enable_interrupts();
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static undo_t * ustack = 0;		/* undo stack */
Packit Bot a3ac83
static int usize = 0;			/* ustack size (in bytes) */
Packit Bot a3ac83
static int u_ptr = 0;			/* undo stack pointer */
Packit Bot a3ac83
static int u_current_addr = -1;		/* if < 0, undo disabled */
Packit Bot a3ac83
static int u_last_addr = -1;		/* if < 0, undo disabled */
Packit Bot a3ac83
static bool u_modified = false;
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void clear_undo_stack( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  while( u_ptr-- )
Packit Bot a3ac83
    if( ustack[u_ptr].type == UDEL )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      line_t * const ep = ustack[u_ptr].tail->q_forw;
Packit Bot a3ac83
      line_t * bp = ustack[u_ptr].head;
Packit Bot a3ac83
      while( bp != ep )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        line_t * const lp = bp->q_forw;
Packit Bot a3ac83
        unmark_line_node( bp );
Packit Bot a3ac83
        unmark_unterminated_line( bp );
Packit Bot a3ac83
        free( bp );
Packit Bot a3ac83
        bp = lp;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
  u_ptr = 0;
Packit Bot a3ac83
  u_current_addr = current_addr_;
Packit Bot a3ac83
  u_last_addr = last_addr_;
Packit Bot a3ac83
  u_modified = modified_;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
void reset_undo_state( void )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  clear_undo_stack();
Packit Bot a3ac83
  u_current_addr = u_last_addr = -1;
Packit Bot a3ac83
  u_modified = false;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return pointer to intialized undo node */
Packit Bot a3ac83
undo_t * push_undo_atom( const int type, const int from, const int to )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  if( !resize_undo_buffer( &ustack, &usize, ( u_ptr + 1 ) * sizeof (undo_t) ) )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( 0, errno );
Packit Bot a3ac83
    set_error_msg( "Memory exhausted" );
Packit Bot a3ac83
    if( ustack )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      clear_undo_stack();
Packit Bot a3ac83
      free( ustack );
Packit Bot a3ac83
      ustack = 0;
Packit Bot a3ac83
      usize = u_ptr = 0;
Packit Bot a3ac83
      u_current_addr = u_last_addr = -1;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    enable_interrupts();
Packit Bot a3ac83
    return 0;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  ustack[u_ptr].type = type;
Packit Bot a3ac83
  ustack[u_ptr].tail = search_line_node( to );
Packit Bot a3ac83
  ustack[u_ptr].head = search_line_node( from );
Packit Bot a3ac83
  return ustack + u_ptr++;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* undo last change to the editor buffer */
Packit Bot a3ac83
bool undo( const bool isglobal )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  int n;
Packit Bot a3ac83
  const int o_current_addr = current_addr_;
Packit Bot a3ac83
  const int o_last_addr = last_addr_;
Packit Bot a3ac83
  const bool o_modified = modified_;
Packit Bot a3ac83
Packit Bot a3ac83
  if( u_ptr <= 0 || u_current_addr < 0 || u_last_addr < 0 )
Packit Bot a3ac83
    { set_error_msg( "Nothing to undo" ); return false; }
Packit Bot a3ac83
  search_line_node( 0 );		/* reset cached value */
Packit Bot a3ac83
  disable_interrupts();
Packit Bot a3ac83
  for( n = u_ptr - 1; n >= 0; --n )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    switch( ustack[n].type )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      case UADD: link_nodes( ustack[n].head->q_back, ustack[n].tail->q_forw );
Packit Bot a3ac83
                 break;
Packit Bot a3ac83
      case UDEL: link_nodes( ustack[n].head->q_back, ustack[n].head );
Packit Bot a3ac83
                 link_nodes( ustack[n].tail, ustack[n].tail->q_forw );
Packit Bot a3ac83
                 break;
Packit Bot a3ac83
      case UMOV:
Packit Bot a3ac83
      case VMOV: link_nodes( ustack[n-1].head, ustack[n].head->q_forw );
Packit Bot a3ac83
                 link_nodes( ustack[n].tail->q_back, ustack[n-1].tail );
Packit Bot a3ac83
                 link_nodes( ustack[n].head, ustack[n].tail ); --n;
Packit Bot a3ac83
                 break;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    ustack[n].type ^= 1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  /* reverse undo stack order */
Packit Bot a3ac83
  for( n = 0; 2 * n < u_ptr - 1; ++n )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    undo_t tmp = ustack[n];
Packit Bot a3ac83
    ustack[n] = ustack[u_ptr-1-n]; ustack[u_ptr-1-n] = tmp;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( isglobal ) clear_active_list();
Packit Bot a3ac83
  current_addr_ = u_current_addr; u_current_addr = o_current_addr;
Packit Bot a3ac83
  last_addr_ = u_last_addr; u_last_addr = o_last_addr;
Packit Bot a3ac83
  modified_ = u_modified; u_modified = o_modified;
Packit Bot a3ac83
  enable_interrupts();
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }