genconf: basic support of completion for nodes starting with "//"
[libcmdline.git] / src / genconf / parse_confnode.c
1 /*
2  * Copyright (c) 2009, Olivier MATZ <zer0@droids-corp.org>
3  * All rights reserved.
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of the University of California, Berkeley nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include <stdio.h>
29 #include <inttypes.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <netinet/in.h>
34 #include <sys/queue.h>
35
36 #include <cmdline_parse_ipaddr.h>
37 #include <cmdline_parse.h>
38
39 #include "expression.h"
40 #include "conf_parser.h"
41 #include "confnode.h"
42 #include "conf_htable.h"
43 #include "parse_confnode.h"
44
45 /* return 0 if string matches pattern (string must not contain '*')*/
46 static int
47 strcmp_joker(const char *s, const char *pattern)
48 {
49         if (s == pattern)
50                 return 0;
51
52         while (*s == *pattern && *pattern != '*') {
53                 if (*s == '\0')
54                         return 0;
55                 s ++;
56                 pattern ++;
57         }
58         if (*pattern != '*')
59                 return -1;
60         if (*(pattern + 1) == '\0')
61                 return 0;
62         while (*s) {
63                 if (strcmp_joker(s, pattern + 1) == 0)
64                         return 0;
65                 s ++;
66         }
67         return -1;
68 }
69
70 static void
71 find_in_children(struct confnode_list *l, struct confnode *n,
72                  const char *pattern, int flags, int mask, int recurs)
73 {
74         struct confnode *c;
75
76         TAILQ_FOREACH(c, &n->children, next) {
77                 /* if it's a "if" node, parse children */
78                 if (c->flags & CONFNODE_F_INVISIBLE)
79                         find_in_children(l, c, pattern, flags, mask, recurs);
80
81                 else {
82                         if ((c->flags & mask) == flags &&
83                             !strcmp_joker(c->name, pattern))
84                                 TAILQ_INSERT_TAIL(l, c, user_next);
85
86                         if (recurs)
87                                 find_in_children(l, c, pattern, flags, mask, recurs);
88                 }
89         }
90 }
91
92 static int
93 parse_conf_node(cmdline_parse_token_hdr_t *tk, const char *buf, void *res,
94                 unsigned ressize)
95 {
96         struct token_conf_node *tk2 = (struct token_conf_node *)tk;
97         struct token_conf_node_data *tkd = &tk2->conf_node_data;
98         struct confnode *n;
99         struct confnode_list l;
100         unsigned int token_len = 0;
101         char token[CMDLINE_MAX_TOKEN_SIZE];
102
103         if (res && ressize < sizeof(struct confnode *))
104                 return -1;
105
106         /* if token is too big... */
107         token_len = snprintf(token, sizeof(token), "%s", buf);
108         if (token_len >= sizeof(token))
109                 return -1;
110
111         TAILQ_INIT(&l);
112
113         /* absolute path */
114         if (token[0] == '/' && token[1] == '/') {
115                 /* if contains joker */
116                 if (strchr(token + 2, '*'))
117                         find_in_children(&l, *tkd->root, token+2, tkd->flags,
118                                          tkd->mask, 1);
119                 else {
120                         n = conf_htable_lookup(token + 2);
121                         if (n == NULL)
122                                 return -1;
123                         if ((n->flags & tkd->mask) != tkd->flags)
124                                 return -1;
125                         TAILQ_INSERT_TAIL(&l, n, user_next);
126                 }
127         }
128         /* relative path */
129         else
130                 find_in_children(&l, *tkd->cur, token, tkd->flags, tkd->mask, 0);
131         if (TAILQ_EMPTY(&l))
132                 return -1;
133
134         /* store the address of object in structure */
135         if (res)
136                 *(struct confnode **)res = TAILQ_FIRST(&l);
137
138         return 0;
139 }
140
141 struct confnode_complete_callback {
142         struct confnode *start;
143         struct confnode *cur;
144         int xpath;
145 };
146
147
148 static struct confnode *get_next_node(struct confnode *start,
149                                       struct confnode *cur, unsigned flags,
150                                       unsigned mask, int xpath)
151 {
152         struct confnode *prev;
153         struct confnode *parent = start;
154         int descend = 1;
155
156         while (1) {
157                 prev = cur;
158
159                 /* no more node to browse */
160                 if (cur == start)
161                         return NULL;
162
163                 /* first iteration */
164                 if (cur == NULL)
165                         cur = TAILQ_FIRST(&parent->children);
166                 /* directory in xpath mode */
167                 else if (xpath && descend &&
168                          (cur->flags & CONFNODE_F_IS_DIR) &&
169                          (cur->flags & CONFNODE_F_INVISIBLE) == 0)
170                         cur = TAILQ_FIRST(&cur->children);
171                 /* other cases */
172                 else
173                         cur = TAILQ_NEXT(cur, next);
174                 descend = 1;
175
176                 /* no next, return to upper level */
177                 if (cur == NULL) {
178                         if (prev == NULL)
179                                 return NULL; /* no node */
180                         cur = prev->parent;
181                         descend = 0;
182                         continue;
183                 }
184
185                 /* skip invisible nodes, and browse their contents */
186                 if (cur->flags & CONFNODE_F_INVISIBLE) {
187                         parent = cur;
188                         cur = NULL;
189                         continue;
190                 }
191
192                 /* skip other nodes with no name */
193                 if (cur->flags & CONFNODE_F_NO_NAME)
194                         continue;
195
196                 /* check that flags match what we want */
197                 if ((cur->flags & mask) != flags)
198                         continue;
199
200                 /* standard node, return it */
201                 return cur;
202         }
203 }
204
205 static int
206 complete_conf_node_start(cmdline_parse_token_hdr_t *tk,
207                          __attribute__((unused)) const char *tokstr,
208                          void **opaque)
209 {
210         struct token_conf_node *tk2 = (struct token_conf_node *)tk;
211         struct token_conf_node_data *tkd = &tk2->conf_node_data;
212         struct confnode_complete_callback *cb;
213         struct confnode *n, *start;
214         unsigned flags = tkd->flags;
215         unsigned mask = tkd->mask;
216
217         *opaque = NULL;
218         cb = malloc(sizeof(*cb));
219         if (cb == NULL)
220                 return -1;
221         *opaque = cb;
222
223         if (strlen(tokstr) >= 2 && tokstr[0] == '/' && tokstr[1] == '/') {
224                 start = *tkd->root;
225                 cb->xpath = 1;
226         }
227         else {
228                 start = *tkd->cur;
229                 cb->xpath = 0;
230         }
231
232         n = get_next_node(start, NULL, flags, mask, cb->xpath);
233         if (n == NULL) {
234                 /* no completion.
235                  * cb is freed in complete_conf_node_end()*/
236                 return -1;
237         }
238
239         cb->start = start;
240         cb->cur = n;
241         return 0;
242 }
243
244 static int
245 complete_conf_node_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
246                           char *dstbuf, unsigned int size)
247 {
248         struct token_conf_node *tk2 = (struct token_conf_node *)tk;
249         struct token_conf_node_data *tkd = &tk2->conf_node_data;
250         struct confnode_complete_callback *cb;
251         struct confnode *n;
252         unsigned flags = tkd->flags;
253         unsigned mask = tkd->mask;
254         int len;
255
256         cb = *opaque;
257         n = cb->cur;
258         if (n == NULL)
259                 return -1;
260         cb->cur = get_next_node(cb->start, n, flags, mask, cb->xpath);
261
262         len = snprintf(dstbuf, size, "%s%s",
263                        cb->xpath ? "//" : "",
264                        n->name);
265         if (len < 0 || len >= size)
266                 return -1;
267
268         return 0;
269 }
270
271 static void
272 complete_conf_node_end(cmdline_parse_token_hdr_t *tk, void **opaque)
273 {
274         if (*opaque)
275                 free(*opaque);
276 }
277
278 int help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
279 {
280         snprintf(dstbuf, size, "conf-node");
281         return 0;
282 }
283
284 struct cmdline_token_ops token_conf_node_ops = {
285         .parse = parse_conf_node,
286         .complete_start = complete_conf_node_start,
287         .complete_iterate = complete_conf_node_iterate,
288         .complete_end = complete_conf_node_end,
289         .help = help_conf_node,
290 };
291