1 /*
2  * libxlu_cfg.c - xl configuration file parsing: setup and helper functions
3  *
4  * Copyright (C) 2010      Citrix Ltd.
5  * Author Ian Jackson <ian.jackson@eu.citrix.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License as published
9  * by the Free Software Foundation; version 2.1 only. with the special
10  * exception on linking described in file LICENSE.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  */
17 
18 
19 #include "libxl_osdeps.h" /* must come before any other headers */
20 
21 #include <limits.h>
22 
23 #include "libxlu_internal.h"
24 #include "libxlu_cfg_y.h"
25 #include "libxlu_cfg_l.h"
26 #include "libxlu_cfg_i.h"
27 
xlu_cfg_init(FILE * report,const char * report_source)28 XLU_Config *xlu_cfg_init(FILE *report, const char *report_source) {
29     XLU_Config *cfg;
30 
31     cfg= malloc(sizeof(*cfg));
32     if (!cfg) return 0;
33 
34     cfg->report= report;
35     cfg->config_source= strdup(report_source);
36     if (!cfg->config_source) { free(cfg); return 0; }
37 
38     cfg->settings= 0;
39     return cfg;
40 }
41 
ctx_prep(CfgParseContext * ctx,XLU_Config * cfg)42 static int ctx_prep(CfgParseContext *ctx, XLU_Config *cfg) {
43     int e;
44 
45     ctx->cfg= cfg;
46     ctx->err= 0;
47     ctx->lexerrlineno= -1;
48     ctx->likely_python= 0;
49     ctx->scanner= 0;
50 
51     e= xlu__cfg_yylex_init_extra(ctx, &ctx->scanner);
52     if (e) {
53         fprintf(cfg->report,"%s: unable to create scanner: %s\n",
54                 cfg->config_source, strerror(e));
55         return e;
56     }
57     return 0;
58 }
59 
ctx_dispose(CfgParseContext * ctx)60 static void ctx_dispose(CfgParseContext *ctx) {
61     if (ctx->scanner) xlu__cfg_yylex_destroy(ctx->scanner);
62 }
63 
parse(CfgParseContext * ctx)64 static void parse(CfgParseContext *ctx) {
65     /* On return, ctx.err will be updated with the error status. */
66     int r;
67 
68     xlu__cfg_yyset_lineno(1, ctx->scanner);
69 
70     r= xlu__cfg_yyparse(ctx);
71     if (r) assert(ctx->err);
72 
73     if (ctx->err && ctx->likely_python) {
74         fputs(
75  "warning: Config file looks like it contains Python code.\n"
76  "warning:  Arbitrary Python is no longer supported.\n"
77  "warning:  See https://wiki.xen.org/wiki/PythonInXlConfig\n",
78               ctx->cfg->report);
79     }
80 }
81 
xlu_cfg_readfile(XLU_Config * cfg,const char * real_filename)82 int xlu_cfg_readfile(XLU_Config *cfg, const char *real_filename) {
83     FILE *f = 0;
84     int e;
85 
86     CfgParseContext ctx;
87     e = ctx_prep(&ctx, cfg);
88     if (e) { ctx.err= e; goto xe; }
89 
90     f= fopen(real_filename, "r");
91     if (!f) {
92         ctx.err = errno;
93         fprintf(cfg->report,"%s: unable to open configuration file: %s\n",
94                 real_filename, strerror(e));
95         goto xe;
96     }
97 
98     xlu__cfg_yyrestart(f, ctx.scanner);
99 
100     parse(&ctx);
101 
102  xe:
103     ctx_dispose(&ctx);
104     if (f) fclose(f);
105 
106     return ctx.err;
107 }
108 
xlu_cfg_readdata(XLU_Config * cfg,const char * data,int length)109 int xlu_cfg_readdata(XLU_Config *cfg, const char *data, int length) {
110     int e;
111     YY_BUFFER_STATE buf= 0;
112 
113     CfgParseContext ctx;
114     e= ctx_prep(&ctx, cfg);
115     if (e) { ctx.err= e; goto xe; }
116 
117     buf = xlu__cfg_yy_scan_bytes(data, length, ctx.scanner);
118     if (!buf) {
119         fprintf(cfg->report,"%s: unable to allocate scanner buffer\n",
120                 cfg->config_source);
121         ctx.err= ENOMEM;
122         goto xe;
123     }
124 
125     parse(&ctx);
126 
127  xe:
128     if (buf) xlu__cfg_yy_delete_buffer(buf, ctx.scanner);
129     ctx_dispose(&ctx);
130 
131     return ctx.err;
132 }
133 
xlu__cfg_value_free(XLU_ConfigValue * value)134 void xlu__cfg_value_free(XLU_ConfigValue *value)
135 {
136     int i;
137 
138     if (!value) return;
139 
140     switch (value->type) {
141     case XLU_STRING:
142         free(value->u.string);
143         break;
144     case XLU_LIST:
145         for (i = 0; i < value->u.list.nvalues; i++)
146             xlu__cfg_value_free(value->u.list.values[i]);
147         free(value->u.list.values);
148     }
149     free(value);
150 }
151 
xlu__cfg_set_free(XLU_ConfigSetting * set)152 void xlu__cfg_set_free(XLU_ConfigSetting *set) {
153     if (!set) return;
154     free(set->name);
155     xlu__cfg_value_free(set->value);
156     free(set);
157 }
158 
xlu_cfg_destroy(XLU_Config * cfg)159 void xlu_cfg_destroy(XLU_Config *cfg) {
160     XLU_ConfigSetting *set, *set_next;
161 
162     if (!cfg) return;
163     for (set= cfg->settings;
164          set;
165          set= set_next) {
166         set_next= set->next;
167         xlu__cfg_set_free(set);
168     }
169     free(cfg->config_source);
170     free(cfg);
171 }
172 
find(const XLU_Config * cfg,const char * n)173 static XLU_ConfigSetting *find(const XLU_Config *cfg, const char *n) {
174     XLU_ConfigSetting *set;
175 
176     for (set= cfg->settings;
177          set;
178          set= set->next)
179         if (!strcmp(set->name, n))
180             return set;
181     return 0;
182 }
183 
find_atom(const XLU_Config * cfg,const char * n,XLU_ConfigSetting ** set_r,int dont_warn)184 static int find_atom(const XLU_Config *cfg, const char *n,
185                      XLU_ConfigSetting **set_r, int dont_warn) {
186     XLU_ConfigSetting *set;
187 
188     set= find(cfg,n);
189     if (!set) return ESRCH;
190 
191     if (set->value->type!=XLU_STRING) {
192         if (!dont_warn)
193             fprintf(cfg->report,
194                     "%s:%d: warning: parameter `%s' is"
195                     " a list but should be a single value\n",
196                     cfg->config_source, set->lineno, n);
197         return EINVAL;
198     }
199     *set_r= set;
200     return 0;
201 }
202 
203 
xlu_cfg_value_type(const XLU_ConfigValue * value)204 enum XLU_ConfigValueType xlu_cfg_value_type(const XLU_ConfigValue *value)
205 {
206     return value->type;
207 }
208 
xlu_cfg_value_get_string(const XLU_Config * cfg,XLU_ConfigValue * value,char ** value_r,int dont_warn)209 int xlu_cfg_value_get_string(const XLU_Config *cfg, XLU_ConfigValue *value,
210                              char **value_r, int dont_warn)
211 {
212     if (value->type != XLU_STRING) {
213         if (!dont_warn)
214             fprintf(cfg->report,
215                     "%s:%d:%d: warning: value is not a string\n",
216                     cfg->config_source, value->loc.first_line,
217                     value->loc.first_column);
218         *value_r = NULL;
219         return EINVAL;
220     }
221 
222     *value_r = value->u.string;
223     return 0;
224 }
225 
xlu_cfg_value_get_list(const XLU_Config * cfg,XLU_ConfigValue * value,XLU_ConfigList ** value_r,int dont_warn)226 int xlu_cfg_value_get_list(const XLU_Config *cfg, XLU_ConfigValue *value,
227                            XLU_ConfigList **value_r, int dont_warn)
228 {
229     if (value->type != XLU_LIST) {
230         if (!dont_warn)
231             fprintf(cfg->report,
232                     "%s:%d:%d: warning: value is not a list\n",
233                     cfg->config_source, value->loc.first_line,
234                     value->loc.first_column);
235         *value_r = NULL;
236         return EINVAL;
237     }
238 
239     *value_r = &value->u.list;
240     return 0;
241 }
242 
xlu_cfg_get_listitem2(const XLU_ConfigList * list,int entry)243 XLU_ConfigValue *xlu_cfg_get_listitem2(const XLU_ConfigList *list,
244                                        int entry)
245 {
246     if (entry < 0 || entry >= list->nvalues) return NULL;
247     return list->values[entry];
248 }
249 
xlu_cfg_get_string(const XLU_Config * cfg,const char * n,const char ** value_r,int dont_warn)250 int xlu_cfg_get_string(const XLU_Config *cfg, const char *n,
251                        const char **value_r, int dont_warn) {
252     XLU_ConfigSetting *set;
253     int e;
254 
255     e= find_atom(cfg,n,&set,dont_warn);  if (e) return e;
256     *value_r= set->value->u.string;
257     return 0;
258 }
259 
xlu_cfg_replace_string(const XLU_Config * cfg,const char * n,char ** value_r,int dont_warn)260 int xlu_cfg_replace_string(const XLU_Config *cfg, const char *n,
261                            char **value_r, int dont_warn) {
262     XLU_ConfigSetting *set;
263     int e;
264 
265     e= find_atom(cfg,n,&set,dont_warn);  if (e) return e;
266     free(*value_r);
267     *value_r= strdup(set->value->u.string);
268     return 0;
269 }
270 
xlu_cfg_get_bounded_long(const XLU_Config * cfg,const char * n,long min,long max,long * value_r,int dont_warn)271 int xlu_cfg_get_bounded_long(const XLU_Config *cfg, const char *n,
272                              long min, long max, long *value_r,
273                              int dont_warn) {
274     long l;
275     XLU_ConfigSetting *set;
276     int e;
277     char *ep;
278 
279     e= find_atom(cfg,n,&set,dont_warn);  if (e) return e;
280     if (set->op == XLU_OP_ADDITION) {
281         if (!dont_warn)
282             fprintf(cfg->report,
283                     "%s:%d: warning: can't use += with numbers"
284                     " for parameter `%s'\n",
285                     cfg->config_source, set->lineno, n);
286         return EINVAL;
287     }
288     errno= 0; l= strtol(set->value->u.string, &ep, 0);
289     e= errno;
290     if (errno) {
291         e= errno;
292         assert(e==EINVAL || e==ERANGE);
293         if (!dont_warn)
294             fprintf(cfg->report,
295                     "%s:%d: warning: parameter `%s' could not be parsed"
296                     " as a number: %s\n",
297                     cfg->config_source, set->lineno, n, strerror(e));
298         return e;
299     }
300     if (*ep || ep==set->value->u.string) {
301         if (!dont_warn)
302             fprintf(cfg->report,
303                     "%s:%d: warning: parameter `%s' is not a valid number\n",
304                     cfg->config_source, set->lineno, n);
305         return EINVAL;
306     }
307     if (l < min) {
308         if (!dont_warn)
309             fprintf(cfg->report,
310                     "%s:%d: warning: value `%ld' is smaller than minimum bound '%ld'\n",
311                     cfg->config_source, set->lineno, l, min);
312         return EINVAL;
313     }
314     if (l > max) {
315         if (!dont_warn)
316             fprintf(cfg->report,
317                     "%s:%d: warning: value `%ld' is greater than maximum bound '%ld'\n",
318                     cfg->config_source, set->lineno, l, max);
319         return EINVAL;
320     }
321 
322     *value_r= l;
323     return 0;
324 }
325 
xlu_cfg_get_long(const XLU_Config * cfg,const char * n,long * value_r,int dont_warn)326 int xlu_cfg_get_long(const XLU_Config *cfg, const char *n,
327                      long *value_r, int dont_warn) {
328     return xlu_cfg_get_bounded_long(cfg, n, LONG_MIN, LONG_MAX, value_r,
329                                     dont_warn);
330 }
331 
xlu_cfg_get_defbool(const XLU_Config * cfg,const char * n,libxl_defbool * b,int dont_warn)332 int xlu_cfg_get_defbool(const XLU_Config *cfg, const char *n, libxl_defbool *b,
333                      int dont_warn)
334 {
335     int ret;
336     long l;
337 
338     ret = xlu_cfg_get_long(cfg, n, &l, dont_warn);
339     if (ret) return ret;
340     libxl_defbool_set(b, !!l);
341     return 0;
342 }
343 
xlu_cfg_get_list(const XLU_Config * cfg,const char * n,XLU_ConfigList ** list_r,int * entries_r,int dont_warn)344 int xlu_cfg_get_list(const XLU_Config *cfg, const char *n,
345                      XLU_ConfigList **list_r, int *entries_r, int dont_warn) {
346     XLU_ConfigSetting *set;
347     set= find(cfg,n);  if (!set) return ESRCH;
348     if (set->value->type!=XLU_LIST) {
349         if (!dont_warn) {
350             fprintf(cfg->report,
351                     "%s:%d: warning: parameter `%s' is a single value"
352                     " but should be a list\n",
353                     cfg->config_source, set->lineno, n);
354         }
355         return EINVAL;
356     }
357     if (list_r) *list_r= &set->value->u.list;
358     if (entries_r) *entries_r= set->value->u.list.nvalues;
359     return 0;
360 }
361 
xlu_cfg_get_list_as_string_list(const XLU_Config * cfg,const char * n,libxl_string_list * psl,int dont_warn)362 int xlu_cfg_get_list_as_string_list(const XLU_Config *cfg, const char *n,
363                      libxl_string_list *psl, int dont_warn) {
364     int i, rc, nr;
365     XLU_ConfigList *list;
366     libxl_string_list sl;
367 
368     rc = xlu_cfg_get_list(cfg, n, &list, &nr, dont_warn);
369     if (rc)  return rc;
370 
371     sl = malloc(sizeof(char*)*(nr + 1));
372     if (sl == NULL) return ENOMEM;
373 
374     for (i=0; i<nr; i++) {
375         const char *a = xlu_cfg_get_listitem(list, i);
376         sl[i] = a ? strdup(a) : NULL;
377     }
378 
379     sl[nr] = NULL;
380 
381     *psl = sl;
382     return 0;
383 }
384 
xlu_cfg_get_listitem(const XLU_ConfigList * list,int entry)385 const char *xlu_cfg_get_listitem(const XLU_ConfigList *list, int entry) {
386     if (entry < 0 || entry >= list->nvalues) return 0;
387     if (list->values[entry]->type != XLU_STRING) return 0;
388     return list->values[entry]->u.string;
389 }
390 
391 
xlu__cfg_string_mk(CfgParseContext * ctx,char * atom,YYLTYPE * loc)392 XLU_ConfigValue *xlu__cfg_string_mk(CfgParseContext *ctx, char *atom,
393                                     YYLTYPE *loc)
394 {
395     XLU_ConfigValue *value = NULL;
396 
397     if (ctx->err) goto x;
398 
399     value = malloc(sizeof(*value));
400     if (!value) goto xe;
401     value->type = XLU_STRING;
402     value->u.string = atom;
403     memcpy(&value->loc, loc, sizeof(*loc));
404 
405     return value;
406 
407  xe:
408     ctx->err= errno;
409  x:
410     free(value);
411     free(atom);
412     return NULL;
413 }
414 
xlu__cfg_list_mk(CfgParseContext * ctx,XLU_ConfigValue * val,YYLTYPE * loc)415 XLU_ConfigValue *xlu__cfg_list_mk(CfgParseContext *ctx,
416                                   XLU_ConfigValue *val,
417                                   YYLTYPE *loc)
418 {
419     XLU_ConfigValue *value = NULL;
420     XLU_ConfigValue **values = NULL;
421 
422     if (ctx->err) goto x;
423 
424     values = malloc(sizeof(*values));
425     if (!values) goto xe;
426     values[0] = val;
427 
428     value = malloc(sizeof(*value));
429     if (!value) goto xe;
430     value->type = XLU_LIST;
431     value->u.list.nvalues = !!val;
432     value->u.list.avalues = 1;
433     value->u.list.values = values;
434     memcpy(&value->loc, loc, sizeof(*loc));
435 
436     return value;
437 
438  xe:
439     ctx->err= errno;
440  x:
441     free(value);
442     free(values);
443     xlu__cfg_value_free(val);
444     return NULL;
445 }
446 
xlu__cfg_list_append(CfgParseContext * ctx,XLU_ConfigValue * list,XLU_ConfigValue * val)447 void xlu__cfg_list_append(CfgParseContext *ctx,
448                           XLU_ConfigValue *list,
449                           XLU_ConfigValue *val)
450 {
451     if (ctx->err) return;
452 
453     assert(val);
454     assert(list->type == XLU_LIST);
455 
456     if (list->u.list.nvalues >= list->u.list.avalues) {
457         int new_avalues;
458         XLU_ConfigValue **new_values = NULL;
459 
460         if (list->u.list.avalues > INT_MAX / 100) {
461             ctx->err = ERANGE;
462             xlu__cfg_value_free(val);
463             return;
464         }
465 
466         new_avalues = list->u.list.avalues * 4;
467         new_values  = realloc(list->u.list.values,
468                               sizeof(*new_values) * new_avalues);
469         if (!new_values) {
470             ctx->err = errno;
471             xlu__cfg_value_free(val);
472             return;
473         }
474 
475         list->u.list.avalues = new_avalues;
476         list->u.list.values  = new_values;
477     }
478 
479     list->u.list.values[list->u.list.nvalues] = val;
480     list->u.list.nvalues++;
481 }
482 
xlu__cfg_concat_vals(CfgParseContext * ctx,XLU_ConfigValue * prev,XLU_ConfigValue * to_add)483 static int xlu__cfg_concat_vals(CfgParseContext *ctx,
484                                 XLU_ConfigValue *prev,
485                                 XLU_ConfigValue *to_add)
486 {
487     int r;
488 
489     if (prev->type != to_add->type) {
490         xlu__cfgl_lexicalerror(ctx,
491                            "can't add [list] to \"string\" or vice versa");
492         return EINVAL;
493     }
494 
495     switch (to_add->type) {
496     case XLU_STRING: {
497         char *new_string = NULL;
498 
499         r = asprintf(&new_string, "%s%s", prev->u.string,
500                      to_add->u.string);
501         if (r < 0) {
502             return errno;
503         }
504         free(to_add->u.string);
505         to_add->u.string = new_string;
506         return 0;
507     }
508     case XLU_LIST: {
509         XLU_ConfigList *const prev_list = &prev->u.list;
510         XLU_ConfigList *const cur_list = &to_add->u.list;
511         int nvalues;
512 
513         if (prev->u.list.nvalues > INT_MAX - to_add->u.list.nvalues) {
514             return ERANGE;
515         }
516         nvalues = prev->u.list.nvalues + to_add->u.list.nvalues;
517 
518         if (nvalues >= cur_list->avalues) {
519             XLU_ConfigValue **new_vals;
520             new_vals = realloc(cur_list->values,
521                                nvalues * sizeof(*new_vals));
522             if (!new_vals) {
523                 return ENOMEM;
524             }
525             cur_list->avalues = nvalues;
526             cur_list->values = new_vals;
527         }
528 
529         /* make space for `prev' into `to_add' */
530         memmove(cur_list->values + prev_list->nvalues,
531                 cur_list->values,
532                 cur_list->nvalues * sizeof(XLU_ConfigValue *));
533         /* move values from `prev' to `to_add' as the list in `prev' will
534          * not be reachable by find(). */
535         memcpy(cur_list->values,
536                prev_list->values,
537                prev_list->nvalues * sizeof(XLU_ConfigValue *));
538         cur_list->nvalues = nvalues;
539         prev_list->nvalues = 0;
540         memset(prev_list->values, 0,
541                prev_list->nvalues * sizeof(XLU_ConfigValue *));
542         return 0;
543     }
544     default:
545         abort();
546     }
547     return -1;
548 }
549 
xlu__cfg_set_store(CfgParseContext * ctx,char * name,enum XLU_Operation op,XLU_ConfigValue * val,int lineno)550 void xlu__cfg_set_store(CfgParseContext *ctx, char *name,
551                         enum XLU_Operation op,
552                         XLU_ConfigValue *val, int lineno) {
553     XLU_ConfigSetting *set;
554     int r;
555 
556     if (ctx->err) goto out;
557 
558     assert(name);
559 
560     if (op == XLU_OP_ADDITION) {
561         /* If we have += concatenate with previous value with same name */
562         XLU_ConfigSetting *prev_set = find(ctx->cfg, name);
563         if (prev_set) {
564             r = xlu__cfg_concat_vals(ctx, prev_set->value, val);
565             if (r) {
566                 ctx->err = r;
567                 goto out;
568             }
569         }
570     }
571 
572     set = malloc(sizeof(*set));
573     if (!set) {
574         ctx->err = errno;
575         goto out;
576     }
577     set->name= name;
578     set->value = val;
579     set->op = op;
580     set->lineno= lineno;
581     set->next= ctx->cfg->settings;
582     ctx->cfg->settings= set;
583     return;
584 out:
585     assert(ctx->err);
586     free(name);
587     xlu__cfg_value_free(val);
588 }
589 
xlu__cfgl_strdup(CfgParseContext * ctx,const char * src)590 char *xlu__cfgl_strdup(CfgParseContext *ctx, const char *src) {
591     char *result;
592 
593     if (ctx->err) return 0;
594     result= strdup(src);
595     if (!result) ctx->err= errno;
596     return result;
597 }
598 
xlu__cfgl_dequote(CfgParseContext * ctx,const char * src)599 char *xlu__cfgl_dequote(CfgParseContext *ctx, const char *src) {
600     char *result;
601     const char *p;
602     char *q;
603     int len, c, nc;
604 
605     if (ctx->err) return 0;
606 
607     len= strlen(src);
608     assert(len>=2 && src[0]==src[len-1]);
609 
610     result= malloc(len-1);
611     if (!result) { ctx->err= errno; return 0; }
612 
613     q= result;
614 
615     for (p= src+1;
616          p < src+len-1;
617          ) {
618         c= *p++;
619         if (c=='\\') {
620             assert(p < src+len-1);
621             nc= *p++;
622             if (nc=='"' || nc=='\'' || nc=='\\') {
623                 *q++= nc;
624             } else if (nc=='a') { *q++= '\007';
625             } else if (nc=='b') { *q++= '\010';
626             } else if (nc=='f') { *q++= '\014';
627             } else if (nc=='n') { *q++= '\n';
628             } else if (nc=='r') { *q++= '\r';
629             } else if (nc=='t') { *q++= '\t';
630             } else if (nc=='v') { *q++= '\013';
631             } else if (nc=='x') {
632 
633 #define NUMERIC_CHAR(minlen,maxlen,base,basetext) do{                        \
634                 char numbuf[(maxlen)+1], *ep;                                \
635                 unsigned long val;                                           \
636                                                                              \
637                 strncpy(numbuf,p,(maxlen));                                  \
638                 numbuf[(maxlen)]= 0;                                         \
639                 val= strtoul(numbuf, &ep, (base));                           \
640                 if (ep <= numbuf+(minlen)) {                                 \
641                     xlu__cfgl_lexicalerror(ctx,"invalid digit after"         \
642                          " backslash " basetext "numerical character escape" \
643                          " in quoted string");                               \
644                     ctx->err= EINVAL;                                        \
645                     goto x;                                                  \
646                 }                                                            \
647                 p += (ep - numbuf);                                          \
648  }while(0)
649 
650                 p++;
651                 NUMERIC_CHAR(2,2,16,"hex");
652             } else if (nc>='0' && nc<='7') {
653                 NUMERIC_CHAR(1,3,10,"octal");
654             } else {
655                 xlu__cfgl_lexicalerror(ctx,
656                            "invalid character after backlash in quoted string");
657                 ctx->err= EINVAL;
658                 goto x;
659             }
660             assert(p <= src+len-1);
661         } else {
662             *q++= c;
663         }
664     }
665 
666  x:
667     *q++= 0;
668     return result;
669 }
670 
xlu__cfgl_lexicalerror(CfgParseContext * ctx,char const * msg)671 void xlu__cfgl_lexicalerror(CfgParseContext *ctx, char const *msg) {
672     YYLTYPE loc;
673     loc.first_line= xlu__cfg_yyget_lineno(ctx->scanner);
674     xlu__cfg_yyerror(&loc, ctx, msg);
675     ctx->lexerrlineno= loc.first_line;
676 }
677 
xlu__cfg_yyerror(YYLTYPE * loc,CfgParseContext * ctx,char const * msg)678 void xlu__cfg_yyerror(YYLTYPE *loc, CfgParseContext *ctx, char const *msg) {
679     const char *text, *newline;
680     int len, lineno;
681 
682     lineno= loc->first_line;
683     if (lineno <= ctx->lexerrlineno) return;
684 
685     text= xlu__cfg_yyget_text(ctx->scanner);
686     len= xlu__cfg_yyget_leng(ctx->scanner);
687     newline= "";
688     if (len>0 && text[len-1]=='\n') {
689         len--;
690         lineno--;
691         if (!len) {
692             newline= "<newline>";
693         }
694     }
695     while (len>0 && (text[len-1]=='\t' || text[len-1]==' ')) {
696         len--;
697     }
698 
699     fprintf(ctx->cfg->report,
700             "%s:%d: config parsing error near %s%.*s%s%s: %s\n",
701             ctx->cfg->config_source, lineno,
702             len?"`":"", len, text, len?"'":"", newline,
703             msg);
704     if (!ctx->err) ctx->err= EINVAL;
705 }
706 
707 /*
708  * Local variables:
709  * mode: C
710  * c-basic-offset: 4
711  * indent-tabs-mode: nil
712  * End:
713  */
714