25
25
26
26
#include "cmd.h"
27
27
#include "common.h"
28
+ #include "compat.h"
28
29
#include "linenoise/linenoise.h"
29
30
30
31
/* from the main.c */
31
32
extern struct ly_ctx * ctx ;
32
33
34
+ /**
35
+ * @brief Add a match to the completions array.
36
+ *
37
+ * @param[in] match Match to be added.
38
+ * @param[in,out] matches Matches provided to the user as a completion hint.
39
+ * @param[in,out] match_count Number of matches.
40
+ */
41
+ static void
42
+ cmd_completion_add_match (const char * match , char * * * matches , unsigned int * match_count )
43
+ {
44
+ void * p ;
45
+
46
+ ++ (* match_count );
47
+ p = realloc (* matches , * match_count * sizeof * * matches );
48
+ if (!p ) {
49
+ YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
50
+ return ;
51
+ }
52
+ * matches = p ;
53
+ (* matches )[* match_count - 1 ] = strdup (match );
54
+ }
55
+
56
+ /**
57
+ * @brief Provides completion for command names.
58
+ *
59
+ * @param[in] hint User input.
60
+ * @param[out] matches Matches provided to the user as a completion hint.
61
+ * @param[out] match_count Number of matches.
62
+ */
33
63
static void
34
64
get_cmd_completion (const char * hint , char * * * matches , unsigned int * match_count )
35
65
{
36
66
int i ;
37
- void * p ;
38
67
39
68
* match_count = 0 ;
40
69
* matches = NULL ;
41
70
42
71
for (i = 0 ; commands [i ].name ; i ++ ) {
43
72
if (!strncmp (hint , commands [i ].name , strlen (hint ))) {
44
- ++ (* match_count );
45
- p = realloc (* matches , * match_count * sizeof * * matches );
46
- if (!p ) {
47
- YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
48
- return ;
49
- }
50
- * matches = p ;
51
- (* matches )[* match_count - 1 ] = strdup (commands [i ].name );
73
+ cmd_completion_add_match (commands [i ].name , matches , match_count );
52
74
}
53
75
}
54
76
}
55
77
78
+ /**
79
+ * @brief Check if the last command line argument is an option.
80
+ *
81
+ * @param[in] hint User input.
82
+ * @return 1 if it is an option, 0 otherwise.
83
+ */
56
84
static int
57
85
last_is_opt (const char * hint )
58
86
{
@@ -79,44 +107,210 @@ last_is_opt(const char *hint)
79
107
return 0 ;
80
108
}
81
109
110
+ /**
111
+ * @brief Provides completion for module names.
112
+ *
113
+ * @param[in] hint User input.
114
+ * @param[out] matches Matches provided to the user as a completion hint.
115
+ * @param[out] match_count Number of matches.
116
+ */
82
117
static void
83
118
get_model_completion (const char * hint , char * * * matches , unsigned int * match_count )
84
119
{
85
120
LY_ARRAY_COUNT_TYPE u ;
86
121
uint32_t idx = 0 ;
87
122
const struct lys_module * module ;
88
- void * p ;
89
123
90
124
* match_count = 0 ;
91
125
* matches = NULL ;
92
126
93
127
while ((module = ly_ctx_get_module_iter (ctx , & idx ))) {
94
128
if (!strncmp (hint , module -> name , strlen (hint ))) {
95
- ++ (* match_count );
96
- p = realloc (* matches , * match_count * sizeof * * matches );
97
- if (!p ) {
98
- YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
99
- return ;
100
- }
101
- * matches = p ;
102
- (* matches )[* match_count - 1 ] = strdup (module -> name );
129
+ cmd_completion_add_match (module -> name , matches , match_count );
103
130
}
104
131
105
132
LY_ARRAY_FOR (module -> parsed -> includes , u ) {
106
133
if (!strncmp (hint , module -> parsed -> includes [u ].submodule -> name , strlen (hint ))) {
107
- ++ (* match_count );
108
- p = realloc (* matches , * match_count * sizeof * * matches );
109
- if (!p ) {
110
- YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
111
- return ;
112
- }
113
- * matches = p ;
114
- (* matches )[* match_count - 1 ] = strdup (module -> parsed -> includes [u ].submodule -> name );
134
+ cmd_completion_add_match (module -> parsed -> includes [u ].submodule -> name , matches , match_count );
115
135
}
116
136
}
117
137
}
118
138
}
119
139
140
+ /**
141
+ * @brief Add all child nodes of a single node to the completion hint.
142
+ *
143
+ * @param[in] last_node Node of which children will be added to the hint.
144
+ * @param matches[out] Matches provided to the user as a completion hint.
145
+ * @param match_count[out] Number of matches.
146
+ */
147
+ static void
148
+ single_hint_add_children (const struct lysc_node * last_node , char * * * matches , unsigned int * match_count )
149
+ {
150
+ const struct lysc_node * node = NULL ;
151
+ char * match ;
152
+
153
+ if (!last_node ) {
154
+ return ;
155
+ }
156
+
157
+ while ((node = lys_getnext (node , last_node , NULL , 0 ))) {
158
+ match = lysc_path (node , LYSC_PATH_DATA , NULL , 0 );
159
+ cmd_completion_add_match (match , matches , match_count );
160
+ free (match );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * @brief Add module and/or node's children names to the hint.
166
+ *
167
+ * @param[in] module Compiled schema module.
168
+ * @param[in] parent Parent node of which children are potential matches.
169
+ * @param[in] hint_node_name Node name contained within the hint specified by user.
170
+ * @param[in,out] matches Matches provided to the user as a completion hint.
171
+ * @param[in,out] match_count Number of matches.
172
+ * @param[out] last_node Last processed node.
173
+ */
174
+ static void
175
+ add_all_children_nodes (const struct lysc_module * module , const struct lysc_node * parent ,
176
+ const char * hint_node_name , char * * * matches , unsigned int * match_count , const struct lysc_node * * last_node )
177
+ {
178
+ const struct lysc_node * node ;
179
+ char * match , * node_name = NULL ;
180
+
181
+ * last_node = NULL ;
182
+
183
+ if (!parent && !module ) {
184
+ return ;
185
+ }
186
+
187
+ node = NULL ;
188
+ while ((node = lys_getnext (node , parent , module , 0 ))) {
189
+ if (parent && (node -> module != parent -> module )) {
190
+ /* augmented node */
191
+ if (asprintf (& node_name , "%s:%s" , node -> module -> name , node -> name ) == -1 ) {
192
+ YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
193
+ break ;
194
+ }
195
+ } else {
196
+ node_name = strdup (node -> name );
197
+ if (!node_name ) {
198
+ YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
199
+ break ;
200
+ }
201
+ }
202
+ if (!hint_node_name || !strncmp (hint_node_name , node_name , strlen (hint_node_name ))) {
203
+ /* adding just module names + their top level node(s) to the hint */
204
+ * last_node = node ;
205
+ match = lysc_path (node , LYSC_PATH_DATA , NULL , 0 );
206
+ cmd_completion_add_match (match , matches , match_count );
207
+ free (match );
208
+ }
209
+ free (node_name );
210
+ }
211
+ }
212
+
213
+ /**
214
+ * @brief Provides completion for schemas.
215
+ *
216
+ * @param[in] hint User input.
217
+ * @param[out] matches Matches provided to the user as a completion hint.
218
+ * @param[out] match_count Number of matches.
219
+ */
220
+ static void
221
+ get_schema_completion (const char * hint , char * * * matches , unsigned int * match_count )
222
+ {
223
+ const struct lys_module * module ;
224
+ uint32_t idx , prev_lo ;
225
+ const char * start ;
226
+ char * end , * module_name = NULL , * path = NULL ;
227
+ const struct lysc_node * parent , * last_node ;
228
+ int rc = 0 ;
229
+ size_t len ;
230
+
231
+ * match_count = 0 ;
232
+ * matches = NULL ;
233
+
234
+ if (strlen (hint )) {
235
+ if (hint [0 ] != '/' ) {
236
+ return ;
237
+ }
238
+ start = hint + 1 ;
239
+ } else {
240
+ start = hint ;
241
+ }
242
+
243
+ end = strchr (start , ':' );
244
+ if (!end ) {
245
+ /* no module name */
246
+ len = strlen (start );
247
+
248
+ /* go through all the modules */
249
+ idx = 0 ;
250
+ while ((module = ly_ctx_get_module_iter (ctx , & idx ))) {
251
+ if (!module -> implemented ) {
252
+ continue ;
253
+ }
254
+
255
+ if (!len || !strncmp (start , module -> name , len )) {
256
+ /* add all their (matching) top level nodes */
257
+ add_all_children_nodes (module -> compiled , NULL , NULL , matches , match_count , & last_node );
258
+ }
259
+ }
260
+ } else {
261
+ /* module name known */
262
+ module_name = strndup (start , end - start );
263
+ if (!module_name ) {
264
+ YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
265
+ rc = 1 ;
266
+ goto cleanup ;
267
+ }
268
+
269
+ module = ly_ctx_get_module_implemented (ctx , module_name );
270
+ if (!module ) {
271
+ goto cleanup ;
272
+ }
273
+
274
+ /* find the last '/', if it is at the beginning of the hint, only path up to the top level node is known,
275
+ * else the name of the last node starts after the found '/' */
276
+ start = strrchr (hint , '/' );
277
+ if (!start ) {
278
+ goto cleanup ;
279
+ }
280
+
281
+ if (start == hint ) {
282
+ /* only the (incomplete) top level node path, add all (matching) top level nodes */
283
+ add_all_children_nodes (module -> compiled , NULL , end + 1 , matches , match_count , & last_node );
284
+ goto cleanup ;
285
+ }
286
+
287
+ /* get rid of stuff after the last '/' to obtain the parent node */
288
+ path = strndup (hint , start - hint );
289
+ if (!path ) {
290
+ YLMSG_E ("Memory allocation failed (%s:%d, %s)" , __FILE__ , __LINE__ , strerror (errno ));
291
+ rc = 1 ;
292
+ goto cleanup ;
293
+ }
294
+
295
+ /* silently get the last parent in the hint (it may not exist) */
296
+ prev_lo = ly_log_options (0 );
297
+ parent = lys_find_path (ctx , NULL , path , 0 );
298
+ ly_log_options (prev_lo );
299
+
300
+ /* add all (matching) child nodes of the parent */
301
+ add_all_children_nodes (NULL , parent , start + 1 , matches , match_count , & last_node );
302
+ }
303
+
304
+ cleanup :
305
+ if (!rc && (* match_count == 1 )) {
306
+ /* to avoid a single hint (space at the end), add all children as hints */
307
+ single_hint_add_children (last_node , matches , match_count );
308
+ }
309
+ free (path );
310
+ free (module_name );
311
+ }
312
+
313
+ /* callback */
120
314
void
121
315
complete_cmd (const char * buf , const char * hint , linenoiseCompletions * lc )
122
316
{
@@ -130,7 +324,11 @@ complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc)
130
324
!strncmp (buf , "xpath " , 6 ) || !strncmp (buf , "clear " , 6 )) && !last_is_opt (hint )) {
131
325
linenoisePathCompletion (buf , hint , lc );
132
326
} else if ((!strncmp (buf , "print " , 6 ) || !strncmp (buf , "feature " , 8 )) && !last_is_opt (hint )) {
133
- get_model_completion (hint , & matches , & match_count );
327
+ if (!strncmp (buf , "print -f info -P " , 17 )) {
328
+ get_schema_completion (hint , & matches , & match_count );
329
+ } else {
330
+ get_model_completion (hint , & matches , & match_count );
331
+ }
134
332
} else if (!strchr (buf , ' ' ) && hint [0 ]) {
135
333
get_cmd_completion (hint , & matches , & match_count );
136
334
}
0 commit comments