-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCommandTreeTemplate.c
executable file
·211 lines (186 loc) · 6.17 KB
/
CommandTreeTemplate.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/******************************************************
* Autogenerated code created by the ConsoleBuilder *
* https://github.com/embedCreativity/ConsoleBuilder *
* *
* Generated on: CODE_GENERATION_DATE *
******************************************************/
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
EXTERNAL_HEADER
// prototype method function pointer
typedef bool (*Method_t)(const char*);
// Structure of a CommandTree Node
typedef struct commandTreeNode_t
{
const char* name;
const char* desc;
const Method_t method;
const char* argDesc;
const uint32_t childCount;
const struct commandTreeNode_t** children;
} commandTreeNode_t;
// root node for tree traversal
static const commandTreeNode_t* rootNode;
// method forward declarations
METHOD_FUNC_FORWARD_DECLARATIONS
// common string declarations
STRING_DECLARATIONS
// node declarations
NODE_DECLARATIONS
#define USER_INPUT_BUF_SIZE 128
char userInput[USER_INPUT_BUF_SIZE];
EXAMPLE_PARSING_ROUTINES
/************************************************************************************/
/* Process user input prior to processing */
/************************************************************************************/
uint32_t sanitizeString(char* input, uint32_t size)
{
uint32_t i;
// protect against an idiotic (0-1) error in the for loop below
if (0 == size)
{
return 0;
}
for (i = 0; i < (size - 1); i++)
{
input[i] = toupper(input[i]);
// quit and NULL terminate when terminating characters have been identified
if (('\n' == input[i]) || ('\r' == input[i]) || ('\0' == input[i]))
{
break;
}
}
// Always NULL terminate where we quit transforming to uppercase
input[i] = 0;
return i;
}
/************************************************************************************/
/* Context-based help menu */
/************************************************************************************/
void printHelp(const commandTreeNode_t* node)
{
printf("HELP:\n");
if (rootNode != node)
{
printf("%s - %s\n", node->name, node->desc);
}
if (NULL != node->method)
{
if (NULL != node->argDesc)
{
printf("ARGS:\n %s\n", node->argDesc);
}
else
{
printf("ARGS:\nNone\n");
}
}
else
{
for (uint32_t i = 0; i < node->childCount; i++)
{
printf(" -> %s - %s\n", node->children[i]->name, node->children[i]->desc);
}
}
}
/************************************************************************************/
/* Example main() */
/************************************************************************************/
int main(void)
{
char* tok;
const char delim[] = " ";
// init local node (where we are now)
const commandTreeNode_t* node = &node1;
// init global node (where we return to)
rootNode = &node1;
while (1)
{
// It's a courtesy to provide a command prompt
printf("->");
// No matter how you get the user data into a buffer, you should call sanitizeString() on it
memset(userInput, 0, sizeof(char) * USER_INPUT_BUF_SIZE);
if (NULL != fgets(userInput, USER_INPUT_BUF_SIZE, stdin))
{
sanitizeString(userInput, USER_INPUT_BUF_SIZE);
}
else {
printf("ERROR - error in capturing user input\n");
break;
}
if (0 == strncmp(userInput, "QUIT", USER_INPUT_BUF_SIZE))
{
// Back out of Gateway "chroot"
rootNode = &node1;
node = &node1;
continue;
}
// Break up the command sentence into command words
tok = strtok(userInput, delim);
while (tok != NULL)
{
bool found = false;
// Check current node for branches matching command word
for (uint32_t i = 0; i < node->childCount; i++)
{
if (0 == strcmp(tok, node->children[i]->name))
{
// update node to point at this new match
node = node->children[i];
found = true;
break;
}
}
// Garbage command found
if (!found)
{
printHelp(node);
node = rootNode; // reset node pointer to root
break;
}
// Check to see if this new node is a method
if (NULL != node->method)
{
char* args = tok;
uint32_t cmdOffset = strlen(tok);
// If there's valid data after our command tokenizer, then those are arguments
bool ret;
if ('\0' != *(tok + cmdOffset + 1))
{
args += cmdOffset + 1;
ret = node->method(args);
}
else
{
// verify the method wasn't expecting an argument by looking for an argument description
if (NULL != node->argDesc)
{
ret = false;
}
else
{
ret = node->method(NULL);
}
}
if (!ret)
{
printHelp(node);
}
node = rootNode; // reset node pointer to root
break; // abandon remaining string if present
}
tok = strtok(NULL, delim);
// Check if we ran out of input string before a valid command was found
if (NULL == tok)
{
printHelp(node);
node = rootNode; // reset node pointer to root
}
} // end input string processing loop
}
printf("Goodbye.\n");
return 0;
}