Imported Upstream version 1.12
[platform/upstream/ed.git] / io.c
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.
5
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.
10
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.
15
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/>.
18 */
19
20 #include <errno.h>
21 #include <stdio.h>
22 #include <string.h>
23
24 #include "ed.h"
25
26
27 /* print text to stdout */
28 static void put_tty_line( const char * p, int len, const int gflags )
29   {
30   const char escapes[] = "\a\b\f\n\r\t\v\\";
31   const char escchars[] = "abfnrtv\\";
32   int col = 0;
33
34   if( gflags & GNP ) { printf( "%d\t", current_addr() ); col = 8; }
35   while( --len >= 0 )
36     {
37     const unsigned char ch = *p++;
38     if( !( gflags & GLS ) ) putchar( ch );
39     else
40       {
41       if( ++col > window_columns() ) { col = 1; fputs( "\\\n", stdout ); }
42       if( ch >= 32 && ch <= 126 && ch != '\\' ) putchar( ch );
43       else
44         {
45         char * const p = strchr( escapes, ch );
46         ++col; putchar('\\');
47         if( ch && p ) putchar( escchars[p-escapes] );
48         else
49           {
50           col += 2;
51           putchar( ( ( ch >> 6 ) & 7 ) + '0' );
52           putchar( ( ( ch >> 3 ) & 7 ) + '0' );
53           putchar( ( ch & 7 ) + '0' );
54           }
55         }
56       }
57     }
58   if( !traditional() && ( gflags & GLS ) ) putchar('$');
59   putchar('\n');
60   }
61
62
63 /* print a range of lines to stdout */
64 bool display_lines( int from, const int to, const int gflags )
65   {
66   line_t * const ep = search_line_node( inc_addr( to ) );
67   line_t * bp = search_line_node( from );
68
69   if( !from ) { set_error_msg( "Invalid address" ); return false; }
70   while( bp != ep )
71     {
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 );
76     bp = bp->q_forw;
77     }
78   return true;
79   }
80
81
82 /* return the parity of escapes at the end of a string */
83 static bool trailing_escape( const char * const s, int len )
84   {
85   bool odd_escape = false;
86   while( --len >= 0 && s[len] == '\\' ) odd_escape = !odd_escape;
87   return odd_escape;
88   }
89
90
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 )
95   {
96   static char * buf = 0;
97   static int bufsz = 0;
98   int len;
99
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 */
107   while( true )
108     {
109     int len2;
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 );
116     len += 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 */
120     }
121   if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return false;
122   buf[len] = 0;
123   *ibufpp = buf;
124   if( lenp ) *lenp = len;
125   return true;
126   }
127
128
129 /* Read a line of text from stdin.
130    Returns pointer to buffer and line size (including trailing newline
131    if it exists) */
132 const char * get_tty_line( int * const sizep )
133   {
134   static char * buf = 0;
135   static int bufsz = 0;
136   int i = 0;
137
138   while( true )
139     {
140     const int c = getchar();
141     if( !resize_buffer( &buf, &bufsz, i + 2 ) )
142       { if( sizep ) *sizep = 0; return 0; }
143     if( c == EOF )
144       {
145       if( ferror( stdin ) )
146         {
147         show_strerror( "stdin", errno );
148         set_error_msg( "Cannot read stdin" );
149         clearerr( stdin ); if( sizep ) *sizep = 0;
150         return 0;
151         }
152       if( feof( stdin ) )
153         {
154         clearerr( stdin );
155         buf[i] = 0; if( sizep ) *sizep = i;
156         return buf;
157         }
158       }
159     else
160       {
161       buf[i++] = c; if( !c ) set_binary(); if( c != '\n' ) continue;
162       buf[i] = 0; if( sizep ) *sizep = i;
163       return buf;
164       }
165     }
166   }
167
168
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 )
174   {
175   static char * buf = 0;
176   static int bufsz = 0;
177   int c, i = 0;
178
179   while( true )
180     {
181     if( !resize_buffer( &buf, &bufsz, i + 2 ) ) return 0;
182     c = getc( fp ); if( c == EOF ) break;
183     buf[i++] = c;
184     if( !c ) set_binary(); else if( c == '\n' ) break;
185     }
186   buf[i] = 0;
187   if( c == EOF )
188     {
189     if( ferror( fp ) )
190       {
191       show_strerror( 0, errno );
192       set_error_msg( "Cannot read input file" );
193       return 0;
194       }
195     else if( i )
196       {
197       buf[i] = '\n'; buf[i+1] = 0; *newline_added_nowp = true;
198       if( !isbinary() ) ++i;
199       }
200     }
201   *sizep = i;
202   return buf;
203   }
204
205
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 )
208   {
209   line_t * lp = search_line_node( addr );
210   undo_t * up = 0;
211   long total_size = 0;
212   const bool o_isbinary = isbinary();
213   const bool appended = ( addr == last_addr() );
214   bool newline_added_now = false;
215
216   set_current_addr( addr );
217   while( true )
218     {
219     int size = 0;
220     const char * const s = read_stream_line( fp, &size, &newline_added_now );
221     if( !s ) return -1;
222     if( size > 0 ) total_size += size;
223     else break;
224     disable_interrupts();
225     if( !put_sbuf_line( s, size + newline_added_now, current_addr() ) )
226       { enable_interrupts(); return -1; }
227     lp = lp->q_forw;
228     if( up ) up->tail = lp;
229     else
230       {
231       up = push_undo_atom( UADD, current_addr(), current_addr() );
232       if( !up ) { enable_interrupts(); return -1; }
233       }
234     enable_interrupts();
235     }
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 )
241     ++total_size;
242   if( !total_size ) newline_added_now = true;
243   if( appended && newline_added_now ) set_newline_added();
244   return total_size;
245   }
246
247
248 /* read a named file/pipe into the buffer; return line count */
249 int read_file( const char * const filename, const int addr )
250   {
251   FILE * fp;
252   long size;
253   int ret;
254
255   if( *filename == '!' ) fp = popen( filename + 1, "r" );
256   else fp = fopen( strip_escapes( filename ), "r" );
257   if( !fp )
258     {
259     show_strerror( filename, errno );
260     set_error_msg( "Cannot open input file" );
261     return -1;
262     }
263   size = read_stream( fp, addr );
264   if( size < 0 ) return -1;
265   if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
266   if( ret != 0 )
267     {
268     show_strerror( filename, errno );
269     set_error_msg( "Cannot close input file" );
270     return -1;
271     }
272   if( !scripted() ) fprintf( stderr, "%lu\n", size );
273   return current_addr() - addr;
274   }
275
276
277 /* write a range of lines to a stream */
278 static long write_stream( FILE * const fp, int from, const int to )
279   {
280   line_t * lp = search_line_node( from );
281   long size = 0;
282
283   while( from && from <= to )
284     {
285     int len;
286     char * p = get_sbuf_line( lp );
287     if( !p ) return -1;
288     len = lp->len;
289     if( from != last_addr() || !isbinary() || !newline_added() )
290       p[len++] = '\n';
291     size += len;
292     while( --len >= 0 )
293       if( fputc( *p++, fp ) == EOF )
294         {
295         show_strerror( 0, errno );
296         set_error_msg( "Cannot write file" );
297         return -1;
298         }
299     ++from; lp = lp->q_forw;
300     }
301   return size;
302   }
303
304
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 )
308   {
309   FILE * fp;
310   long size;
311   int ret;
312
313   if( *filename == '!' ) fp = popen( filename + 1, "w" );
314   else fp = fopen( strip_escapes( filename ), mode );
315   if( !fp )
316     {
317     show_strerror( filename, errno );
318     set_error_msg( "Cannot open output file" );
319     return -1;
320     }
321   size = write_stream( fp, from, to );
322   if( size < 0 ) return -1;
323   if( *filename == '!' ) ret = pclose( fp ); else ret = fclose( fp );
324   if( ret != 0 )
325     {
326     show_strerror( filename, errno );
327     set_error_msg( "Cannot close output file" );
328     return -1;
329     }
330   if( !scripted() ) fprintf( stderr, "%lu\n", size );
331   return ( from && from <= to ) ? to - from + 1 : 0;
332   }