1 /*
2  *   Creation Date: <2003/12/28 14:16:31 samuel>
3  *   Time-stamp: <2004/01/07 10:37:40 samuel>
4  *
5  *	<cmdline.c>
6  *
7  *	OpenFirmwware User Interface
8  *
9  *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
10  *
11  *   This program is free software; you can redistribute it and/or
12  *   modify it under the terms of the GNU General Public License
13  *   version 2
14  *
15  */
16 
17 #include "config.h"
18 #include "libopenbios/bindings.h"
19 #include "packages.h"
20 #include "libc/vsprintf.h"
21 
22 typedef struct {
23 	char	*buf;		/* size: ncol+1 */
24 	char	*killbuf;	/* size: ncol+1 */
25 	char	*history;
26 	int	hsize;		/* size of history buffer */
27 	int	ncol;		/* #columns */
28 } cmdline_info_t;
29 
30 DECLARE_NODE( cmdline, INSTALL_OPEN, sizeof(cmdline_info_t),
31 	      "+/packages/cmdline" );
32 
33 static void
emit(int ch)34 emit( int ch )
35 {
36 	PUSH( ch );
37 	fword("emit");
38 }
39 
40 static int
emit_str(const char * str)41 emit_str( const char *str )
42 {
43 	int n = 0;
44 	while( *str ) {
45 		n++;
46 		emit( *str++ );
47 	}
48 	return n;
49 }
50 
51 static void
move_cursor(int n)52 move_cursor( int n )
53 {
54 	if( n >= 0 ) {
55 		while( n-- )
56 			emit( '\f' );
57 	} else {
58 		while( n++ )
59 			emit( 8 );
60 	}
61 }
62 
63 static void
clear(int n)64 clear( int n )
65 {
66 	int i;
67 	for( i=0; i<n; i++ )
68 		emit(' ');
69 	move_cursor( -n );
70 }
71 
72 static void
clearline(int pos,int n)73 clearline( int pos, int n )
74 {
75 	move_cursor( -pos );
76 	clear( n );
77 }
78 
79 static int
key(void)80 key( void )
81 {
82 	fword("key");
83 	return POP();
84 }
85 
86 /* ( -- flag ) */
87 static void
cmdline_open(cmdline_info_t * ci)88 cmdline_open( cmdline_info_t *ci )
89 {
90 	ci->ncol = 80;
91 	ci->buf = malloc( ci->ncol + 1 );
92 	ci->killbuf = malloc( ci->ncol + 1 );
93 
94 	ci->hsize = 40;
95 	ci->history = malloc( ci->hsize );
96 	ci->history[0] = 0;
97 
98 	RET( -1 );
99 }
100 
101 /* ( -- ) */
102 static void
cmdline_close(cmdline_info_t * ci)103 cmdline_close( cmdline_info_t *ci )
104 {
105 	free( ci->buf );
106 	free( ci->killbuf );
107 	free( ci->history );
108 }
109 
110 
111 static char *
history_get(cmdline_info_t * ci,int n)112 history_get( cmdline_info_t *ci, int n )
113 {
114 	char *p = ci->history;
115 	int len;
116 
117 	while( n-- && p )
118 		if( (p=strchr(p,'\n')) )
119 			p++;
120 
121 	ci->buf[0] = 0;
122 	if( !p )
123                 return NULL;
124 
125 	for( len=0; len <= ci->ncol && p[len] != '\n' && p[len] ; len++ )
126 		;
127 	memcpy( ci->buf, p, len );
128 	ci->buf[len] = 0;
129 	return p;
130 }
131 
132 static int
history_remove(cmdline_info_t * ci,int line)133 history_remove( cmdline_info_t *ci, int line )
134 {
135 	char *s, *p = history_get( ci, line );
136 
137 	if( !p || !(s=strchr(p, '\n')) )
138 		return 1;
139 	s++;
140 	memmove( p, s, strlen(s)+1 );
141 	return 0;
142 }
143 
144 static int /* ( -- ) */
add_to_history(cmdline_info_t * ci,char * str)145 add_to_history( cmdline_info_t *ci, char *str )
146 {
147 	int n, len;
148 
149 	if( !ci->history )
150 		return 0;
151 	len = strlen(str);
152 	if( !len )
153 		return 0;
154 
155 	/* make room for line in history */
156 	for( ;; ) {
157 		char *p;
158 		n = strlen(ci->history) + 1;
159 
160 		if( n + len + 1 <= ci->hsize )
161 			break;
162 
163 		if( !(p=strrchr(ci->history,'\n')) )
164 			return 0;
165 		*p = 0;
166 		if( !(p=strrchr(ci->history, '\n')) )
167 			p = ci->history-1;
168 		p[1] = 0;
169 	}
170 
171 	memmove( ci->history + len + 1, ci->history, n );
172 	memcpy( ci->history, str, len );
173 	ci->history[ len ] = '\n';
174 	return 1;
175 }
176 
177 static void /* ( -- ) */
cmdline_prompt(cmdline_info_t * ci)178 cmdline_prompt( cmdline_info_t *ci )
179 {
180 	int cur_added=0, histind=0, ch, i, pos=0, n=0, prompt=1;
181         char *buf;
182 	int terminate = 0;
183 
184 	buf = ci->buf;
185 	selfword("prepare");
186 
187 	emit('\n');
188 #ifdef NOLEAVE
189 	for (;;)
190 #else
191 	while (rstackcnt && !terminate)
192 #endif
193 	{
194 		int drop = 0;
195 		terminate = 0;
196 
197 		if( prompt ) {
198 			fword("print-prompt");
199 			buf[0] = 0;
200 			cur_added = prompt = histind = pos = n = 0;
201 		}
202 
203 		ch = key();
204 		switch( ch ) {
205 		case 27:
206 			switch( key() ) {
207 			case 'f':
208 				while( buf[pos] == ' ' )
209 					emit( buf[pos++] );
210 				while( buf[pos] && buf[pos] != ' ' )
211 					emit( buf[pos++] );
212 				break;
213 
214 			case 'b':
215 				while( pos && buf[pos-1] == ' ' ) {
216 					move_cursor( -1 );
217 					pos--;
218 				}
219 				while( pos && buf[pos-1] != ' ' ) {
220 					move_cursor( -1 );
221 					pos--;
222 				}
223 				break;
224 			case '[':
225 				switch( key() ) {
226 				case 'A':
227 					goto go_up;
228 				case 'B':
229 					goto go_down;
230 				case 'C':
231 					goto go_right;
232 				case 'D':
233 					goto go_left;
234 				case '3':
235 					key();
236 					goto delete;
237 				}
238 				break;
239 			case 'O':
240 				switch(key()) {
241 				case 'F':
242 					goto go_end;
243 				case 'H':
244 					goto go_home;
245 				}
246 				break;
247 			}
248 			break;
249 		case '\n':
250 		case '\r':
251 			if( cur_added )
252 				history_remove( ci, 0 );
253 			add_to_history( ci, ci->buf );
254 
255 			emit_str( &buf[pos] );
256 			emit(' ');
257 			PUSH( feval(buf) );
258 			fword("print-status");
259 
260 			/* Leave the interpreter if terminate? value set */
261 			fword("terminate?");
262 			if (POP())
263 				terminate = 1;
264 
265 			prompt = 1;
266 			break;
267 
268 		case 3: /* ^c */
269 			emit_str("\n");
270 			prompt = 1;
271 			if( cur_added )
272 				history_remove( ci, 0 );
273 			break;
274 
275 		case 4: /* ^d */
276 delete:
277 			if( pos == n )
278 				break;
279 			emit( buf[pos++] );
280 			/* fall through */
281 
282 		case 8: /* ^h */
283 		case 127: /* backspace */
284 			drop = 1;
285 			if( !pos )
286 				break;
287 			move_cursor( -1 );
288 			emit_str( &buf[pos] );
289 			emit(' ');
290 			memmove( &buf[pos-1], &buf[pos], n+1-pos );
291 			move_cursor( pos-n-1 );
292 			pos--;
293 			n--;
294 			break;
295 
296 		case 1: /* ^a */
297 go_home:
298 			move_cursor( -pos );
299 			pos = 0;
300 			break;
301 
302 		case 5: /* ^e */
303 go_end:
304 			pos += emit_str( &buf[pos] );
305 			break;
306 
307 		//case 68: /* left */
308 		//	drop = 1;
309 		case 2: /* ^b */
310 go_left:
311 			if( pos ) {
312 				move_cursor( -1 );
313 				pos--;
314 			}
315 			break;
316 
317 		//case 67: /* right */
318 		//	drop = 1;
319 		case 6: /* ^f */
320 go_right:
321 			if( pos < n )
322 				emit( buf[pos++] );
323 			break;
324 
325 		case 11: /* ^k */
326 			strcpy( ci->killbuf, &buf[pos] );
327 			clear( n-pos );
328 			n = pos;
329 			buf[pos] = 0;
330 			break;
331 
332 		case 25: /* ^y */
333 			for( i=0; n < ci->ncol && ci->killbuf[i] ; i++, n++ ) {
334 				memmove( &buf[pos+1], &buf[pos], n+1-pos );
335 				buf[pos] = ci->killbuf[i];
336 				move_cursor( 1-emit_str(&buf[pos++]) );
337 			}
338 			break;
339 
340 		case 9: /* TAB */
341 			for( i=0; n < ci->ncol && (!i || (pos%4)) ; i++, n++ ) {
342 				memmove( &buf[pos+1], &buf[pos], n+1-pos );
343 				buf[pos] = ' ';
344 				move_cursor( 1-emit_str(&buf[pos++]) );
345 			}
346 			break;
347 
348 		case 12: /* ^l */
349 			move_cursor( -ci->ncol -pos );
350 			fword("print-prompt");
351 			move_cursor( pos-emit_str(buf) );
352 			break;
353 
354 		//case 66: /* down */
355 		//	drop = 1;
356 		case 14: /* ^n */
357 go_down:
358 			if( !histind )
359 				break;
360 			history_get( ci, --histind - 1);
361 			clearline( pos, n );
362 			emit_str( buf );
363 			pos = n = strlen( buf );
364 			if( !histind && cur_added ) {
365 				cur_added = 0;
366 				history_remove( ci, 0 );
367 			}
368 			break;
369 
370 		//case 65: /* up */
371 		//	drop = 1;
372 		case 16: /* ^p */
373 go_up:
374 			if( !histind && add_to_history(ci, ci->buf) ) {
375 				cur_added = 1;
376 				histind++;
377 			}
378 			if( history_get(ci, histind) )
379 				histind++;
380 			clearline( pos, n );
381 			emit_str( buf );
382 			pos = n = strlen( buf );
383 			break;
384 		}
385 		if( (unsigned int)ch < 32 )
386 			drop = 1;
387 
388 		if( !drop && n < ci->ncol ) {
389 			memmove( &buf[pos+1], &buf[pos], n+1-pos );
390 			n++;
391 			buf[pos] = ch;
392 			move_cursor( 1-emit_str(&buf[pos++]) );
393 		}
394 	}
395 
396 	/* we only get here if terminate? is non-zero; this should
397          * only ever be done for a subordinate forth interpreter
398          * e.g. for debugging */
399 
400 	/* Reset stack and terminate? */
401 	rstackcnt = dbgrstackcnt;
402 	feval("0 to terminate?");
403 }
404 
405 NODE_METHODS( cmdline ) = {
406 	{ "open",       cmdline_open      },
407 	{ "close",      cmdline_close       },
408 	{ "cmdline",     cmdline_prompt      },
409 };
410 
411 void
cmdline_init(void)412 cmdline_init( void )
413 {
414 	REGISTER_NODE( cmdline );
415 }
416