diff -Nur -X/home/thue/freeciv-dev/freeciv/diff_ignore freeciv/server/sernet.c readline/server/sernet.c --- freeciv/server/sernet.c Mon Jul 17 16:45:44 2000 +++ readline/server/sernet.c Wed Jul 19 14:39:50 2000 @@ -212,6 +212,7 @@ rl_initialize(); rl_callback_handler_install("> ", handle_readline_input_callback); + rl_attempted_completion_function = freeciv_completion; readline_initialized = 1; } diff -Nur -X/home/thue/freeciv-dev/freeciv/diff_ignore freeciv/server/stdinhand.c readline/server/stdinhand.c --- freeciv/server/stdinhand.c Sat Jul 15 17:34:26 2000 +++ readline/server/stdinhand.c Wed Jul 19 22:10:14 2000 @@ -21,6 +21,10 @@ #include #include +#ifdef HAVE_LIBREADLINE +#include +#endif + #include "astring.h" #include "attribute.h" #include "events.h" @@ -2900,3 +2904,271 @@ } cmd_reply(CMD_LIST, caller, C_COMMENT, horiz_line); } + +#ifdef HAVE_LIBREADLINE +/********************* RL completion functions ***************************/ +/* To properly complete both commands, player names, options and filenames + I have created one array per type of completion with the commands that + the type is relevant for. + Prehaps these arrays should also be used for auto-creating the command + synopsis. + If there were a command that could use completions from two of these + types (say, you could issue the command followed by either a filenames + or a player names) it would not currently be possible. But currently no + commands need this */ + +/************************************************************************** +Returns a malloced copy of the string str. +**************************************************************************/ +static char *str_dub(char *str) +{ + char *new_str; + int len = strlen(str); + + new_str = fc_malloc((len+1) * sizeof(char)); + strcpy(new_str, str); + return (new_str); +} + +/************************************************************************** +The valid commands at the root of the prompt. +**************************************************************************/ +char *command_generator (char *text, int state) +{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the command list. */ + while (list_index < CMD_NUM) { + name = commands[list_index].name; + list_index++; + + if (mystrncasecmp (name, text, len) == 0) + return str_dub(name); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/************************************************************************** +The valid arguments to "set" and "explain" +**************************************************************************/ +char *option_generator (char *text, int state) +{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the settings list. */ + while ((name = settings[list_index].name)) { + list_index++; + + if (mystrncasecmp (name, text, len) == 0) + return str_dub(name); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/************************************************************************** +The player names. +**************************************************************************/ +char *player_generator (char *text, int state) +{ + static int list_index, len; + char *name; + + /* If this is a new word to complete, initialize now. This includes + saving the length of TEXT for efficiency, and initializing the index + variable to 0. */ + if (!state) { + list_index = 0; + len = strlen (text); + } + + /* Return the next name which partially matches from the command list. */ + while (list_index < game.nplayers) { + name = get_player(list_index)->name; + list_index++; + + if (mystrncasecmp(name, text, len) == 0) + return str_dub(name); + } + + /* If no names matched, then return NULL. */ + return ((char *)NULL); +} + +/************************************************************************** +returns whether the characters before the start position in rl_line_buffer +is of the form [non-alpha]*cmd[non-alpha]* +**************************************************************************/ +static int contains_str_before_start(int start, char *cmd) +{ + char *str_itr = rl_line_buffer; + int cmd_len = strlen(cmd); + + while (str_itr < rl_line_buffer + start && !isalnum(*str_itr)) + str_itr++; + + if (mystrncasecmp(str_itr, cmd, cmd_len) != 0) + return 0; + str_itr += cmd_len; + + for (; str_itr < rl_line_buffer + start; str_itr++) + if (isalnum(*str_itr)) + return 0; + + return 1; +} + +/************************************************************************** +... +**************************************************************************/ +static int is_command(int start) +{ + char *str_itr; + + if (contains_str_before_start(start, commands[CMD_HELP].name)) + return 1; + + /* if there is only it is also OK */ + str_itr = rl_line_buffer; + while (str_itr - rl_line_buffer < start) { + if (isalnum(*str_itr)) + return 0; + str_itr++; + } + return 1; +} + +/************************************************************************** +Commands that may be followed by a player name +**************************************************************************/ +int player_cmd[] = { + CMD_CUT, + CMD_RENAME, + CMD_AITOGGLE, + CMD_EASY, + CMD_NORMAL, + CMD_HARD, + CMD_CMDLEVEL, + CMD_REMOVE, + -1 +}; + +/************************************************************************** +... +**************************************************************************/ +static int is_player(int start) +{ + int i = 0; + + while (player_cmd[i] != -1) { + if (contains_str_before_start(start, commands[player_cmd[i]].name)) + return 1; + i++; + } + + return 0; +} + +/************************************************************************** +Commands that may be followed by a server option name +**************************************************************************/ +int server_option_cmd[] = { + CMD_EXPLAIN, + CMD_SET, + -1 +}; + +/************************************************************************** +... +**************************************************************************/ +static int is_server_option(int start) +{ + int i = 0; + + while (server_option_cmd[i] != -1) { + if (contains_str_before_start(start, commands[server_option_cmd[i]].name)) + return 1; + i++; + } + + return 0; +} + +/************************************************************************** +Commands that may be followed by a filename +**************************************************************************/ +int filename_cmd[] = { + CMD_SAVE, + CMD_READ_SCRIPT, + CMD_WRITE_SCRIPT, + CMD_RULESOUT, + -1 +}; + +/************************************************************************** +... +**************************************************************************/ +static int is_filename(int start) +{ + int i = 0; + + while (filename_cmd[i] != -1) { + if (contains_str_before_start(start, commands[filename_cmd[i]].name)) + return 1; + i++; + } + + return 0; +} + +/************************************************************************** +Attempt to complete on the contents of TEXT. START and END bound the +region of rl_line_buffer that contains the word to complete. TEXT is +the word to complete. We can use the entire contents of rl_line_buffer +in case we want to do some simple parsing. Return the array of matches, +or NULL if there aren't any. +**************************************************************************/ +char **freeciv_completion(char *text, int start, int end) +{ + char **matches = (char **)NULL; + + if (is_command(start)) { + matches = completion_matches(text, command_generator); + } else if (is_player(start)) { + matches = completion_matches(text, player_generator); + } else if (is_server_option(start)) { + matches = completion_matches(text, option_generator); + } else if (is_filename(start)) { + /* This function we get from readline */ + matches = completion_matches(text, filename_completion_function); + } else /* We have no idea what to do */ + matches = NULL; + + /* Don't automatically try to complete with filenames */ + rl_attempted_completion_over = 1; + + return (matches); +} + +#endif /* HAVE_LIBREADLINE */ diff -Nur -X/home/thue/freeciv-dev/freeciv/diff_ignore freeciv/server/stdinhand.h readline/server/stdinhand.h --- freeciv/server/stdinhand.h Sat Jul 15 15:25:09 2000 +++ readline/server/stdinhand.h Wed Jul 19 14:38:39 2000 @@ -26,6 +26,10 @@ void toggle_ai_player_direct(struct player *caller, struct player *subject); +#ifdef HAVE_LIBREADLINE +char **freeciv_completion(char *text, int start, int end); +#endif + extern enum cmdlevel_id default_access_level; /* for sernet.c in */ extern enum cmdlevel_id first_access_level; /* initing a new connection */