]> icculus.org git repositories - divverent/darkplaces.git/blob - console.c
Tomaz's patch to ask the driver for a gl_texture_anisotropy limit, and apply that...
[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         char editlinecopy[257], *text;
683
684         if (!key_consoleactive)
685                 return;         // don't draw anything
686
687         text = strcpy(editlinecopy, key_lines[edit_line]);
688
689         // Advanced Console Editing by Radix radix@planetquake.com
690         // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
691         // use strlen of edit_line instead of key_linepos to allow editing
692         // of early characters w/o erasing
693
694         // add the cursor frame
695         if ((int)(realtime*con_cursorspeed) & 1)                // cursor is visible
696                 text[key_linepos] = 11 + 130 * key_insert;      // either solid or triangle facing right
697
698         text[key_linepos + 1] = 0;
699
700         // prestep if horizontally scrolling
701         if (key_linepos >= con_linewidth)
702                 text += 1 + key_linepos - con_linewidth;
703
704         // draw it
705         DrawQ_String(0, con_vislines - 16, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
706
707         // remove cursor
708         key_lines[edit_line][key_linepos] = 0;
709 }
710
711
712 /*
713 ================
714 Con_DrawNotify
715
716 Draws the last few lines of output transparently over the game top
717 ================
718 */
719 void Con_DrawNotify (void)
720 {
721         int             x, v;
722         char    *text;
723         int             i;
724         float   time;
725         extern char chat_buffer[];
726         char    temptext[256];
727
728         if (con_notify.integer < 0)
729                 Cvar_SetValueQuick(&con_notify, 0);
730         if (con_notify.integer > MAX_NOTIFYLINES)
731                 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
732         if (gamemode == GAME_TRANSFUSION)
733                 v = 8;
734         else
735                 v = 0;
736         for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
737         {
738                 if (i < 0)
739                         continue;
740                 time = con_times[i % con_notify.integer];
741                 if (time == 0)
742                         continue;
743                 time = cl.time - time;
744                 if (time > con_notifytime.value)
745                         continue;
746                 text = con_text + (i % con_totallines)*con_linewidth;
747
748                 clearnotify = 0;
749
750                 DrawQ_String(0, v, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
751
752                 v += 8;
753         }
754
755
756         if (key_dest == key_message)
757         {
758                 clearnotify = 0;
759
760                 x = 0;
761
762                 // LordHavoc: speedup, and other improvements
763                 if (chat_team)
764                         sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
765                 else
766                         sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
767                 while (strlen(temptext) >= (size_t) con_linewidth)
768                 {
769                         DrawQ_String (0, v, temptext, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
770                         strcpy(temptext, &temptext[con_linewidth]);
771                         v += 8;
772                 }
773                 if (strlen(temptext) > 0)
774                 {
775                         DrawQ_String (0, v, temptext, 0, 8, 8, 1, 1, 1, 1, 0);
776                         v += 8;
777                 }
778         }
779 }
780
781 /*
782 ================
783 Con_DrawConsole
784
785 Draws the console with the solid background
786 The typing input line at the bottom should only be drawn if typing is allowed
787 ================
788 */
789 extern char engineversion[40];
790 void Con_DrawConsole (int lines)
791 {
792         int i, y, rows, j;
793         char *text;
794
795         if (lines <= 0)
796                 return;
797
798 // draw the background
799         if (scr_conbrightness.value >= 0.01f)
800                 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);
801         else
802                 DrawQ_Fill(0, lines - vid.conheight, vid.conwidth, vid.conheight, 0, 0, 0, scr_conalpha.value, 0);
803         DrawQ_String(vid.conwidth - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
804
805 // draw the text
806         con_vislines = lines;
807
808         rows = (lines-16)>>3;           // rows of text to draw
809         y = lines - 16 - (rows<<3);     // may start slightly negative
810
811         for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
812         {
813                 j = max(i - con_backscroll, 0);
814                 text = con_text + (j % con_totallines)*con_linewidth;
815
816                 DrawQ_String(0, y, text, con_linewidth, 8, 8, 1, 1, 1, 1, 0);
817         }
818
819 // draw the input prompt, user text, and cursor if desired
820         Con_DrawInput ();
821 }
822
823 /*
824         Con_DisplayList
825
826         New function for tab-completion system
827         Added by EvilTypeGuy
828         MEGA Thanks to Taniwha
829
830 */
831 void Con_DisplayList(const char **list)
832 {
833         int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
834         const char **walk = list;
835
836         while (*walk) {
837                 len = strlen(*walk);
838                 if (len > maxlen)
839                         maxlen = len;
840                 walk++;
841         }
842         maxlen += 1;
843
844         while (*list) {
845                 len = strlen(*list);
846                 if (pos + maxlen >= width) {
847                         Con_Print("\n");
848                         pos = 0;
849                 }
850
851                 Con_Print(*list);
852                 for (i = 0; i < (maxlen - len); i++)
853                         Con_Print(" ");
854
855                 pos += maxlen;
856                 list++;
857         }
858
859         if (pos)
860                 Con_Print("\n\n");
861 }
862
863 /*
864         Con_CompleteCommandLine
865
866         New function for tab-completion system
867         Added by EvilTypeGuy
868         Thanks to Fett erich@heintz.com
869         Thanks to taniwha
870
871 */
872 void Con_CompleteCommandLine (void)
873 {
874         const char *cmd = "", *s;
875         const char **list[3] = {0, 0, 0};
876         int c, v, a, i, cmd_len;
877
878         s = key_lines[edit_line] + 1;
879         // Count number of possible matches
880         c = Cmd_CompleteCountPossible(s);
881         v = Cvar_CompleteCountPossible(s);
882         a = Cmd_CompleteAliasCountPossible(s);
883
884         if (!(c + v + a))       // No possible matches
885                 return;
886
887         if (c + v + a == 1) {
888                 if (c)
889                         list[0] = Cmd_CompleteBuildList(s);
890                 else if (v)
891                         list[0] = Cvar_CompleteBuildList(s);
892                 else
893                         list[0] = Cmd_CompleteAliasBuildList(s);
894                 cmd = *list[0];
895                 cmd_len = strlen (cmd);
896         } else {
897                 if (c)
898                         cmd = *(list[0] = Cmd_CompleteBuildList(s));
899                 if (v)
900                         cmd = *(list[1] = Cvar_CompleteBuildList(s));
901                 if (a)
902                         cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
903
904                 cmd_len = strlen (s);
905                 do {
906                         for (i = 0; i < 3; i++) {
907                                 char ch = cmd[cmd_len];
908                                 const char **l = list[i];
909                                 if (l) {
910                                         while (*l && (*l)[cmd_len] == ch)
911                                                 l++;
912                                         if (*l)
913                                                 break;
914                                 }
915                         }
916                         if (i == 3)
917                                 cmd_len++;
918                 } while (i == 3);
919                 // 'quakebar'
920                 Con_Print("\n\35");
921                 for (i = 0; i < con_linewidth - 4; i++)
922                         Con_Print("\36");
923                 Con_Print("\37\n");
924
925                 // Print Possible Commands
926                 if (c) {
927                         Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
928                         Con_DisplayList(list[0]);
929                 }
930
931                 if (v) {
932                         Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
933                         Con_DisplayList(list[1]);
934                 }
935
936                 if (a) {
937                         Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
938                         Con_DisplayList(list[2]);
939                 }
940         }
941
942         if (cmd) {
943                 strncpy(key_lines[edit_line] + 1, cmd, cmd_len);
944                 key_linepos = cmd_len + 1;
945                 if (c + v + a == 1) {
946                         key_lines[edit_line][key_linepos] = ' ';
947                         key_linepos++;
948                 }
949                 key_lines[edit_line][key_linepos] = 0;
950         }
951         for (i = 0; i < 3; i++)
952                 if (list[i])
953                         Mem_Free((void *)list[i]);
954 }
955