Blame io.c

Packit Bot a3ac83
/* io.c: i/o 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 <stdio.h>
Packit Bot a3ac83
#include <string.h>
Packit Bot a3ac83
Packit Bot a3ac83
#include "ed.h"
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
static const line_t * unterminated_line = 0;	/* last line has no '\n' */
Packit Bot a3ac83
int linenum_ = 0;				/* script line number */
Packit Bot a3ac83
Packit Bot a3ac83
void reset_unterminated_line( void ) { unterminated_line = 0; }
Packit Bot a3ac83
Packit Bot a3ac83
void unmark_unterminated_line( const line_t * const lp )
Packit Bot a3ac83
  { if( unterminated_line == lp ) unterminated_line = 0; }
Packit Bot a3ac83
Packit Bot a3ac83
static bool unterminated_last_line( void )
Packit Bot a3ac83
  { return ( unterminated_line != 0 &&
Packit Bot a3ac83
             unterminated_line == search_line_node( last_addr() ) ); }
Packit Bot a3ac83
Packit Bot a3ac83
int linenum( void ) { return linenum_; }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* print text to stdout */
Packit Bot a3ac83
static void print_line( const char * p, int len, const int pflags )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  const char escapes[] = "\a\b\f\n\r\t\v\\";
Packit Bot a3ac83
  const char escchars[] = "abfnrtv\\";
Packit Bot a3ac83
  int col = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  if( pflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; }
Packit Bot a3ac83
  while( --len >= 0 )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const unsigned char ch = *p++;
Packit Bot a3ac83
    if( !( pflags & GLS ) ) putchar( ch );
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); }
Packit Bot a3ac83
      if( ch >= 32 && ch <= 126 && ch != '\\' ) putchar( ch );
Packit Bot a3ac83
      else
Packit Bot a3ac83
        {
Packit Bot a3ac83
        char * const p = strchr( escapes, ch );
Packit Bot a3ac83
        ++col; putchar('\\');
Packit Bot a3ac83
        if( ch && p ) putchar( escchars[p-escapes] );
Packit Bot a3ac83
        else
Packit Bot a3ac83
          {
Packit Bot a3ac83
          col += 2;
Packit Bot a3ac83
          putchar( ( ( ch >> 6 ) & 7 ) + '0' );
Packit Bot a3ac83
          putchar( ( ( ch >> 3 ) & 7 ) + '0' );
Packit Bot a3ac83
          putchar( ( ch & 7 ) + '0' );
Packit Bot a3ac83
          }
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !traditional() && ( pflags & GLS ) ) putchar('$');
Packit Bot a3ac83
  putchar('\n');
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* print a range of lines to stdout */
Packit Bot a3ac83
bool print_lines( int from, const int to, const int pflags )
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
Packit Bot a3ac83
  if( !from ) { set_error_msg( "Invalid address" ); return false; }
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 ) return false;
Packit Bot a3ac83
    set_current_addr( from++ );
Packit Bot a3ac83
    print_line( s, bp->len, pflags );
Packit Bot a3ac83
    bp = bp->q_forw;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* return the parity of escapes at the end of a string */
Packit Bot a3ac83
static bool trailing_escape( const char * const s, int len )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  bool odd_escape = false;
Packit Bot a3ac83
  while( --len >= 0 && s[len] == '\\' ) odd_escape = !odd_escape;
Packit Bot a3ac83
  return odd_escape;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* If *ibufpp contains an escaped newline, get an extended line (one
Packit Bot a3ac83
   with escaped newlines) from stdin.
Packit Bot a3ac83
   Return line length in *lenp, including the trailing newline. */
Packit Bot a3ac83
bool get_extended_line( const char ** const ibufpp, int * const lenp,
Packit Bot a3ac83
                        const bool strip_escaped_newlines )
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
  for( len = 0; (*ibufpp)[len++] != '\n'; ) ;
Packit Bot a3ac83
  if( len < 2 || !trailing_escape( *ibufpp, len - 1 ) )
Packit Bot a3ac83
    { if( lenp ) *lenp = len; return true; }
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, len ) ) return false;
Packit Bot a3ac83
  memcpy( buf, *ibufpp, len );
Packit Bot a3ac83
  --len; buf[len-1] = '\n';			/* strip trailing esc */
Packit Bot a3ac83
  if( strip_escaped_newlines ) --len;		/* strip newline */
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int len2;
Packit Bot a3ac83
    const char * const s = get_stdin_line( &len2 );
Packit Bot a3ac83
    if( !s ) return false;			/* error */
Packit Bot a3ac83
    if( len2 <= 0 ) return false;		/* EOF */
Packit Bot a3ac83
    if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return false;
Packit Bot a3ac83
    memcpy( buf + len, s, len2 );
