Skip to content

Commit

Permalink
modularize string arrays for line completion
Browse files Browse the repository at this point in the history
Instead of using various different forms of string arrays and having to handle them
differently for string completion, we now always use char pointer arrays. This allows
us to remove some large stack allocations, remove a bunch of confusing defines that
keep track of global array sizes, and generally unclutters the code so it's easier
to read.
  • Loading branch information
JFreegman committed Oct 28, 2020
1 parent 9dafa15 commit bd4fc6a
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 212 deletions.
65 changes: 27 additions & 38 deletions src/autocomplete.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,21 +51,6 @@ static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matc
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}

static void print_dir_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size)
{
if (m) {
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
}

const char *L = (char *)list;

for (size_t i = 0; i < n_items; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
}

line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}

/* puts match in match buffer. if more than one match, add first n chars that are identical.
* e.g. if matches contains: [foo, foobar, foe] we put fo in match.
*
Expand Down Expand Up @@ -102,8 +87,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
* `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE.
*
* dir_search should be true if the line being completed is a file path.
*
Expand All @@ -112,19 +96,18 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
*
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
*/
static int complete_line_helper(ToxWindow *self, const void *list, const size_t n_items, size_t size, bool dir_search)
static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search)
{
ChatContext *ctx = self->chatwin;

if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
return -1;
}

if (ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) {
if (ctx->len >= MAX_STR_SIZE) {
return -1;
}

const char *L = (const char *) list;
const char *endchrs = " ";
char ubuf[MAX_STR_SIZE] = {0};

Expand Down Expand Up @@ -172,22 +155,17 @@ static int complete_line_helper(ToxWindow *self, const void *list, const size_t
int s_len = strlen(sub);
size_t n_matches = 0;

char **matches = (char **)malloc_ptr_array(n_items, MAX_STR_SIZE, sizeof(char *));
char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE, sizeof(char *));

if (matches == NULL) {
free(sub);
return -1;
}

int i = 0;

/* put all list matches in matches array */
for (i = 0; i < n_items; ++i) {
char str[MAX_CMDNAME_SIZE + 1];
snprintf(str, sizeof(str), "%s", &L[i * size]);

if (strncasecmp(str, sub, s_len) == 0) {
strcpy(matches[n_matches++], str);
for (size_t i = 0; i < n_items; ++i) {
if (strncasecmp(list[i], sub, s_len) == 0) {
snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]);
}
}

Expand Down Expand Up @@ -281,14 +259,14 @@ static int complete_line_helper(ToxWindow *self, const void *list, const size_t
return diff;
}

int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
int complete_line(ToxWindow *self, const char **list, size_t n_items)
{
return complete_line_helper(self, list, n_items, size, false);
return complete_line_helper(self, list, n_items, false);
}

static int complete_path(ToxWindow *self, const void *list, size_t n_items, size_t size)
static int complete_path(ToxWindow *self, const char **list, const size_t n_items)
{
return complete_line_helper(self, list, n_items, size, true);
return complete_line_helper(self, list, n_items, true);
}

/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
Expand Down Expand Up @@ -365,28 +343,39 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
return -1;
}

char dirnames[MAX_DIRS][NAME_MAX + 1];
char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1, sizeof(char *));

if (dirnames == NULL) {
return -1;
}

struct dirent *entry;

int dircount = 0;

while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name);
++dircount;
}
}

closedir(dp);

if (dircount == 0) {
free_ptr_array((void **) dirnames, MAX_DIRS);
return -1;
}

if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
print_dir_matches(self, m, dirnames, dircount, NAME_MAX + 1);
qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper);
print_ac_matches(self, m, dirnames, dircount);
}

return complete_path(self, dirnames, dircount, NAME_MAX + 1);
int ret = complete_path(self, (const char **) dirnames, dircount);

free_ptr_array((void **) dirnames, MAX_DIRS);

return ret;
}
9 changes: 6 additions & 3 deletions src/autocomplete.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
* `list` is a pointer to `n_items` strings.
*
* dir_search should be true if the line being completed is a file path.
*
* Returns the difference between the old len and new len of line on success.
* Returns -1 on error.
*
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
*/
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
int complete_line(ToxWindow *self, const char **list, size_t n_items);

/* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
Expand Down
95 changes: 39 additions & 56 deletions src/chat.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,67 +65,50 @@ static void init_infobox(ToxWindow *self);
static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */

#ifdef AUDIO
#define AC_NUM_CHAT_COMMANDS_AUDIO 9
#else
#define AC_NUM_CHAT_COMMANDS_AUDIO 0
#endif /* AUDIO */
#ifdef PYTHON
#define AC_NUM_CHAT_COMMANDS_PYTHON 1
#else
#define AC_NUM_CHAT_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_CHAT_COMMANDS_QRCODE 1
#else
#define AC_NUM_CHAT_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_CHAT_COMMANDS (21 + AC_NUM_CHAT_COMMANDS_AUDIO + AC_NUM_CHAT_COMMANDS_PYTHON + AC_NUM_CHAT_COMMANDS_QRCODE)

/* Array of chat command names used for tab completion. */
static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/accept" },
{ "/add" },
{ "/avatar" },
{ "/cancel" },
{ "/clear" },
{ "/close" },
{ "/connect" },
{ "/exit" },
{ "/group" },
{ "/help" },
{ "/invite" },
{ "/join" },
{ "/log" },
{ "/myid" },
static const char *chat_cmd_list[] = {
"/accept",
"/add",
"/avatar",
"/cancel",
"/clear",
"/close",
"/connect",
"/exit",
"/group",
"/help",
"/invite",
"/join",
"/log",
"/myid",
#ifdef QRCODE
{ "/myqr" },
"/myqr",
#endif /* QRCODE */
{ "/nick" },
{ "/note" },
{ "/nospam" },
{ "/quit" },
{ "/savefile" },
{ "/sendfile" },
{ "/status" },
"/nick",
"/note",
"/nospam",
"/quit",
"/savefile",
"/sendfile",
"/status",

#ifdef AUDIO

{ "/call" },
{ "/answer" },
{ "/reject" },
{ "/hangup" },
{ "/sdev" },
{ "/mute" },
{ "/sense" },
{ "/video" },
{ "/bitrate" },
"/call",
"/answer",
"/reject",
"/hangup",
"/sdev",
"/mute",
"/sense",
"/video",
"/bitrate",

#endif /* AUDIO */

#ifdef PYTHON

{ "/run" },
"/run",

#endif /* PYTHON */
};
Expand Down Expand Up @@ -1090,14 +1073,14 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
#endif

else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = {
{"online"},
{"away"},
{"busy"},
const char *status_cmd_list[] = {
"online",
"away",
"busy",
};
diff = complete_line(self, status_cmd_list, 3, 8);
diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *));
} else {
diff = complete_line(self, chat_cmd_list, AC_NUM_CHAT_COMMANDS, MAX_CMDNAME_SIZE);
diff = complete_line(self, chat_cmd_list, sizeof(chat_cmd_list) / sizeof(char *));
}

if (diff != -1) {
Expand Down
Loading

0 comments on commit bd4fc6a

Please sign in to comment.