Blame buffer.c

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