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 = 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");
907 f = FS_Open(t->filenames[i], "rb", true, false);
910 memset(buf, 0, 1024);
911 FS_Read(f, buf, 1024);
912 if (!memcmp(buf, "IBSP", 4))
914 p = LittleLong(((int *)buf)[1]);
915 if (p == Q3BSPVERSION)
917 q3dheader_t *header = (q3dheader_t *)buf;
918 lumpofs = LittleLong(header->lumps[Q3LUMP_ENTITIES].fileofs);
919 lumplen = LittleLong(header->lumps[Q3LUMP_ENTITIES].filelen);
921 else if (p == Q2BSPVERSION)
923 q2dheader_t *header = (q2dheader_t *)buf;
924 lumpofs = LittleLong(header->lumps[Q2LUMP_ENTITIES].fileofs);
925 lumplen = LittleLong(header->lumps[Q2LUMP_ENTITIES].filelen);
928 else if (!memcmp(buf, "MCBSPpad", 8))
930 p = LittleLong(((int *)buf)[2]);
931 if (p == MCBSPVERSION)
933 int numhulls = LittleLong(((int *)buf)[3]);
934 lumpofs = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+0]);
935 lumplen = LittleLong(((int *)buf)[3 + numhulls + LUMP_ENTITIES*2+1]);
938 else if((p = LittleLong(((int *)buf)[0])) == BSPVERSION || p == 30)
940 dheader_t *header = (dheader_t *)buf;
941 lumpofs = LittleLong(header->lumps[LUMP_ENTITIES].fileofs);
942 lumplen = LittleLong(header->lumps[LUMP_ENTITIES].filelen);
946 strlcpy(entfilename, t->filenames[i], sizeof(entfilename));
947 strcpy(entfilename + strlen(entfilename) - 4, ".ent");
948 entities = (char *)FS_LoadFile(entfilename, tempmempool, true, NULL);
949 if (!entities && lumplen >= 10)
951 FS_Seek(f, lumpofs, SEEK_SET);
952 entities = Z_Malloc(lumplen + 1);
953 FS_Read(f, entities, lumplen);
957 // if there are entities to parse, a missing message key just
958 // means there is no title, so clear the message string now
964 if (!COM_ParseToken(&data, false))
966 if (com_token[0] == '{')
968 if (com_token[0] == '}')
970 // skip leading whitespace
971 for (k = 0;com_token[k] && com_token[k] <= ' ';k++);
972 for (l = 0;l < (int)sizeof(keyname) - 1 && com_token[k+l] && com_token[k+l] > ' ';l++)
973 keyname[l] = com_token[k+l];
975 if (!COM_ParseToken(&data, false))
977 if (developer.integer >= 2)
978 Con_Printf("key: %s %s\n", keyname, com_token);
979 if (!strcmp(keyname, "message"))
981 // get the message contents
982 strlcpy(message, com_token, sizeof(message));
992 *(t->filenames[i]+len[i]+5) = 0;
995 case Q3BSPVERSION: strcpy((char *)buf, "Q3");break;
996 case Q2BSPVERSION: strcpy((char *)buf, "Q2");break;
997 case BSPVERSION: strcpy((char *)buf, "Q1");break;
998 case MCBSPVERSION: strcpy((char *)buf, "MC");break;
999 case 30: strcpy((char *)buf, "HL");break;
1000 default: strcpy((char *)buf, "??");break;
1002 Con_Printf("%16s (%s) %s\n", t->filenames[i]+5, buf, message);
1007 k = *(t->filenames[0]+5+p);
1008 for(i=1;i<t->numfilenames;i++)
1009 if(*(t->filenames[i]+5+p) != k)
1015 memset(completedname, 0, completednamebufferlength);
1016 memcpy(completedname, (t->filenames[0]+5), p);
1026 New function for tab-completion system
1027 Added by EvilTypeGuy
1028 MEGA Thanks to Taniwha
1031 void Con_DisplayList(const char **list)
1033 int i = 0, pos = 0, len = 0, maxlen = 0, width = (con_linewidth - 4);
1034 const char **walk = list;
1037 len = (int)strlen(*walk);
1045 len = (int)strlen(*list);
1046 if (pos + maxlen >= width) {
1052 for (i = 0; i < (maxlen - len); i++)
1064 Con_CompleteCommandLine
1066 New function for tab-completion system
1067 Added by EvilTypeGuy
1068 Thanks to Fett erich@heintz.com
1070 Enhanced to tab-complete map names by [515]
1073 void Con_CompleteCommandLine (void)
1075 const char *cmd = "", *s;
1076 const char **list[3] = {0, 0, 0};
1078 int c, v, a, i, cmd_len, pos, k;
1080 //find what we want to complete
1084 k = key_lines[edit_line][pos];
1085 if(k == '\"' || k == ';' || k == ' ' || k == '\'')
1090 s = key_lines[edit_line] + pos;
1091 strlcpy(s2, key_lines[edit_line] + key_linepos, sizeof(s2)); //save chars after cursor
1092 key_lines[edit_line][key_linepos] = 0; //hide them
1095 for(k=pos-1;k>2;k--)
1096 if(key_lines[edit_line][k] != ' ')
1098 if(key_lines[edit_line][k] == '\"' || key_lines[edit_line][k] == ';' || key_lines[edit_line][k] == '\'')
1100 if ((pos+k > 2 && !strncmp(key_lines[edit_line]+k-2, "map", 3))
1101 || (pos+k > 10 && !strncmp(key_lines[edit_line]+k-10, "changelevel", 11)))
1104 if (GetMapList(s, t, sizeof(t)))
1106 i = strlen(t) - strlen(s);
1107 strcpy((char*)s, t);
1108 if(s2[0]) //add back chars after cursor
1109 strcpy(&key_lines[edit_line][key_linepos], s2);
1116 // Count number of possible matches
1117 c = Cmd_CompleteCountPossible(s);
1118 v = Cvar_CompleteCountPossible(s);
1119 a = Cmd_CompleteAliasCountPossible(s);
1121 if (!(c + v + a)) // No possible matches
1124 strcpy(&key_lines[edit_line][key_linepos], s2);
1128 if (c + v + a == 1) {
1130 list[0] = Cmd_CompleteBuildList(s);
1132 list[0] = Cvar_CompleteBuildList(s);
1134 list[0] = Cmd_CompleteAliasBuildList(s);
1136 cmd_len = (int)strlen (cmd);
1139 cmd = *(list[0] = Cmd_CompleteBuildList(s));
1141 cmd = *(list[1] = Cvar_CompleteBuildList(s));
1143 cmd = *(list[2] = Cmd_CompleteAliasBuildList(s));
1145 cmd_len = (int)strlen (s);
1147 for (i = 0; i < 3; i++) {
1148 char ch = cmd[cmd_len];
1149 const char **l = list[i];
1151 while (*l && (*l)[cmd_len] == ch)
1162 for (i = 0; i < con_linewidth - 4; i++)
1166 // Print Possible Commands
1168 Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":");
1169 Con_DisplayList(list[0]);
1173 Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":");
1174 Con_DisplayList(list[1]);
1178 Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":");
1179 Con_DisplayList(list[2]);
1184 strncpy(key_lines[edit_line] + pos, cmd, cmd_len);
1185 key_linepos = cmd_len + pos;
1186 if (c + v + a == 1) {
1187 key_lines[edit_line][key_linepos] = ' ';
1191 strcpy(&key_lines[edit_line][key_linepos], s2);
1193 key_lines[edit_line][key_linepos] = 0;
1197 strcpy(&key_lines[edit_line][key_linepos], s2);
1198 for (i = 0; i < 3; i++)
1200 Mem_Free((void *)list[i]);