cmdline: add the incomplete token string as an argument of iter_start()
[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->flags & tkd->mask) != tkd->flags)
122                                 return -1;
123                         TAILQ_INSERT_TAIL(&l, n, user_next);
124                 }
125         }
126         /* relative path */
127         else
128                 find_in_children(&l, *tkd->cur, token, tkd->flags, tkd->mask, 0);
129         if (TAILQ_EMPTY(&l))
130                 return -1;
131
132         /* store the address of object in structure */
133         if (res)
134                 *(struct confnode **)res = TAILQ_FIRST(&l);
135
136         return token_len;
137 }
138
139 struct confnode_complete_callback {
140         struct confnode *start;
141         struct confnode *cur;
142 };
143
144
145 static struct confnode *get_next_node(struct confnode *start,
146                                       struct confnode *cur, unsigned flags,
147                                       unsigned mask)
148 {
149         struct confnode *prev;
150         struct confnode *parent = start;
151
152         while (1) {
153                 prev = cur;
154
155                 /* no more node to browse */
156                 if (cur == start)
157                         return NULL;
158
159                 /* first iteration */
160                 if (cur == NULL)
161                         cur = TAILQ_FIRST(&parent->children);
162                 else
163                         cur = TAILQ_NEXT(cur, next);
164
165                 /* no next, return to upper level */
166                 if (cur == NULL) {
167                         if (prev == NULL)
168                                 return NULL; /* no node */
169                         cur = prev->parent;
170                         continue;
171                 }
172
173                 /* skip invisible nodes, and browse their contents */
174                 if (cur->flags & CONFNODE_F_INVISIBLE) {
175                         parent = cur;
176                         cur = NULL;
177                         continue;
178                 }
179
180                 /* check that flags match what we want */
181                 if ((cur->flags & mask) != flags)
182                         continue;
183
184                 /* standard node, return it */
185                 return cur;
186         }
187 }
188
189 static int
190 complete_conf_node_start(cmdline_parse_token_hdr_t *tk,
191                          __attribute__((unused)) const char *tokstr,
192                          void **opaque)
193 {
194         struct token_conf_node *tk2 = (struct token_conf_node *)tk;
195         struct token_conf_node_data *tkd = &tk2->conf_node_data;
196         struct confnode_complete_callback *cb;
197         struct confnode *n;
198         unsigned flags = tkd->flags;
199         unsigned mask = tkd->mask;
200
201         cb = malloc(sizeof(*cb));
202         if (cb == NULL)
203                 return -1;
204         *opaque = cb;
205
206         n = get_next_node(*tkd->cur, NULL, flags, mask);
207         if (n == NULL)
208                 return -1; /* no completion */
209
210         cb->start = *tkd->cur;
211         cb->cur = n;
212         return 0;
213 }
214
215 static int
216 complete_conf_node_iterate(cmdline_parse_token_hdr_t *tk, void **opaque,
217                           char *dstbuf, unsigned int size)
218 {
219         struct token_conf_node *tk2 = (struct token_conf_node *)tk;
220         struct token_conf_node_data *tkd = &tk2->conf_node_data;
221         struct confnode_complete_callback *cb;
222         struct confnode *n;
223         unsigned len;
224         unsigned flags = tkd->flags;
225         unsigned mask = tkd->mask;
226
227         cb = *opaque;
228         n = cb->cur;
229         if (n == NULL)
230                 return -1;
231         cb->cur = get_next_node(cb->start, n, flags, mask);
232
233         len = strlen(n->name) + 1;
234         if (len > size)
235                 return -1;
236
237         strcpy(dstbuf, n->name);
238         return 0;
239 }
240
241 static void
242 complete_conf_node_end(cmdline_parse_token_hdr_t *tk, void **opaque)
243 {
244         free(*opaque);
245 }
246
247 int help_conf_node(cmdline_parse_token_hdr_t *tk, char *dstbuf, unsigned int size)
248 {
249         snprintf(dstbuf, size, "conf-node");
250         return 0;
251 }
252
253 struct cmdline_token_ops token_conf_node_ops = {
254         .parse = parse_conf_node,
255         .complete_start = complete_conf_node_start,
256         .complete_iterate = complete_conf_node_iterate,
257         .complete_end = complete_conf_node_end,
258         .help = help_conf_node,
259 };
260