2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
22 #if !defined(WIN32) || defined(__MINGW32__)
30 float con_cursorspeed = 4;
32 #define CON_TEXTSIZE 131072
34 // total lines in console scrollback
36 // lines up from bottom to display
38 // where next message will be printed
40 // offset in current line for next print
42 char con_text[CON_TEXTSIZE];
45 cvar_t con_notifytime = {CVAR_SAVE, "con_notifytime","3"};
46 cvar_t con_notify = {CVAR_SAVE, "con_notify","4"};
48 #define MAX_NOTIFYLINES 32
49 // cl.time time the line was generated for transparent notify lines
50 float con_times[MAX_NOTIFYLINES];
54 qboolean con_initialized;
58 ==============================================================================
62 ==============================================================================
65 cvar_t log_file = {0, "log_file",""};
66 char crt_log_file [MAX_OSPATH] = "";
67 qfile_t* logfile = NULL;
69 unsigned char* logqueue = NULL;
73 void Log_ConPrint (const char *msg);
80 const char* Log_Timestamp (const char *desc)
82 static char timestamp [128];
84 const struct tm *crt_tm;
87 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
89 crt_tm = localtime (&crt_time);
90 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
93 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
95 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
108 if (logfile != NULL || log_file.string[0] == '\0')
111 logfile = FS_Open (log_file.string, "ab", false, false);
114 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
115 FS_Print (logfile, Log_Timestamp ("Log started"));
125 void Log_Close (void)
130 FS_Print (logfile, Log_Timestamp ("Log stopped"));
131 FS_Print (logfile, "\n");
135 crt_log_file[0] = '\0';
144 void Log_Start (void)
148 // Dump the contents of the log queue into the log file and free it
149 if (logqueue != NULL)
151 if (logfile != NULL && logq_ind != 0)
152 FS_Write (logfile, logqueue, logq_ind);
166 void Log_ConPrint (const char *msg)
168 static qboolean inprogress = false;
170 // don't allow feedback loops with memory error reports
175 // Until the host is completely initialized, we maintain a log queue
176 // to store the messages, since the log can't be started before
177 if (logqueue != NULL)
179 size_t remain = logq_size - logq_ind;
180 size_t len = strlen (msg);
182 // If we need to enlarge the log queue
185 size_t factor = ((logq_ind + len) / logq_size) + 1;
186 unsigned char* newqueue;
189 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
190 memcpy (newqueue, logqueue, logq_ind);
193 remain = logq_size - logq_ind;
195 memcpy (&logqueue[logq_ind], msg, len);
202 // Check if log_file has changed
203 if (strcmp (crt_log_file, log_file.string) != 0)
209 // If a log file is available
211 FS_Print (logfile, msg);
221 void Log_Printf (const char *logfilename, const char *fmt, ...)
225 file = FS_Open (logfilename, "ab", true, false);
230 va_start (argptr, fmt);
231 FS_VPrintf (file, fmt, argptr);
240 ==============================================================================
244 ==============================================================================
252 void Con_ToggleConsole_f (void)
254 // toggle the 'user wants console' bit
255 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
256 memset (con_times, 0, sizeof(con_times));
264 void Con_Clear_f (void)
267 memset (con_text, ' ', CON_TEXTSIZE);
276 void Con_ClearNotify (void)
280 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
290 void Con_MessageMode_f (void)
292 key_dest = key_message;
302 void Con_MessageMode2_f (void)
304 key_dest = key_message;
313 If the line width has changed, reformat the buffer.
316 void Con_CheckResize (void)
318 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
319 char tbuf[CON_TEXTSIZE];
321 width = (vid_conwidth.integer >> 3);
323 if (width == con_linewidth)
326 oldwidth = con_linewidth;
327 con_linewidth = width;
328 oldtotallines = con_totallines;
329 con_totallines = CON_TEXTSIZE / con_linewidth;
330 numlines = oldtotallines;
332 if (con_totallines < numlines)
333 numlines = con_totallines;
337 if (con_linewidth < numchars)
338 numchars = con_linewidth;
340 memcpy (tbuf, con_text, CON_TEXTSIZE);
341 memset (con_text, ' ', CON_TEXTSIZE);
343 for (i=0 ; i<numlines ; i++)
345 for (j=0 ; j<numchars ; j++)
347 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
348 tbuf[((con_current - i + oldtotallines) %
349 oldtotallines) * oldwidth + j];
356 con_current = con_totallines - 1;
359 //[515]: the simplest command ever
360 //LordHavoc: not so simple after I made it print usage...
361 static void Con_Maps_f (void)
365 Con_Printf("usage: maps [mapnameprefix]\n");
368 else if (Cmd_Argc() == 2)
369 GetMapList(Cmd_Argv(1), NULL, 0);
371 GetMapList("", NULL, 0);
381 memset (con_text, ' ', CON_TEXTSIZE);
383 con_totallines = CON_TEXTSIZE / con_linewidth;
385 // Allocate a log queue
386 logq_size = MAX_INPUTLINE;
387 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
390 Cvar_RegisterVariable (&log_file);
392 // support for the classic Quake option
393 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
394 if (COM_CheckParm ("-condebug") != 0)
395 Cvar_SetQuick (&log_file, "qconsole.log");
398 void Con_Init_Commands (void)
400 // register our cvars
401 Cvar_RegisterVariable (&con_notifytime);
402 Cvar_RegisterVariable (&con_notify);
404 // register our commands
405 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
406 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
407 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
408 Cmd_AddCommand ("clear", Con_Clear_f);
409 Cmd_AddCommand ("maps", Con_Maps_f); // By [515]
411 con_initialized = true;
412 Con_Print("Console initialized.\n");
421 void Con_Linefeed (void)
428 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
435 Handles cursor positioning, line wrapping, etc
436 All console printing must go through this in order to be displayed
437 If no console is visible, the notify window will pop up.
440 void Con_PrintToHistory(const char *txt, int mask)
448 for (l=0 ; l< con_linewidth ; l++)
453 if (l != con_linewidth && (con_x + l > con_linewidth) )
468 // mark time for transparent overlay
469 if (con_current >= 0)
471 if (con_notify.integer < 0)
472 Cvar_SetValueQuick(&con_notify, 0);
473 if (con_notify.integer > MAX_NOTIFYLINES)
474 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
475 if (con_notify.integer > 0)
476 con_times[con_current % con_notify.integer] = cl.time;
491 default: // display character and advance
492 y = con_current % con_totallines;
493 con_text[y*con_linewidth+con_x] = c | mask;
495 if (con_x >= con_linewidth)
503 /* The translation table between the graphical font and plain ASCII --KB */
504 static char qfont_table[256] = {
505 '\0', '#', '#', '#', '#', '.', '#', '#',
506 '#', 9, 10, '#', ' ', 13, '.', '.',
507 '[', ']', '0', '1', '2', '3', '4', '5',
508 '6', '7', '8', '9', '.', '<', '=', '>',
509 ' ', '!', '"', '#', '$', '%', '&', '\'',
510 '(', ')', '*', '+', ',', '-', '.', '/',
511 '0', '1', '2', '3', '4', '5', '6', '7',
512 '8', '9', ':', ';', '<', '=', '>', '?',
513 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
514 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
515 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
516 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
517 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
518 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
519 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
520 'x', 'y', 'z', '{', '|', '}', '~', '<',
522 '<', '=', '>', '#', '#', '.', '#', '#',
523 '#', '#', ' ', '#', ' ', '>', '.', '.',
524 '[', ']', '0', '1', '2', '3', '4', '5',
525 '6', '7', '8', '9', '.', '<', '=', '>',
526 ' ', '!', '"', '#', '$', '%', '&', '\'',
527 '(', ')', '*', '+', ',', '-', '.', '/',
528 '0', '1', '2', '3', '4', '5', '6', '7',
529 '8', '9', ':', ';', '<', '=', '>', '?',
530 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
531 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
532 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
533 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
534 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
535 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
536 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
537 'x', 'y', 'z', '{', '|', '}', '~', '<'
544 Prints to all appropriate console targets, and adds timestamps
547 extern cvar_t timestamps;
548 extern cvar_t timeformat;
549 extern qboolean sys_nostdout;
550 void Con_Print(const char *msg)
553 static int index = 0;
554 static char line[MAX_INPUTLINE];
560 // if this is the beginning of a new line, print timestamp
561 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
563 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
564 line[index++] = STRING_COLOR_TAG;
565 // assert( STRING_COLOR_DEFAULT < 10 )
566 line[index++] = STRING_COLOR_DEFAULT + '0';
567 // special color codes for chat messages must always come first
568 // for Con_PrintToHistory to work properly
574 S_LocalSound ("sound/misc/talk.wav");
576 //if (gamemode == GAME_NEXUIZ)
578 line[index++] = STRING_COLOR_TAG;
583 // // go to colored text
589 for (;*timestamp;index++, timestamp++)
590 if (index < (int)sizeof(line) - 2)
591 line[index] = *timestamp;
593 // append the character
594 line[index++] = *msg;
595 // if this is a newline character, we have a complete line to print
596 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
598 // terminate the line
602 // send to scrollable buffer
603 if (con_initialized && cls.state != ca_dedicated)
604 Con_PrintToHistory(line, mask);
605 // send to terminal or dedicated server window
609 for (p = (unsigned char *) line;*p; p++)
610 *p = qfont_table[*p];
611 Sys_PrintToTerminal(line);
613 // empty the line buffer
624 Prints to all appropriate console targets
627 void Con_Printf(const char *fmt, ...)
630 char msg[MAX_INPUTLINE];
632 va_start(argptr,fmt);
633 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
643 A Con_Print that only shows up if the "developer" cvar is set
646 void Con_DPrint(const char *msg)
648 if (!developer.integer)
649 return; // don't confuse non-developers with techie stuff...
657 A Con_Printf that only shows up if the "developer" cvar is set
660 void Con_DPrintf(const char *fmt, ...)
663 char msg[MAX_INPUTLINE];
665 if (!developer.integer)
666 return; // don't confuse non-developers with techie stuff...
668 va_start(argptr,fmt);
669 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
677 ==============================================================================
681 ==============================================================================
688 The input line scrolls horizontally if typing goes beyond the right edge
690 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
693 void Con_DrawInput (void)
697 char editlinecopy[MAX_INPUTLINE+1], *text;
699 if (!key_consoleactive)
700 return; // don't draw anything
702 text = strcpy(editlinecopy, key_lines[edit_line]);
704 // Advanced Console Editing by Radix radix@planetquake.com
705 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
706 // use strlen of edit_line instead of key_linepos to allow editing
707 // of early characters w/o erasing
709 y = (int)strlen(text);
711 // fill out remainder with spaces
712 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
715 // add the cursor frame
716 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
717 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
719 // text[key_linepos + 1] = 0;
721 // prestep if horizontally scrolling
722 if (key_linepos >= con_linewidth)
723 text += 1 + key_linepos - con_linewidth;
726 DrawQ_ColoredString(0, con_vislines - 16, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, NULL );
729 // key_lines[edit_line][key_linepos] = 0;
737 Draws the last few lines of output transparently over the game top
740 void Con_DrawNotify (void)
746 char temptext[MAX_INPUTLINE];
747 int colorindex = -1; //-1 for default
749 if (con_notify.integer < 0)
750 Cvar_SetValueQuick(&con_notify, 0);
751 if (con_notify.integer > MAX_NOTIFYLINES)
752 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
753 if (gamemode == GAME_TRANSFUSION)
757 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
762 time = con_times[i % con_notify.integer];
765 time = cl.time - time;
766 if (time > con_notifytime.value)
768 text = con_text + (i % con_totallines)*con_linewidth;
770 if (gamemode == GAME_NEXUIZ) {
773 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
774 x = (vid_conwidth.integer - linewidth * 8) / 2;
778 DrawQ_ColoredString( x, v, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
784 if (key_dest == key_message)
790 // LordHavoc: speedup, and other improvements
792 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
794 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
795 while ((int)strlen(temptext) >= con_linewidth)
797 DrawQ_ColoredString( 0, v, temptext, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
798 strcpy(temptext, &temptext[con_linewidth]);
801 if (strlen(temptext) > 0)
803 DrawQ_ColoredString( 0, v, temptext, 0, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
813 Draws the console with the solid background
814 The typing input line at the bottom should only be drawn if typing is allowed
817 void Con_DrawConsole (int lines)
826 // draw the background
827 if (scr_conbrightness.value >= 0.01f)
828 DrawQ_Pic(0, lines - vid_conheight.integer, "gfx/conback", vid_conwidth.integer, vid_conheight.integer, scr_conbrightness.value, scr_conbrightness.value, scr_conbrightness.value, scr_conalpha.value, 0);
830 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
831 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * 8 - 8, lines - 8, engineversion, 0, 8, 8, 1, 0, 0, 1, 0);
834 con_vislines = lines;
836 rows = (lines-16)>>3; // rows of text to draw
837 y = lines - 16 - (rows<<3); // may start slightly negative
839 for (i = con_current - rows + 1;i <= con_current;i++, y += 8)
841 j = max(i - con_backscroll, 0);
842 text = con_text + (j % con_totallines)*con_linewidth;
844 DrawQ_ColoredString( 0, y, text, con_linewidth, 8, 8, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
847 // draw the input prompt, user text, and cursor if desired
855 Prints not only map filename, but also
856 its format (q1/q2/q3/hl) and even its message
858 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
859 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
860 //LordHavoc: added .ent file loading, and redesigned error handling to still try the .ent file even if the map format is not recognized, this also eliminated one goto
861 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
862 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
866 int i, k, max, p, o, min;
869 unsigned char buf[1024];
871 sprintf(message, "maps/%s*.bsp", s);
872 t = FS_Search(message, 1, true);
875 if (t->numfilenames > 1)
876 Con_Printf("^1 %i maps found :\n", t->numfilenames);
877 len = Z_Malloc(t->numfilenames);
879 for(max=i=0;i<t->numfilenames;i++)
881 k = strlen(t->filenames[i]);
891 for(i=0;i<t->numfilenames;i++)
893 int lumpofs = 0, lumplen = 0;
894 char *entities = NULL;
895 const char *data = NULL;
897 char entfilename[MAX_QPATH];
898 strcpy(message, "^1**ERROR**^7");
899 f = FS_Open(t->filenames[i], "rb", true, false);
902 memset(buf, 0, 1024);
903 FS_Read(f, buf, 1024);
904 if (!memcmp(buf, "IBSP", 4))
906 p = LittleLong(((int *)buf)[1]);
907 if (p == Q3BSPVERSION)
909 q3dheader_t *header = (q3dheader_t *)buf;
910 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
911 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
913 else if (p == Q2BSPVERSION)
915 q2dheader_t *header = (q2dheader_t *)buf;
916 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
917 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
920 else if (!memcmp(buf, "MCBSPpad", 8))
922 p = LittleLong(((int *)buf)[2]);
923 if (p == MCBSPVERSION)
925 int numhulls = LittleLong(((int *)buf)[3]);
926 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
927 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
930 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
932 dheader_t *header = (dheader_t *)buf;
933 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
934 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
938 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
939 strcpy(entfilename + strlen(entfilename) - 4, ".ent");
940 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
941 if (!entities && lumplen >= 10)
943 FS_Seek(f, lumpofs, SEEK_SET);
944 entities = Z_Malloc(lumplen + 1);
945 FS_Read(f, entities, lumplen);
949 // if there are entities to parse, a missing message key just
950 // means there is no title, so clear the message string now
956 if (!COM_ParseToken(&data, false))
958 if (com_token[0] == '{')
960 if (com_token[0] == '}')
962 // skip leading whitespace
963 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
964 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
965 keyname[l] = com_token[k+l];
967 if (!COM_ParseToken(&data, false))
969 if (developer.integer >= 2)
970 Con_Printf("key: %s %s\n", keyname, com_token);
971 if (!strcmp(keyname, "message"))
973 // get the message contents
974 strlcpy(message, com_token, sizeof(message));
984 *(t->filenames[i]+len[i]+5) = 0;
987 case Q3BSPVERSION: strcpy((char *)buf, "Q3");break;
988 case Q2BSPVERSION: strcpy((char *)buf, "Q2");break;
989 case BSPVERSION: strcpy((char *)buf, "Q1");break;
990 case MCBSPVERSION: strcpy((char *)buf, "MC");break;
991 case 30: strcpy((char *)buf, "HL");break;
992 default: strcpy((char *)buf, "??");break;
994 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
999 k = *(t->filenames[0]+5+p);
1000 for(i=1;i<t->numfilenames;i++)
1001 if(*(t->filenames[i]+5+p) != k)
1007 memset(completedname, 0, completednamebufferlength);
1008 memcpy(completedname, (t->filenames[0]+5), p);
1018 New function for tab-completion system
1019 Added by EvilTypeGuy
1020 MEGA Thanks to Taniwha
1023 void Con_DisplayList(const char **list)
1025 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1026 const char **walk = list;
1029 len = (int)strlen(*walk);
1037 len = (int)strlen(*list);
1038 if (pos + maxlen >= width) {
1044 for (i = 0; i < (maxlen - len); i++)
1056 Con_CompleteCommandLine
1058 New function for tab-completion system
1059 Added by EvilTypeGuy
1060 Thanks to Fett erich@heintz.com
1062 Enhanced to tab-complete map names by [515]
1065 void Con_CompleteCommandLine (void)
1067 const char *cmd = "", *s;
1068 const char **list[3] = {0, 0, 0};
1070 int c, v, a, i, cmd_len, pos, k;
1072 //find what we want to complete
1076 k = key_lines[edit_line][pos];
1077 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1082 s = key_lines[edit_line] + pos;
1083 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1084 key_lines[edit_line][key_linepos] = 0; //hide them
1087 for(k=pos-1;k>2;k--)
1088 if(key_lines[edit_line][k] != ' ')
1090 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1092 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1093 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1096 if (GetMapList(s, t, sizeof(t)))
1098 i = strlen(t) - strlen(s);
1099 strcpy((char*)s, t);
1100 if(s2[0]) //add back chars after cursor
1101 strcpy(&key_lines[edit_line][key_linepos], s2);
1108 // Count number of possible matches
1109 c = Cmd_CompleteCountPossible(s);
1110 v = Cvar_CompleteCountPossible(s);
1111 a = Cmd_CompleteAliasCountPossible(s);
1113 if (!(c + v + a)) // No possible matches
1116 strcpy(&key_lines[edit_line][key_linepos], s2);
1120 if (c + v + a == 1) {
1122 list[0] = Cmd_CompleteBuildList(s);
1124 list[0] = Cvar_CompleteBuildList(s);
1126 list[0] = Cmd_CompleteAliasBuildList(s);
1128 cmd_len = (int)strlen (cmd);
1131 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1133 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1135 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1137 cmd_len = (int)strlen (s);
1139 for (i = 0; i < 3; i++) {
1140 char ch = cmd[cmd_len];
1141 const char **l = list[i];
1143 while (*l && (*l)[cmd_len] == ch)
1154 for (i = 0; i < con_linewidth - 4; i++)
1158 // Print Possible Commands
1160 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1161 Con_DisplayList(list[0]);
1165 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1166 Con_DisplayList(list[1]);
1170 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1171 Con_DisplayList(list[2]);
1176 strncpy(key_lines[edit_line] + pos, cmd, cmd_len);
1177 key_linepos = cmd_len + pos;
1178 if (c + v + a == 1) {
1179 key_lines[edit_line][key_linepos] = ' ';
1183 strcpy(&key_lines[edit_line][key_linepos], s2);
1185 key_lines[edit_line][key_linepos] = 0;
1189 strcpy(&key_lines[edit_line][key_linepos], s2);
1190 for (i = 0; i < 3; i++)
1192 Mem_Free((void *)list[i]);