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