Packit Bot a3ac83
    len += len2;
Packit Bot a3ac83
    if( len2 < 2 || !trailing_escape( buf, len - 1 ) ) break;
Packit Bot a3ac83
    --len; buf[len-1] = '\n';			/* strip trailing esc */
Packit Bot a3ac83
    if( strip_escaped_newlines ) --len;		/* strip newline */
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return false;
Packit Bot a3ac83
  buf[len] = 0;
Packit Bot a3ac83
  *ibufpp = buf;
Packit Bot a3ac83
  if( lenp ) *lenp = len;
Packit Bot a3ac83
  return true;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Read a line of text from stdin.
Packit Bot a3ac83
   Incomplete lines (lacking the trailing newline) are discarded.
Packit Bot a3ac83
   Returns pointer to buffer and line size (including trailing newline),
Packit Bot a3ac83
   or 0 if error, or *sizep = 0 if EOF */
Packit Bot a3ac83
const char * get_stdin_line( int * const sizep )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  int i = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    const int c = getchar();
Packit Bot a3ac83
    if( !resize_buffer( &buf, &bufsz, i + 2 ) ) { *sizep = 0; return 0; }
Packit Bot a3ac83
    if( c == EOF )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      if( ferror( stdin ) )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        show_strerror( "stdin", errno );
Packit Bot a3ac83
        set_error_msg( "Cannot read stdin" );
Packit Bot a3ac83
        clearerr( stdin );
Packit Bot a3ac83
        *sizep = 0; return 0;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      if( feof( stdin ) )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        set_error_msg( "Unexpected end-of-file" );
Packit Bot a3ac83
        clearerr( stdin );
Packit Bot a3ac83
        buf[0] = 0; *sizep = 0; if( i > 0 ) ++linenum_;	/* discard line */
Packit Bot a3ac83
        return buf;
Packit Bot a3ac83
        }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else
Packit Bot a3ac83
      {
Packit Bot a3ac83
      buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
Packit Bot a3ac83
      ++linenum_; buf[i] = 0; *sizep = i;
Packit Bot a3ac83
      return buf;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* Read a line of text from a stream.
Packit Bot a3ac83
   Returns pointer to buffer and line size (including trailing newline
Packit Bot a3ac83
   if it exists and is not added now) */
Packit Bot a3ac83
static const char * read_stream_line( FILE * const fp, int * const sizep,
Packit Bot a3ac83
                                      bool * const newline_addedp )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  static char * buf = 0;
Packit Bot a3ac83
  static int bufsz = 0;
Packit Bot a3ac83
  int c, i = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
Packit Bot a3ac83
    c = getc( fp ); if( c == EOF ) break;
Packit Bot a3ac83
    buf[i++] = c;
Packit Bot a3ac83
    if( !c ) set_binary(); else if( c == '\n' ) break;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  buf[i] = 0;
Packit Bot a3ac83
  if( c == EOF )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    if( ferror( fp ) )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      show_strerror( 0, errno );
Packit Bot a3ac83
      set_error_msg( "Cannot read input file" );
Packit Bot a3ac83
      return 0;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    else if( i )
Packit Bot a3ac83
      {
Packit Bot a3ac83
      buf[i] = '\n'; buf[i+1] = 0; *newline_addedp = true;
Packit Bot a3ac83
      if( !isbinary() ) ++i;
Packit Bot a3ac83
      }
Packit Bot a3ac83
    }
Packit Bot a3ac83
  *sizep = i;
Packit Bot a3ac83
  return buf;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* read a stream into the editor buffer;
Packit Bot a3ac83
   return total size of data read, or -1 if error */
Packit Bot a3ac83
static long read_stream( FILE * const fp, const int addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * lp = search_line_node( addr );
Packit Bot a3ac83
  undo_t * up = 0;
Packit Bot a3ac83
  long total_size = 0;
Packit Bot a3ac83
  const bool o_isbinary = isbinary();
Packit Bot a3ac83
  const bool appended = ( addr == last_addr() );
Packit Bot a3ac83
  const bool o_unterminated_last_line = unterminated_last_line();
Packit Bot a3ac83
  bool newline_added = false;
Packit Bot a3ac83
Packit Bot a3ac83
  set_current_addr( addr );
Packit Bot a3ac83
  while( true )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int size = 0;
Packit Bot a3ac83
    const char * const s = read_stream_line( fp, &size, &newline_added );
Packit Bot a3ac83
    if( !s ) return -1;
Packit Bot a3ac83
    if( size <= 0 ) break;
Packit Bot a3ac83
    total_size += size;
Packit Bot a3ac83
    disable_interrupts();
Packit Bot a3ac83
    if( !put_sbuf_line( s, size + newline_added ) )
Packit Bot a3ac83
      { enable_interrupts(); return -1; }
Packit Bot a3ac83
    lp = lp->q_forw;
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 -1; }
Packit Bot a3ac83
      }
