diff options
Diffstat (limited to 'code')
| -rw-r--r-- | code/client/cl_keys.c | 4 | ||||
| -rw-r--r-- | code/qcommon/cmd.c | 23 | ||||
| -rw-r--r-- | code/qcommon/common.c | 263 | ||||
| -rw-r--r-- | code/qcommon/files.c | 23 | ||||
| -rw-r--r-- | code/qcommon/q_shared.c | 79 | ||||
| -rw-r--r-- | code/qcommon/q_shared.h | 2 | ||||
| -rw-r--r-- | code/qcommon/qcommon.h | 5 | ||||
| -rw-r--r-- | code/unix/unix_main.c | 18 | 
8 files changed, 327 insertions, 90 deletions
diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index a23b4ee..889ca82 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -483,7 +483,7 @@ void Console_Key (int key) {  	// enter finishes the line  	if ( key == K_ENTER || key == K_KP_ENTER ) { -		// if not in the game explicitly prepent a slash if needed +		// if not in the game explicitly prepend a slash if needed  		if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\'   			&& g_consoleField.buffer[0] != '/' ) {  			char	temp[MAX_STRING_CHARS]; @@ -528,7 +528,7 @@ void Console_Key (int key) {  	// command completion  	if (key == K_TAB) { -		Field_CompleteCommand(&g_consoleField); +		Field_AutoComplete(&g_consoleField);  		return;  	} diff --git a/code/qcommon/cmd.c b/code/qcommon/cmd.c index b1506f5..5af0f95 100644 --- a/code/qcommon/cmd.c +++ b/code/qcommon/cmd.c @@ -439,7 +439,7 @@ will point into this temporary buffer.  */  // NOTE TTimo define that to track tokenization issues  //#define TKN_DBG -void Cmd_TokenizeString( const char *text_in ) { +static void Cmd_TokenizeString2( const char *text_in, qboolean ignoreQuotes ) {  	const char	*text;  	char	*textOut; @@ -495,7 +495,7 @@ void Cmd_TokenizeString( const char *text_in ) {  		// handle quoted strings      // NOTE TTimo this doesn't handle \" escaping -		if ( *text == '"' ) { +		if ( !ignoreQuotes && *text == '"' ) {  			cmd_argv[cmd_argc] = textOut;  			cmd_argc++;  			text++; @@ -516,7 +516,7 @@ void Cmd_TokenizeString( const char *text_in ) {  		// skip until whitespace, quote, or command  		while ( *text > ' ' ) { -			if ( text[0] == '"' ) { +			if ( !ignoreQuotes && text[0] == '"' ) {  				break;  			} @@ -541,6 +541,23 @@ void Cmd_TokenizeString( const char *text_in ) {  } +/* +============ +Cmd_TokenizeString +============ +*/ +void Cmd_TokenizeString( const char *text_in ) { +	Cmd_TokenizeString2( text_in, qfalse ); +} + +/* +============ +Cmd_TokenizeStringIgnoreQuotes +============ +*/ +void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) { +	Cmd_TokenizeString2( text_in, qtrue ); +}  /*  ============ diff --git a/code/qcommon/common.c b/code/qcommon/common.c index ba95f09..eb46426 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -2912,7 +2912,7 @@ void Field_Clear( field_t *edit ) {  static const char *completionString;  static char shortestMatch[MAX_TOKEN_CHARS];  static int	matchCount; -// field we are working on, passed to Field_CompleteCommand (&g_consoleCommand for instance) +// field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)  static field_t *completionField;  /* @@ -2948,11 +2948,11 @@ static void FindMatches( const char *s ) {  /*  =============== -PrintCmdMatches +PrintMatches  ===============  */ -static void PrintCmdMatches( const char *s ) { +static void PrintMatches( const char *s ) {  	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {  		Com_Printf( "    %s\n", s );  	} @@ -2970,96 +2970,231 @@ static void PrintCvarMatches( const char *s ) {  	}  } -static void keyConcatArgs( void ) { -	int		i; -	char	*arg; +/* +=============== +Field_FindFirstSeparator +=============== +*/ +static char *Field_FindFirstSeparator( char *s ) +{ +	int i; -	for ( i = 1 ; i < Cmd_Argc() ; i++ ) { -		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); -		arg = Cmd_Argv( i ); -		while (*arg) { -			if (*arg == ' ') { -				Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  "\""); -				break; -			} -			arg++; -		} -		Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  Cmd_Argv( i ) ); -		if (*arg == ' ') { -			Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  "\""); -		} +	for( i = 0; i < strlen( s ); i++ ) +	{ +		if( s[ i ] == ';' ) +			return &s[ i ];  	} + +	return NULL;  } -static void ConcatRemaining( const char *src, const char *start ) { -	char *str; +/* +=============== +Field_CompleteFilename +=============== +*/ +static void Field_CompleteFilename( const char *dir, +		const char *ext, qboolean stripExt ) +{ +	matchCount = 0; +	shortestMatch[ 0 ] = 0; + +	FS_FilenameCompletion( dir, ext, stripExt, FindMatches ); + +	if( matchCount == 0 ) +		return; + +	Q_strcat( completionField->buffer, sizeof( completionField->buffer ), +			shortestMatch + strlen( completionString ) ); +	completionField->cursor = strlen( completionField->buffer ); -	str = strstr(src, start); -	if (!str) { -		keyConcatArgs(); +	if( matchCount == 1 ) +	{ +		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); +		completionField->cursor++;  		return;  	} -	str += strlen(start); -	Q_strcat( completionField->buffer, sizeof( completionField->buffer ), str); +	Com_Printf( "]%s\n", completionField->buffer ); +	 +	FS_FilenameCompletion( dir, ext, stripExt, PrintMatches );  }  /*  ===============  Field_CompleteCommand - -perform Tab expansion -NOTE TTimo this was originally client code only -  moved to common code when writing tty console for *nix dedicated server  ===============  */ -void Field_CompleteCommand( field_t *field ) { -	field_t		temp; +static void Field_CompleteCommand( char *cmd, +		qboolean doCommands, qboolean doCvars ) +{ +	int		completionArgument = 0; +	char	*p; -	completionField = field; +	// Skip leading whitespace and quotes +	cmd = Com_SkipCharset( cmd, " \"" ); -	// only look at the first token for completion purposes -	Cmd_TokenizeString( completionField->buffer ); +	Cmd_TokenizeStringIgnoreQuotes( cmd ); +	completionArgument = Cmd_Argc( ); -	completionString = Cmd_Argv(0); -	if ( completionString[0] == '\\' || completionString[0] == '/' ) { -		completionString++; +	// If there is trailing whitespace on the cmd +	if( *( cmd + strlen( cmd ) - 1 ) == ' ' ) +	{ +		completionString = ""; +		completionArgument++;  	} -	matchCount = 0; -	shortestMatch[0] = 0; +	else +		completionString = Cmd_Argv( completionArgument - 1 ); -	if ( strlen( completionString ) == 0 ) { -		return; -	} +	if( completionArgument > 1 ) +	{ +		const char *baseCmd = Cmd_Argv( 0 ); + +#ifndef DEDICATED +		// If the very first token does not have a leading \ or /, +		// refuse to autocomplete +		if( cmd == completionField->buffer ) +		{ +			if( baseCmd[ 0 ] != '\\' && baseCmd[ 0 ] != '/' ) +				return; + +			baseCmd++; +		} +#endif + +		if( ( p = Field_FindFirstSeparator( cmd ) ) ) +		{ +			// Compound command +			Field_CompleteCommand( p + 1, qtrue, qtrue ); +		} +		else +		{ +			// FIXME: all this junk should really be associated with the respective +			// commands, instead of being hard coded here +			if( ( !Q_stricmp( baseCmd, "map" ) || +						!Q_stricmp( baseCmd, "devmap" ) || +						!Q_stricmp( baseCmd, "spmap" ) || +						!Q_stricmp( baseCmd, "spdevmap" ) ) && +					completionArgument == 2 ) +			{ +				Field_CompleteFilename( "maps", "bsp", qtrue ); +			} +			else if( ( !Q_stricmp( baseCmd, "exec" ) || +						!Q_stricmp( baseCmd, "writeconfig" ) ) && +					completionArgument == 2 ) +			{ +				Field_CompleteFilename( "", "cfg", qfalse ); +			} +			else if( !Q_stricmp( baseCmd, "condump" ) && +					completionArgument == 2 ) +			{ +				Field_CompleteFilename( "", "txt", qfalse ); +			} +			else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 ) +			{ +				char demoExt[ 16 ]; + +				Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION ); +				Field_CompleteFilename( "demos", demoExt, qtrue ); +			} +			else if( ( !Q_stricmp( baseCmd, "toggle" ) || +						!Q_stricmp( baseCmd, "vstr" ) || +						!Q_stricmp( baseCmd, "set" ) || +						!Q_stricmp( baseCmd, "seta" ) || +						!Q_stricmp( baseCmd, "setu" ) || +						!Q_stricmp( baseCmd, "sets" ) ) && +					completionArgument == 2 ) +			{ +				// Skip "<cmd> " +				p = Com_SkipTokens( cmd, 1, " " ); + +				if( p > cmd ) +					Field_CompleteCommand( p, qfalse, qtrue ); +			} +			else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 ) +			{ +				// Skip "rcon " +				p = Com_SkipTokens( cmd, 1, " " ); -	Cmd_CommandCompletion( FindMatches ); -	Cvar_CommandCompletion( FindMatches ); +				if( p > cmd ) +					Field_CompleteCommand( p, qtrue, qtrue ); +			} +			else if( !Q_stricmp( baseCmd, "bind" ) && completionArgument >= 3 ) +			{ +				// Skip "bind <key> " +				p = Com_SkipTokens( cmd, 2, " " ); -	if ( matchCount == 0 ) { -		return;	// no matches +				if( p > cmd ) +					Field_CompleteCommand( p, qtrue, qtrue ); +			} +		}  	} +	else +	{ +		if( completionString[0] == '\\' || completionString[0] == '/' ) +			completionString++; -	Com_Memcpy(&temp, completionField, sizeof(field_t)); +		matchCount = 0; +		shortestMatch[ 0 ] = 0; -	if ( matchCount == 1 ) { -		Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); -		if ( Cmd_Argc() == 1 ) { -			Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); -		} else { -			ConcatRemaining( temp.buffer, completionString ); +		if( strlen( completionString ) == 0 ) +			return; + +		if( doCommands ) +			Cmd_CommandCompletion( FindMatches ); + +		if( doCvars ) +			Cvar_CommandCompletion( FindMatches ); + +		if( matchCount == 0 ) +			return;	// no matches + +		if( cmd == completionField->buffer ) +		{ +#ifndef DEDICATED +			Com_sprintf( completionField->buffer, +					sizeof( completionField->buffer ), "\\%s", shortestMatch ); +#else +			Com_sprintf( completionField->buffer, +					sizeof( completionField->buffer ), "%s", shortestMatch ); +#endif +		} +		else +		{ +			Q_strcat( completionField->buffer, sizeof( completionField->buffer ), +					shortestMatch + strlen( completionString ) );  		} +  		completionField->cursor = strlen( completionField->buffer ); -		return; + +		if( matchCount == 1 ) +		{ +			Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " ); +			completionField->cursor++; +			return; +		} + +		Com_Printf( "]%s\n", completionField->buffer ); + +		// run through again, printing matches +		if( doCommands ) +			Cmd_CommandCompletion( PrintMatches ); + +		if( doCvars ) +			Cvar_CommandCompletion( PrintCvarMatches );  	} +} -	// multiple matches, complete to shortest -	Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch ); -	completionField->cursor = strlen( completionField->buffer ); -	ConcatRemaining( temp.buffer, completionString ); +/* +=============== +Field_AutoComplete -	Com_Printf( "]%s\n", completionField->buffer ); +Perform Tab expansion +=============== +*/ +void Field_AutoComplete( field_t *field ) +{ +	completionField = field; -	// run through again, printing matches -	Cmd_CommandCompletion( PrintCmdMatches ); -	Cvar_CommandCompletion( PrintCvarMatches ); +	Field_CompleteCommand( completionField->buffer, qtrue, qtrue );  } diff --git a/code/qcommon/files.c b/code/qcommon/files.c index a4fa79d..3d85c7c 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -3426,3 +3426,26 @@ void	FS_Flush( fileHandle_t f ) {  	fflush(fsh[f].handleFiles.file.o);  } +void	FS_FilenameCompletion( const char *dir, const char *ext, +		qboolean stripExt, void(*callback)(const char *s) ) { +	char	**filenames; +	int		nfiles; +	int		i; +	char	filename[ MAX_STRING_CHARS ]; + +	filenames = FS_ListFilteredFiles( dir, ext, NULL, &nfiles ); + +	FS_SortFileList( filenames, nfiles ); + +	for( i = 0; i < nfiles; i++ ) { +		FS_ConvertPath( filenames[ i ] ); +		Q_strncpyz( filename, filenames[ i ], MAX_STRING_CHARS ); + +		if( stripExt ) { +			COM_StripExtension( filename, filename ); +		} + +		callback( filename ); +	} +	FS_FreeFileList( filenames ); +} diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index af72ec2..2100390 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -59,10 +59,19 @@ COM_StripExtension  ============  */  void COM_StripExtension( const char *in, char *out ) { -	while ( *in && *in != '.' ) { -		*out++ = *in++; +	int             length; + +	strcpy( out, in ); + +	length = strlen(out)-1; +	while (length > 0 && out[length] != '.') +	{ +		length--; +		if (out[length] == '/') +			return;		// no extension  	} -	*out = 0; +	if (length) +		out[length] = 0;  } @@ -1249,4 +1258,68 @@ void Info_SetValueForKey_Big( char *s, const char *key, const char *value ) {  //==================================================================== +/* +================== +Com_CharIsOneOfCharset +================== +*/ +static qboolean Com_CharIsOneOfCharset( char c, char *set ) +{ +	int i; +	for( i = 0; i < strlen( set ); i++ ) +	{ +		if( set[ i ] == c ) +			return qtrue; +	} + +	return qfalse; +} + +/* +================== +Com_SkipCharset +================== +*/ +char *Com_SkipCharset( char *s, char *sep ) +{ +	char	*p = s; + +	while( p ) +	{ +		if( Com_CharIsOneOfCharset( *p, sep ) ) +			p++; +		else +			break; +	} + +	return p; +} + +/* +================== +Com_SkipTokens +================== +*/ +char *Com_SkipTokens( char *s, int numTokens, char *sep ) +{ +	int		sepCount = 0; +	char	*p = s; + +	while( sepCount < numTokens ) +	{ +		if( Com_CharIsOneOfCharset( *p++, sep ) ) +		{ +			sepCount++; +			while( Com_CharIsOneOfCharset( *p, sep ) ) +				p++; +		} +		else if( *p == '\0' ) +			break; +	} + +	if( sepCount == numTokens ) +		return p; +	else +		return s; +} diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index e80074d..2dc5500 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -610,6 +610,8 @@ void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m);  void	QDECL Com_sprintf (char *dest, int size, const char *fmt, ...); +char *Com_SkipTokens( char *s, int numTokens, char *sep ); +char *Com_SkipCharset( char *s, char *sep );  // mode parm for FS_FOpenFile  typedef enum { diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 1b2fc0b..21c74e9 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -415,6 +415,7 @@ char	*Cmd_Cmd (void);  // if arg > argc, so string operations are allways safe.  void	Cmd_TokenizeString( const char *text ); +void	Cmd_TokenizeStringIgnoreQuotes( const char *text_in );  // Takes a null terminated string.  Does not need to be /n terminated.  // breaks the string up into arg tokens. @@ -657,6 +658,8 @@ void FS_Rename( const char *from, const char *to );  void FS_Remove( const char *osPath );  void FS_HomeRemove( const char *homePath ); +void	FS_FilenameCompletion( const char *dir, const char *ext, +		qboolean stripExt, void(*callback)(const char *s) );  /*  ============================================================== @@ -674,7 +677,7 @@ typedef struct {  } field_t;  void Field_Clear( field_t *edit ); -void Field_CompleteCommand( field_t *edit ); +void Field_AutoComplete( field_t *edit );  /*  ============================================================== diff --git a/code/unix/unix_main.c b/code/unix/unix_main.c index 4216b1c..8cd0886 100644 --- a/code/unix/unix_main.c +++ b/code/unix/unix_main.c @@ -549,7 +549,6 @@ char *Sys_ConsoleInput(void)  {    // we use this when sending back commands    static char text[256]; -  int i;    int avail;    char key;    field_t *history; @@ -588,22 +587,7 @@ char *Sys_ConsoleInput(void)          if (key == '\t')          {            tty_Hide(); -          Field_CompleteCommand( &tty_con ); -          // Field_CompleteCommand does weird things to the string, do a cleanup -          //   it adds a '\' at the beginning of the string -          //   cursor doesn't reflect actual length of the string that's sent back -          tty_con.cursor = strlen(tty_con.buffer); -          if (tty_con.cursor>0) -          { -            if (tty_con.buffer[0] == '\\') -            { -              for (i=0; i<=tty_con.cursor; i++) -              { -                tty_con.buffer[i] = tty_con.buffer[i+1]; -              } -              tty_con.cursor--; -            } -          } +          Field_AutoComplete( &tty_con );            tty_Show();            return NULL;          }  | 
