1 /*
2 Copyright (C) 1996-2001 Id Software, Inc.
3 Copyright (C) 2002-2009 John Fitzgibbons and others
4 Copyright (C) 2010-2014 QuakeSpasm developers
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 */
22 // console.c
23
24 #include <sys/types.h>
25 #include <time.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #ifdef _WIN32
29 #include <io.h>
30 #else
31 #include <unistd.h>
32 #endif
33 #include "quakedef.h"
34
35 int con_linewidth;
36
37 float con_cursorspeed = 4;
38
39 #define CON_TEXTSIZE (1024 * 1024) //ericw -- was 65536. johnfitz -- new default size
40 #define CON_MINSIZE 16384 //johnfitz -- old default, now the minimum size
41
42 int con_buffersize; //johnfitz -- user can now override default
43
44 qboolean con_forcedup; // because no entities to refresh
45
46 int con_totallines; // total lines in console scrollback
47 int con_backscroll; // lines up from bottom to display
48 int con_current; // where next message will be printed
49 int con_x; // offset in current line for next print
50 char *con_text = NULL;
51
52 cvar_t con_notifytime = {"con_notifytime","3",CVAR_NONE}; //seconds
53 cvar_t con_logcenterprint = {"con_logcenterprint", "1", CVAR_NONE}; //johnfitz
54
55 char con_lastcenterstring[1024]; //johnfitz
56 void (*con_redirect_flush)(const char *buffer); //call this to flush the redirection buffer (for rcon)
57 char con_redirect_buffer[8192];
58
59 #define NUM_CON_TIMES 4
60 float con_times[NUM_CON_TIMES]; // realtime time the line was generated
61 // for transparent notify lines
62
63 int con_vislines;
64
65 qboolean con_debuglog = false;
66
67 qboolean con_initialized;
68
69
70 /*
71 ================
72 Con_Quakebar -- johnfitz -- returns a bar of the desired length, but never wider than the console
73
74 includes a newline, unless len >= con_linewidth.
75 ================
76 */
Con_Quakebar(int len)77 const char *Con_Quakebar (int len)
78 {
79 static char bar[42];
80 int i;
81
82 len = q_min(len, (int)sizeof(bar) - 2);
83 len = q_min(len, con_linewidth);
84
85 bar[0] = '\35';
86 for (i = 1; i < len - 1; i++)
87 bar[i] = '\36';
88 bar[len-1] = '\37';
89
90 if (len < con_linewidth)
91 {
92 bar[len] = '\n';
93 bar[len+1] = 0;
94 }
95 else
96 bar[len] = 0;
97
98 return bar;
99 }
100
101 /*
102 ================
103 Con_ToggleConsole_f
104 ================
105 */
106 extern int history_line; //johnfitz
107
Con_ToggleConsole_f(void)108 void Con_ToggleConsole_f (void)
109 {
110 if (key_dest == key_console/* || (key_dest == key_game && con_forcedup)*/)
111 {
112 key_lines[edit_line][1] = 0; // clear any typing
113 key_linepos = 1;
114 con_backscroll = 0; //johnfitz -- toggleconsole should return you to the bottom of the scrollback
115 history_line = edit_line; //johnfitz -- it should also return you to the bottom of the command history
116
117 if (cls.state == ca_connected)
118 {
119 IN_Activate();
120 key_dest = key_game;
121 }
122 else
123 {
124 M_Menu_Main_f ();
125 }
126 }
127 else
128 {
129 IN_Deactivate(modestate == MS_WINDOWED);
130 key_dest = key_console;
131 }
132
133 SCR_EndLoadingPlaque ();
134 memset (con_times, 0, sizeof(con_times));
135 }
136
137 /*
138 ================
139 Con_Clear_f
140 ================
141 */
Con_Clear_f(void)142 static void Con_Clear_f (void)
143 {
144 if (con_text)
145 Q_memset (con_text, ' ', con_buffersize); //johnfitz -- con_buffersize replaces CON_TEXTSIZE
146 con_backscroll = 0; //johnfitz -- if console is empty, being scrolled up is confusing
147 }
148
149 /*
150 ================
151 Con_Dump_f -- johnfitz -- adapted from quake2 source
152 ================
153 */
Con_Dump_f(void)154 static void Con_Dump_f (void)
155 {
156 int l, x;
157 const char *line;
158 FILE *f;
159 char buffer[1024];
160 char name[MAX_OSPATH];
161
162 q_snprintf (name, sizeof(name), "%s/condump.txt", com_gamedir);
163 COM_CreatePath (name);
164 f = fopen (name, "w");
165 if (!f)
166 {
167 Con_Printf ("ERROR: couldn't open file %s.\n", name);
168 return;
169 }
170
171 // skip initial empty lines
172 for (l = con_current - con_totallines + 1; l <= con_current; l++)
173 {
174 line = con_text + (l % con_totallines)*con_linewidth;
175 for (x = 0; x < con_linewidth; x++)
176 if (line[x] != ' ')
177 break;
178 if (x != con_linewidth)
179 break;
180 }
181
182 // write the remaining lines
183 buffer[con_linewidth] = 0;
184 for ( ; l <= con_current; l++)
185 {
186 line = con_text + (l%con_totallines)*con_linewidth;
187 strncpy (buffer, line, con_linewidth);
188 for (x = con_linewidth - 1; x >= 0; x--)
189 {
190 if (buffer[x] == ' ')
191 buffer[x] = 0;
192 else
193 break;
194 }
195 for (x = 0; buffer[x]; x++)
196 buffer[x] &= 0x7f;
197
198 fprintf (f, "%s\n", buffer);
199 }
200
201 fclose (f);
202 Con_Printf ("Dumped console text to %s.\n", name);
203 }
204
205 /*
206 ================
207 Con_ClearNotify
208 ================
209 */
Con_ClearNotify(void)210 void Con_ClearNotify (void)
211 {
212 int i;
213
214 for (i = 0; i < NUM_CON_TIMES; i++)
215 con_times[i] = 0;
216 }
217
218
219 /*
220 ================
221 Con_MessageMode_f
222 ================
223 */
Con_MessageMode_f(void)224 static void Con_MessageMode_f (void)
225 {
226 if (cls.state != ca_connected || cls.demoplayback)
227 return;
228 chat_team = false;
229 key_dest = key_message;
230 }
231
232 /*
233 ================
234 Con_MessageMode2_f
235 ================
236 */
Con_MessageMode2_f(void)237 static void Con_MessageMode2_f (void)
238 {
239 if (cls.state != ca_connected || cls.demoplayback)
240 return;
241 chat_team = true;
242 key_dest = key_message;
243 }
244
245
246 /*
247 ================
248 Con_CheckResize
249
250 If the line width has changed, reformat the buffer.
251 ================
252 */
Con_CheckResize(void)253 void Con_CheckResize (void)
254 {
255 int i, j, width, oldwidth, oldtotallines, numlines, numchars;
256 char *tbuf; //johnfitz -- tbuf no longer a static array
257 int mark; //johnfitz
258
259 width = (vid.conwidth >> 3) - 2; //johnfitz -- use vid.conwidth instead of vid.width
260
261 if (width == con_linewidth)
262 return;
263
264 oldwidth = con_linewidth;
265 con_linewidth = width;
266 oldtotallines = con_totallines;
267 con_totallines = con_buffersize / con_linewidth; //johnfitz -- con_buffersize replaces CON_TEXTSIZE
268 numlines = oldtotallines;
269
270 if (con_totallines < numlines)
271 numlines = con_totallines;
272
273 numchars = oldwidth;
274
275 if (con_linewidth < numchars)
276 numchars = con_linewidth;
277
278 mark = Hunk_LowMark (); //johnfitz
279 tbuf = (char *) Hunk_Alloc (con_buffersize); //johnfitz
280
281 Q_memcpy (tbuf, con_text, con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE
282 Q_memset (con_text, ' ', con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE
283
284 for (i = 0; i < numlines; i++)
285 {
286 for (j = 0; j < numchars; j++)
287 {
288 con_text[(con_totallines - 1 - i) * con_linewidth + j] =
289 tbuf[((con_current - i + oldtotallines) % oldtotallines) * oldwidth + j];
290 }
291 }
292
293 Hunk_FreeToLowMark (mark); //johnfitz
294
295 Con_ClearNotify ();
296
297 con_backscroll = 0;
298 con_current = con_totallines - 1;
299 }
300
301
302 /*
303 ================
304 Con_Init
305 ================
306 */
Con_Init(void)307 void Con_Init (void)
308 {
309 int i;
310
311 //johnfitz -- user settable console buffer size
312 i = COM_CheckParm("-consize");
313 if (i && i < com_argc-1)
314 con_buffersize = q_max(CON_MINSIZE,Q_atoi(com_argv[i+1])*1024);
315 else
316 con_buffersize = CON_TEXTSIZE;
317 //johnfitz
318
319 con_text = (char *) Hunk_AllocName (con_buffersize, "context");//johnfitz -- con_buffersize replaces CON_TEXTSIZE
320 Q_memset (con_text, ' ', con_buffersize);//johnfitz -- con_buffersize replaces CON_TEXTSIZE
321 con_linewidth = -1;
322
323 //johnfitz -- no need to run Con_CheckResize here
324 con_linewidth = 38;
325 con_totallines = con_buffersize / con_linewidth;//johnfitz -- con_buffersize replaces CON_TEXTSIZE
326 con_backscroll = 0;
327 con_current = con_totallines - 1;
328 //johnfitz
329
330 Con_Printf ("Console initialized.\n");
331
332 Cvar_RegisterVariable (&con_notifytime);
333 Cvar_RegisterVariable (&con_logcenterprint); //johnfitz
334
335 Cmd_AddCommand ("toggleconsole", Con_ToggleConsole_f);
336 Cmd_AddCommand ("messagemode", Con_MessageMode_f);
337 Cmd_AddCommand ("messagemode2", Con_MessageMode2_f);
338 Cmd_AddCommand ("clear", Con_Clear_f);
339 Cmd_AddCommand ("condump", Con_Dump_f); //johnfitz
340 con_initialized = true;
341 }
342
343
344 /*
345 ===============
346 Con_Linefeed
347 ===============
348 */
Con_Linefeed(void)349 static void Con_Linefeed (void)
350 {
351 //johnfitz -- improved scrolling
352 if (con_backscroll)
353 con_backscroll++;
354 if (con_backscroll > con_totallines - (glheight>>3) - 1)
355 con_backscroll = con_totallines - (glheight>>3) - 1;
356 //johnfitz
357
358 con_x = 0;
359 con_current++;
360 Q_memset (&con_text[(con_current%con_totallines)*con_linewidth], ' ', con_linewidth);
361 }
362
363 /*
364 ================
365 Con_Print
366
367 Handles cursor positioning, line wrapping, etc
368 All console printing must go through this in order to be logged to disk
369 If no console is visible, the notify window will pop up.
370 ================
371 */
Con_Print(const char * txt)372 static void Con_Print (const char *txt)
373 {
374 int y;
375 int c, l;
376 static int cr;
377 int mask;
378 qboolean boundary;
379
380 //con_backscroll = 0; //johnfitz -- better console scrolling
381
382 if (txt[0] == 1)
383 {
384 mask = 128; // go to colored text
385 S_LocalSound ("misc/talk.wav"); // play talk wav
386 txt++;
387 }
388 else if (txt[0] == 2)
389 {
390 mask = 128; // go to colored text
391 txt++;
392 }
393 else
394 mask = 0;
395
396 boundary = true;
397
398 while ( (c = *txt) )
399 {
400 if (c <= ' ')
401 {
402 boundary = true;
403 }
404 else if (boundary)
405 {
406 // count word length
407 for (l = 0; l < con_linewidth; l++)
408 if (txt[l] <= ' ')
409 break;
410
411 // word wrap
412 if (l != con_linewidth && (con_x + l > con_linewidth))
413 con_x = 0;
414
415 boundary = false;
416 }
417
418 txt++;
419
420 if (cr)
421 {
422 con_current--;
423 cr = false;
424 }
425
426 if (!con_x)
427 {
428 Con_Linefeed ();
429 // mark time for transparent overlay
430 if (con_current >= 0)
431 con_times[con_current % NUM_CON_TIMES] = realtime;
432 }
433
434 switch (c)
435 {
436 case '\n':
437 con_x = 0;
438 break;
439
440 case '\r':
441 con_x = 0;
442 cr = 1;
443 break;
444
445 default: // display character and advance
446 y = con_current % con_totallines;
447 con_text[y*con_linewidth+con_x] = c | mask;
448 con_x++;
449 if (con_x >= con_linewidth)
450 con_x = 0;
451 break;
452 }
453 }
454 }
455
456
457 // borrowed from uhexen2 by S.A. for new procs, LOG_Init, LOG_Close
458
459 static char logfilename[MAX_OSPATH]; // current logfile name
460 static int log_fd = -1; // log file descriptor
461
462 /*
463 ================
464 Con_DebugLog
465 ================
466 */
Con_DebugLog(const char * msg)467 void Con_DebugLog(const char *msg)
468 {
469 if (log_fd == -1)
470 return;
471
472 size_t msg_len = strlen(msg);
473 if (write(log_fd, msg, msg_len) != msg_len)
474 return; // Nonsense to supress warning
475 }
476
477
478 /*
479 ================
480 Con_Printf
481
482 Handles cursor positioning, line wrapping, etc
483 ================
484 */
485 #define MAXPRINTMSG 4096
Con_Printf(const char * fmt,...)486 void Con_Printf (const char *fmt, ...)
487 {
488 va_list argptr;
489 char msg[MAXPRINTMSG];
490 static qboolean inupdate;
491
492 va_start (argptr, fmt);
493 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
494 va_end (argptr);
495
496 if (con_redirect_flush)
497 q_strlcat(con_redirect_buffer, msg, sizeof(con_redirect_buffer));
498 // also echo to debugging console
499 Sys_Printf ("%s", msg);
500
501 // log all messages to file
502 if (con_debuglog)
503 Con_DebugLog(msg);
504
505 if (!con_initialized)
506 return;
507
508 if (cls.state == ca_dedicated)
509 return; // no graphics mode
510
511 // write it to the scrollable buffer
512 Con_Print (msg);
513
514 // update the screen if the console is displayed
515 if (cls.signon != SIGNONS && !scr_disabled_for_loading )
516 {
517 // protect against infinite loop if something in SCR_UpdateScreen calls
518 // Con_Printd
519 if (!inupdate)
520 {
521 inupdate = true;
522 SCR_UpdateScreen ();
523 inupdate = false;
524 }
525 }
526 }
527
528 /*
529 ================
530 Con_DWarning -- ericw
531
532 same as Con_Warning, but only prints if "developer" cvar is set.
533 use for "exceeds standard limit of" messages, which are only relevant for developers
534 targetting vanilla engines
535 ================
536 */
Con_DWarning(const char * fmt,...)537 void Con_DWarning (const char *fmt, ...)
538 {
539 va_list argptr;
540 char msg[MAXPRINTMSG];
541
542 if (developer.value >= 2)
543 { // don't confuse non-developers with techie stuff...
544 // (this is limit exceeded warnings)
545
546 va_start (argptr, fmt);
547 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
548 va_end (argptr);
549
550 Con_SafePrintf ("\x02Warning: ");
551 Con_Printf ("%s", msg);
552 }
553 }
554
555 /*
556 ================
557 Con_Warning -- johnfitz -- prints a warning to the console
558 ================
559 */
Con_Warning(const char * fmt,...)560 void Con_Warning (const char *fmt, ...)
561 {
562 va_list argptr;
563 char msg[MAXPRINTMSG];
564
565 va_start (argptr, fmt);
566 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
567 va_end (argptr);
568
569 Con_SafePrintf ("\x02Warning: ");
570 Con_Printf ("%s", msg);
571 }
572
573 /*
574 ================
575 Con_DPrintf
576
577 A Con_Printf that only shows up if the "developer" cvar is set
578 ================
579 */
Con_DPrintf(const char * fmt,...)580 void Con_DPrintf (const char *fmt, ...)
581 {
582 va_list argptr;
583 char msg[MAXPRINTMSG];
584
585 if (!developer.value)
586 return; // don't confuse non-developers with techie stuff...
587
588 va_start (argptr, fmt);
589 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
590 va_end (argptr);
591
592 Con_SafePrintf ("%s", msg); //johnfitz -- was Con_Printf
593 }
594
595 /*
596 ================
597 Con_DPrintf2 -- johnfitz -- only prints if "developer" >= 2
598
599 currently not used
600 ================
601 */
Con_DPrintf2(const char * fmt,...)602 void Con_DPrintf2 (const char *fmt, ...)
603 {
604 va_list argptr;
605 char msg[MAXPRINTMSG];
606
607 if (developer.value >= 2)
608 {
609 va_start (argptr, fmt);
610 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
611 va_end (argptr);
612 Con_Printf ("%s", msg);
613 }
614 }
615
616
617 /*
618 ==================
619 Con_SafePrintf
620
621 Okay to call even when the screen can't be updated
622 ==================
623 */
Con_SafePrintf(const char * fmt,...)624 void Con_SafePrintf (const char *fmt, ...)
625 {
626 va_list argptr;
627 char msg[1024];
628 int temp;
629
630 va_start (argptr, fmt);
631 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
632 va_end (argptr);
633
634 temp = scr_disabled_for_loading;
635 scr_disabled_for_loading = true;
636 Con_Printf ("%s", msg);
637 scr_disabled_for_loading = temp;
638 }
639
640 /*
641 ================
642 Con_CenterPrintf -- johnfitz -- pad each line with spaces to make it appear centered
643 ================
644 */
645 void Con_CenterPrintf (int linewidth, const char *fmt, ...) FUNC_PRINTF(2,3);
Con_CenterPrintf(int linewidth,const char * fmt,...)646 void Con_CenterPrintf (int linewidth, const char *fmt, ...)
647 {
648 va_list argptr;
649 char msg[MAXPRINTMSG]; //the original message
650 char line[MAXPRINTMSG]; //one line from the message
651 char spaces[21]; //buffer for spaces
652 char *src, *dst;
653 int len, s;
654
655 va_start (argptr, fmt);
656 q_vsnprintf (msg, sizeof(msg), fmt, argptr);
657 va_end (argptr);
658
659 linewidth = q_min(linewidth, con_linewidth);
660 for (src = msg; *src; )
661 {
662 dst = line;
663 while (*src && *src != '\n')
664 *dst++ = *src++;
665 *dst = 0;
666 if (*src == '\n')
667 src++;
668
669 len = strlen(line);
670 if (len < linewidth)
671 {
672 s = (linewidth-len)/2;
673 memset (spaces, ' ', s);
674 spaces[s] = 0;
675 Con_Printf ("%s%s\n", spaces, line);
676 }
677 else
678 Con_Printf ("%s\n", line);
679 }
680 }
681
682 /*
683 ==================
684 Con_LogCenterPrint -- johnfitz -- echo centerprint message to the console
685 ==================
686 */
Con_LogCenterPrint(const char * str)687 void Con_LogCenterPrint (const char *str)
688 {
689 if (!strcmp(str, con_lastcenterstring))
690 return; //ignore duplicates
691
692 if (cl.gametype == GAME_DEATHMATCH && con_logcenterprint.value != 2)
693 return; //don't log in deathmatch
694
695 strcpy(con_lastcenterstring, str);
696
697 if (con_logcenterprint.value)
698 {
699 Con_Printf ("%s", Con_Quakebar(40));
700 Con_CenterPrintf (40, "%s\n", str);
701 Con_Printf ("%s", Con_Quakebar(40));
702 Con_ClearNotify ();
703 }
704 }
705
Con_IsRedirected(void)706 qboolean Con_IsRedirected(void)
707 {
708 return !!con_redirect_flush;
709 }
Con_Redirect(void (* flush)(const char *))710 void Con_Redirect(void(*flush)(const char *))
711 {
712 if (con_redirect_flush)
713 con_redirect_flush(con_redirect_buffer);
714 *con_redirect_buffer = 0;
715 con_redirect_flush = flush;
716 }
717
718 /*
719 ==============================================================================
720
721 TAB COMPLETION
722
723 ==============================================================================
724 */
725
726 //johnfitz -- tab completion stuff
727 //unique defs
728 char key_tabpartial[MAXCMDLINE];
729 typedef struct tab_s
730 {
731 const char *name;
732 const char *type;
733 struct tab_s *next;
734 struct tab_s *prev;
735 } tab_t;
736 tab_t *tablist;
737
738 //defs from elsewhere
739 extern qboolean keydown[256];
740 extern cmd_function_t *cmd_functions;
741 #define MAX_ALIAS_NAME 32
742 typedef struct cmdalias_s
743 {
744 struct cmdalias_s *next;
745 char name[MAX_ALIAS_NAME];
746 char *value;
747 } cmdalias_t;
748 extern cmdalias_t *cmd_alias;
749
750 /*
751 ============
752 AddToTabList -- johnfitz
753
754 tablist is a doubly-linked loop, alphabetized by name
755 ============
756 */
757
758 // bash_partial is the string that can be expanded,
759 // aka Linux Bash shell. -- S.A.
760 static char bash_partial[80];
761 static qboolean bash_singlematch;
762
AddToTabList(const char * name,const char * type)763 void AddToTabList (const char *name, const char *type)
764 {
765 tab_t *t,*insert;
766 char *i_bash;
767 const char *i_name;
768
769 if (!*bash_partial)
770 {
771 strncpy (bash_partial, name, 79);
772 bash_partial[79] = '\0';
773 }
774 else
775 {
776 bash_singlematch = 0;
777 // find max common between bash_partial and name
778 i_bash = bash_partial;
779 i_name = name;
780 while (*i_bash && (*i_bash == *i_name))
781 {
782 i_bash++;
783 i_name++;
784 }
785 *i_bash = 0;
786 }
787
788 t = (tab_t *) Hunk_Alloc(sizeof(tab_t));
789 t->name = name;
790 t->type = type;
791
792 if (!tablist) //create list
793 {
794 tablist = t;
795 t->next = t;
796 t->prev = t;
797 }
798 else if (strcmp(name, tablist->name) < 0) //insert at front
799 {
800 t->next = tablist;
801 t->prev = tablist->prev;
802 t->next->prev = t;
803 t->prev->next = t;
804 tablist = t;
805 }
806 else //insert later
807 {
808 insert = tablist;
809 do
810 {
811 if (strcmp(name, insert->name) < 0)
812 break;
813 insert = insert->next;
814 } while (insert != tablist);
815
816 t->next = insert;
817 t->prev = insert->prev;
818 t->next->prev = t;
819 t->prev->next = t;
820 }
821 }
822
823 typedef struct arg_completion_type_s
824 {
825 const char *command;
826 filelist_item_t **filelist;
827 } arg_completion_type_t;
828
829 static const arg_completion_type_t arg_completion_types[] =
830 {
831 { "map ", &extralevels },
832 { "changelevel ", &extralevels },
833 { "game ", &modlist },
834 { "record ", &demolist },
835 { "playdemo ", &demolist },
836 { "timedemo ", &demolist }
837 };
838
839 static const int num_arg_completion_types =
840 sizeof(arg_completion_types)/sizeof(arg_completion_types[0]);
841
842 /*
843 ============
844 FindCompletion -- stevenaaus
845 ============
846 */
FindCompletion(const char * partial,filelist_item_t * filelist,int * nummatches_out)847 const char *FindCompletion (const char *partial, filelist_item_t *filelist, int *nummatches_out)
848 {
849 static char matched[32];
850 char *i_matched, *i_name;
851 filelist_item_t *file;
852 int init, match, plen;
853
854 memset(matched, 0, sizeof(matched));
855 plen = strlen(partial);
856 match = 0;
857
858 for (file = filelist, init = 0; file; file = file->next)
859 {
860 if (!strncmp(file->name, partial, plen))
861 {
862 if (init == 0)
863 {
864 init = 1;
865 strncpy (matched, file->name, sizeof(matched)-1);
866 matched[sizeof(matched)-1] = '\0';
867 }
868 else
869 { // find max common
870 i_matched = matched;
871 i_name = file->name;
872 while (*i_matched && (*i_matched == *i_name))
873 {
874 i_matched++;
875 i_name++;
876 }
877 *i_matched = 0;
878 }
879 match++;
880 }
881 }
882
883 *nummatches_out = match;
884
885 if (match > 1)
886 {
887 for (file = filelist; file; file = file->next)
888 {
889 if (!strncmp(file->name, partial, plen))
890 Con_SafePrintf (" %s\n", file->name);
891 }
892 Con_SafePrintf ("\n");
893 }
894
895 return matched;
896 }
897
898 /*
899 ============
900 BuildTabList -- johnfitz
901 ============
902 */
BuildTabList(const char * partial)903 void BuildTabList (const char *partial)
904 {
905 cmdalias_t *alias;
906 cvar_t *cvar;
907 cmd_function_t *cmd;
908 int len;
909
910 tablist = NULL;
911 len = strlen(partial);
912
913 bash_partial[0] = 0;
914 bash_singlematch = 1;
915
916 cvar = Cvar_FindVarAfter ("", CVAR_NONE);
917 for ( ; cvar ; cvar=cvar->next)
918 if (!Q_strncmp (partial, cvar->name, len))
919 AddToTabList (cvar->name, "cvar");
920
921 for (cmd=cmd_functions ; cmd ; cmd=cmd->next)
922 if (!Q_strncmp (partial,cmd->name, len))
923 AddToTabList (cmd->name, "command");
924
925 for (alias=cmd_alias ; alias ; alias=alias->next)
926 if (!Q_strncmp (partial, alias->name, len))
927 AddToTabList (alias->name, "alias");
928 }
929
930 /*
931 ============
932 Con_TabComplete -- johnfitz
933 ============
934 */
Con_TabComplete(void)935 void Con_TabComplete (void)
936 {
937 char partial[MAXCMDLINE];
938 const char *match;
939 static char *c;
940 tab_t *t;
941 int mark, i, j;
942
943 // if editline is empty, return
944 if (key_lines[edit_line][1] == 0)
945 return;
946
947 // get partial string (space -> cursor)
948 if (!key_tabpartial[0]) //first time through, find new insert point. (Otherwise, use previous.)
949 {
950 //work back from cursor until you find a space, quote, semicolon, or prompt
951 c = key_lines[edit_line] + key_linepos - 1; //start one space left of cursor
952 while (*c!=' ' && *c!='\"' && *c!=';' && c!=key_lines[edit_line])
953 c--;
954 c++; //start 1 char after the separator we just found
955 }
956 for (i = 0; c + i < key_lines[edit_line] + key_linepos; i++)
957 partial[i] = c[i];
958 partial[i] = 0;
959
960 // Map autocomplete function -- S.A
961 // Since we don't have argument completion, this hack will do for now...
962 for (j=0; j<num_arg_completion_types; j++)
963 {
964 // arg_completion contains a command we can complete the arguments
965 // for (like "map ") and a list of all the maps.
966 arg_completion_type_t arg_completion = arg_completion_types[j];
967 const char *command_name = arg_completion.command;
968
969 if (!strncmp (key_lines[edit_line] + 1, command_name, strlen(command_name)))
970 {
971 int nummatches = 0;
972 const char *matched_map = FindCompletion(partial, *arg_completion.filelist, &nummatches);
973 if (!*matched_map)
974 return;
975 q_strlcpy (partial, matched_map, MAXCMDLINE);
976 *c = '\0';
977 q_strlcat (key_lines[edit_line], partial, MAXCMDLINE);
978 key_linepos = c - key_lines[edit_line] + Q_strlen(matched_map); //set new cursor position
979 if (key_linepos >= MAXCMDLINE)
980 key_linepos = MAXCMDLINE - 1;
981 // if only one match, append a space
982 if (key_linepos < MAXCMDLINE - 1 &&
983 key_lines[edit_line][key_linepos] == 0 && (nummatches == 1))
984 {
985 key_lines[edit_line][key_linepos] = ' ';
986 key_linepos++;
987 key_lines[edit_line][key_linepos] = 0;
988 }
989 c = key_lines[edit_line] + key_linepos;
990 return;
991 }
992 }
993
994 //if partial is empty, return
995 if (partial[0] == 0)
996 return;
997
998 //trim trailing space becuase it screws up string comparisons
999 if (i > 0 && partial[i-1] == ' ')
1000 partial[i-1] = 0;
1001
1002 // find a match
1003 mark = Hunk_LowMark();
1004 if (!key_tabpartial[0]) //first time through
1005 {
1006 q_strlcpy (key_tabpartial, partial, MAXCMDLINE);
1007 BuildTabList (key_tabpartial);
1008
1009 if (!tablist)
1010 return;
1011
1012 // print list if length > 1
1013 if (tablist->next != tablist)
1014 {
1015 t = tablist;
1016 Con_SafePrintf("\n");
1017 do
1018 {
1019 Con_SafePrintf(" %s (%s)\n", t->name, t->type);
1020 t = t->next;
1021 } while (t != tablist);
1022 Con_SafePrintf("\n");
1023 }
1024
1025 // match = tablist->name;
1026 // First time, just show maximum matching chars -- S.A.
1027 match = bash_partial;
1028 }
1029 else
1030 {
1031 BuildTabList (key_tabpartial);
1032
1033 if (!tablist)
1034 return;
1035
1036 //find current match -- can't save a pointer because the list will be rebuilt each time
1037 t = tablist;
1038 match = keydown[K_SHIFT] ? t->prev->name : t->name;
1039 do
1040 {
1041 if (!Q_strcmp(t->name, partial))
1042 {
1043 match = keydown[K_SHIFT] ? t->prev->name : t->next->name;
1044 break;
1045 }
1046 t = t->next;
1047 } while (t != tablist);
1048 }
1049 Hunk_FreeToLowMark(mark); //it's okay to free it here because match is a pointer to persistent data
1050
1051 // insert new match into edit line
1052 q_strlcpy (partial, match, MAXCMDLINE); //first copy match string
1053 q_strlcat (partial, key_lines[edit_line] + key_linepos, MAXCMDLINE); //then add chars after cursor
1054 *c = '\0'; //now copy all of this into edit line
1055 q_strlcat (key_lines[edit_line], partial, MAXCMDLINE);
1056 key_linepos = c - key_lines[edit_line] + Q_strlen(match); //set new cursor position
1057 if (key_linepos >= MAXCMDLINE)
1058 key_linepos = MAXCMDLINE - 1;
1059
1060 // if cursor is at end of string, let's append a space to make life easier
1061 if (key_linepos < MAXCMDLINE - 1 &&
1062 key_lines[edit_line][key_linepos] == 0 && bash_singlematch)
1063 {
1064 key_lines[edit_line][key_linepos] = ' ';
1065 key_linepos++;
1066 key_lines[edit_line][key_linepos] = 0;
1067 // S.A.: the map argument completion (may be in combination with the bash-style
1068 // display behavior changes, causes weirdness when completing the arguments for
1069 // the changelevel command. the line below "fixes" it, although I'm not sure about
1070 // the reason, yet, neither do I know any possible side effects of it:
1071 c = key_lines[edit_line] + key_linepos;
1072 }
1073 }
1074
1075 /*
1076 ==============================================================================
1077
1078 DRAWING
1079
1080 ==============================================================================
1081 */
1082
1083 /*
1084 ================
1085 Con_DrawNotify
1086
1087 Draws the last few lines of output transparently over the game top
1088 ================
1089 */
Con_DrawNotify(void)1090 void Con_DrawNotify (void)
1091 {
1092 int i, x, v;
1093 const char *text;
1094 float time;
1095
1096 GL_SetCanvas (CANVAS_CONSOLE); //johnfitz
1097 v = vid.conheight; //johnfitz
1098
1099 for (i = con_current-NUM_CON_TIMES+1; i <= con_current; i++)
1100 {
1101 if (i < 0)
1102 continue;
1103 time = con_times[i % NUM_CON_TIMES];
1104 if (time == 0)
1105 continue;
1106 time = realtime - time;
1107 if (time > con_notifytime.value)
1108 continue;
1109 text = con_text + (i % con_totallines)*con_linewidth;
1110
1111 clearnotify = 0;
1112
1113 for (x = 0; x < con_linewidth; x++)
1114 Draw_Character ((x+1)<<3, v, text[x]);
1115
1116 v += 8;
1117
1118 scr_tileclear_updates = 0; //johnfitz
1119 }
1120
1121 if (key_dest == key_message)
1122 {
1123 clearnotify = 0;
1124
1125 if (chat_team)
1126 {
1127 Draw_String (8, v, "say_team:");
1128 x = 11;
1129 }
1130 else
1131 {
1132 Draw_String (8, v, "say:");
1133 x = 6;
1134 }
1135
1136 text = Key_GetChatBuffer();
1137 i = Key_GetChatMsgLen();
1138 if (i > con_linewidth - x - 1)
1139 text += i - con_linewidth + x + 1;
1140
1141 while (*text)
1142 {
1143 Draw_Character (x<<3, v, *text);
1144 x++;
1145 text++;
1146 }
1147
1148 Draw_Character (x<<3, v, 10 + ((int)(realtime*con_cursorspeed)&1));
1149 v += 8;
1150
1151 scr_tileclear_updates = 0; //johnfitz
1152 }
1153 }
1154
1155 /*
1156 ================
1157 Con_DrawInput -- johnfitz -- modified to allow insert editing
1158
1159 The input line scrolls horizontally if typing goes beyond the right edge
1160 ================
1161 */
1162 extern qpic_t *pic_ovr, *pic_ins; //johnfitz -- new cursor handling
1163
Con_DrawInput(void)1164 void Con_DrawInput (void)
1165 {
1166 int i, ofs;
1167
1168 if (key_dest != key_console && !con_forcedup)
1169 return; // don't draw anything
1170
1171 // prestep if horizontally scrolling
1172 if (key_linepos >= con_linewidth)
1173 ofs = 1 + key_linepos - con_linewidth;
1174 else
1175 ofs = 0;
1176
1177 // draw input string
1178 for (i = 0; key_lines[edit_line][i+ofs] && i < con_linewidth; i++)
1179 Draw_Character ((i+1)<<3, vid.conheight - 16, key_lines[edit_line][i+ofs]);
1180
1181 // johnfitz -- new cursor handling
1182 if (!((int)((realtime-key_blinktime)*con_cursorspeed) & 1))
1183 {
1184 i = key_linepos - ofs;
1185 Draw_Pic ((i+1)<<3, vid.conheight - 16, key_insert ? pic_ins : pic_ovr, 1.0f, false);
1186 }
1187 }
1188
1189 /*
1190 ================
1191 Con_DrawConsole -- johnfitz -- heavy revision
1192
1193 Draws the console with the solid background
1194 The typing input line at the bottom should only be drawn if typing is allowed
1195 ================
1196 */
Con_DrawConsole(int lines,qboolean drawinput)1197 void Con_DrawConsole (int lines, qboolean drawinput)
1198 {
1199 int i, x, y, j, sb, rows;
1200 const char *text;
1201 char ver[32];
1202
1203 if (lines <= 0)
1204 return;
1205
1206 con_vislines = lines * vid.conheight / glheight;
1207 GL_SetCanvas (CANVAS_CONSOLE);
1208
1209 // draw the background
1210 Draw_ConsoleBackground ();
1211
1212 // draw the buffer text
1213 rows = (con_vislines +7)/8;
1214 y = vid.conheight - rows*8;
1215 rows -= 2; //for input and version lines
1216 sb = (con_backscroll) ? 2 : 0;
1217
1218 for (i = con_current - rows + 1; i <= con_current - sb; i++, y += 8)
1219 {
1220 j = i - con_backscroll;
1221 if (j < 0)
1222 j = 0;
1223 text = con_text + (j % con_totallines)*con_linewidth;
1224
1225 for (x = 0; x < con_linewidth; x++)
1226 Draw_Character ( (x + 1)<<3, y, text[x]);
1227 }
1228
1229 // draw scrollback arrows
1230 if (con_backscroll)
1231 {
1232 y += 8; // blank line
1233 for (x = 0; x < con_linewidth; x += 4)
1234 Draw_Character ((x + 1)<<3, y, '^');
1235 y += 8;
1236 }
1237
1238 // draw the input prompt, user text, and cursor
1239 if (drawinput)
1240 Con_DrawInput ();
1241
1242 //draw version number in bottom right
1243 y += 8;
1244 q_snprintf (ver, sizeof(ver), "vkQuake " VKQUAKE_VER_STRING);
1245 for (x = 0; x < (int)strlen(ver); x++)
1246 Draw_Character ((con_linewidth - strlen(ver) + x + 2)<<3, y, ver[x] /*+ 128*/);
1247 }
1248
1249
1250 /*
1251 ==================
1252 Con_NotifyBox
1253 ==================
1254 */
Con_NotifyBox(const char * text)1255 void Con_NotifyBox (const char *text)
1256 {
1257 double t1, t2;
1258 int lastkey, lastchar;
1259
1260 // during startup for sound / cd warnings
1261 Con_Printf ("\n\n%s", Con_Quakebar(40)); //johnfitz
1262 Con_Printf ("%s", text);
1263 Con_Printf ("Press a key.\n");
1264 Con_Printf ("%s", Con_Quakebar(40)); //johnfitz
1265
1266 IN_Deactivate(modestate == MS_WINDOWED);
1267 key_dest = key_console;
1268
1269 Key_BeginInputGrab ();
1270 do
1271 {
1272 t1 = Sys_DoubleTime ();
1273 SCR_UpdateScreen ();
1274 Sys_SendKeyEvents ();
1275 Key_GetGrabbedInput (&lastkey, &lastchar);
1276 Sys_Sleep (16);
1277 t2 = Sys_DoubleTime ();
1278 realtime += t2-t1; // make the cursor blink
1279 } while (lastkey == -1 && lastchar == -1);
1280 Key_EndInputGrab ();
1281
1282 Con_Printf ("\n");
1283 IN_Activate();
1284 key_dest = key_game;
1285 realtime = 0; // put the cursor back to invisible
1286 }
1287
1288
LOG_Init(quakeparms_t * parms)1289 void LOG_Init (quakeparms_t *parms)
1290 {
1291 time_t inittime;
1292 char session[24];
1293
1294 if (!COM_CheckParm("-condebug"))
1295 return;
1296
1297 inittime = time (NULL);
1298 strftime (session, sizeof(session), "%m/%d/%Y %H:%M:%S", localtime(&inittime));
1299 q_snprintf (logfilename, sizeof(logfilename), "%s/qconsole.log", parms->basedir);
1300
1301 // unlink (logfilename);
1302
1303 log_fd = open (logfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
1304 if (log_fd == -1)
1305 {
1306 fprintf (stderr, "Error: Unable to create log file %s\n", logfilename);
1307 return;
1308 }
1309
1310 con_debuglog = true;
1311 Con_DebugLog (va("LOG started on: %s \n", session));
1312
1313 }
1314
LOG_Close(void)1315 void LOG_Close (void)
1316 {
1317 if (log_fd == -1)
1318 return;
1319 close (log_fd);
1320 log_fd = -1;
1321 }
1322
1323