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