1 /* Copyright (c) 2018-2022 Adrian Lopez
3 This software is provided 'as-is', without any express or implied
4 warranty. In no event will the authors be held liable for any damages
5 arising from the use of this software.
7 Permission is granted to anyone to use this software for any purpose,
8 including commercial applications, and to alter it and redistribute it
9 freely, subject to the following restrictions:
11 1. The origin of this software must not be misrepresented; you must not
12 claim that you wrote the original software. If you use this software
13 in a product, an acknowledgment in the product documentation would be
14 appreciated but is not required.
15 2. Altered source versions must be plainly marked as such, and must not be
16 misrepresented as being the original software.
17 3. This notice may not be removed or altered from any source distribution. */
20 #include "commandidentifier.h"
22 /* insert new node into command identifier tree */
23 int insert_command_identifier_command(struct command_identifier_node *tree, struct command_map *command, size_t ch)
25 struct command_identifier_node *child;
26 struct command_identifier_node **alloc_children;
30 /* find node for current character in command name */
32 for (c = 0; c < tree->num_children; ++c)
34 if (tree->children[c]->character == command->command_name[ch])
36 child = tree->children[c];
41 /* if sought node does not exist, create it */
44 child = (struct command_identifier_node*) malloc(sizeof(struct command_identifier_node));
46 return COMMAND_RECOGNIZER_OUT_OF_MEMORY;
48 child->command = COMMAND_UNDEFINED;
49 child->character = command->command_name[ch];
51 child->num_children = 0;
53 alloc_children = realloc(tree->children, sizeof(struct command_identifier_node*) * (tree->num_children + 1));
54 if (alloc_children == 0)
55 return COMMAND_RECOGNIZER_OUT_OF_MEMORY;
57 tree->children = alloc_children;
59 tree->children[tree->num_children++] = child;
62 /* if last character in command name, make child a leaf node */
63 if (command->command_name[ch] == L'\0')
65 child->command = command->command;
66 return child->command;
68 else /* grow the tree */
70 returned_command = insert_command_identifier_command(child, command, ch + 1);
72 /* record whether the tree at this point leads to a single command (abbreviation) or many (ambiguous) */
73 if (tree->command == COMMAND_UNDEFINED)
74 tree->command = returned_command;
76 tree->command = COMMAND_AMBIGUOUS;
82 /* compare two command identifier nodes by the characters they match */
83 int compare_command_identifier_nodes(const void *a, const void *b)
85 const struct command_identifier_node *aa;
86 const struct command_identifier_node *bb;
88 aa = *(struct command_identifier_node**)a;
89 bb = *(struct command_identifier_node**)b;
91 if (aa->character > bb->character)
93 else if (aa->character < bb->character)
99 /* sort command identifier nodes in alphabetical order */
100 void sort_command_identifier_nodes(struct command_identifier_node *root)
104 if (root->num_children > 1)
105 qsort(root->children, root->num_children, sizeof(struct command_identifier_node *), compare_command_identifier_nodes);
107 for (c = 0; c < root->num_children; ++c)
108 sort_command_identifier_nodes(root->children[c]);
111 /* build tree to identify command names through state transitions */
112 struct command_identifier_node *build_command_identifier_tree(struct command_map *commands)
114 struct command_identifier_node *root;
117 root = (struct command_identifier_node*) malloc(sizeof(struct command_identifier_node));
121 root->command = COMMAND_UNDEFINED;
122 root->character = L'\0';
124 root->num_children = 0;
127 while (commands[c].command_name != 0)
129 insert_command_identifier_command(root, commands + c, 0);
133 sort_command_identifier_nodes(root);
138 /* free memory used by command identifier tree structure */
139 void free_command_identifier_tree(struct command_identifier_node *tree)
143 for (c = 0; c < tree->num_children; ++c)
144 free_command_identifier_tree(tree->children[c]);
146 free(tree->children);
150 /* find command identifier node matching given character */
151 struct command_identifier_node *find_command_identifier_node(struct command_identifier_node *root, wchar_t character)
157 if (root->num_children == 0)
161 max = root->num_children - 1;
165 mid = (min + max) / 2;
167 if (character > root->children[mid]->character)
169 else if (character < root->children[mid]->character)
172 return root->children[mid];
173 } while (min <= max);
178 /* determine ID for given command string (possibly abbreviated), if found */
179 int identify_command(struct command_identifier_node *tree, wchar_t *command_buffer, size_t ch)
181 struct command_identifier_node *found;
183 if (command_buffer[ch] != L' ')
184 found = find_command_identifier_node(tree, command_buffer[ch]);
186 found = find_command_identifier_node(tree, L'\0');
190 if (command_buffer[ch] == L'\0' || command_buffer[ch] == L' ')
191 return found->command;
193 return identify_command(found, command_buffer, ch + 1);
197 if (command_buffer[ch] == L'\0' || command_buffer[ch] == L' ')
198 return tree->command;
200 return COMMAND_UNDEFINED;