]> icculus.org git repositories - taylor/freespace2.git/blob - src/debugconsole/console.cpp
The Great Newline Fix
[taylor/freespace2.git] / src / debugconsole / console.cpp
1 /*
2  * $Logfile: /Freespace2/code/DebugConsole/Console.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Routines for managing the debug console window.
8  *
9  * $Log$
10  * Revision 1.2  2002/05/07 03:16:43  theoddone33
11  * The Great Newline Fix
12  *
13  * Revision 1.1.1.1  2002/05/03 03:28:08  root
14  * Initial import.
15  *
16  * 
17  * 4     6/04/99 10:35a Dave
18  * 
19  * 3     10/13/98 9:28a Dave
20  * Started neatening up freespace.h. Many variables renamed and
21  * reorganized. Added AlphaColors.[h,cpp]
22  * 
23  * 2     10/07/98 10:52a Dave
24  * Initial checkin.
25  * 
26  * 1     10/07/98 10:48a Dave
27  * 
28  * 17    2/05/98 11:43a Allender
29  * enhcanced network statistic collection.  Made callback in debug console
30  * to do networking if player is in the console
31  * 
32  * 16    1/22/98 6:42p John
33  * Move game_flush out of debug console into freespace.     Made object
34  * pair code throw out some weapons that turn.   Added stats for how many
35  * object pair are actually checked.
36  * 
37  * 15    1/16/98 11:56a Allender
38  * ability to scroll back in debug console, and call game_flush() when
39  * leaving
40  * 
41  * 14    1/10/98 1:14p John
42  * Added explanation to debug console commands
43  * 
44  * 13    12/21/97 4:39p John
45  * fixed bug in name compare
46  * 
47  * 12    12/21/97 4:33p John
48  * Made debug console functions a class that registers itself
49  * automatically, so you don't need to add the function to
50  * debugfunctions.cpp.  
51  * 
52  * 11    9/13/97 9:31a Lawrance
53  * if playing a demo, clear key filter then reset it once done with debug
54  * console
55  * 
56  * 10    9/09/97 3:39p Sandeep
57  * warning level 4 bugs
58  * 
59  * 9     6/13/97 3:50p John
60  * sped up debug console printf
61  * 
62  * 8     6/13/97 3:27p John
63  * made debug console pretty
64  * 
65  * 7     6/09/97 9:24a John
66  * Changed the way fonts are set.
67  * 
68  * 6     5/29/97 3:09p John
69  * Took out debug menu.  
70  * Made software scaler draw larger bitmaps.
71  * Optimized Direct3D some.
72  * 
73  * 5     5/13/97 4:07p John
74  * made numbers also be marked as strings.
75  * 
76  * 4     4/28/97 5:24p John
77  * made so : and \ don't get parsed out in debug console.
78  * 
79  * 3     4/24/97 11:49a John
80  * added new lighting commands to console.
81  * 
82  * 2     4/24/97 10:36a John
83  * moved the debug console stuff into it's own lib... made it compile with
84  * Fred.
85  * 
86  * 1     4/24/97 10:16a John
87  * Initial rev
88  *
89  * $NoKeywords: $
90  */
91
92 #include <stdlib.h>
93 #include <stdio.h>
94 #include <stdarg.h>
95 #include <setjmp.h>
96 #include <string.h>
97
98 #include "pstypes.h"
99 #include "freespace.h"
100 #include "font.h"
101 #include "timer.h"
102 #include "2d.h"
103 #include "key.h"
104 #include "alphacolors.h"
105 #include "osapi.h"
106
107 #define MAX_COMMANDS 300
108
109 static int Num_debug_commands = 0;
110 static debug_command *Debug_command[MAX_COMMANDS];
111
112
113 debug_command::debug_command(char *_name, char *_help, void (*_func)() )
114 {
115         int i;
116
117         if ( Num_debug_commands >= MAX_COMMANDS )       {
118                 Int3();                 // Too many debug console commands!! Increase MAX_COMMANDS!!
119                 return;
120         }
121
122         for (i=0; i<Num_debug_commands; i++ )   {
123                 int ret  = stricmp( Debug_command[i]->name, _name );
124
125                 if ( ret == 0)  {
126                         Int3();         // This debug console command already exists!!!! 
127                         return;
128                 } else if ( ret > 0 )   {
129                         break;          // Insert it here
130
131                 } else if ( ret < 0 )   {
132                         // do nothing
133                 }
134         }
135
136         if ( i < Num_debug_commands )   {
137                 // Insert it at element i
138                 int j;
139                 for (j=Num_debug_commands; j>i; j-- )   {
140                         Debug_command[j] = Debug_command[j-1];
141                 }
142                 Debug_command[i] = this;                
143                 Num_debug_commands++;
144         } else {
145                 Debug_command[Num_debug_commands] = this;               
146                 Num_debug_commands++;
147         }
148
149         name = _name;
150         help = _help;
151         func = _func;
152 }
153
154 // some global variables
155 int Dc_command; // If this is set, then process the command
156 int Dc_help;            // If this is set, then print out the help text in the form, "usage: ... \nLong description\n" );
157 int Dc_status;          // If this is set, then print out the current status of the command.
158 char *Dc_arg;           // The (lowercased) string value of the argument retrieved from dc_arg
159 char *Dc_arg_org;       // Dc_arg before it got converted to lowercase
160 uint Dc_arg_type;       // The type of dc_arg.
161 char *Dc_command_line;          // The rest of the command line, from the end of the last processed arg on.
162 int Dc_arg_int;         // If Dc_arg_type & ARG_INT is set, then this is the value
163 float Dc_arg_float;     // If Dc_arg_type & ARG_FLOAT is set, then this is the value
164
165 int scroll_times = 0;           // incremented each time display scrolls
166
167 int debug_inited = 0;
168
169 #define DROWS 25
170 #define DCOLS 80
171
172 int debug_x=0, debug_y=0;
173 char debug_text[DROWS][DCOLS];
174
175
176 static char command_line[1024];
177 static int command_line_pos = 0;
178 #define DEBUG_HISTORY 16
179 static char oldcommand_line[DEBUG_HISTORY][1024];
180 int last_oldcommand=-1;
181 int command_scroll = 0;
182
183 ///=========================== SCANNER =======================
184 typedef enum {
185         LETTER, QUOTE, SPECIAL, EOF_CODE, DIGIT,
186 } CHAR_CODE;
187
188 typedef enum {
189         NO_TOKEN, IDENTIFIER, NUMBER, STRING, 
190 } TOKEN_CODE;
191
192
193 #define MAX_TOKEN_STRING_LENGTH 128
194
195 char                    scanner_ch;
196 TOKEN_CODE      scanner_token;
197
198 char scanner_token_string[MAX_TOKEN_STRING_LENGTH];
199 char scanner_word_string[MAX_TOKEN_STRING_LENGTH];
200 char * scanner_bufferp = "";
201 char * scanner_tokenp = scanner_token_string;
202
203 CHAR_CODE scanner_char_table[256];
204
205 #define scanner_char_code(x) scanner_char_table[x]
206
207 void scanner_get_char()
208 {
209         if ( *scanner_bufferp == '\0' ) {
210                 scanner_ch = 0;
211                 return;
212         }
213         scanner_ch = *scanner_bufferp++;
214 }
215
216 void scanner_init()
217 {
218         int ch;
219         for (ch=0; ch<256; ++ch) scanner_char_table[ch] = SPECIAL;
220         for (ch='0'; ch<='9'; ++ch) scanner_char_table[ch] = DIGIT;
221         for (ch='A'; ch<='Z'; ++ch) scanner_char_table[ch] = LETTER;
222         for (ch='a'; ch<='z'; ++ch) scanner_char_table[ch] = LETTER;
223
224         scanner_char_table['.'] = DIGIT;
225         scanner_char_table['-'] = DIGIT;
226         scanner_char_table['+'] = DIGIT;
227         
228         scanner_char_table['_'] = LETTER;
229         scanner_char_table[34] = QUOTE;
230         scanner_char_table[0] = EOF_CODE;
231
232
233         scanner_char_table[':'] = LETTER;
234         scanner_char_table['\\'] = LETTER;
235
236         scanner_ch = 0;
237 }
238
239
240 void scanner_skip_blanks()
241 {
242         while( (scanner_ch ==' ') || (scanner_ch =='\t') ) 
243                 scanner_get_char();
244 }
245
246
247 void scanner_downshift_word()
248 {
249         int offset = 'a' - 'A';
250         char * tp;
251
252         strcpy( scanner_word_string, scanner_token_string );
253         
254         tp = scanner_word_string;
255         do {
256                 *tp = (char)((*tp>='A') && (*tp <='Z') ? *tp + offset : *tp) ;
257                 tp++;
258         } while (*tp != '\0' );
259 }
260
261
262 void scanner_get_word()
263 {
264         while( (scanner_char_code(scanner_ch)==LETTER) || (scanner_char_code(scanner_ch)==DIGIT)  )     {
265                 *scanner_tokenp++ = scanner_ch;
266                 scanner_get_char();
267         }
268         *scanner_tokenp = '\0';
269
270         scanner_token = IDENTIFIER;
271 }
272
273
274 void scanner_get_string()
275 {
276         *scanner_tokenp++ = 34;
277         scanner_get_char();
278
279         while(scanner_ch != 34 )        {
280                 *scanner_tokenp++ = scanner_ch;
281                 scanner_get_char();
282         }
283         scanner_get_char();
284         *scanner_tokenp++ = 34;
285         *scanner_tokenp = '\0';
286         scanner_token = STRING;
287 }
288
289
290
291 void scanner_get_token()
292 {
293         scanner_skip_blanks();
294         scanner_tokenp = scanner_token_string;
295         *scanner_tokenp = 0;
296
297
298         switch( scanner_char_code(scanner_ch) ) {
299         case QUOTE:             scanner_get_string();   break;
300         case EOF_CODE:  scanner_token = NO_TOKEN;       break;
301         case DIGIT:
302         case LETTER:    scanner_get_word(); break;
303         default:                        
304                 *scanner_tokenp++ = scanner_ch;
305                 *scanner_tokenp = '\0';
306                 scanner_get_char();
307                 scanner_token = IDENTIFIER;
308                 break;
309         }
310
311         scanner_downshift_word();
312 }
313
314 void scanner_start_command( char * s )
315 {
316         scanner_bufferp = s;
317         scanner_get_char();
318 }
319
320
321 int Dc_debug_on = 0;
322 jmp_buf dc_bad_arg;
323
324 void dc_get_arg(uint type)
325 {
326         scanner_get_token();
327
328         Dc_command_line = scanner_bufferp;      
329         Dc_arg_org = scanner_token_string;
330         Dc_arg = scanner_word_string;
331
332         if (Dc_debug_on)        {
333                 dc_printf( "next arg is '%s', was originally '%s'\n", Dc_arg, Dc_arg_org );
334                 dc_printf( "Rest of the command line is '%s'\n", Dc_command_line );
335         }
336         
337         if ( scanner_token == NO_TOKEN )        {
338                 Dc_arg_type = ARG_NONE;
339         } else if ( scanner_token == IDENTIFIER )       {
340                 Dc_arg_type = ARG_STRING;
341         } else if ( scanner_token == STRING )   {
342                 Dc_arg_type = ARG_QUOTE;
343         } else {
344                 Dc_arg_type = ARG_STRING;
345         }
346
347         if ( Dc_arg_type & ARG_STRING ) {
348                 int i, num_digits, len;
349
350                 len = strlen(Dc_arg);
351                 num_digits = 0;
352
353                 for (i=0; i<len; i++)
354                         if ( scanner_char_table[Dc_arg[i]] == DIGIT ) num_digits++;
355
356                 if ( num_digits==len )  {
357                         Dc_arg_type |= ARG_FLOAT;
358                         Dc_arg_float = (float)atof(Dc_arg);
359                         if ( !strchr( Dc_arg, '.' ))    {
360                                 Dc_arg_type |= ARG_INT;
361                                 Dc_arg_int = atoi(Dc_arg);
362                         }
363                 } else {
364                         if ( (Dc_arg[0] == '0') && (Dc_arg[1] == 'x') ) {
365                                 char *p;
366                                 int n;
367                                 n = strtol(Dc_arg,&p,0);
368                                 if ( *p == 0 )  {
369                                         Dc_arg_type |= ARG_INT|ARG_HEX;
370                                         Dc_arg_int = n;
371                                 }
372                         } 
373                 }
374
375                 if (Dc_debug_on)        {
376                         if ( Dc_arg_type & ARG_FLOAT )
377                                 dc_printf( "Found float number! %f\n", Dc_arg_float );
378
379                         if ( Dc_arg_type & ARG_INT )
380                                 dc_printf( "Found int number! %d\n", Dc_arg_int );
381
382                         if ( Dc_arg_type & ARG_HEX )
383                                 dc_printf( "Found hex number! 0x%x\n", Dc_arg_int );
384                 }
385
386                 if ( !stricmp( Dc_arg, "on" ))
387                         Dc_arg_type |= ARG_TRUE;
388                 if ( !stricmp( Dc_arg, "true" ))
389                         Dc_arg_type |= ARG_TRUE;
390                 if ( !stricmp( Dc_arg, "off" ))
391                         Dc_arg_type |= ARG_FALSE;
392                 if ( !stricmp( Dc_arg, "false" ))
393                         Dc_arg_type |= ARG_FALSE;
394
395                 if ( !stricmp( Dc_arg, "+" ))
396                         Dc_arg_type |= ARG_PLUS;
397
398                 if ( !stricmp( Dc_arg, "-" ))
399                         Dc_arg_type |= ARG_MINUS;
400
401                 if ( !stricmp( Dc_arg, "," ))
402                         Dc_arg_type |= ARG_COMMA;
403         }
404
405         if ( Dc_arg_type & ARG_INT)     {
406                 if ( Dc_arg_int )       
407                         Dc_arg_type |= ARG_TRUE;
408                 else
409                         Dc_arg_type |= ARG_FALSE;
410         }
411
412         if ( !(Dc_arg_type&type) )      {
413                 if ( (Dc_arg_type & ARG_NONE) && !(type & ARG_NONE))    
414                         dc_printf( "Error: Not enough parameters.\n" );
415                 else
416                         dc_printf( "Error: '%s' invalid type\n", Dc_arg );
417                 longjmp(dc_bad_arg,1);
418         }
419
420 }
421
422 void debug_help();
423
424 void debug_do_command(char * command)
425 {
426
427         int i;
428         int mode = 0;
429
430         if ( strlen(command) < 1 ) return;
431         
432         Dc_debug_on = 0;
433         Dc_command_line = command;
434         scanner_start_command(command);
435
436         if (setjmp(dc_bad_arg) )        {
437                 return;
438         }
439         
440         dc_get_arg( ARG_ANY );
441
442         if ( !strcmp( Dc_arg, "debug" ) )       {
443                 Dc_debug_on = 1;
444                 dc_printf( "Command line: '%s'\n", Dc_command_line );
445                 dc_get_arg( ARG_ANY );
446         }
447
448         if ( !strcmp( Dc_arg, "?" ) )   {
449                 mode = 1;
450                 dc_get_arg( ARG_ANY );
451
452                 if ( Dc_arg_type&ARG_NONE )     {
453                         debug_help();           
454                         return;
455                 }
456         }
457
458         if ( !strcmp( Dc_arg, "help" ) || !strcmp( Dc_arg, "man" ) )    {
459                 mode = 2;
460                 dc_get_arg( ARG_ANY );
461                 if ( Dc_arg_type&ARG_NONE )     {
462                         debug_help();           
463                         return;
464                 }
465         }
466
467         if ( strstr( Dc_command_line, "?" ) )   {
468                 mode = 2;
469         }
470
471         if ( !(Dc_arg_type&ARG_STRING) )        {
472                 dc_printf( "Invalid keyword '%s'\n", Dc_arg );
473                 return;
474         }
475
476
477         if (Dc_debug_on)        {
478                 dc_printf( "Searching for command '%s'\n", Dc_arg );
479         }
480
481         for (i=0; i<Num_debug_commands; i++ )   {
482                 if ( !stricmp( Debug_command[i]->name, Dc_arg ))        {
483                 
484                         if (mode==0)    {
485                                 if (Dc_debug_on)        
486                                         dc_printf( "Calling function '%s'\n", Dc_arg );
487                                 Dc_command = 1;
488                                 Dc_help = 0;
489                                 Dc_status = 1;
490                         } else if (mode==1) {
491                                 if (Dc_debug_on)        
492                                         dc_printf( "Checking status for '%s'\n", Dc_arg );
493                                 Dc_command = 0;
494                                 Dc_help = 0;
495                                 Dc_status = 1;
496                         } else {
497                                 if (Dc_debug_on)        
498                                         dc_printf( "Doing help for '%s'\n", Dc_arg );
499                                 Dc_command = 0;
500                                 Dc_help = 1;
501                                 Dc_status = 0;
502                         }
503
504                         (*Debug_command[i]->func)();
505
506                         if (mode==0)    {
507                                 dc_get_arg(ARG_ANY);
508                                 if (!(Dc_arg_type&ARG_NONE))    {
509                                         dc_printf( "Ignoring the unused command line tail '%s %s'\n", Dc_arg_org, Dc_command_line );
510                                 }
511                         }
512
513                         return;
514                 }
515         }
516
517         dc_printf( "Unknown command '%s'\n", Dc_arg );
518 }
519
520 void debug_draw()
521 {
522         int i;
523         
524         gr_clear();
525         gr_set_font(FONT1);
526         gr_set_color_fast( &Color_bright );
527         gr_string( 0x8000, 3, "Debug Console" );
528
529         gr_set_color_fast( &Color_normal );
530
531         for (i=0; i<DROWS; i++ )        {
532                 gr_string( 0, i*16+16, debug_text[i] );
533         }
534
535         int t = timer_get_fixed_seconds() / (F1_0/3);
536         if ( t & 1 )    {
537                 int w,h;
538                 char c;
539
540                 c = debug_text[debug_y][command_line_pos+1];
541                 debug_text[debug_y][command_line_pos+1] = 0;
542
543                 gr_get_string_size( &w, &h, debug_text[debug_y] );
544                 
545                 //gr_string( w, debug_y*16, "_" );
546                 gr_rect(w+1,debug_y*16+1+16,2,14);
547         
548                 debug_text[debug_y][command_line_pos+1] = c;
549         }
550
551         gr_flip();
552 }
553
554
555 void debug_output( char c )
556 {
557         if ( c == '\t' )        {
558                 int next_tab = ((debug_x/28)+1)*28;
559
560                 if ( next_tab >= DCOLS-1 )      {
561                         debug_x=0;
562                         debug_y++;
563                         scroll_times++;
564                         if ( debug_y >= DROWS ) {
565                                 int i;
566                                 for (i=1; i<DROWS; i++ )
567                                         strcpy( debug_text[i-1], debug_text[i] );
568                                 debug_y = DROWS-1;
569                                 debug_x = 0;
570                                 debug_text[debug_y][debug_x] = 0;
571                         }
572                         debug_text[debug_y][debug_x] = 0;
573                         return;
574                 }
575         
576                 for ( ; debug_x < next_tab; )
577                         debug_text[debug_y][debug_x++] = ' ';
578                 debug_text[debug_y][debug_x] = 0;
579                 return;
580         }
581
582         if ( (c == '\n') || (debug_x >= DCOLS-1) )      {
583                 debug_x=0;
584                 debug_y++;
585                 scroll_times++;
586                 if ( debug_y >= DROWS ) {
587                         int i;
588                         for (i=1; i<DROWS; i++ )
589                                 strcpy( debug_text[i-1], debug_text[i] );
590                         debug_y = DROWS-1;
591                         debug_x = 0;
592                         debug_text[debug_y][debug_x] = 0;
593                 }
594                 debug_text[debug_y][debug_x] = 0;
595                 if ( c == '\n' ) return;
596         }
597
598         debug_text[debug_y][debug_x++] = c;
599         debug_text[debug_y][debug_x] = 0;
600 }
601
602 void dc_printf(char *format, ...)
603 {
604         char tmp[DCOLS*2];
605         va_list args;
606         
607         va_start(args, format);
608         vsprintf(tmp, format, args);
609         va_end(args);
610
611         char *p = tmp;
612         while( *p )     {
613                 debug_output(*p);
614                 p++;
615         } 
616 }
617
618
619
620 void debug_init()
621 {
622         int i;
623         if ( debug_inited ) return;
624
625         debug_inited = 1;
626
627         debug_x=0;
628         debug_y=0;
629
630         for (i=0; i<DROWS; i++ )        {
631                 debug_text[i][0] = 0;
632         }
633         
634         dc_printf("Debug console started.\n" );
635 }
636
637
638 void debug_console( void (*_func)() )
639 {
640         int done = 0;
641
642         scanner_init();
643
644         while( key_inkey() ){
645                 os_poll();
646         }
647
648         if ( !debug_inited ) debug_init();
649
650
651         debug_draw();
652         
653         while (!done)   {
654                 // poll the os
655                 os_poll();
656
657                 int k = key_inkey();
658                 switch( k )     {
659
660                 case KEY_SHIFTED+KEY_ENTER:
661                 case KEY_ESC:   
662                         done=1; break;
663
664                 case KEY_BACKSP:
665                         if ( command_line_pos > 0 )     {
666                                 command_line[--command_line_pos] = 0;
667                         }
668                         break;
669
670                 case KEY_F3:
671                         if ( last_oldcommand > -1 )     {
672                                 strcpy( command_line, oldcommand_line[last_oldcommand] );
673                                 command_line_pos = strlen(command_line);
674                                 command_line[command_line_pos] = 0;
675                         }
676                         break;
677
678                 case KEY_UP:
679                         command_scroll--;
680                         if (command_scroll<0) 
681                                 command_scroll = last_oldcommand;
682
683                         if ( command_scroll > -1 )      {
684                                 strcpy( command_line, oldcommand_line[command_scroll] );
685                                 command_line_pos = strlen(command_line);
686                                 command_line[command_line_pos] = 0;
687                         }
688                         break;
689
690                 case KEY_DOWN:
691                         command_scroll++;
692                         if (command_scroll>last_oldcommand) 
693                                 command_scroll = 0;
694                         if (command_scroll>last_oldcommand) 
695                                 command_scroll = -1;
696                         if ( command_scroll > -1 )      {
697                                 strcpy( command_line, oldcommand_line[command_scroll] );
698                                 command_line_pos = strlen(command_line);
699                                 command_line[command_line_pos] = 0;
700                         }
701                         break;
702
703                 case KEY_ENTER: {
704                         debug_output( '\n' );
705                         debug_draw();
706
707                         debug_do_command(command_line);
708
709                         int i, found = 0;
710                         for (i=0; i<=last_oldcommand; i++ )     {
711                                 if (!stricmp( oldcommand_line[i], command_line ))       {
712                                         found = 1;
713                                 }
714                         }
715                         if ( !found )   {
716                                 if ( last_oldcommand < DEBUG_HISTORY-1 )        {
717                                         last_oldcommand++;
718                                         strcpy( oldcommand_line[last_oldcommand], command_line);
719                                 } else {
720                                         int i;
721                                         for (i=0; i<last_oldcommand; i++ )      {
722                                                 strcpy( oldcommand_line[i], oldcommand_line[i+1] );
723                                         }
724                                         strcpy( oldcommand_line[last_oldcommand], command_line);
725                                 }
726                         }
727 //                      int i;
728 //                      for (i=0; i<=last_oldcommand; i++ )     {
729 //                              dc_printf( "OC %d. %s\n", i, oldcommand_line[i] );
730 //                      }
731         
732                         debug_output( '\n' );
733                         command_line_pos = 0;
734                         command_line[command_line_pos] = 0;
735
736                         command_scroll = 0;
737
738                         } 
739                         break;
740                 default:        {
741                                 ubyte c = (ubyte)key_to_ascii(k);
742                                 if ( c != 255 ) {
743                                         command_line[command_line_pos++] = c;
744                                         command_line[command_line_pos] = 0;
745                                 }
746                         }
747                 }
748
749                 strcpy( debug_text[debug_y], ">" );
750                 strcat( debug_text[debug_y], command_line );
751                 debug_draw();
752
753                 if ( _func ){
754                         _func();
755                 }
756         }
757
758         while( key_inkey() ){
759                 os_poll();
760         }
761 }
762
763 void debug_help()
764 {
765         int s, i;
766
767         dc_printf( "Available functions:\n\n" );        
768
769         s = scroll_times;
770         for (i=0; i<Num_debug_commands; i++ )   {
771                 dc_printf( " %s - %s\n", Debug_command[i]->name, Debug_command[i]->help );
772                 //mprintf(( "Scroll times %d\n", scroll_times - s ));
773                 if ( scroll_times - s > DROWS - 3 )     {
774                         int k;
775                         dc_printf( "       Press a key...B for back\n" );
776                         debug_draw();
777                         k = key_getch();
778                         s = scroll_times;
779                         if ( k == KEY_B )  {
780                                 i -= ((DROWS-3)*2);
781                                 if ( i <= 0 )
782                                         i = -1;
783                         }
784                 } 
785                 debug_draw();
786         }
787         dc_printf( "\n" );
788
789         dc_printf( "Typing '? function_name' will give the current status.\n" );
790         dc_printf( "Typing 'function_name ?' will give help on the function.\n" );
791         dc_printf( "Typing ? or help will give you help.\n");
792         dc_printf( "F3 selects last command line.\n" );
793 }
794