/* io.c: i/o routines for the ed line editor */
/* GNU ed - The GNU line editor.
Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
Copyright (C) 2006, 2007, 2008, 2009 Antonio Diaz Diaz.
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 .
*/
#include
#include
#include
#include "ed.h"
/* print text to stdout */
static char put_tty_line( const char *s, int len, const int gflags )
{
const char escapes[] = "\a\b\f\n\r\t\v\\";
const char escchars[] = "abfnrtv\\";
int col = 0;
if( gflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; }
while( --len >= 0 )
{
const unsigned char ch = *s++;
if( !( gflags & GLS ) ) putchar( ch );
else
{
if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); }
if( ch >= 32 && ch <= 126 && ch != '\\' ) putchar( ch );
else
{
char *cp = strchr( escapes, ch );
++col; putchar('\\');
if( cp ) putchar( escchars[cp-escapes] );
else
{
col += 2;
putchar( ( ( ch >> 6 ) & 7 ) + '0' );
putchar( ( ( ch >> 3 ) & 7 ) + '0' );
putchar( ( ch & 7 ) + '0' );
}
}
}
}
if( !traditional() && ( gflags & GLS ) ) putchar('$');
putchar('\n');
return 1;
}
/* print a range of lines to stdout */
char display_lines( int from, const int to, const int gflags )
{
line_t *ep = search_line_node( inc_addr( to ) );
line_t *bp = search_line_node( from );
if( !from ) { set_error_msg( "Invalid address" ); return 0; }
while( bp != ep )
{
char *s = get_sbuf_line( bp );
if( !s ) return 0;
set_current_addr( from++ );
if( !put_tty_line( s, bp->len, gflags ) ) return 0;
bp = bp->q_forw;
}
return 1;
}
/* return the parity of escapes at the end of a string */
static char trailing_escape( const char * const s, int len )
{
char parity = 0;
while( --len >= 0 && s[len] == '\\' ) parity = !parity;
return parity;
}
/* get an extended line from stdin */
const char *get_extended_line( const char *ibufp, int *lenp, const char nonl )
{
static char *buf = 0;
static int bufsz = 0;
int len;
for( len = 0; ibufp[len++] != '\n'; ) ;
if( len < 2 || !trailing_escape( ibufp, len - 1 ) )
{ if( lenp ) *lenp = len; return ibufp; }
if( !resize_buffer( &buf, &bufsz, len ) ) return 0;
memcpy( buf, ibufp, len );
--len; buf[len-1] = '\n'; /* strip trailing esc */
if( nonl ) --len; /* strip newline */
while( 1 )
{
int len2;
if( !( ibufp = get_tty_line( &len2 ) ) ) return 0;
if( len2 == 0 || ibufp[len2-1] != '\n' )
{ set_error_msg( "Unexpected end-of-file" ); return 0; }
if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return 0;
memcpy( buf + len, ibufp, len2 );
len += len2;
if( len2 < 2 || !trailing_escape( buf, len - 1 ) ) break;
--len; buf[len-1] = '\n'; /* strip trailing esc */
if( nonl ) --len; /* strip newline */
}
if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
buf[len] = 0;
if( lenp ) *lenp = len;
return buf;
}
/* read a line of text from stdin; return pointer to buffer and line length */
const char *get_tty_line( int *lenp )
{
static char *buf = 0;
static int bufsz = 0;
int i = 0, oi = -1;
while( 1 )
{
const int c = getchar();
if( c == EOF )
{
if( ferror( stdin ) )
{
show_strerror( "stdin", errno ); set_error_msg( "Cannot read stdin" );
clearerr( stdin ); if( lenp ) *lenp = 0;
return 0;
}
else
{
clearerr( stdin ); if( i != oi ) { oi = i; continue; }
if( i ) buf[i] = 0; if( lenp ) *lenp = i;
return buf;
}
}
else
{
if( !resize_buffer( &buf, &bufsz, i + 2 ) )
{ if( lenp ) *lenp = 0; return 0; }
buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
buf[i] = 0; if( lenp ) *lenp = i;
return buf;
}
}
}
/* read a line of text from a stream */
static const char * read_stream_line( FILE *fp, int *lenp, char *newline_added_now )
{
static char *buf = 0;
static int bufsz = 0;
int c, i = 0;
while( 1 )
{
if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
c = getc( fp ); if( c == EOF ) break;
buf[i++] = c;
if( !c ) set_binary(); else if( c == '\n' ) break;
}
buf[i] = 0;
if( c == EOF )
{
if( ferror( fp ) )
{
show_strerror( 0, errno );
set_error_msg( "Cannot read input file" );
return 0;
}
else if( i )
{
buf[i] = '\n'; buf[i+1] = 0; *newline_added_now = 1;
if( !isbinary() ) ++i;
}
}
*lenp = i;
return buf;
}
/* read a stream into the editor buffer; return size of data read */
static long read_stream( FILE *fp, const int addr )
{
line_t *lp = search_line_node( addr );
undo_t *up = 0;
long size = 0;
const char o_isbinary = isbinary();
const char appended = ( addr == last_addr() );
char newline_added_now = 0;
set_current_addr( addr );
while( 1 )
{
int len = 0;
const char *buf = read_stream_line( fp, &len, &newline_added_now );
if( !buf ) return -1;
if( len > 0 ) size += len; else break;
disable_interrupts();
if( !put_sbuf_line( buf, current_addr() ) )
{ enable_interrupts(); return -1; }
lp = lp->q_forw;
if( up ) up->tail = lp;
else if( !( up = push_undo_atom( UADD, -1, -1 ) ) )
{ enable_interrupts(); return -1; }
enable_interrupts();
}
if( addr && appended && size && o_isbinary && newline_added() )
fputs( "Newline inserted\n", stderr );
else if( newline_added_now && appended )
fputs( "Newline appended\n", stderr );
if( isbinary() && !o_isbinary && newline_added_now && !appended ) ++size;
if( !size ) newline_added_now = 1;
if( appended && newline_added_now ) set_newline_added();
return size;
}
/* read a named file/pipe into the buffer; return line count */
int read_file( const char *filename, const int addr )
{
FILE *fp;
long size;
if( *filename == '!' ) fp = popen( filename + 1, "r" );
else fp = fopen( strip_escapes( filename ), "r" );
if( !fp )
{
show_strerror( filename, errno );
set_error_msg( "Cannot open input file" );
return -1;
}
if( ( size = read_stream( fp, addr ) ) < 0 ) return -1;
if( ( (*filename == '!' ) ? pclose( fp ) : fclose( fp ) ) < 0 )
{
show_strerror( filename, errno );
set_error_msg( "Cannot close input file" );
return -1;
}
if( !scripted() ) fprintf( stderr, "%lu\n", size );
return current_addr() - addr;
}
/* write a range of lines to a stream */
static long write_stream( FILE *fp, int from, const int to )
{
line_t *lp = search_line_node( from );
long size = 0;
while( from && from <= to )
{
int len;
char *s = get_sbuf_line( lp );
if( !s ) return -1;
len = lp->len;
if( from != last_addr() || !isbinary() || !newline_added() )
s[len++] = '\n';
size += len;
while( --len >= 0 )
if( fputc( *s++, fp ) < 0 )
{
show_strerror( 0, errno );
set_error_msg( "Cannot write file" );
return -1;
}
++from; lp = lp->q_forw;
}
return size;
}
/* write a range of lines to a named file/pipe; return line count */
int write_file( const char * const filename, const char * const mode,
const int from, const int to )
{
FILE *fp;
long size;
if( *filename == '!' ) fp = popen( filename + 1, "w" );
else fp = fopen( strip_escapes( filename ), mode );
if( !fp )
{
show_strerror( filename, errno );
set_error_msg( "Cannot open output file" );
return -1;
}
if( ( size = write_stream( fp, from, to ) ) < 0 ) return -1;
if( ( (*filename == '!' ) ? pclose( fp ) : fclose( fp ) ) < 0 )
{
show_strerror( filename, errno );
set_error_msg( "Cannot close output file" );
return -1;
}
if( !scripted() ) fprintf( stderr, "%lu\n", size );
return ( from && from <= to ) ? to - from + 1 : 0;
}