@@ -57,190 +57,220 @@ STATIC char *str_dup_maybe(const char *str) {
5757 return s2 ;
5858}
5959
60- int readline (vstr_t * line , const char * prompt ) {
61- stdout_tx_str (prompt );
62- int orig_line_len = line -> len ;
63- int escape_seq = ESEQ_NONE ;
64- char escape_seq_buf [1 ] = {0 };
65- int hist_cur = -1 ;
66- int cursor_pos = orig_line_len ;
67- for (;;) {
68- int c = stdin_rx_chr ();
69- int last_line_len = line -> len ;
70- int redraw_step_back = 0 ;
71- bool redraw_from_cursor = false;
72- int redraw_step_forward = 0 ;
73- if (escape_seq == ESEQ_NONE ) {
74- if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len (line ) == orig_line_len ) {
75- // control character with empty line
76- return c ;
77- } else if (c == CHAR_CTRL_A ) {
78- // CTRL-A with non-empty line is go-to-start-of-line
79- goto home_key ;
80- } else if (c == CHAR_CTRL_C ) {
81- // CTRL-C with non-empty line is cancel
82- return c ;
83- } else if (c == CHAR_CTRL_E ) {
84- // CTRL-E is go-to-end-of-line
85- goto end_key ;
86- } else if (c == '\r' ) {
87- // newline
88- stdout_tx_str ("\r\n" );
89- if (line -> len > orig_line_len && (MP_STATE_PORT (readline_hist )[0 ] == NULL || strcmp (MP_STATE_PORT (readline_hist )[0 ], line -> buf + orig_line_len ) != 0 )) {
90- // a line which is not empty and different from the last one
91- // so update the history
92- char * most_recent_hist = str_dup_maybe (line -> buf + orig_line_len );
93- if (most_recent_hist != NULL ) {
94- for (int i = READLINE_HIST_SIZE - 1 ; i > 0 ; i -- ) {
95- MP_STATE_PORT (readline_hist )[i ] = MP_STATE_PORT (readline_hist )[i - 1 ];
96- }
97- MP_STATE_PORT (readline_hist )[0 ] = most_recent_hist ;
60+ typedef struct _readline_t {
61+ vstr_t * line ;
62+ int orig_line_len ;
63+ int escape_seq ;
64+ int hist_cur ;
65+ int cursor_pos ;
66+ char escape_seq_buf [1 ];
67+ } readline_t ;
68+
69+ readline_t rl ;
70+
71+ int readline_process_char (int c ) {
72+ int last_line_len = rl .line -> len ;
73+ int redraw_step_back = 0 ;
74+ bool redraw_from_cursor = false;
75+ int redraw_step_forward = 0 ;
76+ if (rl .escape_seq == ESEQ_NONE ) {
77+ if (CHAR_CTRL_A <= c && c <= CHAR_CTRL_D && vstr_len (rl .line ) == rl .orig_line_len ) {
78+ // control character with empty line
79+ return c ;
80+ } else if (c == CHAR_CTRL_A ) {
81+ // CTRL-A with non-empty line is go-to-start-of-line
82+ goto home_key ;
83+ } else if (c == CHAR_CTRL_C ) {
84+ // CTRL-C with non-empty line is cancel
85+ return c ;
86+ } else if (c == CHAR_CTRL_E ) {
87+ // CTRL-E is go-to-end-of-line
88+ goto end_key ;
89+ } else if (c == '\r' ) {
90+ // newline
91+ stdout_tx_str ("\r\n" );
92+ if (rl .line -> len > rl .orig_line_len && (MP_STATE_PORT (readline_hist )[0 ] == NULL || strcmp (MP_STATE_PORT (readline_hist )[0 ], rl .line -> buf + rl .orig_line_len ) != 0 )) {
93+ // a line which is not empty and different from the last one
94+ // so update the history
95+ char * most_recent_hist = str_dup_maybe (rl .line -> buf + rl .orig_line_len );
96+ if (most_recent_hist != NULL ) {
97+ for (int i = READLINE_HIST_SIZE - 1 ; i > 0 ; i -- ) {
98+ MP_STATE_PORT (readline_hist )[i ] = MP_STATE_PORT (readline_hist )[i - 1 ];
9899 }
100+ MP_STATE_PORT (readline_hist )[0 ] = most_recent_hist ;
99101 }
100- return 0 ;
101- } else if (c == 27 ) {
102- // escape sequence
103- escape_seq = ESEQ_ESC ;
104- } else if (c == 8 || c == 127 ) {
105- // backspace/delete
106- if (cursor_pos > orig_line_len ) {
107- vstr_cut_out_bytes (line , cursor_pos - 1 , 1 );
108- // set redraw parameters
109- redraw_step_back = 1 ;
110- redraw_from_cursor = true;
111- }
112- } else if (32 <= c && c <= 126 ) {
113- // printable character
114- vstr_ins_char (line , cursor_pos , c );
102+ }
103+ return 0 ;
104+ } else if (c == 27 ) {
105+ // escape sequence
106+ rl .escape_seq = ESEQ_ESC ;
107+ } else if (c == 8 || c == 127 ) {
108+ // backspace/delete
109+ if (rl .cursor_pos > rl .orig_line_len ) {
110+ vstr_cut_out_bytes (rl .line , rl .cursor_pos - 1 , 1 );
115111 // set redraw parameters
112+ redraw_step_back = 1 ;
116113 redraw_from_cursor = true;
117- redraw_step_forward = 1 ;
118- }
119- } else if (escape_seq == ESEQ_ESC ) {
120- switch (c ) {
121- case '[' :
122- escape_seq = ESEQ_ESC_BRACKET ;
123- break ;
124- case 'O' :
125- escape_seq = ESEQ_ESC_O ;
126- break ;
127- default :
128- DEBUG_printf ("(ESC %d)" , c );
129- escape_seq = ESEQ_NONE ;
130114 }
131- } else if (escape_seq == ESEQ_ESC_BRACKET ) {
132- if ('0' <= c && c <= '9' ) {
133- escape_seq = ESEQ_ESC_BRACKET_DIGIT ;
134- escape_seq_buf [0 ] = c ;
135- } else {
136- escape_seq = ESEQ_NONE ;
137- if (c == 'A' ) {
138- // up arrow
139- if (hist_cur + 1 < READLINE_HIST_SIZE && MP_STATE_PORT (readline_hist )[hist_cur + 1 ] != NULL ) {
140- // increase hist num
141- hist_cur += 1 ;
142- // set line to history
143- line -> len = orig_line_len ;
144- vstr_add_str (line , MP_STATE_PORT (readline_hist )[hist_cur ]);
145- // set redraw parameters
146- redraw_step_back = cursor_pos - orig_line_len ;
147- redraw_from_cursor = true;
148- redraw_step_forward = line -> len - orig_line_len ;
149- }
150- } else if (c == 'B' ) {
151- // down arrow
152- if (hist_cur >= 0 ) {
153- // decrease hist num
154- hist_cur -= 1 ;
155- // set line to history
156- vstr_cut_tail_bytes (line , line -> len - orig_line_len );
157- if (hist_cur >= 0 ) {
158- vstr_add_str (line , MP_STATE_PORT (readline_hist )[hist_cur ]);
159- }
160- // set redraw parameters
161- redraw_step_back = cursor_pos - orig_line_len ;
162- redraw_from_cursor = true;
163- redraw_step_forward = line -> len - orig_line_len ;
164- }
165- } else if (c == 'C' ) {
166- // right arrow
167- if (cursor_pos < line -> len ) {
168- redraw_step_forward = 1 ;
169- }
170- } else if (c == 'D' ) {
171- // left arrow
172- if (cursor_pos > orig_line_len ) {
173- redraw_step_back = 1 ;
115+ } else if (32 <= c && c <= 126 ) {
116+ // printable character
117+ vstr_ins_char (rl .line , rl .cursor_pos , c );
118+ // set redraw parameters
119+ redraw_from_cursor = true;
120+ redraw_step_forward = 1 ;
121+ }
122+ } else if (rl .escape_seq == ESEQ_ESC ) {
123+ switch (c ) {
124+ case '[' :
125+ rl .escape_seq = ESEQ_ESC_BRACKET ;
126+ break ;
127+ case 'O' :
128+ rl .escape_seq = ESEQ_ESC_O ;
129+ break ;
130+ default :
131+ DEBUG_printf ("(ESC %d)" , c );
132+ rl .escape_seq = ESEQ_NONE ;
133+ }
134+ } else if (rl .escape_seq == ESEQ_ESC_BRACKET ) {
135+ if ('0' <= c && c <= '9' ) {
136+ rl .escape_seq = ESEQ_ESC_BRACKET_DIGIT ;
137+ rl .escape_seq_buf [0 ] = c ;
138+ } else {
139+ rl .escape_seq = ESEQ_NONE ;
140+ if (c == 'A' ) {
141+ // up arrow
142+ if (rl .hist_cur + 1 < READLINE_HIST_SIZE && MP_STATE_PORT (readline_hist )[rl .hist_cur + 1 ] != NULL ) {
143+ // increase hist num
144+ rl .hist_cur += 1 ;
145+ // set line to history
146+ rl .line -> len = rl .orig_line_len ;
147+ vstr_add_str (rl .line , MP_STATE_PORT (readline_hist )[rl .hist_cur ]);
148+ // set redraw parameters
149+ redraw_step_back = rl .cursor_pos - rl .orig_line_len ;
150+ redraw_from_cursor = true;
151+ redraw_step_forward = rl .line -> len - rl .orig_line_len ;
152+ }
153+ } else if (c == 'B' ) {
154+ // down arrow
155+ if (rl .hist_cur >= 0 ) {
156+ // decrease hist num
157+ rl .hist_cur -= 1 ;
158+ // set line to history
159+ vstr_cut_tail_bytes (rl .line , rl .line -> len - rl .orig_line_len );
160+ if (rl .hist_cur >= 0 ) {
161+ vstr_add_str (rl .line , MP_STATE_PORT (readline_hist )[rl .hist_cur ]);
174162 }
175- } else if (c == 'H' ) {
176- // home
177- goto home_key ;
178- } else if (c == 'F' ) {
179- // end
180- goto end_key ;
181- } else {
182- DEBUG_printf ("(ESC [ %d)" , c );
163+ // set redraw parameters
164+ redraw_step_back = rl .cursor_pos - rl .orig_line_len ;
165+ redraw_from_cursor = true;
166+ redraw_step_forward = rl .line -> len - rl .orig_line_len ;
167+ }
168+ } else if (c == 'C' ) {
169+ // right arrow
170+ if (rl .cursor_pos < rl .line -> len ) {
171+ redraw_step_forward = 1 ;
172+ }
173+ } else if (c == 'D' ) {
174+ // left arrow
175+ if (rl .cursor_pos > rl .orig_line_len ) {
176+ redraw_step_back = 1 ;
183177 }
178+ } else if (c == 'H' ) {
179+ // home
180+ goto home_key ;
181+ } else if (c == 'F' ) {
182+ // end
183+ goto end_key ;
184+ } else {
185+ DEBUG_printf ("(ESC [ %d)" , c );
184186 }
185- } else if (escape_seq == ESEQ_ESC_BRACKET_DIGIT ) {
186- if (c == '~' ) {
187- if (escape_seq_buf [0 ] == '1' || escape_seq_buf [0 ] == '7' ) {
187+ }
188+ } else if (rl .escape_seq == ESEQ_ESC_BRACKET_DIGIT ) {
189+ if (c == '~' ) {
190+ if (rl .escape_seq_buf [0 ] == '1' || rl .escape_seq_buf [0 ] == '7' ) {
188191home_key :
189- redraw_step_back = cursor_pos - orig_line_len ;
190- } else if (escape_seq_buf [0 ] == '4' || escape_seq_buf [0 ] == '8' ) {
192+ redraw_step_back = rl . cursor_pos - rl . orig_line_len ;
193+ } else if (rl . escape_seq_buf [0 ] == '4' || rl . escape_seq_buf [0 ] == '8' ) {
191194end_key :
192- redraw_step_forward = line -> len - cursor_pos ;
193- } else {
194- DEBUG_printf ("(ESC [ %c %d)" , escape_seq_buf [0 ], c );
195- }
195+ redraw_step_forward = rl .line -> len - rl .cursor_pos ;
196196 } else {
197- DEBUG_printf ("(ESC [ %c %d)" , escape_seq_buf [0 ], c );
198- }
199- escape_seq = ESEQ_NONE ;
200- } else if (escape_seq == ESEQ_ESC_O ) {
201- switch (c ) {
202- case 'H' :
203- goto home_key ;
204- case 'F' :
205- goto end_key ;
206- default :
207- DEBUG_printf ("(ESC O %d)" , c );
208- escape_seq = ESEQ_NONE ;
197+ DEBUG_printf ("(ESC [ %c %d)" , rl .escape_seq_buf [0 ], c );
209198 }
210199 } else {
211- escape_seq = ESEQ_NONE ;
200+ DEBUG_printf ( "(ESC [ %c %d)" , rl . escape_seq_buf [ 0 ], c ) ;
212201 }
202+ rl .escape_seq = ESEQ_NONE ;
203+ } else if (rl .escape_seq == ESEQ_ESC_O ) {
204+ switch (c ) {
205+ case 'H' :
206+ goto home_key ;
207+ case 'F' :
208+ goto end_key ;
209+ default :
210+ DEBUG_printf ("(ESC O %d)" , c );
211+ rl .escape_seq = ESEQ_NONE ;
212+ }
213+ } else {
214+ rl .escape_seq = ESEQ_NONE ;
215+ }
213216
214- // redraw command prompt, efficiently
215- // TODO we can probably use some more sophisticated VT100 commands here
216- if (redraw_step_back > 0 ) {
217- for (int i = 0 ; i < redraw_step_back ; i ++ ) {
218- stdout_tx_str ("\b" );
219- }
220- cursor_pos -= redraw_step_back ;
217+ // redraw command prompt, efficiently
218+ // TODO we can probably use some more sophisticated VT100 commands here
219+ if (redraw_step_back > 0 ) {
220+ for (int i = 0 ; i < redraw_step_back ; i ++ ) {
221+ stdout_tx_str ("\b" );
221222 }
222- if (redraw_from_cursor ) {
223- if (line -> len < last_line_len ) {
224- // erase old chars
225- for (int i = cursor_pos ; i < last_line_len ; i ++ ) {
226- stdout_tx_str (" " );
227- }
228- // step back
229- for (int i = cursor_pos ; i < last_line_len ; i ++ ) {
230- stdout_tx_str ("\b" );
231- }
223+ rl .cursor_pos -= redraw_step_back ;
224+ }
225+ if (redraw_from_cursor ) {
226+ if (rl .line -> len < last_line_len ) {
227+ // erase old chars
228+ for (int i = rl .cursor_pos ; i < last_line_len ; i ++ ) {
229+ stdout_tx_str (" " );
232230 }
233- // draw new chars
234- stdout_tx_strn (line -> buf + cursor_pos , line -> len - cursor_pos );
235- // move cursor forward if needed (already moved forward by length of line, so move it back)
236- for (int i = cursor_pos + redraw_step_forward ; i < line -> len ; i ++ ) {
231+ // step back
232+ for (int i = rl .cursor_pos ; i < last_line_len ; i ++ ) {
237233 stdout_tx_str ("\b" );
238234 }
239- cursor_pos += redraw_step_forward ;
240- } else if (redraw_step_forward > 0 ) {
241- // draw over old chars to move cursor forwards
242- stdout_tx_strn (line -> buf + cursor_pos , redraw_step_forward );
243- cursor_pos += redraw_step_forward ;
235+ }
236+ // draw new chars
237+ stdout_tx_strn (rl .line -> buf + rl .cursor_pos , rl .line -> len - rl .cursor_pos );
238+ // move cursor forward if needed (already moved forward by length of line, so move it back)
239+ for (int i = rl .cursor_pos + redraw_step_forward ; i < rl .line -> len ; i ++ ) {
240+ stdout_tx_str ("\b" );
241+ }
242+ rl .cursor_pos += redraw_step_forward ;
243+ } else if (redraw_step_forward > 0 ) {
244+ // draw over old chars to move cursor forwards
245+ stdout_tx_strn (rl .line -> buf + rl .cursor_pos , redraw_step_forward );
246+ rl .cursor_pos += redraw_step_forward ;
247+ }
248+
249+ return -1 ;
250+ }
251+
252+ void readline_note_newline () {
253+ rl .orig_line_len = rl .line -> len ;
254+ rl .cursor_pos = rl .orig_line_len ;
255+ }
256+
257+ void readline_init (vstr_t * line ) {
258+ rl .line = line ;
259+ rl .orig_line_len = line -> len ;
260+ rl .escape_seq = ESEQ_NONE ;
261+ rl .escape_seq_buf [0 ] = 0 ;
262+ rl .hist_cur = -1 ;
263+ rl .cursor_pos = rl .orig_line_len ;
264+ }
265+
266+ int readline (vstr_t * line , const char * prompt ) {
267+ stdout_tx_str (prompt );
268+ readline_init (line );
269+ for (;;) {
270+ int c = stdin_rx_chr ();
271+ int r = readline_process_char (c );
272+ if (r >= 0 ) {
273+ return r ;
244274 }
245275 }
246276}
0 commit comments