Tomaz enabled some numpad keys in the console and the insert key, and also fixed...
[divverent/darkplaces.git] / console.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // console.c
21
22 #ifndef WIN32
23 # include <unistd.h>
24 #endif
25 #include <time.h>
26 #include "quakedef.h"
27
28 int con_linewidth;
29
30 float con_cursorspeed = 4;
31
32 #define         CON_TEXTSIZE    131072
33
34 // total lines in console scrollback
35 int con_totallines;
36 // lines up from bottom to display
37 int con_backscroll;
38 // where next message will be printed
39 int con_current;
40 // offset in current line for next print
41 int con_x;
42 char *con_text = 0;
43
44 //seconds
45 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
46 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
47
48 #define MAX_NOTIFYLINES 32
49 // cl.time time the line was generated for transparent notify lines
50 float con_times[MAX_NOTIFYLINES];
51
52 int con_vislines;
53
54 #define MAXCMDLINE      256
55 extern char key_lines[32][MAXCMDLINE];
56 extern int edit_line;
57 extern int key_linepos;
58 extern int key_insert;
59
60
61 qboolean con_initialized;
62
63 mempool_t *console_mempool;
64
65
66 /*
67 ==============================================================================
68
69 LOGGING
70
71 ==============================================================================
72 */
73
74 cvar_t log_file = {0, "log_file",""};
75 cvar_t log_sync = {0, "log_sync","0"};
76 char crt_log_file [MAX_OSPATH] = "";
77 qfile_t* logfile = NULL;
78
79 qbyte* logqueue = NULL;
80 size_t logq_ind = 0;
81 size_t logq_size = 0;
82
83 void Log_ConPrint (const char *msg);
84
85 /*
86 ====================
87 Log_Timestamp
88 ====================
89 */
90 const char* Log_Timestamp (const char *desc)
91 {
92         static char timestamp [128];
93         time_t crt_time;
94         const struct tm *crt_tm;
95         char timestring [64];
96
97         // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
98         time (&crt_time);
99         crt_tm = localtime (&crt_time);
100         strftime (timestring, sizeof (timestring), "%a %b %d %T %Y", crt_tm);
101
102         if (desc != NULL)
103                 snprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
104         else
105                 snprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
106
107         return timestamp;
108 }
109
110
111 /*
112 ====================
113 Log_Init
114 ====================
115 */
116 void Log_Init (void)
117 {
118         Cvar_RegisterVariable (&log_file);
119         Cvar_RegisterVariable (&log_sync);
120
121         // support for the classic Quake option
122         if (COM_CheckParm ("-condebug") != 0)
123         {
124                 Cvar_SetQuick (&log_file, "qconsole.log");
125                 Cvar_SetValueQuick (&log_sync, 1);
126                 unlink (va("%s/qconsole.log", fs_gamedir));
127         }
128
129         // Allocate a log queue
130         logq_size = 512;
131         logqueue = Mem_Alloc (tempmempool, logq_size);
132         logq_ind = 0;
133 }
134
135
136 /*
137 ====================
138 Log_Open
139 ====================
140 */
141 void Log_Open (void)
142 {
143         if (logfile != NULL || log_file.string[0] == '\0')
144                 return;
145
146         logfile = FS_Open (log_file.string, "at", false);
147         if (logfile != NULL)
148         {
149                 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
150                 FS_Print (logfile, Log_Timestamp ("Log started"));
151         }
152 }
153
154
155 /*
156 ====================
157 Log_Close
158 ====================
159 */
160 void Log_Close (void)
161 {
162         if (logfile == NULL)
163                 return;
164
165         FS_Print (logfile, Log_Timestamp ("Log stopped"));
166         FS_Print (logfile, "\n");
167         FS_Close (logfile);
168
169         logfile = NULL;
170         crt_log_file[0] = '\0';
171 }
172
173
174 /*
175 ====================
176 Log_Start
177 ====================
178 */
179 void Log_Start (void)
180 {
181         Log_Open ();
182
183         // Dump the contents of the log queue into the log file and free it
184         if (logqueue != NULL)
185         {
186                 if (logfile != NULL && logq_ind != 0)
187                         FS_Write (logfile, logqueue, logq_ind);
188                 Mem_Free (logqueue);
189                 logqueue = NULL;
190                 logq_ind = 0;
191                 logq_size = 0;
192         }
193 }
194
195
196 /*
197 ================
198 Log_ConPrint
199 ================
200 */
201 void Log_ConPrint (const char *msg)
202 {
203         // Until the host is completely initialized, we maintain a log queue
204         // to store the messages, since the log can't be started before
205         if (logqueue != NULL)
206         {
207                 size_t remain = logq_size - logq_ind;
208                 size_t len = strlen (msg);
209
210                 // If we need to enlarge the log queue
211                 if (len > remain)
212                 {
213                         unsigned int factor = ((logq_ind + len) / logq_size) + 1;
214                         qbyte* newqueue;
215
216                         logq_size *= factor;
217                         newqueue = Mem_Alloc (tempmempool, logq_size);
218                         memcpy (newqueue, logqueue, logq_ind);
219                         Mem_Free (logqueue);
220                         logqueue = newqueue;
221                         remain = logq_size - logq_ind;
222                 }
223                 memcpy (&logqueue[logq_ind], msg, len);
224                 logq_ind += len;
225
226                 return;
227         }
228
229         // Check if log_file has changed
230         if (strcmp (crt_log_file, log_file.string) != 0)
231         {
232                 Log_Close ();
233                 Log_Open ();
234         }
235
236         // If a log file is available
237         if (logfile != NULL)
238         {
239                 FS_Print (logfile, msg);
240                 if (log_sync.integer)
241                         FS_Flush (logfile);
242         }
243 }
244
245
246 /*
247 ================
248 Log_Print
249 ================
250 */
251 void Log_Print (const char *logfilename, const char *msg)
252 {
253         qfile_t *file;
254         file = FS_Open(logfilename, "at", true);
255         if (file)
256         {
257                 FS_Print(file, msg);
258                 FS_Close(file);
259         }
260 }
261
262 /*
263 ================
264 Log_Printf
265 ================
266 */
267 void Log_Printf (const char *logfilename, const char *fmt, ...)
268 {
269         qfile_t *file;
270
271         file = FS_Open (logfilename, "at", true);
272         if (file != NULL)
273         {
274                 va_list argptr;
275
276                 va_start (argptr, fmt);
277                 FS_VPrintf (file, fmt, argptr);
278                 va_end (argptr);
279
280                 FS_Close (file);
281         }
282 }
283
284
285 /*
286 ==============================================================================
287
288 CONSOLE
289
290 ==============================================================================
291 */
292
293 /*
294 ================
295 Con_ToggleConsole_f
296 ================
297 */
298 void Con_ToggleConsole_f (void)
299 {
300         // toggle the 'user wants console' bit
301         key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
302         memset (con_times, 0, sizeof(con_times));
303 }
304
305 /*
306 ================
307 Con_Clear_f
308 ================
309 */
310 void Con_Clear_f (void)
311 {
312         if (con_text)
313                 memset (con_text, ' ', CON_TEXTSIZE);
314 }
315
316
317 /*
318 ================
319 Con_ClearNotify
320 ================
321 */
322 void Con_ClearNotify (void)
323 {
324         int i;
325
326         for (i=0 ; i<MAX_NOTIFYLINES ; i++)
327                 con_times[i] = 0;
328 }
329
330
331 /*
332 ================
333 Con_MessageMode_f
334 ================
335 */
336 void Con_MessageMode_f (void)
337 {
338         key_dest = key_message;
339         chat_team = false;
340 }
341
342
343 /*
344 ================
345 Con_MessageMode2_f
346 ================
347 */
348 void Con_MessageMode2_f (void)
349 {
350         key_dest = key_message;
351         chat_team = true;
352 }
353
354
355 /*
356 ================
357 Con_CheckResize
358
359 If the line width has changed, reformat the buffer.
360 ================
361 */
362 void Con_CheckResize (void)
363 {
364         int i, j, width, oldwidth, oldtotallines, numlines, numchars;
365         char tbuf[CON_TEXTSIZE];
366
367         width = (vid.conwidth >> 3);
368
369         if (width == con_linewidth)
370                 return;
371
372         if (width < 1)                  // video hasn't been initialized yet
373         {
374                 width = 80;
375                 con_linewidth = width;
376                 con_totallines = CON_TEXTSIZE / con_linewidth;
377                 memset (con_text, ' ', CON_TEXTSIZE);
378         }
379         else
380         {
381                 oldwidth = con_linewidth;
382                 con_linewidth = width;
383                 oldtotallines = con_totallines;
384                 con_totallines = CON_TEXTSIZE / con_linewidth;
385                 numlines = oldtotallines;
386
387                 if (con_totallines < numlines)
388                         numlines = con_totallines;
389
390                 numchars = oldwidth;
391
392                 if (con_linewidth < numchars)
393                         numchars = con_linewidth;
394
395                 memcpy (tbuf, con_text, CON_TEXTSIZE);
396                 memset (con_text, ' ', CON_TEXTSIZE);
397
398                 for (i=0 ; i<numlines ; i++)
399                 {
400                         for (j=0 ; j<numchars ; j++)
401                         {
402                                 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
403                                                 tbuf[((con_current - i + oldtotallines) %
404                                                           oldtotallines) * oldwidth + j];
405                         }
406                 }
407
408                 Con_ClearNotify ();
409         }
410
411         con_backscroll = 0;
412         con_current = con_totallines - 1;
413 }
414
415 /*
416 ================
417 Con_Init
418 ================
419 */
420 void Con_Init (void)
421 {
422         console_mempool = Mem_AllocPool("console");
423         con_text = Mem_Alloc(console_mempool, CON_TEXTSIZE);
424         memset (con_text, ' ', CON_TEXTSIZE);
425         con_linewidth = -1;
426         Con_CheckResize ();
427
428         Con_Print("Console initialized.\n");
429
430         // register our cvars
431         Cvar_RegisterVariable (&con_notifytime);
432         Cvar_RegisterVariable (&con_notify);
433
434         // register our commands
435         Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
436         Cmd_AddCommand ("messagemode", Con_MessageMode_f);
437         Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
438         Cmd_AddCommand ("clear", Con_Clear_f);
439         con_initialized = true;
440 }
441
442
443 /*
444 ===============
445 Con_Linefeed
446 ===============
447 */
448 void Con_Linefeed (void)
449 {
450         con_x = 0;
451         con_current++;
452         memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
453 }
454
455 /*
456 ================
457 Con_PrintToHistory
458
459 Handles cursor positioning, line wrapping, etc
460 All console printing must go through this in order to be displayed
461 If no console is visible, the notify window will pop up.
462 ================
463 */
464 void Con_PrintToHistory(const char *txt)
465 {
466         int y, c, l, mask;
467         static int cr;
468
469         con_backscroll = 0;
470
471         if (txt[0] == 1)
472         {
473                 mask = 128;             // go to colored text
474                 S_LocalSound ("misc/talk.wav");
475         // play talk wav
476                 txt++;
477         }
478         else if (txt[0] == 2)
479         {
480                 mask = 128;             // go to colored text
481                 txt++;
482         }
483         else
484                 mask = 0;
485
486
487         while ( (c = *txt) )
488         {
489         // count word length
490                 for (l=0 ; l< con_linewidth ; l++)
491                         if ( txt[l] <= ' ')
492                                 break;
493
494         // word wrap
495                 if (l != con_linewidth && (con_x + l > con_linewidth) )
496                         con_x = 0;
497
498                 txt++;
499
500                 if (cr)
501                 {
502                         con_current--;
503                         cr = false;
504                 }
505
506
507                 if (!con_x)
508                 {
509                         Con_Linefeed ();
510                 // mark time for transparent overlay
511                         if (con_current >= 0)
512                         {
513                                 if (con_notify.integer < 0)
514                                         Cvar_SetValueQuick(&con_notify, 0);
515                                 if (con_notify.integer > MAX_NOTIFYLINES)
516                                         Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
517                                 if (con_notify.integer > 0)
518                                         con_times[con_current % con_notify.integer] = cl.time;
519                         }
520                 }
521
522                 switch (c)
523                 {
524                 case '\n':
525                         con_x = 0;
526                         break;
527
528                 case '\r':
529                         con_x = 0;
530                         cr = 1;
531                         break;
532
533                 default:        // display character and advance
534                         y = con_current % con_totallines;
535                         con_text[y*con_linewidth+con_x] = c | mask;
536                         con_x++;
537                         if (con_x >= con_linewidth)
538                                 con_x = 0;
539                         break;
540                 }
541
542         }
543 }
544
545 /*
546 ================
547 Con_Print
548
549 Prints to all appropriate console targets
550 ================
551 */
552 void Con_Print(const char *msg)
553 {
554         // also echo to debugging console
555         Sys_Print(msg);
556
557         // log all messages to file
558         Log_ConPrint (msg);
559
560         if (!con_initialized)
561                 return;
562
563         if (cls.state == ca_dedicated)
564                 return;         // no graphics mode
565
566         // write it to the scrollable buffer
567         Con_PrintToHistory(msg);
568 }
569
570
571 // LordHavoc: increased from 4096 to 16384
572 #define MAXPRINTMSG     16384
573
574 /*
575 ================
576 Con_Printf
577
578 Prints to all appropriate console targets
579 ================
580 */
581 void Con_Printf(const char *fmt, ...)
582 {
583         va_list argptr;
584         char msg[MAXPRINTMSG];
585
586         va_start(argptr,fmt);
587         vsprintf(msg,fmt,argptr);
588         va_end(argptr);
589
590         Con_Print(msg);
591 }
592
593 /*
594 ================
595 Con_DPrint
596
597 A Con_Print that only shows up if the "developer" cvar is set
598 ================
599 */
600 void Con_DPrint(const char *msg)
601 {
602         if (!developer.integer)
603                 return;                 // don't confuse non-developers with techie stuff...
604         Con_Print(msg);
605 }
606
607 /*
608 ================
609 Con_DPrintf
610
611 A Con_Printf that only shows up if the "developer" cvar is set
612 ================
613 */
614 void Con_DPrintf(const char *fmt, ...)
615 {
616         va_list argptr;
617         char msg[MAXPRINTMSG];
618
619         if (!developer.integer)
620                 return;                 // don't confuse non-developers with techie stuff...
621
622         va_start(argptr,fmt);
623         vsprintf(msg,fmt,argptr);
624         va_end(argptr);
625
626         Con_Print(msg);
627 }
628
629
630 /*
631 ================
632 Con_SafePrint
633
634 Okay to call even when the screen can't be updated
635 ==================
636 */
637 void Con_SafePrint(const char *msg)
638 {
639         Con_Print(msg);
640 }
641
642 /*
643 ==================
644 Con_SafePrintf
645
646 Okay to call even when the screen can't be updated
647 ==================
648 */
649 void Con_SafePrintf(const char *fmt, ...)
650 {
651         va_list argptr;
652         char msg[MAXPRINTMSG];
653
654         va_start(argptr,fmt);
655         vsprintf(msg,fmt,argptr);
656         va_end(argptr);
657
658         Con_Print(msg);
659 }
660
661
662 /*
663 ==============================================================================
664
665 DRAWING
666
667 ==============================================================================
668 */
669
670
671 /*
672 ================
673 Con_DrawInput
674
675 The input line scrolls horizontally if typing goes beyond the right edge
676
677 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
678 ================
679 */
680 void Con_DrawInput (void)
681 {
682         int             y;
683         int             i;
684         char editlinecopy[257], *text;
685
686         if (!key_consoleactive)
687                 return;         // don't draw anything
688
689         text = strcpy(editlinecopy, key_lines[edit_line]);
690
691         // Advanced Console Editing by Radix radix@planetquake.com
692         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
693         // use strlen of edit_line instead of key_linepos to allow editing
694         // of early characters w/o erasing
695
696         y = strlen(text);
697
698 // fill out remainder with spaces
699         for (i = y; i < 256; i++)
700                 text[i] = ' ';
701
702         // add the cursor frame
703         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
704                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
705
706 //      text[key_linepos + 1] = 0;
707
708         // prestep if horizontally scrolling
709         if (key_linepos >= con_linewidth)
710                 text += 1 + key_linepos - con_linewidth;
711
712         // draw it
713         DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
714
715         // remove cursor
716 //      key_lines[edit_line][key_linepos] = 0;
717 }
718
719
720 /*
721 ================
722 Con_DrawNotify
723
724 Draws the last few lines of output transparently over the game top
725 ================
726 */
727 void Con_DrawNotify (void)
728 {
729         int             x, v;
730         char    *text;
731         int             i;
732         float   time;
733         extern char chat_buffer[];
734         char    temptext[256];
735
736         if (con_notify.integer < 0)
737                 Cvar_SetValueQuick(&con_notify, 0);
738         if (con_notify.integer > MAX_NOTIFYLINES)
739                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
740         if (gamemode == GAME_TRANSFUSION)
741                 v = 8;
742         else
743                 v = 0;
744         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
745         {
746                 if (i < 0)
747                         continue;
748                 time = con_times[i % con_notify.integer];
749                 if (time == 0)
750                         continue;
751                 time = cl.time - time;
752                 if (time > con_notifytime.value)
753                         continue;
754                 text = con_text + (i % con_totallines)*con_linewidth;
755
756                 clearnotify = 0;
757
758                 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
759
760                 v += 8;
761         }
762
763
764         if (key_dest == key_message)
765         {
766                 clearnotify = 0;
767
768                 x = 0;
769
770                 // LordHavoc: speedup, and other improvements
771                 if (chat_team)
772                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
773                 else
774                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
775                 while (strlen(temptext) >= (size_t) con_linewidth)
776                 {
777                         DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
778                         strcpy(temptext, &temptext[con_linewidth]);
779                         v += 8;
780                 }
781                 if (strlen(temptext) > 0)
782                 {
783                         DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
784                         v += 8;
785                 }
786         }
787 }
788
789 /*
790 ================
791 Con_DrawConsole
792
793 Draws the console with the solid background
794 The typing input line at the bottom should only be drawn if typing is allowed
795 ================
796 */
797 extern char engineversion[40];
798 void Con_DrawConsole (int lines)
799 {
800         int i, y, rows, j;
801         char *text;
802
803         if (lines <= 0)
804                 return;
805
806 // draw the background
807         if (scr_conbrightness.value >= 0.01f)
808                 DrawQ_Pic(0, lines - vid.conheight, "gfx/conback", vid.conwidth, vid.conheight, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
809         else
810                 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
811         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
812
813 // draw the text
814         con_vislines = lines;
815
816         rows = (lines-16)>>3;           // rows of text to draw
817         y = lines - 16 - (rows<<3);     // may start slightly negative
818
819         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
820         {
821                 j = max(i - con_backscroll, 0);
822                 text = con_text + (j % con_totallines)*con_linewidth;
823
824                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
825         }
826
827 // draw the input prompt, user text, and cursor if desired
828         Con_DrawInput ();
829 }
830
831 /*
832         Con_DisplayList
833
834         New function for tab-completion system
835         Added by EvilTypeGuy
836         MEGA Thanks to Taniwha
837
838 */
839 void Con_DisplayList(const char **list)
840 {
841         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
842         const char **walk = list;
843
844         while (*walk) {
845                 len = strlen(*walk);
846                 if (len > maxlen)
847                         maxlen = len;
848                 walk++;
849         }
850         maxlen += 1;
851
852         while (*list) {
853                 len = strlen(*list);
854                 if (pos + maxlen >= width) {
855                         Con_Print("\n");
856                         pos = 0;
857                 }
858
859                 Con_Print(*list);
860                 for (i = 0; i < (maxlen - len); i++)
861                         Con_Print(" ");
862
863                 pos += maxlen;
864                 list++;
865         }
866
867         if (pos)
868                 Con_Print("\n\n");
869 }
870
871 /*
872         Con_CompleteCommandLine
873
874         New function for tab-completion system
875         Added by EvilTypeGuy
876         Thanks to Fett erich@heintz.com
877         Thanks to taniwha
878
879 */
880 void Con_CompleteCommandLine (void)
881 {
882         const char *cmd = "", *s;
883         const char **list[3] = {0, 0, 0};
884         int c, v, a, i, cmd_len;
885
886         s = key_lines[edit_line] + 1;
887         // Count number of possible matches
888         c = Cmd_CompleteCountPossible(s);
889         v = Cvar_CompleteCountPossible(s);
890         a = Cmd_CompleteAliasCountPossible(s);
891
892         if (!(c + v + a))       // No possible matches
893                 return;
894
895         if (c + v + a == 1) {
896                 if (c)
897                         list[0] = Cmd_CompleteBuildList(s);
898                 else if (v)
899                         list[0] = Cvar_CompleteBuildList(s);
900                 else
901                         list[0] = Cmd_CompleteAliasBuildList(s);
902                 cmd = *list[0];
903                 cmd_len = strlen (cmd);
904         } else {
905                 if (c)
906                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
907                 if (v)
908                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
909                 if (a)
910                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
911
912                 cmd_len = strlen (s);
913                 do {
914                         for (i = 0; i < 3; i++) {
915                                 char ch = cmd[cmd_len];
916                                 const char **l = list[i];
917                                 if (l) {
918                                         while (*l && (*l)[cmd_len] == ch)
919                                                 l++;
920                                         if (*l)
921                                                 break;
922                                 }
923                         }
924                         if (i == 3)
925                                 cmd_len++;
926                 } while (i == 3);
927                 // 'quakebar'
928                 Con_Print("\n\35");
929                 for (i = 0; i < con_linewidth - 4; i++)
930                         Con_Print("\36");
931                 Con_Print("\37\n");
932
933                 // Print Possible Commands
934                 if (c) {
935                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
936                         Con_DisplayList(list[0]);
937                 }
938
939                 if (v) {
940                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
941                         Con_DisplayList(list[1]);
942                 }
943
944                 if (a) {
945                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
946                         Con_DisplayList(list[2]);
947                 }
948         }
949
950         if (cmd) {
951                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
952                 key_linepos = cmd_len + 1;
953                 if (c + v + a == 1) {
954                         key_lines[edit_line][key_linepos] = ' ';
955                         key_linepos++;
956                 }
957                 key_lines[edit_line][key_linepos] = 0;
958         }
959         for (i = 0; i < 3; i++)
960                 if (list[i])
961                         Mem_Free((void *)list[i]);
962 }
963