Skip to content
This repository was archived by the owner on Apr 15, 2025. It is now read-only.

Commit 9dfbd48

Browse files
authored
Merge pull request #4 from milosz275/autocompletion
Autocompletion
2 parents 9ab891b + b825ae3 commit 9dfbd48

File tree

3 files changed

+91
-6
lines changed

3 files changed

+91
-6
lines changed

bitlab/include/cli.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#define CLI_DELIM " "
1010
#define CLI_COMMANDS_NUM (int) (sizeof(cli_commands) / sizeof(cli_command))
1111
#define CLI_HISTORY_FILE "cli_history.txt"
12+
#define CLI_PREFIX "BitLab> "
1213

1314
/**
1415
* CLI command structure.
@@ -48,6 +49,8 @@ int cli_history(char** args);
4849
*/
4950
int cli_clear(char** args);
5051

52+
void print_help();
53+
5154
/**
5255
* Prints CLI command help.
5356
*
@@ -83,6 +86,10 @@ char* cli_read_line(void);
8386
*/
8487
int cli_exec_line(char* line);
8588

89+
char** cli_completion(const char* text, int start, int end);
90+
91+
char* cli_command_generator(const char* text, int state);
92+
8693
/**
8794
* CLI handler thread.
8895
*

bitlab/include/utils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#define TIMESTAMP_LENGTH 20
88

99
void usleep(unsigned int usec);
10+
char* strdup(const char* str1);
11+
char* strndup(const char* src, size_t size);
1012

1113
/**
1214
* Get the timestamp. This function is used to get the timestamp in a YYYYMMDDHHMMSS format.

bitlab/src/cli.c

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ static cli_command cli_commands[] =
1818
{.cli_command = &cli_history, .cli_command_name = "history", .cli_command_description = "Prints command history." },
1919
{.cli_command = &cli_clear, .cli_command_name = "clear", .cli_command_description = "Clears CLI screen." },
2020
{.cli_command = &cli_help, .cli_command_name = "help", .cli_command_description = "Prints command descriptions." },
21-
};
21+
}; // do not add NULLs at the end
2222

2323
int cli_exit(char** args)
2424
{
@@ -42,11 +42,8 @@ int cli_history(char** args)
4242
return 0;
4343
}
4444

45-
int cli_help(char** args)
45+
void print_help()
4646
{
47-
if (args[0] != NULL)
48-
log_message(LOG_WARN, BITLAB_LOG, __FILE__, "Arguments provided for help command ignored"); // [ ] Add help for each command
49-
5047
int longest_length = 0;
5148
for (int i = 0; i < CLI_COMMANDS_NUM; ++i)
5249
{
@@ -56,6 +53,25 @@ int cli_help(char** args)
5653
}
5754
for (int i = 0; i < CLI_COMMANDS_NUM; ++i)
5855
printf("%-*s - %s\n", longest_length, cli_commands[i].cli_command_name, cli_commands[i].cli_command_description);
56+
}
57+
58+
int cli_help(char** args)
59+
{
60+
if (args[0] != NULL)
61+
{
62+
if (strcmp(args[0], "exit") == 0)
63+
printf(" * Detailed information about exit command:\n * exit - Stops the server.\n");
64+
else if (strcmp(args[0], "history") == 0)
65+
printf(" * Detailed information about history command:\n * history - Prints command history.\n");
66+
else if (strcmp(args[0], "clear") == 0)
67+
printf(" * Detailed information about clear command:\n * clear - Clears CLI screen.\n");
68+
else if (strcmp(args[0], "help") == 0)
69+
printf(" * Detailed information about help command:\n * help - Prints command descriptions.\n");
70+
else
71+
printf("Unknown command: %s\n", args[0]);
72+
}
73+
else
74+
print_help();
5975
return 0;
6076
}
6177

@@ -108,7 +124,7 @@ int cli_get_line(char** lineptr, size_t* n, FILE* stream)
108124

109125
char* cli_read_line(void)
110126
{
111-
char* line = readline("BitLab> ");
127+
char* line = readline(CLI_PREFIX);
112128
if (line && *line)
113129
add_history(line);
114130
return line;
@@ -175,6 +191,57 @@ int cli_exec_line(char* line)
175191
return 1;
176192
}
177193

194+
char** cli_completion(const char* text, int start, int end)
195+
{
196+
rl_attempted_completion_over = 1;
197+
start = start; // unused
198+
end = end; // unused
199+
char* line = rl_line_buffer;
200+
int spaces = 0;
201+
202+
// print help for empty input
203+
if (strlen(line) == 0)
204+
{
205+
putchar('\n');
206+
print_help();
207+
printf(CLI_PREFIX);
208+
return NULL;
209+
}
210+
211+
// count spaces
212+
for (long unsigned i = 0; i < strlen(line); ++i)
213+
if (line[i] == ' ')
214+
spaces++;
215+
216+
// check if the input starts with "help" and allow for one space
217+
if ((strncmp(line, "help", 4) == 0) && (spaces <= 1))
218+
return rl_completion_matches(text, cli_command_generator);
219+
else
220+
return NULL;
221+
222+
return rl_completion_matches(text, cli_command_generator);
223+
}
224+
225+
char* cli_command_generator(const char* text, int state)
226+
{
227+
static int list_index, len;
228+
char* name;
229+
if (!state)
230+
{
231+
list_index = 0;
232+
len = strlen(text);
233+
}
234+
while (list_index < CLI_COMMANDS_NUM)
235+
{
236+
name = cli_commands[list_index].cli_command_name;
237+
list_index++;
238+
239+
if (strncmp(name, text, len) == 0)
240+
return strdup(name);
241+
}
242+
return NULL;
243+
}
244+
178245
void* handle_cli(void* arg)
179246
{
180247
char* line = NULL;
@@ -188,6 +255,15 @@ void* handle_cli(void* arg)
188255
snprintf(full_path, sizeof(full_path), "%s/%s", cli_history_dir, CLI_HISTORY_FILE);
189256
read_history(full_path);
190257
usleep(50000); // 50 ms
258+
259+
rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
260+
rl_completer_word_break_characters = " \t\n\"\\'`@$><=;|&{(";
261+
rl_completer_quote_characters = "\"\\'`";
262+
rl_completion_append_character = '\0';
263+
rl_attempted_completion_over = 1;
264+
rl_attempted_completion_function = cli_completion;
265+
// [ ] Find setting to disable inputting space after tabbing
266+
191267
while (!get_exit_flag(&state))
192268
{
193269
line = cli_read_line();

0 commit comments

Comments
 (0)