Packit Bot a3ac83
    enable_interrupts();
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( addr && appended && total_size && o_unterminated_last_line )
Packit Bot a3ac83
    fputs( "Newline inserted\n", stdout );		/* before stream */
Packit Bot a3ac83
  else if( newline_added && ( !appended || !isbinary() ) )
Packit Bot a3ac83
    fputs( "Newline appended\n", stdout );		/* after stream */
Packit Bot a3ac83
  if( !appended && isbinary() && !o_isbinary && newline_added )
Packit Bot a3ac83
    ++total_size;
Packit Bot a3ac83
  if( appended && isbinary() && ( newline_added || total_size == 0 ) )
Packit Bot a3ac83
    unterminated_line = search_line_node( last_addr() );
Packit Bot a3ac83
  return total_size;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* read a named file/pipe into the buffer; return line count, or -1 if error */
Packit Bot a3ac83
int read_file( const char * const filename, const int addr )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  FILE * fp;
Packit Bot a3ac83
  long size;
Packit Bot a3ac83
  int ret;
Packit Bot a3ac83
Packit Bot a3ac83
  if( *filename == '!' ) fp = popen( filename + 1, "r" );
Packit Bot a3ac83
  else fp = fopen( strip_escapes( filename ), "r" );
Packit Bot a3ac83
  if( !fp )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( filename, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot open input file" );
Packit Bot a3ac83
    return -1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  size = read_stream( fp, addr );
Packit Bot a3ac83
  if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
Packit Bot a3ac83
  if( size < 0 ) return -1;
Packit Bot a3ac83
  if( ret != 0 )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( filename, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot close input file" );
Packit Bot a3ac83
    return -1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !scripted() ) printf( "%lu\n", size );
Packit Bot a3ac83
  return current_addr() - addr;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* write a range of lines to a stream */
Packit Bot a3ac83
static long write_stream( FILE * const fp, int from, const int to )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  line_t * lp = search_line_node( from );
Packit Bot a3ac83
  long size = 0;
Packit Bot a3ac83
Packit Bot a3ac83
  while( from && from <= to )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    int len;
Packit Bot a3ac83
    char * p = get_sbuf_line( lp );
Packit Bot a3ac83
    if( !p ) return -1;
Packit Bot a3ac83
    len = lp->len;
Packit Bot a3ac83
    if( from != last_addr() || !isbinary() || !unterminated_last_line() )
Packit Bot a3ac83
      p[len++] = '\n';
Packit Bot a3ac83
    size += len;
Packit Bot a3ac83
    while( --len >= 0 )
Packit Bot a3ac83
      if( fputc( *p++, fp ) == EOF )
Packit Bot a3ac83
        {
Packit Bot a3ac83
        show_strerror( 0, errno );
Packit Bot a3ac83
        set_error_msg( "Cannot write file" );
Packit Bot a3ac83
        return -1;
Packit Bot a3ac83
        }
Packit Bot a3ac83
    ++from; lp = lp->q_forw;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  return size;
Packit Bot a3ac83
  }
Packit Bot a3ac83
Packit Bot a3ac83
Packit Bot a3ac83
/* write a range of lines to a named file/pipe; return line count */
Packit Bot a3ac83
int write_file( const char * const filename, const char * const mode,
Packit Bot a3ac83
                const int from, const int to )
Packit Bot a3ac83
  {
Packit Bot a3ac83
  FILE * fp;
Packit Bot a3ac83
  long size;
Packit Bot a3ac83
  int ret;
Packit Bot a3ac83
Packit Bot a3ac83
  if( *filename == '!' ) fp = popen( filename + 1, "w" );
Packit Bot a3ac83
  else fp = fopen( strip_escapes( filename ), mode );
Packit Bot a3ac83
  if( !fp )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( filename, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot open output file" );
Packit Bot a3ac83
    return -1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  size = write_stream( fp, from, to );
Packit Bot a3ac83
  if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
Packit Bot a3ac83
  if( size < 0 ) return -1;
Packit Bot a3ac83
  if( ret != 0 )
Packit Bot a3ac83
    {
Packit Bot a3ac83
    show_strerror( filename, errno );
Packit Bot a3ac83
    set_error_msg( "Cannot close output file" );
Packit Bot a3ac83
    return -1;
Packit Bot a3ac83
    }
Packit Bot a3ac83
  if( !scripted() ) printf( "%lu\n", size );
Packit Bot a3ac83
  return ( from && from <= to ) ? to - from + 1 : 0;
Packit Bot a3ac83
  }