Imported Upstream version 1.12
[platform/upstream/ed.git] / signal.c
1 /* signal.c: signal and miscellaneous 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 <ctype.h>
21 #include <errno.h>
22 #include <limits.h>
23 #include <setjmp.h>
24 #include <signal.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <termios.h>
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30
31 #include "ed.h"
32
33
34 jmp_buf jmp_state;
35 static int mutex = 0;                   /* If > 0, signals stay pending */
36 static int window_lines_ = 22;          /* scroll lines set by sigwinch_handler */
37 static int window_columns_ = 72;
38 static bool sighup_pending = false;
39 static bool sigint_pending = false;
40
41
42 static void sighup_handler( int signum )
43   {
44   if( signum ) {}                       /* keep compiler happy */
45   if( mutex ) sighup_pending = true;
46   else
47     {
48     const char hb[] = "ed.hup";
49     sighup_pending = false;
50     if( last_addr() && modified() &&
51         write_file( hb, "w", 1, last_addr() ) < 0 )
52       {
53       char * const s = getenv( "HOME" );
54       const int len = ( s ? strlen( s ) : 0 );
55       const int need_slash = ( ( !len || s[len-1] != '/' ) ? 1 : 0 );
56       char * const hup = ( ( len + need_slash + (int)sizeof hb < path_max( 0 ) ) ?
57                     (char *) malloc( len + need_slash + sizeof hb ) : 0 );
58       if( len && hup )                  /* hup filename */
59         {
60         memcpy( hup, s, len );
61         if( need_slash ) hup[len] = '/';
62         memcpy( hup + len + need_slash, hb, sizeof hb );
63         if( write_file( hup, "w", 1, last_addr() ) >= 0 ) exit( 0 );
64         }
65       exit( 1 );                        /* hup file write failed */
66       }
67     exit( 0 );
68     }
69   }
70
71
72 static void sigint_handler( int signum )
73   {
74   if( mutex ) sigint_pending = true;
75   else
76     {
77     sigset_t set;
78     sigint_pending = false;
79     sigemptyset( &set );
80     sigaddset( &set, signum );
81     sigprocmask( SIG_UNBLOCK, &set, 0 );
82     longjmp( jmp_state, -1 );
83     }
84   }
85
86
87 static void sigwinch_handler( int signum )
88   {
89 #ifdef TIOCGWINSZ
90   struct winsize ws;                    /* window size structure */
91
92   if( ioctl( 0, TIOCGWINSZ, (char *) &ws ) >= 0 )
93     {
94     /* Sanity check values of environment vars */
95     if( ws.ws_row > 2 && ws.ws_row < 600 ) window_lines_ = ws.ws_row - 2;
96     if( ws.ws_col > 8 && ws.ws_col < 1800 ) window_columns_ = ws.ws_col - 8;
97     }
98 #endif
99   if( signum ) {}                       /* keep compiler happy */
100   }
101
102
103 static int set_signal( const int signum, void (*handler)( int ) )
104   {
105   struct sigaction new_action;
106
107   new_action.sa_handler = handler;
108   sigemptyset( &new_action.sa_mask );
109 #ifdef SA_RESTART
110   new_action.sa_flags = SA_RESTART;
111 #else
112   new_action.sa_flags = 0;
113 #endif
114   return sigaction( signum, &new_action, 0 );
115   }
116
117
118 void enable_interrupts( void )
119   {
120   if( --mutex <= 0 )
121     {
122     mutex = 0;
123     if( sighup_pending ) sighup_handler( SIGHUP );
124     if( sigint_pending ) sigint_handler( SIGINT );
125     }
126   }
127
128
129 void disable_interrupts( void ) { ++mutex; }
130
131
132 void set_signals( void )
133   {
134 #ifdef SIGWINCH
135   sigwinch_handler( SIGWINCH );
136   if( isatty( 0 ) ) set_signal( SIGWINCH, sigwinch_handler );
137 #endif
138   set_signal( SIGHUP, sighup_handler );
139   set_signal( SIGQUIT, SIG_IGN );
140   set_signal( SIGINT, sigint_handler );
141   }
142
143
144 void set_window_lines( const int lines ) { window_lines_ = lines; }
145 int window_columns( void ) { return window_columns_; }
146 int window_lines( void ) { return window_lines_; }
147
148
149 /* convert a string to int with out_of_range detection */
150 bool parse_int( int * const i, const char * const str, const char ** const tail )
151   {
152   char * tmp;
153   long li;
154
155   errno = 0;
156   *i = li = strtol( str, &tmp, 10 );
157   if( tail ) *tail = tmp;
158   if( tmp == str )
159     {
160     set_error_msg( "Bad numerical result" );
161     *i = 0;
162     return false;
163     }
164   if( errno == ERANGE || li > INT_MAX || li < INT_MIN )
165     {
166     set_error_msg( "Numerical result out of range" );
167     *i = 0;
168     return false;
169     }
170   return true;
171   }
172
173
174 /* assure at least a minimum size for buffer 'buf' */
175 bool resize_buffer( char ** const buf, int * const size, const int min_size )
176   {
177   if( *size < min_size )
178     {
179     const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 );
180     void * new_buf = 0;
181     disable_interrupts();
182     if( *buf ) new_buf = realloc( *buf, new_size );
183     else new_buf = malloc( new_size );
184     if( !new_buf )
185       {
186       show_strerror( 0, errno );
187       set_error_msg( "Memory exhausted" );
188       enable_interrupts();
189       return false;
190       }
191     *size = new_size;
192     *buf = (char *)new_buf;
193     enable_interrupts();
194     }
195   return true;
196   }
197
198
199 /* assure at least a minimum size for buffer 'buf' */
200 bool resize_line_buffer( const line_t *** const buf, int * const size,
201                          const int min_size )
202   {
203   if( *size < min_size )
204     {
205     const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 );
206     void * new_buf = 0;
207     disable_interrupts();
208     if( *buf ) new_buf = realloc( *buf, new_size );
209     else new_buf = malloc( new_size );
210     if( !new_buf )
211       {
212       show_strerror( 0, errno );
213       set_error_msg( "Memory exhausted" );
214       enable_interrupts();
215       return false;
216       }
217     *size = new_size;
218     *buf = (const line_t **)new_buf;
219     enable_interrupts();
220     }
221   return true;
222   }
223
224
225 /* assure at least a minimum size for buffer 'buf' */
226 bool resize_undo_buffer( undo_t ** const buf, int * const size,
227                          const int min_size )
228   {
229   if( *size < min_size )
230     {
231     const int new_size = ( min_size < 512 ? 512 : ( min_size / 512 ) * 1024 );
232     void * new_buf = 0;
233     disable_interrupts();
234     if( *buf ) new_buf = realloc( *buf, new_size );
235     else new_buf = malloc( new_size );
236     if( !new_buf )
237       {
238       show_strerror( 0, errno );
239       set_error_msg( "Memory exhausted" );
240       enable_interrupts();
241       return false;
242       }
243     *size = new_size;
244     *buf = (undo_t *)new_buf;
245     enable_interrupts();
246     }
247   return true;
248   }
249
250
251 /* return unescaped copy of escaped string */
252 const char * strip_escapes( const char * p )
253   {
254   static char * buf = 0;
255   static int bufsz = 0;
256   const int len = strlen( p );
257   int i = 0;
258
259   if( !resize_buffer( &buf, &bufsz, len + 1 ) ) return 0;
260   /* assert: no trailing escape */
261   while( ( buf[i++] = ( (*p == '\\' ) ? *++p : *p ) ) )
262     ++p;
263   return buf;
264   }