1*0a6a1f1dSLionel Sambuc /* $NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $ */
2ebfedea0SLionel Sambuc
3ebfedea0SLionel Sambuc /* Copyright (c) 2010 The NetBSD Foundation, Inc.
4ebfedea0SLionel Sambuc * All rights reserved.
5ebfedea0SLionel Sambuc *
6ebfedea0SLionel Sambuc * This code is derived from software contributed to The NetBSD Foundation
7ebfedea0SLionel Sambuc * by Mateusz Kocielski.
8ebfedea0SLionel Sambuc *
9ebfedea0SLionel Sambuc * Redistribution and use in source and binary forms, with or without
10ebfedea0SLionel Sambuc * modification, are permitted provided that the following conditions
11ebfedea0SLionel Sambuc * are met:
12ebfedea0SLionel Sambuc * 1. Redistributions of source code must retain the above copyright
13ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer.
14ebfedea0SLionel Sambuc * 2. Redistributions in binary form must reproduce the above copyright
15ebfedea0SLionel Sambuc * notice, this list of conditions and the following disclaimer in the
16ebfedea0SLionel Sambuc * documentation and/or other materials provided with the distribution.
17ebfedea0SLionel Sambuc * 3. All advertising materials mentioning features or use of this software
18ebfedea0SLionel Sambuc * must display the following acknowledgement:
19ebfedea0SLionel Sambuc * This product includes software developed by the NetBSD
20ebfedea0SLionel Sambuc * Foundation, Inc. and its contributors.
21ebfedea0SLionel Sambuc * 4. Neither the name of The NetBSD Foundation nor the names of its
22ebfedea0SLionel Sambuc * contributors may be used to endorse or promote products derived
23ebfedea0SLionel Sambuc * from this software without specific prior written permission.
24ebfedea0SLionel Sambuc *
25ebfedea0SLionel Sambuc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
26ebfedea0SLionel Sambuc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27ebfedea0SLionel Sambuc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28ebfedea0SLionel Sambuc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
29ebfedea0SLionel Sambuc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30ebfedea0SLionel Sambuc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31ebfedea0SLionel Sambuc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32ebfedea0SLionel Sambuc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33ebfedea0SLionel Sambuc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34ebfedea0SLionel Sambuc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35ebfedea0SLionel Sambuc * POSSIBILITY OF SUCH DAMAGE.
36ebfedea0SLionel Sambuc */
37ebfedea0SLionel Sambuc #include <sys/cdefs.h>
38*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $");
39ebfedea0SLionel Sambuc
40ebfedea0SLionel Sambuc #include <sys/stat.h>
41ebfedea0SLionel Sambuc #include <sys/syslimits.h> /* for PATH_MAX */
42ebfedea0SLionel Sambuc
43ebfedea0SLionel Sambuc #include <ctype.h>
44ebfedea0SLionel Sambuc #include <err.h>
45ebfedea0SLionel Sambuc #include <errno.h>
46ebfedea0SLionel Sambuc #include <saslc.h>
47ebfedea0SLionel Sambuc #include <stdio.h>
48ebfedea0SLionel Sambuc #include <stdlib.h>
49ebfedea0SLionel Sambuc #include <string.h>
50ebfedea0SLionel Sambuc
51ebfedea0SLionel Sambuc #include "dict.h"
52ebfedea0SLionel Sambuc #include "msg.h"
53ebfedea0SLionel Sambuc #include "parser.h"
54ebfedea0SLionel Sambuc #include "saslc_private.h"
55ebfedea0SLionel Sambuc
56ebfedea0SLionel Sambuc #define SASLC__COMMENT_CHAR '#'
57ebfedea0SLionel Sambuc
58ebfedea0SLionel Sambuc /* config file location defines */
59ebfedea0SLionel Sambuc #define SASLC__CONFIG_PATH "/etc/saslc.d"
60ebfedea0SLionel Sambuc #define SASLC__CONFIG_MAIN_FILE "saslc"
61ebfedea0SLionel Sambuc #define SASLC__CONFIG_MECH_DIRECTORY "mech"
62ebfedea0SLionel Sambuc #define SASLC__CONFIG_SUFFIX ".conf"
63ebfedea0SLionel Sambuc #define SASLC__DEFAULT_APPNAME "saslc"
64ebfedea0SLionel Sambuc
65ebfedea0SLionel Sambuc /* token types */
66ebfedea0SLionel Sambuc enum {
67ebfedea0SLionel Sambuc TOKEN_KEY, /* option (key) */
68ebfedea0SLionel Sambuc TOKEN_STRING, /* quoted string */
69ebfedea0SLionel Sambuc TOKEN_NUM, /* number */
70ebfedea0SLionel Sambuc TOKEN_COMMENT, /* comment character */
71ebfedea0SLionel Sambuc TOKEN_UNKNOWN /* unknown */
72ebfedea0SLionel Sambuc };
73ebfedea0SLionel Sambuc
74ebfedea0SLionel Sambuc /* token structure */
75ebfedea0SLionel Sambuc typedef struct saslc__token_t {
76ebfedea0SLionel Sambuc int type; /**< token type */
77ebfedea0SLionel Sambuc char *val; /**< token string value */
78ebfedea0SLionel Sambuc } saslc__token_t;
79ebfedea0SLionel Sambuc
80ebfedea0SLionel Sambuc static inline char *
skip_WS(char * p)81ebfedea0SLionel Sambuc skip_WS(char *p)
82ebfedea0SLionel Sambuc {
83ebfedea0SLionel Sambuc
84ebfedea0SLionel Sambuc while (*p == ' ' || *p == '\t')
85ebfedea0SLionel Sambuc p++;
86ebfedea0SLionel Sambuc return p;
87ebfedea0SLionel Sambuc }
88ebfedea0SLionel Sambuc
89ebfedea0SLionel Sambuc /**
90ebfedea0SLionel Sambuc * @brief gets token from string c and updates pointer position.
91ebfedea0SLionel Sambuc * @param c pointer to string
92ebfedea0SLionel Sambuc * @return token on success, NULL on failure (e.g. at end of string).
93ebfedea0SLionel Sambuc * On success, c is updated to point on next token. It's position is
94ebfedea0SLionel Sambuc * undefined on failure.
95ebfedea0SLionel Sambuc *
96ebfedea0SLionel Sambuc * Note: A legal key begins with an isalpha(3) character and is
97ebfedea0SLionel Sambuc * followed by isalnum(3) or '_' characters.
98ebfedea0SLionel Sambuc */
99ebfedea0SLionel Sambuc static saslc__token_t *
saslc__parse_get_token(char ** c)100ebfedea0SLionel Sambuc saslc__parse_get_token(char **c)
101ebfedea0SLionel Sambuc {
102ebfedea0SLionel Sambuc saslc__token_t *token;
103ebfedea0SLionel Sambuc char *e;
104ebfedea0SLionel Sambuc
105ebfedea0SLionel Sambuc *c = skip_WS(*c);
106ebfedea0SLionel Sambuc if (**c == '\0')
107ebfedea0SLionel Sambuc return NULL;
108ebfedea0SLionel Sambuc
109ebfedea0SLionel Sambuc if ((token = calloc(1, sizeof(*token))) == NULL)
110ebfedea0SLionel Sambuc return NULL;
111ebfedea0SLionel Sambuc
112ebfedea0SLionel Sambuc token->val = *c;
113ebfedea0SLionel Sambuc
114ebfedea0SLionel Sambuc if (**c == SASLC__COMMENT_CHAR)
115ebfedea0SLionel Sambuc token->type = TOKEN_COMMENT;
116ebfedea0SLionel Sambuc
117ebfedea0SLionel Sambuc else if (**c == '\"')
118ebfedea0SLionel Sambuc token->type = TOKEN_STRING;
119ebfedea0SLionel Sambuc
120ebfedea0SLionel Sambuc else if (isdigit((unsigned char)**c))
121ebfedea0SLionel Sambuc token->type = TOKEN_NUM;
122ebfedea0SLionel Sambuc
123ebfedea0SLionel Sambuc else if (isalpha((unsigned char)**c))
124ebfedea0SLionel Sambuc token->type = TOKEN_KEY;
125ebfedea0SLionel Sambuc
126ebfedea0SLionel Sambuc else
127ebfedea0SLionel Sambuc token->type = TOKEN_UNKNOWN;
128ebfedea0SLionel Sambuc
129ebfedea0SLionel Sambuc switch (token->type) {
130ebfedea0SLionel Sambuc case TOKEN_COMMENT:
131ebfedea0SLionel Sambuc break;
132ebfedea0SLionel Sambuc case TOKEN_NUM:
133ebfedea0SLionel Sambuc errno = 0;
134ebfedea0SLionel Sambuc (void)strtoll(*c, &e, 0);
135ebfedea0SLionel Sambuc if (errno != 0)
136ebfedea0SLionel Sambuc goto err;
137ebfedea0SLionel Sambuc *c = e;
138ebfedea0SLionel Sambuc break;
139ebfedea0SLionel Sambuc case TOKEN_KEY:
140ebfedea0SLionel Sambuc (*c)++;
141ebfedea0SLionel Sambuc while (isalnum((unsigned char)**c) || **c == '_')
142ebfedea0SLionel Sambuc (*c)++;
143ebfedea0SLionel Sambuc break;
144ebfedea0SLionel Sambuc case TOKEN_STRING:
145ebfedea0SLionel Sambuc token->val++; /* skip initial '\"' */
146ebfedea0SLionel Sambuc (*c)++;
147ebfedea0SLionel Sambuc /*
148ebfedea0SLionel Sambuc * XXX: should we allow escapes inside the string?
149ebfedea0SLionel Sambuc */
150ebfedea0SLionel Sambuc while (**c != '\0' && **c != '\"')
151ebfedea0SLionel Sambuc (*c)++;
152ebfedea0SLionel Sambuc if (**c != '\"')
153ebfedea0SLionel Sambuc goto err;
154ebfedea0SLionel Sambuc **c = '\0'; /* kill trailing '\"' */
155ebfedea0SLionel Sambuc (*c)++;
156ebfedea0SLionel Sambuc break;
157ebfedea0SLionel Sambuc case TOKEN_UNKNOWN:
158ebfedea0SLionel Sambuc goto err;
159ebfedea0SLionel Sambuc }
160ebfedea0SLionel Sambuc
161ebfedea0SLionel Sambuc if (isspace((unsigned char)**c))
162ebfedea0SLionel Sambuc *(*c)++ = '\0';
163ebfedea0SLionel Sambuc else if (**c == SASLC__COMMENT_CHAR)
164ebfedea0SLionel Sambuc **c = '\0';
165ebfedea0SLionel Sambuc else if (**c != '\0')
166ebfedea0SLionel Sambuc goto err;
167ebfedea0SLionel Sambuc
168ebfedea0SLionel Sambuc return token;
169ebfedea0SLionel Sambuc err:
170ebfedea0SLionel Sambuc free(token);
171ebfedea0SLionel Sambuc return NULL;
172ebfedea0SLionel Sambuc }
173ebfedea0SLionel Sambuc
174ebfedea0SLionel Sambuc /**
175ebfedea0SLionel Sambuc * @brief parses line and store result in dict.
176ebfedea0SLionel Sambuc * @param line input line
177ebfedea0SLionel Sambuc * @param dict dictionary in which parsed options will be stored
178ebfedea0SLionel Sambuc * @return 0 on success, -1 on failure.
179ebfedea0SLionel Sambuc */
180ebfedea0SLionel Sambuc static int
saslc__parse_line(char * line,saslc__dict_t * dict)181ebfedea0SLionel Sambuc saslc__parse_line(char *line, saslc__dict_t *dict)
182ebfedea0SLionel Sambuc {
183ebfedea0SLionel Sambuc saslc__dict_result_t rv;
184ebfedea0SLionel Sambuc saslc__token_t *t;
185ebfedea0SLionel Sambuc char *key;
186ebfedea0SLionel Sambuc
187ebfedea0SLionel Sambuc key = NULL;
188ebfedea0SLionel Sambuc while ((t = saslc__parse_get_token(&line)) != NULL) {
189*0a6a1f1dSLionel Sambuc if (t->type == TOKEN_COMMENT) {
190*0a6a1f1dSLionel Sambuc free(t);
191ebfedea0SLionel Sambuc break;
192*0a6a1f1dSLionel Sambuc }
193ebfedea0SLionel Sambuc
194ebfedea0SLionel Sambuc if (key == NULL) { /* get the key */
195ebfedea0SLionel Sambuc if (t->type != TOKEN_KEY)
196ebfedea0SLionel Sambuc goto err;
197ebfedea0SLionel Sambuc key = t->val;
198ebfedea0SLionel Sambuc }
199ebfedea0SLionel Sambuc else { /* get the value and insert in dictionary */
200ebfedea0SLionel Sambuc if (t->type != TOKEN_STRING && t->type != TOKEN_NUM)
201ebfedea0SLionel Sambuc goto err;
202ebfedea0SLionel Sambuc rv = saslc__dict_insert(dict, key, t->val);
203ebfedea0SLionel Sambuc if (rv != DICT_OK && rv != DICT_KEYEXISTS)
204ebfedea0SLionel Sambuc goto err;
205ebfedea0SLionel Sambuc key = NULL;
206ebfedea0SLionel Sambuc }
207ebfedea0SLionel Sambuc free(t);
208ebfedea0SLionel Sambuc }
209ebfedea0SLionel Sambuc if (*line != '\0') /* processed entire line */
210ebfedea0SLionel Sambuc return -1;
211ebfedea0SLionel Sambuc if (key != NULL) /* completed key/value cycle */
212ebfedea0SLionel Sambuc return -1;
213ebfedea0SLionel Sambuc return 0;
214ebfedea0SLionel Sambuc err:
215ebfedea0SLionel Sambuc free(t);
216ebfedea0SLionel Sambuc return -1;
217ebfedea0SLionel Sambuc }
218ebfedea0SLionel Sambuc
219ebfedea0SLionel Sambuc /**
220ebfedea0SLionel Sambuc * @brief parses file and store result in dict
221ebfedea0SLionel Sambuc * @param ctx saslc context
222ebfedea0SLionel Sambuc * @param path path to the file
223ebfedea0SLionel Sambuc * @param dict dictionary in which parsed options will be stored
224ebfedea0SLionel Sambuc * @return 0 on success, -1 on failure.
225ebfedea0SLionel Sambuc */
226ebfedea0SLionel Sambuc static int
saslc__parse_file(saslc_t * ctx,char * path,saslc__dict_t * dict)227ebfedea0SLionel Sambuc saslc__parse_file(saslc_t *ctx, char *path, saslc__dict_t *dict)
228ebfedea0SLionel Sambuc {
229ebfedea0SLionel Sambuc FILE *fp;
230ebfedea0SLionel Sambuc char *buf, *lbuf;
231ebfedea0SLionel Sambuc size_t len;
232ebfedea0SLionel Sambuc int rv;
233ebfedea0SLionel Sambuc
234ebfedea0SLionel Sambuc if ((fp = fopen(path, "r")) == NULL) {
235ebfedea0SLionel Sambuc /* Don't fail if we can't open the file. */
236ebfedea0SLionel Sambuc saslc__msg_dbg("%s: fopen: %s: %s", __func__, path,
237ebfedea0SLionel Sambuc strerror(errno));
238ebfedea0SLionel Sambuc return 0;
239ebfedea0SLionel Sambuc }
240ebfedea0SLionel Sambuc saslc__msg_dbg("%s: parsing: \"%s\"", __func__, path);
241ebfedea0SLionel Sambuc rv = 0;
242ebfedea0SLionel Sambuc lbuf = NULL;
243ebfedea0SLionel Sambuc while ((buf = fgetln(fp, &len)) != NULL) {
244ebfedea0SLionel Sambuc if (buf[len - 1] == '\n')
245ebfedea0SLionel Sambuc buf[len - 1] = '\0';
246ebfedea0SLionel Sambuc else {
247ebfedea0SLionel Sambuc if ((lbuf = malloc(len + 1)) == NULL) {
248ebfedea0SLionel Sambuc saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL);
249ebfedea0SLionel Sambuc rv = -1;
250ebfedea0SLionel Sambuc break;
251ebfedea0SLionel Sambuc }
252ebfedea0SLionel Sambuc memcpy(lbuf, buf, len);
253ebfedea0SLionel Sambuc lbuf[len] = '\0';
254ebfedea0SLionel Sambuc buf = lbuf;
255ebfedea0SLionel Sambuc }
256ebfedea0SLionel Sambuc if (saslc__parse_line(buf, dict) == -1) {
257ebfedea0SLionel Sambuc saslc__error_set(ERR(ctx), ERROR_PARSE,
258ebfedea0SLionel Sambuc "can't parse file");
259ebfedea0SLionel Sambuc rv = -1;
260ebfedea0SLionel Sambuc break;
261ebfedea0SLionel Sambuc }
262ebfedea0SLionel Sambuc if (lbuf != NULL) {
263ebfedea0SLionel Sambuc free(lbuf);
264ebfedea0SLionel Sambuc lbuf = NULL;
265ebfedea0SLionel Sambuc }
266ebfedea0SLionel Sambuc }
267ebfedea0SLionel Sambuc if (lbuf != NULL)
268ebfedea0SLionel Sambuc free(lbuf);
269ebfedea0SLionel Sambuc
270ebfedea0SLionel Sambuc fclose(fp);
271ebfedea0SLionel Sambuc return rv;
272ebfedea0SLionel Sambuc }
273ebfedea0SLionel Sambuc
274ebfedea0SLionel Sambuc /**
275ebfedea0SLionel Sambuc * @brief determine if a string indicates true or not.
276ebfedea0SLionel Sambuc * @return true if the string is "true", "yes", or any nonzero
277ebfedea0SLionel Sambuc * integer; false otherwise.
278ebfedea0SLionel Sambuc *
279ebfedea0SLionel Sambuc * XXX: does this really belong here? Used in parser.c and xsess.c.
280ebfedea0SLionel Sambuc */
281ebfedea0SLionel Sambuc bool
saslc__parser_is_true(const char * str)282ebfedea0SLionel Sambuc saslc__parser_is_true(const char *str)
283ebfedea0SLionel Sambuc {
284ebfedea0SLionel Sambuc static const char *true_str[] = {
285ebfedea0SLionel Sambuc "true",
286ebfedea0SLionel Sambuc "yes"
287ebfedea0SLionel Sambuc };
288ebfedea0SLionel Sambuc char *e;
289ebfedea0SLionel Sambuc size_t i;
290ebfedea0SLionel Sambuc long int val;
291ebfedea0SLionel Sambuc
292ebfedea0SLionel Sambuc if (str == NULL)
293ebfedea0SLionel Sambuc return false;
294ebfedea0SLionel Sambuc
295ebfedea0SLionel Sambuc val = strtol(str, &e, 0);
296ebfedea0SLionel Sambuc if (*str != '\0' && *e == '\0')
297ebfedea0SLionel Sambuc return val != 0;
298ebfedea0SLionel Sambuc
299ebfedea0SLionel Sambuc for (i = 0; i < __arraycount(true_str); i++)
300ebfedea0SLionel Sambuc if (strcasecmp(str, true_str[i]) == 0)
301ebfedea0SLionel Sambuc return true;
302ebfedea0SLionel Sambuc
303ebfedea0SLionel Sambuc return false;
304ebfedea0SLionel Sambuc }
305ebfedea0SLionel Sambuc
306ebfedea0SLionel Sambuc /**
307ebfedea0SLionel Sambuc * @brief parse configuration files. By default function reads
308ebfedea0SLionel Sambuc * files from /etc/saslc.d/saslc/ directory if appname is not setup. Otherwise
309ebfedea0SLionel Sambuc * function uses /etc/saslc.d/[appname]/ directory. /etc/saslc.d/ is default
310ebfedea0SLionel Sambuc * directory which stores configuration for all applications, but can be
311ebfedea0SLionel Sambuc * overwritten by SASLC_CONFIG variable in environment.
312ebfedea0SLionel Sambuc * @param ctx saslc context
313ebfedea0SLionel Sambuc * @return 0 on success, -1 on failure.
314ebfedea0SLionel Sambuc */
315ebfedea0SLionel Sambuc int
saslc__parser_config(saslc_t * ctx)316ebfedea0SLionel Sambuc saslc__parser_config(saslc_t *ctx)
317ebfedea0SLionel Sambuc {
318ebfedea0SLionel Sambuc char path[PATH_MAX + 1];
319ebfedea0SLionel Sambuc struct stat sb;
320ebfedea0SLionel Sambuc saslc__mech_list_node_t *mech_node;
321ebfedea0SLionel Sambuc const char *config_path, *debug, *appname;
322ebfedea0SLionel Sambuc
323ebfedea0SLionel Sambuc config_path = ctx->pathname;
324ebfedea0SLionel Sambuc if (config_path == NULL)
325ebfedea0SLionel Sambuc config_path = getenv(SASLC_ENV_CONFIG);
326ebfedea0SLionel Sambuc if (config_path == NULL)
327ebfedea0SLionel Sambuc config_path = SASLC__CONFIG_PATH;
328ebfedea0SLionel Sambuc
329ebfedea0SLionel Sambuc if (stat(config_path, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
330ebfedea0SLionel Sambuc /* XXX: should this be fatal or silently ignored? */
331ebfedea0SLionel Sambuc saslc__msg_err("%s: stat: config_path='%s': %s", __func__,
332ebfedea0SLionel Sambuc config_path, strerror(errno));
333ebfedea0SLionel Sambuc return 0;
334ebfedea0SLionel Sambuc }
335ebfedea0SLionel Sambuc
336ebfedea0SLionel Sambuc if ((appname = ctx->appname) == NULL)
337ebfedea0SLionel Sambuc appname = SASLC__DEFAULT_APPNAME;
338ebfedea0SLionel Sambuc
339ebfedea0SLionel Sambuc /* parse global config file */
340ebfedea0SLionel Sambuc snprintf(path, sizeof(path), "%s/%s/%s%s", config_path,
341ebfedea0SLionel Sambuc appname, SASLC__CONFIG_MAIN_FILE, SASLC__CONFIG_SUFFIX);
342ebfedea0SLionel Sambuc if (saslc__parse_file(ctx, path, ctx->prop) == -1)
343ebfedea0SLionel Sambuc return -1;
344ebfedea0SLionel Sambuc
345ebfedea0SLionel Sambuc /* XXX: check this as early as possible! */
346ebfedea0SLionel Sambuc debug = saslc__dict_get(ctx->prop, SASLC_PROP_DEBUG);
347ebfedea0SLionel Sambuc if (debug != NULL)
348ebfedea0SLionel Sambuc saslc_debug = saslc__parser_is_true(debug);
349ebfedea0SLionel Sambuc
350ebfedea0SLionel Sambuc /* parse mechanism config files */
351ebfedea0SLionel Sambuc LIST_FOREACH(mech_node, ctx->mechanisms, nodes) {
352ebfedea0SLionel Sambuc snprintf(path, sizeof(path), "%s/%s/%s/%s%s",
353ebfedea0SLionel Sambuc config_path, appname, SASLC__CONFIG_MECH_DIRECTORY,
354ebfedea0SLionel Sambuc mech_node->mech->name, SASLC__CONFIG_SUFFIX);
355ebfedea0SLionel Sambuc if (saslc__parse_file(ctx, path, mech_node->prop) == -1)
356ebfedea0SLionel Sambuc return -1;
357ebfedea0SLionel Sambuc }
358ebfedea0SLionel Sambuc
359ebfedea0SLionel Sambuc return 0;
360ebfedea0SLionel Sambuc }
361