1 /* io.c: i/o routines for the ed line editor */
2 /* GNU ed - The GNU line editor.
3 Copyright (C) 1993, 1994 Andrew Moore, Talke Studio
4 Copyright (C) 2006-2015 Antonio Diaz Diaz.
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 /* print text to stdout */
28 static void put_tty_line( const char * p, int len, const int gflags )
30 const char escapes[] = "\a\b\f\n\r\t\v\\";
31 const char escchars[] = "abfnrtv\\";
34 if( gflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; }
37 const unsigned char ch = *p++;
38 if( !( gflags & GLS ) ) putchar( ch );
41 if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); }
42 if( ch >= 32 && ch <= 126 && ch != '\\' ) putchar( ch );
45 char * const p = strchr( escapes, ch );
47 if( ch && p ) putchar( escchars[p-escapes] );
51 putchar( ( ( ch >> 6 ) & 7 ) + '0' );
52 putchar( ( ( ch >> 3 ) & 7 ) + '0' );
53 putchar( ( ch & 7 ) + '0' );
58 if( !traditional() && ( gflags & GLS ) ) putchar('$');
63 /* print a range of lines to stdout */
64 bool display_lines( int from, const int to, const int gflags )
66 line_t * const ep = search_line_node( inc_addr( to ) );
67 line_t * bp = search_line_node( from );
69 if( !from ) { set_error_msg( "Invalid address" ); return false; }
72 const char * const s = get_sbuf_line( bp );
73 if( !s ) return false;
74 set_current_addr( from++ );
75 put_tty_line( s, bp->len, gflags );
82 /* return the parity of escapes at the end of a string */
83 static bool trailing_escape( const char * const s, int len )
85 bool odd_escape = false;
86 while( --len >= 0 && s[len] == '\\' ) odd_escape = !odd_escape;
91 /* If *ibufpp contains an escaped newline, get an extended line (one
92 with escaped newlines) from stdin */
93 bool get_extended_line( const char ** const ibufpp, int * const lenp,
94 const bool strip_escaped_newlines )
96 static char * buf = 0;
100 for( len = 0; (*ibufpp)[len++] != '\n'; ) ;
101 if( len < 2 || !trailing_escape( *ibufpp, len - 1 ) )
102 { if( lenp ) *lenp = len; return true; }
103 if( !resize_buffer( &buf, &bufsz, len ) ) return false;
104 memcpy( buf, *ibufpp, len );
105 --len; buf[len-1] = '\n'; /* strip trailing esc */
106 if( strip_escaped_newlines ) --len; /* strip newline */
110 const char * const s = get_tty_line( &len2 );
111 if( !s ) return false;
112 if( len2 == 0 || s[len2-1] != '\n' )
113 { set_error_msg( "Unexpected end-of-file" ); return false; }
114 if( !resize_buffer( &buf, &bufsz, len + len2 ) ) return false;
115 memcpy( buf + len, s, len2 );
117 if( len2 < 2 || !trailing_escape( buf, len - 1 ) ) break;
118 --len; buf[len-1] = '\n'; /* strip trailing esc */
119 if( strip_escaped_newlines ) --len; /* strip newline */
121 if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return false;
124 if( lenp ) *lenp = len;
129 /* Read a line of text from stdin.
130 Returns pointer to buffer and line size (including trailing newline
132 const char * get_tty_line( int * const sizep )
134 static char * buf = 0;
135 static int bufsz = 0;
140 const int c = getchar();
141 if( !resize_buffer( &buf, &bufsz, i + 2 ) )
142 { if( sizep ) *sizep = 0; return 0; }
145 if( ferror( stdin ) )
147 show_strerror( "stdin", errno );
148 set_error_msg( "Cannot read stdin" );
149 clearerr( stdin ); if( sizep ) *sizep = 0;
155 buf[i] = 0; if( sizep ) *sizep = i;
161 buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
162 buf[i] = 0; if( sizep ) *sizep = i;
169 /* Read a line of text from a stream.
170 Returns pointer to buffer and line size (including trailing newline
171 if it exists and is not added now) */
172 static const char * read_stream_line( FILE * const fp, int * const sizep,
173 bool * const newline_added_nowp )
175 static char * buf = 0;
176 static int bufsz = 0;
181 if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
182 c = getc( fp ); if( c == EOF ) break;
184 if( !c ) set_binary(); else if( c == '\n' ) break;
191 show_strerror( 0, errno );
192 set_error_msg( "Cannot read input file" );
197 buf[i] = '\n'; buf[i+1] = 0; *newline_added_nowp = true;
198 if( !isbinary() ) ++i;
206 /* read a stream into the editor buffer; return total size of data read */
207 static long read_stream( FILE * const fp, const int addr )
209 line_t * lp = search_line_node( addr );
212 const bool o_isbinary = isbinary();
213 const bool appended = ( addr == last_addr() );
214 bool newline_added_now = false;
216 set_current_addr( addr );
220 const char * const s = read_stream_line( fp, &size, &newline_added_now );
222 if( size > 0 ) total_size += size;
224 disable_interrupts();
225 if( !put_sbuf_line( s, size + newline_added_now, current_addr() ) )
226 { enable_interrupts(); return -1; }
228 if( up ) up->tail = lp;
231 up = push_undo_atom( UADD, current_addr(), current_addr() );
232 if( !up ) { enable_interrupts(); return -1; }
236 if( addr && appended && total_size && o_isbinary && newline_added() )
237 fputs( "Newline inserted\n", stderr );
238 else if( newline_added_now && ( !appended || !isbinary() ) )
239 fputs( "Newline appended\n", stderr );
240 if( isbinary() && !o_isbinary && newline_added_now && !appended )
242 if( !total_size ) newline_added_now = true;
243 if( appended && newline_added_now ) set_newline_added();
248 /* read a named file/pipe into the buffer; return line count */
249 int read_file( const char * const filename, const int addr )
255 if( *filename == '!' ) fp = popen( filename + 1, "r" );
256 else fp = fopen( strip_escapes( filename ), "r" );
259 show_strerror( filename, errno );
260 set_error_msg( "Cannot open input file" );
263 size = read_stream( fp, addr );
264 if( size < 0 ) return -1;
265 if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
268 show_strerror( filename, errno );
269 set_error_msg( "Cannot close input file" );
272 if( !scripted() ) fprintf( stderr, "%lu\n", size );
273 return current_addr() - addr;
277 /* write a range of lines to a stream */
278 static long write_stream( FILE * const fp, int from, const int to )
280 line_t * lp = search_line_node( from );
283 while( from && from <= to )
286 char * p = get_sbuf_line( lp );
289 if( from != last_addr() || !isbinary() || !newline_added() )
293 if( fputc( *p++, fp ) == EOF )
295 show_strerror( 0, errno );
296 set_error_msg( "Cannot write file" );
299 ++from; lp = lp->q_forw;
305 /* write a range of lines to a named file/pipe; return line count */
306 int write_file( const char * const filename, const char * const mode,
307 const int from, const int to )
313 if( *filename == '!' ) fp = popen( filename + 1, "w" );
314 else fp = fopen( strip_escapes( filename ), mode );
317 show_strerror( filename, errno );
318 set_error_msg( "Cannot open output file" );
321 size = write_stream( fp, from, to );
322 if( size < 0 ) return -1;
323 if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
326 show_strerror( filename, errno );
327 set_error_msg( "Cannot close output file" );
330 if( !scripted() ) fprintf( stderr, "%lu\n", size );
331 return ( from && from <= to ) ? to - from + 1 : 0;