mirror of
https://github.com/Q3Rally-Team/q3rally.git
synced 2024-11-25 13:21:08 +00:00
0d5fb492cd
Fix GCC 6 misleading-indentation warning add SECURITY.md OpenGL2: Restore adding fixed ambient light when HDR is enabled Few LCC memory fixes. fix a few potential buffer overwrite in Game VM Enable compiler optimization on all macOS architectures Don't allow qagame module to create "botlib.log" at ANY filesystem location Make FS_BuildOSPath for botlib.log consistent with typical usage tiny readme thing Remove extra plus sign from Huff_Compress() Fix VMs being able to change CVAR_PROTECTED cvars Don't register fs_game cvar everywhere just to get the value Don't let VMs change engine latch cvars immediately Fix fs_game '..' reading outside of home and base path Fix VMs forcing engine latch cvar to update to latched value Revert my recent cvar latch changes Revert "Don't let VMs change engine latch cvars immediately" Partially revert "Fix fs_game '..' reading outside of home and base path" Revert "Fix VMs forcing engine latch cvar to update to latched value" Fix exploit to bypass filename restrictions on Windows Changes to systemd q3a.service Fix Q_vsnprintf for mingw-w64 Fix timelimit causing an infinite map ending loop Fix invalid access to cluster 0 in AAS_AreaRouteToGoalArea() Fix negative frag/capturelimit causing an infinite map end loop OpenGL2: Fix dark lightmap on shader in mpteam6 Make FS_InvalidGameDir() consider subdirectories invalid [qcommon] Remove dead serialization code [qcommon] Make several zone variables and functions static. Fix MAC_OS_X_VERSION_MIN_REQUIRED for macOS 10.10 and later Increase q3_ui .arena filename list buffer size to 4096 bytes OpenGL2: Fix crash when BSP has deluxe maps and vertex lit surfaces Support Unicode characters greater than 0xFF in cl_consoleKeys Fix macOS app bundle with space in name OpenGL1: Use glGenTextures instead of hardcoded values Remove CON_FlushIn function and where STDIN needs flushing, use tcflush POSIX function Update libogg from 1.3.2 to 1.3.3 Rename (already updated) libogg-1.3.2 to libogg-1.3.3 Update libvorbis from 1.3.5 to 1.3.6 * Fix CVE-2018-5146 - out-of-bounds write on codebook decoding. * Fix CVE-2017-14632 - free() on unitialized data * Fix CVE-2017-14633 - out-of-bounds read Rename (already updated) libvorbis-1.3.5 to libvorbis-1.3.6 Update opus from 1.1.4 to 1.2.1 Rename (already updated) opus-1.1.4 to opus-1.2.1 Update opusfile from 0.8 to 0.9 Rename (already updated) opusfile-0.8 to opusfile-0.9 First swing at a CONTRIBUTING.md Allow loading system OpenAL library on macOS again Remove duplicate setting of FREETYPE_CFLAGS in Makefile Fix exploit to reset player by sending wrong serverId Fix "Going to CS_ZOMBIE for [clientname]" developer message Fix MSG_Read*String*() functions not being able to read last byte from message
537 lines
12 KiB
C
537 lines
12 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
#include "../qcommon/q_shared.h"
|
|
#include "../qcommon/qcommon.h"
|
|
#include "sys_local.h"
|
|
|
|
#ifndef DEDICATED
|
|
#include "../client/client.h"
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <signal.h>
|
|
#include <termios.h>
|
|
#include <fcntl.h>
|
|
#include <sys/time.h>
|
|
|
|
/*
|
|
=============================================================
|
|
tty console routines
|
|
|
|
NOTE: if the user is editing a line when something gets printed to the early
|
|
console then it won't look good so we provide CON_Hide and CON_Show to be
|
|
called before and after a stdout or stderr output
|
|
=============================================================
|
|
*/
|
|
|
|
extern qboolean stdinIsATTY;
|
|
static qboolean stdin_active;
|
|
// general flag to tell about tty console mode
|
|
static qboolean ttycon_on = qfalse;
|
|
static int ttycon_hide = 0;
|
|
static int ttycon_show_overdue = 0;
|
|
|
|
// some key codes that the terminal may be using, initialised on start up
|
|
static int TTY_erase;
|
|
static int TTY_eof;
|
|
|
|
static struct termios TTY_tc;
|
|
|
|
static field_t TTY_con;
|
|
|
|
// This is somewhat of aduplicate of the graphical console history
|
|
// but it's safer more modular to have our own here
|
|
#define CON_HISTORY 32
|
|
static field_t ttyEditLines[ CON_HISTORY ];
|
|
static int hist_current = -1, hist_count = 0;
|
|
|
|
#ifndef DEDICATED
|
|
// Don't use "]" as it would be the same as in-game console,
|
|
// this makes it clear where input came from.
|
|
#define TTY_CONSOLE_PROMPT "tty]"
|
|
#else
|
|
#define TTY_CONSOLE_PROMPT "]"
|
|
#endif
|
|
|
|
/*
|
|
==================
|
|
CON_Back
|
|
|
|
Output a backspace
|
|
|
|
NOTE: it seems on some terminals just sending '\b' is not enough so instead we
|
|
send "\b \b"
|
|
(FIXME there may be a way to find out if '\b' alone would work though)
|
|
==================
|
|
*/
|
|
static void CON_Back( void )
|
|
{
|
|
char key;
|
|
size_t UNUSED_VAR size;
|
|
|
|
key = '\b';
|
|
size = write(STDOUT_FILENO, &key, 1);
|
|
key = ' ';
|
|
size = write(STDOUT_FILENO, &key, 1);
|
|
key = '\b';
|
|
size = write(STDOUT_FILENO, &key, 1);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Hide
|
|
|
|
Clear the display of the line currently edited
|
|
bring cursor back to beginning of line
|
|
==================
|
|
*/
|
|
static void CON_Hide( void )
|
|
{
|
|
if( ttycon_on )
|
|
{
|
|
int i;
|
|
if (ttycon_hide)
|
|
{
|
|
ttycon_hide++;
|
|
return;
|
|
}
|
|
if (TTY_con.cursor>0)
|
|
{
|
|
for (i=0; i<TTY_con.cursor; i++)
|
|
{
|
|
CON_Back();
|
|
}
|
|
}
|
|
// Delete prompt
|
|
for (i = strlen(TTY_CONSOLE_PROMPT); i > 0; i--) {
|
|
CON_Back();
|
|
}
|
|
ttycon_hide++;
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Show
|
|
|
|
Show the current line
|
|
FIXME need to position the cursor if needed?
|
|
==================
|
|
*/
|
|
static void CON_Show( void )
|
|
{
|
|
if( ttycon_on )
|
|
{
|
|
int i;
|
|
|
|
assert(ttycon_hide>0);
|
|
ttycon_hide--;
|
|
if (ttycon_hide == 0)
|
|
{
|
|
size_t UNUSED_VAR size;
|
|
size = write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT));
|
|
if (TTY_con.cursor)
|
|
{
|
|
for (i=0; i<TTY_con.cursor; i++)
|
|
{
|
|
size = write(STDOUT_FILENO, TTY_con.buffer+i, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Shutdown
|
|
|
|
Never exit without calling this, or your terminal will be left in a pretty bad state
|
|
==================
|
|
*/
|
|
void CON_Shutdown( void )
|
|
{
|
|
if (ttycon_on)
|
|
{
|
|
CON_Hide();
|
|
tcsetattr (STDIN_FILENO, TCSADRAIN, &TTY_tc);
|
|
}
|
|
|
|
// Restore blocking to stdin reads
|
|
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Hist_Add
|
|
==================
|
|
*/
|
|
void Hist_Add(field_t *field)
|
|
{
|
|
int i;
|
|
|
|
// Don't save blank lines in history.
|
|
if (!field->cursor)
|
|
return;
|
|
|
|
assert(hist_count <= CON_HISTORY);
|
|
assert(hist_count >= 0);
|
|
assert(hist_current >= -1);
|
|
assert(hist_current <= hist_count);
|
|
// make some room
|
|
for (i=CON_HISTORY-1; i>0; i--)
|
|
{
|
|
ttyEditLines[i] = ttyEditLines[i-1];
|
|
}
|
|
ttyEditLines[0] = *field;
|
|
if (hist_count<CON_HISTORY)
|
|
{
|
|
hist_count++;
|
|
}
|
|
hist_current = -1; // re-init
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Hist_Prev
|
|
==================
|
|
*/
|
|
field_t *Hist_Prev( void )
|
|
{
|
|
int hist_prev;
|
|
assert(hist_count <= CON_HISTORY);
|
|
assert(hist_count >= 0);
|
|
assert(hist_current >= -1);
|
|
assert(hist_current <= hist_count);
|
|
hist_prev = hist_current + 1;
|
|
if (hist_prev >= hist_count)
|
|
{
|
|
return NULL;
|
|
}
|
|
hist_current++;
|
|
return &(ttyEditLines[hist_current]);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
Hist_Next
|
|
==================
|
|
*/
|
|
field_t *Hist_Next( void )
|
|
{
|
|
assert(hist_count <= CON_HISTORY);
|
|
assert(hist_count >= 0);
|
|
assert(hist_current >= -1);
|
|
assert(hist_current <= hist_count);
|
|
if (hist_current >= 0)
|
|
{
|
|
hist_current--;
|
|
}
|
|
if (hist_current == -1)
|
|
{
|
|
return NULL;
|
|
}
|
|
return &(ttyEditLines[hist_current]);
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_SigCont
|
|
Reinitialize console input after receiving SIGCONT, as on Linux the terminal seems to lose all
|
|
set attributes if user did CTRL+Z and then does fg again.
|
|
==================
|
|
*/
|
|
|
|
void CON_SigCont(int signum)
|
|
{
|
|
CON_Init();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Init
|
|
|
|
Initialize the console input (tty mode if possible)
|
|
==================
|
|
*/
|
|
void CON_Init( void )
|
|
{
|
|
struct termios tc;
|
|
|
|
// If the process is backgrounded (running non interactively)
|
|
// then SIGTTIN or SIGTOU is emitted, if not caught, turns into a SIGSTP
|
|
signal(SIGTTIN, SIG_IGN);
|
|
signal(SIGTTOU, SIG_IGN);
|
|
|
|
// If SIGCONT is received, reinitialize console
|
|
signal(SIGCONT, CON_SigCont);
|
|
|
|
// Make stdin reads non-blocking
|
|
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK );
|
|
|
|
if (!stdinIsATTY)
|
|
{
|
|
Com_Printf("tty console mode disabled\n");
|
|
ttycon_on = qfalse;
|
|
stdin_active = qtrue;
|
|
return;
|
|
}
|
|
|
|
Field_Clear(&TTY_con);
|
|
tcgetattr (STDIN_FILENO, &TTY_tc);
|
|
TTY_erase = TTY_tc.c_cc[VERASE];
|
|
TTY_eof = TTY_tc.c_cc[VEOF];
|
|
tc = TTY_tc;
|
|
|
|
/*
|
|
ECHO: don't echo input characters
|
|
ICANON: enable canonical mode. This enables the special
|
|
characters EOF, EOL, EOL2, ERASE, KILL, REPRINT,
|
|
STATUS, and WERASE, and buffers by lines.
|
|
ISIG: when any of the characters INTR, QUIT, SUSP, or
|
|
DSUSP are received, generate the corresponding signal
|
|
*/
|
|
tc.c_lflag &= ~(ECHO | ICANON);
|
|
|
|
/*
|
|
ISTRIP strip off bit 8
|
|
INPCK enable input parity checking
|
|
*/
|
|
tc.c_iflag &= ~(ISTRIP | INPCK);
|
|
tc.c_cc[VMIN] = 1;
|
|
tc.c_cc[VTIME] = 0;
|
|
tcsetattr (STDIN_FILENO, TCSADRAIN, &tc);
|
|
ttycon_on = qtrue;
|
|
ttycon_hide = 1; // Mark as hidden, so prompt is shown in CON_Show
|
|
CON_Show();
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Input
|
|
==================
|
|
*/
|
|
char *CON_Input( void )
|
|
{
|
|
// we use this when sending back commands
|
|
static char text[MAX_EDIT_LINE];
|
|
int avail;
|
|
char key;
|
|
field_t *history;
|
|
size_t UNUSED_VAR size;
|
|
|
|
if(ttycon_on)
|
|
{
|
|
avail = read(STDIN_FILENO, &key, 1);
|
|
if (avail != -1)
|
|
{
|
|
// we have something
|
|
// backspace?
|
|
// NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
|
|
if ((key == TTY_erase) || (key == 127) || (key == 8))
|
|
{
|
|
if (TTY_con.cursor > 0)
|
|
{
|
|
TTY_con.cursor--;
|
|
TTY_con.buffer[TTY_con.cursor] = '\0';
|
|
CON_Back();
|
|
}
|
|
return NULL;
|
|
}
|
|
// check if this is a control char
|
|
if ((key) && (key) < ' ')
|
|
{
|
|
if (key == '\n')
|
|
{
|
|
#ifndef DEDICATED
|
|
// if not in the game explicitly prepend a slash if needed
|
|
if (clc.state != CA_ACTIVE && con_autochat->integer && TTY_con.cursor &&
|
|
TTY_con.buffer[0] != '/' && TTY_con.buffer[0] != '\\')
|
|
{
|
|
memmove(TTY_con.buffer + 1, TTY_con.buffer, sizeof(TTY_con.buffer) - 1);
|
|
TTY_con.buffer[0] = '\\';
|
|
TTY_con.cursor++;
|
|
}
|
|
|
|
if (TTY_con.buffer[0] == '/' || TTY_con.buffer[0] == '\\') {
|
|
Q_strncpyz(text, TTY_con.buffer + 1, sizeof(text));
|
|
} else if (TTY_con.cursor) {
|
|
if (con_autochat->integer) {
|
|
Com_sprintf(text, sizeof(text), "cmd say %s", TTY_con.buffer);
|
|
} else {
|
|
Q_strncpyz(text, TTY_con.buffer, sizeof(text));
|
|
}
|
|
} else {
|
|
text[0] = '\0';
|
|
}
|
|
|
|
// push it in history
|
|
Hist_Add(&TTY_con);
|
|
CON_Hide();
|
|
Com_Printf("%s%s\n", TTY_CONSOLE_PROMPT, TTY_con.buffer);
|
|
Field_Clear(&TTY_con);
|
|
CON_Show();
|
|
#else
|
|
// push it in history
|
|
Hist_Add(&TTY_con);
|
|
Q_strncpyz(text, TTY_con.buffer, sizeof(text));
|
|
Field_Clear(&TTY_con);
|
|
key = '\n';
|
|
size = write(STDOUT_FILENO, &key, 1);
|
|
size = write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT));
|
|
#endif
|
|
return text;
|
|
}
|
|
if (key == '\t')
|
|
{
|
|
CON_Hide();
|
|
Field_AutoComplete( &TTY_con );
|
|
CON_Show();
|
|
return NULL;
|
|
}
|
|
avail = read(STDIN_FILENO, &key, 1);
|
|
if (avail != -1)
|
|
{
|
|
// VT 100 keys
|
|
if (key == '[' || key == 'O')
|
|
{
|
|
avail = read(STDIN_FILENO, &key, 1);
|
|
if (avail != -1)
|
|
{
|
|
switch (key)
|
|
{
|
|
case 'A':
|
|
history = Hist_Prev();
|
|
if (history)
|
|
{
|
|
CON_Hide();
|
|
TTY_con = *history;
|
|
CON_Show();
|
|
}
|
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
|
return NULL;
|
|
break;
|
|
case 'B':
|
|
history = Hist_Next();
|
|
CON_Hide();
|
|
if (history)
|
|
{
|
|
TTY_con = *history;
|
|
} else
|
|
{
|
|
Field_Clear(&TTY_con);
|
|
}
|
|
CON_Show();
|
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
|
return NULL;
|
|
break;
|
|
case 'C':
|
|
return NULL;
|
|
case 'D':
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Com_DPrintf("droping ISCTL sequence: %d, TTY_erase: %d\n", key, TTY_erase);
|
|
tcflush(STDIN_FILENO, TCIFLUSH);
|
|
return NULL;
|
|
}
|
|
if (TTY_con.cursor >= sizeof(text) - 1)
|
|
return NULL;
|
|
// push regular character
|
|
TTY_con.buffer[TTY_con.cursor] = key;
|
|
TTY_con.cursor++; // next char will always be '\0'
|
|
// print the current line (this is differential)
|
|
size = write(STDOUT_FILENO, &key, 1);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
else if (stdin_active)
|
|
{
|
|
int len;
|
|
fd_set fdset;
|
|
struct timeval timeout;
|
|
|
|
FD_ZERO(&fdset);
|
|
FD_SET(STDIN_FILENO, &fdset); // stdin
|
|
timeout.tv_sec = 0;
|
|
timeout.tv_usec = 0;
|
|
if(select (STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(STDIN_FILENO, &fdset))
|
|
return NULL;
|
|
|
|
len = read(STDIN_FILENO, text, sizeof(text));
|
|
if (len == 0)
|
|
{ // eof!
|
|
stdin_active = qfalse;
|
|
return NULL;
|
|
}
|
|
|
|
if (len < 1)
|
|
return NULL;
|
|
text[len-1] = 0; // rip off the /n and terminate
|
|
|
|
return text;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
==================
|
|
CON_Print
|
|
==================
|
|
*/
|
|
void CON_Print( const char *msg )
|
|
{
|
|
if (!msg[0])
|
|
return;
|
|
|
|
CON_Hide( );
|
|
|
|
if( com_ansiColor && com_ansiColor->integer )
|
|
Sys_AnsiColorPrint( msg );
|
|
else
|
|
fputs( msg, stderr );
|
|
|
|
if (!ttycon_on) {
|
|
// CON_Hide didn't do anything.
|
|
return;
|
|
}
|
|
|
|
// Only print prompt when msg ends with a newline, otherwise the console
|
|
// might get garbled when output does not fit on one line.
|
|
if (msg[strlen(msg) - 1] == '\n') {
|
|
CON_Show();
|
|
|
|
// Run CON_Show the number of times it was deferred.
|
|
while (ttycon_show_overdue > 0) {
|
|
CON_Show();
|
|
ttycon_show_overdue--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Defer calling CON_Show
|
|
ttycon_show_overdue++;
|
|
}
|
|
}
|