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"};
47 cvar_t con_textsize = {CVAR_SAVE, "con_textsize","8"}; //[515]: console text size in pixels
49 #define MAX_NOTIFYLINES 32
50 // cl.time time the line was generated for transparent notify lines
51 float con_times[MAX_NOTIFYLINES];
55 qboolean con_initialized;
59 ==============================================================================
63 ==============================================================================
66 cvar_t log_file = {0, "log_file",""};
67 char crt_log_file [MAX_OSPATH] = "";
68 qfile_t* logfile = NULL;
70 unsigned char* logqueue = NULL;
74 void Log_ConPrint (const char *msg);
81 const char* Log_Timestamp (const char *desc)
83 static char timestamp [128];
85 const struct tm *crt_tm;
88 // Build the time stamp (ex: "Wed Jun 30 21:49:08 1993");
90 crt_tm = localtime (&crt_time);
91 strftime (timestring, sizeof (timestring), "%a %b %d %H:%M:%S %Y", crt_tm);
94 dpsnprintf (timestamp, sizeof (timestamp), "====== %s (%s) ======\n", desc, timestring);
96 dpsnprintf (timestamp, sizeof (timestamp), "====== %s ======\n", timestring);
109 if (logfile != NULL || log_file.string[0] == '\0')
112 logfile = FS_Open (log_file.string, "ab", false, false);
115 strlcpy (crt_log_file, log_file.string, sizeof (crt_log_file));
116 FS_Print (logfile, Log_Timestamp ("Log started"));
126 void Log_Close (void)
131 FS_Print (logfile, Log_Timestamp ("Log stopped"));
132 FS_Print (logfile, "\n");
136 crt_log_file[0] = '\0';
145 void Log_Start (void)
149 // Dump the contents of the log queue into the log file and free it
150 if (logqueue != NULL)
152 if (logfile != NULL && logq_ind != 0)
153 FS_Write (logfile, logqueue, logq_ind);
167 void Log_ConPrint (const char *msg)
169 static qboolean inprogress = false;
171 // don't allow feedback loops with memory error reports
176 // Until the host is completely initialized, we maintain a log queue
177 // to store the messages, since the log can't be started before
178 if (logqueue != NULL)
180 size_t remain = logq_size - logq_ind;
181 size_t len = strlen (msg);
183 // If we need to enlarge the log queue
186 size_t factor = ((logq_ind + len) / logq_size) + 1;
187 unsigned char* newqueue;
190 newqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
191 memcpy (newqueue, logqueue, logq_ind);
194 remain = logq_size - logq_ind;
196 memcpy (&logqueue[logq_ind], msg, len);
203 // Check if log_file has changed
204 if (strcmp (crt_log_file, log_file.string) != 0)
210 // If a log file is available
212 FS_Print (logfile, msg);
222 void Log_Printf (const char *logfilename, const char *fmt, ...)
226 file = FS_Open (logfilename, "ab", true, false);
231 va_start (argptr, fmt);
232 FS_VPrintf (file, fmt, argptr);
241 ==============================================================================
245 ==============================================================================
253 void Con_ToggleConsole_f (void)
255 // toggle the 'user wants console' bit
256 key_consoleactive ^= KEY_CONSOLEACTIVE_USER;
257 memset (con_times, 0, sizeof(con_times));
265 void Con_Clear_f (void)
268 memset (con_text, ' ', CON_TEXTSIZE);
277 void Con_ClearNotify (void)
281 for (i=0 ; i<MAX_NOTIFYLINES ; i++)
291 void Con_MessageMode_f (void)
293 key_dest = key_message;
303 void Con_MessageMode2_f (void)
305 key_dest = key_message;
314 If the line width has changed, reformat the buffer.
317 void Con_CheckResize (void)
319 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
321 char tbuf[CON_TEXTSIZE];
323 f = bound(1, con_textsize.value, 128);
324 if(f != con_textsize.value)
325 Cvar_SetValueQuick(&con_textsize, f);
326 width = (int)floor(vid_conwidth.value / con_textsize.value);
327 width = bound(1, width, CON_TEXTSIZE/4);
329 if (width == con_linewidth)
332 oldwidth = con_linewidth;
333 con_linewidth = width;
334 oldtotallines = con_totallines;
335 con_totallines = CON_TEXTSIZE / con_linewidth;
336 numlines = oldtotallines;
338 if (con_totallines < numlines)
339 numlines = con_totallines;
343 if (con_linewidth < numchars)
344 numchars = con_linewidth;
346 memcpy (tbuf, con_text, CON_TEXTSIZE);
347 memset (con_text, ' ', CON_TEXTSIZE);
349 for (i=0 ; i<numlines ; i++)
351 for (j=0 ; j<numchars ; j++)
353 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
354 tbuf[((con_current - i + oldtotallines) %
355 oldtotallines) * oldwidth + j];
362 con_current = con_totallines - 1;
365 //[515]: the simplest command ever
366 //LordHavoc: not so simple after I made it print usage...
367 static void Con_Maps_f (void)
371 Con_Printf("usage: maps [mapnameprefix]\n");
374 else if (Cmd_Argc() == 2)
375 GetMapList(Cmd_Argv(1), NULL, 0);
377 GetMapList("", NULL, 0);
387 memset (con_text, ' ', CON_TEXTSIZE);
389 con_totallines = CON_TEXTSIZE / con_linewidth;
391 // Allocate a log queue
392 logq_size = MAX_INPUTLINE;
393 logqueue = (unsigned char *)Mem_Alloc (tempmempool, logq_size);
396 Cvar_RegisterVariable (&log_file);
398 // support for the classic Quake option
399 // COMMANDLINEOPTION: Console: -condebug logs console messages to qconsole.log, see also log_file
400 if (COM_CheckParm ("-condebug") != 0)
401 Cvar_SetQuick (&log_file, "qconsole.log");
404 void Con_Init_Commands (void)
406 // register our cvars
407 Cvar_RegisterVariable (&con_notifytime);
408 Cvar_RegisterVariable (&con_notify);
409 Cvar_RegisterVariable (&con_textsize);
411 // register our commands
412 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
413 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
414 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
415 Cmd_AddCommand ("clear", Con_Clear_f);
416 Cmd_AddCommand ("maps", Con_Maps_f); // By [515]
418 con_initialized = true;
419 Con_Print("Console initialized.\n");
428 void Con_Linefeed (void)
435 memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
442 Handles cursor positioning, line wrapping, etc
443 All console printing must go through this in order to be displayed
444 If no console is visible, the notify window will pop up.
447 void Con_PrintToHistory(const char *txt, int mask)
455 for (l=0 ; l< con_linewidth ; l++)
460 if (l != con_linewidth && (con_x + l > con_linewidth) )
475 // mark time for transparent overlay
476 if (con_current >= 0)
478 if (con_notify.integer < 0)
479 Cvar_SetValueQuick(&con_notify, 0);
480 if (con_notify.integer > MAX_NOTIFYLINES)
481 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
482 if (con_notify.integer > 0)
483 con_times[con_current % con_notify.integer] = cl.time;
498 default: // display character and advance
499 y = con_current % con_totallines;
500 con_text[y*con_linewidth+con_x] = c | mask;
502 if (con_x >= con_linewidth)
510 /* The translation table between the graphical font and plain ASCII --KB */
511 static char qfont_table[256] = {
512 '\0', '#', '#', '#', '#', '.', '#', '#',
513 '#', 9, 10, '#', ' ', 13, '.', '.',
514 '[', ']', '0', '1', '2', '3', '4', '5',
515 '6', '7', '8', '9', '.', '<', '=', '>',
516 ' ', '!', '"', '#', '$', '%', '&', '\'',
517 '(', ')', '*', '+', ',', '-', '.', '/',
518 '0', '1', '2', '3', '4', '5', '6', '7',
519 '8', '9', ':', ';', '<', '=', '>', '?',
520 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
521 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
522 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
523 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
524 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
525 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
526 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
527 'x', 'y', 'z', '{', '|', '}', '~', '<',
529 '<', '=', '>', '#', '#', '.', '#', '#',
530 '#', '#', ' ', '#', ' ', '>', '.', '.',
531 '[', ']', '0', '1', '2', '3', '4', '5',
532 '6', '7', '8', '9', '.', '<', '=', '>',
533 ' ', '!', '"', '#', '$', '%', '&', '\'',
534 '(', ')', '*', '+', ',', '-', '.', '/',
535 '0', '1', '2', '3', '4', '5', '6', '7',
536 '8', '9', ':', ';', '<', '=', '>', '?',
537 '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
538 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
539 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
540 'X', 'Y', 'Z', '[', '\\', ']', '^', '_',
541 '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
542 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
543 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
544 'x', 'y', 'z', '{', '|', '}', '~', '<'
551 Prints to all appropriate console targets, and adds timestamps
554 extern cvar_t timestamps;
555 extern cvar_t timeformat;
556 extern qboolean sys_nostdout;
557 void Con_Print(const char *msg)
560 static int index = 0;
561 static char line[MAX_INPUTLINE];
567 // if this is the beginning of a new line, print timestamp
568 const char *timestamp = timestamps.integer ? Sys_TimeString(timeformat.string) : "";
570 // FIXME: 1. perhaps we should use a terminal system 2. use a constant instead of 7!
571 line[index++] = STRING_COLOR_TAG;
572 // assert( STRING_COLOR_DEFAULT < 10 )
573 line[index++] = STRING_COLOR_DEFAULT + '0';
574 // special color codes for chat messages must always come first
575 // for Con_PrintToHistory to work properly
581 S_LocalSound ("sound/misc/talk.wav");
583 //if (gamemode == GAME_NEXUIZ)
585 line[index++] = STRING_COLOR_TAG;
590 // // go to colored text
596 for (;*timestamp;index++, timestamp++)
597 if (index < (int)sizeof(line) - 2)
598 line[index] = *timestamp;
600 // append the character
601 line[index++] = *msg;
602 // if this is a newline character, we have a complete line to print
603 if (*msg == '\n' || index >= (int)sizeof(line) / 2)
605 // terminate the line
609 // send to scrollable buffer
610 if (con_initialized && cls.state != ca_dedicated)
611 Con_PrintToHistory(line, mask);
612 // send to terminal or dedicated server window
616 for (p = (unsigned char *) line;*p; p++)
617 *p = qfont_table[*p];
618 Sys_PrintToTerminal(line);
620 // empty the line buffer
631 Prints to all appropriate console targets
634 void Con_Printf(const char *fmt, ...)
637 char msg[MAX_INPUTLINE];
639 va_start(argptr,fmt);
640 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
650 A Con_Print that only shows up if the "developer" cvar is set
653 void Con_DPrint(const char *msg)
655 if (!developer.integer)
656 return; // don't confuse non-developers with techie stuff...
664 A Con_Printf that only shows up if the "developer" cvar is set
667 void Con_DPrintf(const char *fmt, ...)
670 char msg[MAX_INPUTLINE];
672 if (!developer.integer)
673 return; // don't confuse non-developers with techie stuff...
675 va_start(argptr,fmt);
676 dpvsnprintf(msg,sizeof(msg),fmt,argptr);
684 ==============================================================================
688 ==============================================================================
695 The input line scrolls horizontally if typing goes beyond the right edge
697 Modified by EvilTypeGuy eviltypeguy@qeradiant.com
700 void Con_DrawInput (void)
704 char editlinecopy[MAX_INPUTLINE+1], *text;
706 if (!key_consoleactive)
707 return; // don't draw anything
709 text = strcpy(editlinecopy, key_lines[edit_line]);
711 // Advanced Console Editing by Radix radix@planetquake.com
712 // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com
713 // use strlen of edit_line instead of key_linepos to allow editing
714 // of early characters w/o erasing
716 y = (int)strlen(text);
718 // fill out remainder with spaces
719 for (i = y; i < (int)sizeof(editlinecopy)-1; i++)
722 // add the cursor frame
723 if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible
724 text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right
726 // text[key_linepos + 1] = 0;
728 // prestep if horizontally scrolling
729 if (key_linepos >= con_linewidth)
730 text += 1 + key_linepos - con_linewidth;
733 DrawQ_ColoredString(0, con_vislines - con_textsize.value*2, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, NULL );
736 // key_lines[edit_line][key_linepos] = 0;
744 Draws the last few lines of output transparently over the game top
747 void Con_DrawNotify (void)
753 char temptext[MAX_INPUTLINE];
754 int colorindex = -1; //-1 for default
756 if (con_notify.integer < 0)
757 Cvar_SetValueQuick(&con_notify, 0);
758 if (con_notify.integer > MAX_NOTIFYLINES)
759 Cvar_SetValueQuick(&con_notify, MAX_NOTIFYLINES);
760 if (gamemode == GAME_TRANSFUSION)
764 for (i= con_current-con_notify.integer+1 ; i<=con_current ; i++)
769 time = con_times[i % con_notify.integer];
772 time = cl.time - time;
773 if (time > con_notifytime.value)
775 text = con_text + (i % con_totallines)*con_linewidth;
777 if (gamemode == GAME_NEXUIZ) {
780 for (linewidth = con_linewidth; linewidth && text[linewidth-1] == ' '; linewidth--);
781 x = (vid_conwidth.integer - linewidth * con_textsize.value) * 0.5;
785 DrawQ_ColoredString( x, v, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
787 v += con_textsize.value;
791 if (key_dest == key_message)
797 // LordHavoc: speedup, and other improvements
799 sprintf(temptext, "say_team:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
801 sprintf(temptext, "say:%s%c", chat_buffer, (int) 10+((int)(realtime*con_cursorspeed)&1));
802 while ((int)strlen(temptext) >= con_linewidth)
804 DrawQ_ColoredString( 0, v, temptext, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
805 strcpy(temptext, &temptext[con_linewidth]);
806 v += con_textsize.value;
808 if (strlen(temptext) > 0)
810 DrawQ_ColoredString( 0, v, temptext, 0, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
811 v += con_textsize.value;
820 Draws the console with the solid background
821 The typing input line at the bottom should only be drawn if typing is allowed
824 void Con_DrawConsole (int lines)
834 // draw the background
835 if (scr_conbrightness.value >= 0.01f)
836 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);
838 DrawQ_Fill(0, lines - vid_conheight.integer, vid_conwidth.integer, vid_conheight.integer, 0, 0, 0, scr_conalpha.value, 0);
839 DrawQ_String(vid_conwidth.integer - strlen(engineversion) * con_textsize.value - con_textsize.value, lines - con_textsize.value, engineversion, 0, con_textsize.value, con_textsize.value, 1, 0, 0, 1, 0);
842 con_vislines = lines;
844 rows = (int)ceil((lines/con_textsize.value)-2); // rows of text to draw
845 y = lines - (rows+2)*con_textsize.value; // may start slightly negative
847 for (i = con_current - rows + 1;i <= con_current;i++, y += con_textsize.value)
849 j = max(i - con_backscroll, 0);
850 text = con_text + (j % con_totallines)*con_linewidth;
852 DrawQ_ColoredString( 0, y, text, con_linewidth, con_textsize.value, con_textsize.value, 1.0, 1.0, 1.0, 1.0, 0, &colorindex );
855 // draw the input prompt, user text, and cursor if desired
863 Prints not only map filename, but also
864 its format (q1/q2/q3/hl) and even its message
866 //[515]: here is an ugly hack.. two gotos... oh my... *but it works*
867 //LordHavoc: rewrote bsp type detection, added mcbsp support and rewrote message extraction to do proper worldspawn parsing
868 //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
869 //LordHavoc: FIXME: man this GetMapList is STILL ugly code even after my cleanups...
870 qboolean GetMapList (const char *s, char *completedname, int completednamebufferlength)
874 int i, k, max, p, o, min;
877 unsigned char buf[1024];
879 sprintf(message, "maps/%s*.bsp", s);
880 t = FS_Search(message, 1, true);
883 if (t->numfilenames > 1)
884 Con_Printf("^1 %i maps found :\n", t->numfilenames);
885 len = Z_Malloc(t->numfilenames);
887 for(max=i=0;i<t->numfilenames;i++)
889 k = (int)strlen(t->filenames[i]);
899 for(i=0;i<t->numfilenames;i++)
901 int lumpofs = 0, lumplen = 0;
902 char *entities = NULL;
903 const char *data = NULL;
905 char entfilename[MAX_QPATH];
906 strcpy(message, "^1**ERROR**^7");
908 f = FS_Open(t->filenames[i], "rb", true, false);
911 memset(buf, 0, 1024);
912 FS_Read(f, buf, 1024);
913 if (!memcmp(buf, "IBSP", 4))
915 p = LittleLong(((int *)buf)[1]);
916 if (p == Q3BSPVERSION)
918 q3dheader_t *header = (q3dheader_t *)buf;
919 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
920 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
922 else if (p == Q2BSPVERSION)
924 q2dheader_t *header = (q2dheader_t *)buf;
925 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
926 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
929 else if (!memcmp(buf, "MCBSPpad", 8))
931 p = LittleLong(((int *)buf)[2]);
932 if (p == MCBSPVERSION)
934 int numhulls = LittleLong(((int *)buf)[3]);
935 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
936 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
939 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
941 dheader_t *header = (dheader_t *)buf;
942 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
943 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
947 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
948 strcpy(entfilename + strlen(entfilename) - 4, ".ent");
949 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
950 if (!entities && lumplen >= 10)
952 FS_Seek(f, lumpofs, SEEK_SET);
953 entities = Z_Malloc(lumplen + 1);
954 FS_Read(f, entities, lumplen);
958 // if there are entities to parse, a missing message key just
959 // means there is no title, so clear the message string now
965 if (!COM_ParseToken(&data, false))
967 if (com_token[0] == '{')
969 if (com_token[0] == '}')
971 // skip leading whitespace
972 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
973 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
974 keyname[l] = com_token[k+l];
976 if (!COM_ParseToken(&data, false))
978 if (developer.integer >= 2)
979 Con_Printf("key: %s %s\n", keyname, com_token);
980 if (!strcmp(keyname, "message"))
982 // get the message contents
983 strlcpy(message, com_token, sizeof(message));
993 *(t->filenames[i]+len[i]+5) = 0;
996 case Q3BSPVERSION: strcpy((char *)buf, "Q3");break;
997 case Q2BSPVERSION: strcpy((char *)buf, "Q2");break;
998 case BSPVERSION: strcpy((char *)buf, "Q1");break;
999 case MCBSPVERSION: strcpy((char *)buf, "MC");break;
1000 case 30: strcpy((char *)buf, "HL");break;
1001 default: strcpy((char *)buf, "??");break;
1003 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1008 k = *(t->filenames[0]+5+p);
1009 for(i=1;i<t->numfilenames;i++)
1010 if(*(t->filenames[i]+5+p) != k)
1016 memset(completedname, 0, completednamebufferlength);
1017 memcpy(completedname, (t->filenames[0]+5), p);
1027 New function for tab-completion system
1028 Added by EvilTypeGuy
1029 MEGA Thanks to Taniwha
1032 void Con_DisplayList(const char **list)
1034 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1035 const char **walk = list;
1038 len = (int)strlen(*walk);
1046 len = (int)strlen(*list);
1047 if (pos + maxlen >= width) {
1053 for (i = 0; i < (maxlen - len); i++)
1065 Con_CompleteCommandLine
1067 New function for tab-completion system
1068 Added by EvilTypeGuy
1069 Thanks to Fett erich@heintz.com
1071 Enhanced to tab-complete map names by [515]
1074 void Con_CompleteCommandLine (void)
1076 const char *cmd = "", *s;
1077 const char **list[3] = {0, 0, 0};
1079 int c, v, a, i, cmd_len, pos, k;
1081 //find what we want to complete
1085 k = key_lines[edit_line][pos];
1086 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1091 s = key_lines[edit_line] + pos;
1092 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1093 key_lines[edit_line][key_linepos] = 0; //hide them
1096 for(k=pos-1;k>2;k--)
1097 if(key_lines[edit_line][k] != ' ')
1099 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1101 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1102 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1105 if (GetMapList(s, t, sizeof(t)))
1107 i = (int)(strlen(t) - strlen(s));
1108 strcpy((char*)s, t);
1109 if(s2[0]) //add back chars after cursor
1110 strcpy(&key_lines[edit_line][key_linepos], s2);
1117 // Count number of possible matches
1118 c = Cmd_CompleteCountPossible(s);
1119 v = Cvar_CompleteCountPossible(s);
1120 a = Cmd_CompleteAliasCountPossible(s);
1122 if (!(c + v + a)) // No possible matches
1125 strcpy(&key_lines[edit_line][key_linepos], s2);
1129 if (c + v + a == 1) {
1131 list[0] = Cmd_CompleteBuildList(s);
1133 list[0] = Cvar_CompleteBuildList(s);
1135 list[0] = Cmd_CompleteAliasBuildList(s);
1137 cmd_len = (int)strlen (cmd);
1140 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1142 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1144 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1146 cmd_len = (int)strlen (s);
1148 for (i = 0; i < 3; i++) {
1149 char ch = cmd[cmd_len];
1150 const char **l = list[i];
1152 while (*l && (*l)[cmd_len] == ch)
1163 for (i = 0; i < con_linewidth - 4; i++)
1167 // Print Possible Commands
1169 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1170 Con_DisplayList(list[0]);
1174 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1175 Con_DisplayList(list[1]);
1179 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1180 Con_DisplayList(list[2]);
1185 strncpy(key_lines[edit_line] + pos, cmd, cmd_len);
1186 key_linepos = cmd_len + pos;
1187 if (c + v + a == 1) {
1188 key_lines[edit_line][key_linepos] = ' ';
1192 strcpy(&key_lines[edit_line][key_linepos], s2);
1194 key_lines[edit_line][key_linepos] = 0;
1198 strcpy(&key_lines[edit_line][key_linepos], s2);
1199 for (i = 0; i < 3; i++)
1201 Mem_Free((void *)list[i]);