~jamesodhunt/ubuntu/raring/upstart/1.6

« back to all changes in this revision

Viewing changes to nih/config.c

  • Committer: Scott James Remnant
  • Date: 2010-02-04 23:39:59 UTC
  • mfrom: (1182.1.45 upstart)
  • mto: This revision was merged to the branch mainline in revision 1250.
  • Revision ID: scott@netsplit.com-20100204233959-7kajqjnaoh7208ob
Tags: upstream-0.6.5
ImportĀ upstreamĀ versionĀ 0.6.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* libnih
2
 
 *
3
 
 * config.c - configuration file parsing
4
 
 *
5
 
 * Copyright Ā© 2009 Scott James Remnant <scott@netsplit.com>.
6
 
 * Copyright Ā© 2009 Canonical Ltd.
7
 
 *
8
 
 * This program is free software; you can redistribute it and/or modify
9
 
 * it under the terms of the GNU General Public License version 2, as
10
 
 * published by the Free Software Foundation.
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 General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public License along
18
 
 * with this program; if not, write to the Free Software Foundation, Inc.,
19
 
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
 
 */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
# include <config.h>
24
 
#endif /* HAVE_CONFIG_H */
25
 
 
26
 
 
27
 
#include <sys/types.h>
28
 
 
29
 
#include <limits.h>
30
 
#include <string.h>
31
 
 
32
 
#include <nih/macros.h>
33
 
#include <nih/alloc.h>
34
 
#include <nih/string.h>
35
 
#include <nih/file.h>
36
 
#include <nih/config.h>
37
 
#include <nih/logging.h>
38
 
#include <nih/error.h>
39
 
#include <nih/errors.h>
40
 
 
41
 
 
42
 
/* Prototypes for static functions */
43
 
static int              nih_config_block_end  (const char *file, size_t len,
44
 
                                               size_t *lineno, size_t *pos,
45
 
                                               const char *type,
46
 
                                               size_t *endpos)
47
 
        __attribute__ ((warn_unused_result));
48
 
static NihConfigStanza *nih_config_get_stanza (const char *name,
49
 
                                               NihConfigStanza *stanzas);
50
 
 
51
 
 
52
 
/**
53
 
 * nih_config_has_token:
54
 
 * @file: file or string to parse,
55
 
 * @len: length of @file,
56
 
 * @pos: offset within @file,
57
 
 * @lineno: line number.
58
 
 *
59
 
 * Checks the current position in @file to see whether it has a parseable
60
 
 * token at this position; ie. we're not at the end of file, and the
61
 
 * current character is neither a comment or newline character.
62
 
 *
63
 
 * If this returns FALSE, it's normal to call nih_config_skip_comment()
64
 
 * to move to the next parseable point and check again.
65
 
 *
66
 
 * @file may be a memory mapped file, in which case @pos should be given
67
 
 * as the offset within and @len should be the length of the file as a
68
 
 * whole.
69
 
 *
70
 
 * @pos is used as the offset within @file to begin, otherwise the start
71
 
 * is assumed.
72
 
 *
73
 
 * Returns: TRUE if the current character is before the end of file and
74
 
 * is neither a comment or newline, FALSE otherwise.
75
 
 **/
76
 
int
77
 
nih_config_has_token (const char *file,
78
 
                      size_t      len,
79
 
                      size_t     *pos,
80
 
                      size_t     *lineno)
81
 
{
82
 
        size_t p;
83
 
 
84
 
        nih_assert (file != NULL);
85
 
 
86
 
        p = (pos ? *pos : 0);
87
 
        if ((p < len) && (! strchr (NIH_CONFIG_CNL, file[p]))) {
88
 
                return TRUE;
89
 
        } else {
90
 
                return FALSE;
91
 
        }
92
 
}
93
 
 
94
 
 
95
 
/**
96
 
 * nih_config_token:
97
 
 * @file: file or string to parse,
98
 
 * @len: length of @file,
99
 
 * @pos: offset within @file,
100
 
 * @lineno: line number,
101
 
 * @dest: destination to copy to,
102
 
 * @delim: characters to stop on,
103
 
 * @dequote: remove quotes and escapes.
104
 
 * @toklen: pointer to store token length in.
105
 
 *
106
 
 * Parses a single token from @file which is stopped when any character
107
 
 * in @delim is encountered outside of a quoted string and not escaped
108
 
 * using a backslash.  The length of the parsed token is stored in @toklen
109
 
 * if given.
110
 
 *
111
 
 * @file may be a memory mapped file, in which case @pos should be given
112
 
 * as the offset within and @len should be the length of the file as a
113
 
 * whole.  Usually when @dest is given, @file is instead the pointer to
114
 
 * the start of the token and @len is the difference between the start
115
 
 * and end of the token (NOT the return value from this function).
116
 
 *
117
 
 * If @pos is given then it will be used as the offset within @file to
118
 
 * begin (otherwise the start is assumed), and will be updated to point
119
 
 * to @delim or past the end of the file.
120
 
 *
121
 
 * If @lineno is given it will be incremented each time a new line is
122
 
 * discovered in the file.
123
 
 *
124
 
 * To copy the token into another string, collapsing any newlines and
125
 
 * surrounding whitespace to a single space, pass @dest which should be
126
 
 * pre-allocated to the right size (obtained by calling this function
127
 
 * with NULL).
128
 
 *
129
 
 * If you also want quotes to be removed and escaped characters to be
130
 
 * replaced with the character itself, set @dequote to TRUE.
131
 
 *
132
 
 * Returns: zero on success, negative value on raised error.
133
 
 **/
134
 
int
135
 
nih_config_token (const char *file,
136
 
                  size_t      len,
137
 
                  size_t     *pos,
138
 
                  size_t     *lineno,
139
 
                  char       *dest,
140
 
                  const char *delim,
141
 
                  int         dequote,
142
 
                  size_t     *toklen)
143
 
{
144
 
        size_t  p, ws = 0, nlws = 0, qc = 0, i = 0;
145
 
        int     slash = FALSE, quote = 0, nl = FALSE, ret = 0;
146
 
 
147
 
        nih_assert (file != NULL);
148
 
        nih_assert (delim != NULL);
149
 
 
150
 
        /* We keep track of the following:
151
 
         *   slash  whether a \ is in effect
152
 
         *   quote  whether " or ' is in effect (set to which)
153
 
         *   ws     number of consecutive whitespace chars so far
154
 
         *   nlws   number of whitespace/newline chars
155
 
         *   nl     TRUE if we need to copy ws into nlws at first non-WS
156
 
         *   qc     number of quote characters that need removing.
157
 
         */
158
 
 
159
 
        for (p = (pos ? *pos : 0); p < len; p++) {
160
 
                int extra = 0, isq = FALSE;
161
 
 
162
 
                if (slash) {
163
 
                        slash = FALSE;
164
 
 
165
 
                        /* Escaped newline */
166
 
                        if (file[p] == '\n') {
167
 
                                nlws++;
168
 
                                nl = TRUE;
169
 
                                if (lineno)
170
 
                                        (*lineno)++;
171
 
                                continue;
172
 
                        } else if ((file[p] == '\\')
173
 
                                   || strchr (NIH_CONFIG_WS, file[p])) {
174
 
                                extra++;
175
 
                                if (dequote)
176
 
                                        qc++;
177
 
                        } else if (dest) {
178
 
                                dest[i++] = '\\';
179
 
                        }
180
 
                } else if (file[p] == '\\') {
181
 
                        slash = TRUE;
182
 
                        continue;
183
 
                } else if (quote) {
184
 
                        if (file[p] == quote) {
185
 
                                quote = 0;
186
 
                                isq = TRUE;
187
 
                        } else if (file[p] == '\n') {
188
 
                                nl = TRUE;
189
 
                                if (lineno)
190
 
                                        (*lineno)++;
191
 
                                continue;
192
 
                        } else if (strchr (NIH_CONFIG_WS, file[p])) {
193
 
                                ws++;
194
 
                                continue;
195
 
                        }
196
 
                } else if ((file[p] == '\"') || (file[p] == '\'')) {
197
 
                        quote = file[p];
198
 
                        isq = TRUE;
199
 
                } else if (strchr (delim, file[p])) {
200
 
                        break;
201
 
                } else if (strchr (NIH_CONFIG_WS, file[p])) {
202
 
                        ws++;
203
 
                        continue;
204
 
                }
205
 
 
206
 
                if (nl) {
207
 
                        /* Newline is recorded as a single space;
208
 
                         * any surrounding whitespace is lost.
209
 
                         */
210
 
                        nlws += ws;
211
 
                        if (dest)
212
 
                                dest[i++] = ' ';
213
 
                } else if (ws && dest) {
214
 
                        /* Whitespace that we've encountered to date is
215
 
                         * copied as it is.
216
 
                         */
217
 
                        memcpy (dest + i, file + p - ws - extra, ws);
218
 
                        i += ws;
219
 
                }
220
 
 
221
 
                /* Extra characters (the slash) needs to be copied
222
 
                 * unless we're dequoting the string
223
 
                 */
224
 
                if (extra && dest && (! dequote)) {
225
 
                        memcpy (dest + i, file + p - extra, extra);
226
 
                        i += extra;
227
 
                }
228
 
 
229
 
                if (dest && (! (isq && dequote)))
230
 
                        dest[i++] = file[p];
231
 
 
232
 
                if (isq && dequote)
233
 
                        qc++;
234
 
 
235
 
                ws = 0;
236
 
                nl = FALSE;
237
 
                extra = 0;
238
 
        }
239
 
 
240
 
        /* Add the NULL byte */
241
 
        if (dest)
242
 
                dest[i++] = '\0';
243
 
 
244
 
 
245
 
        /* A trailing slash on the end of the file makes no sense. */
246
 
        if (slash) {
247
 
                nih_error_raise (NIH_CONFIG_TRAILING_SLASH,
248
 
                                 _(NIH_CONFIG_TRAILING_SLASH_STR));
249
 
                ret = -1;
250
 
                goto finish;
251
 
        }
252
 
 
253
 
        /* Leaving quotes open is also generally bad. */
254
 
        if (quote) {
255
 
                nih_error_raise (NIH_CONFIG_UNTERMINATED_QUOTE,
256
 
                                 _(NIH_CONFIG_UNTERMINATED_QUOTE_STR));
257
 
                ret = -1;
258
 
                goto finish;
259
 
        }
260
 
 
261
 
 
262
 
        /* The token length we return is the length of the token with any
263
 
         * newlines and surrounding whitespace converted to a single
264
 
         * character and any trailing whitespace removed.
265
 
         *
266
 
         * The actual end of the text read is returned in *pos.
267
 
         */
268
 
        if (toklen)
269
 
                *toklen = p - (pos ? *pos : 0) - ws - nlws - qc;
270
 
 
271
 
finish:
272
 
        if (pos)
273
 
                *pos = p;
274
 
 
275
 
        return ret;
276
 
}
277
 
 
278
 
/**
279
 
 * nih_config_next_token:
280
 
 * @parent: parent object for returned token,
281
 
 * @file: file or string to parse,
282
 
 * @len: length of @file,
283
 
 * @pos: offset within @file,
284
 
 * @lineno: line number,
285
 
 * @delim: characters to stop on,
286
 
 * @dequote: remove quotes and escapes.
287
 
 *
288
 
 * Extracts a single token from @file which is stopped when any character
289
 
 * in @delim is encountered outside of a quoted string and not escaped
290
 
 * using a backslash.  If @delim contains any whitespace character, then
291
 
 * all whitespace after the token is also consumed, but not returned,
292
 
 * including that with escaped newlines within it.
293
 
 *
294
 
 * @file may be a memory mapped file, in which case @pos should be given
295
 
 * as the offset within and @len should be the length of the file as a
296
 
 * whole.
297
 
 *
298
 
 * If @pos is given then it will be used as the offset within @file to
299
 
 * begin (otherwise the start is assumed), and will be updated to point
300
 
 * to @delim or past the end of the file.
301
 
 *
302
 
 * If @lineno is given it will be incremented each time a new line is
303
 
 * discovered in the file.
304
 
 *
305
 
 * If you also want quotes to be removed and escaped characters to be
306
 
 * replaced with the character itself, set @dequote to TRUE.
307
 
 *
308
 
 * If @parent is not NULL, it should be a pointer to another object which
309
 
 * will be used as a parent for the returned token.  When all parents
310
 
 * of the returned token are freed, the returned token will also be
311
 
 * freed.
312
 
 *
313
 
 * Returns: the token found or NULL on raised error.
314
 
 **/
315
 
char *
316
 
nih_config_next_token (const void *parent,
317
 
                       const char *file,
318
 
                       size_t      len,
319
 
                       size_t     *pos,
320
 
                       size_t     *lineno,
321
 
                       const char *delim,
322
 
                       int         dequote)
323
 
{
324
 
        size_t  p, arg_start, arg_len, arg_end;
325
 
        char   *arg = NULL;
326
 
 
327
 
        nih_assert (file != NULL);
328
 
 
329
 
        p = (pos ? *pos : 0);
330
 
        arg_start = p;
331
 
        if (nih_config_token (file, len, &p, lineno, NULL, delim, dequote,
332
 
                              &arg_len) < 0)
333
 
                goto finish;
334
 
 
335
 
        arg_end = p;
336
 
        if (! arg_len) {
337
 
                nih_error_raise (NIH_CONFIG_EXPECTED_TOKEN,
338
 
                                 _(NIH_CONFIG_EXPECTED_TOKEN_STR));
339
 
                goto finish;
340
 
        }
341
 
 
342
 
        nih_config_skip_whitespace (file, len, &p, lineno);
343
 
 
344
 
        /* Copy in the new token */
345
 
        arg = nih_alloc (parent, arg_len + 1);
346
 
        if (! arg)
347
 
                nih_return_system_error (NULL);
348
 
 
349
 
        if (nih_config_token (file + arg_start, arg_end - arg_start, NULL,
350
 
                              NULL, arg, delim, dequote, NULL) < 0)
351
 
                goto finish;
352
 
 
353
 
finish:
354
 
        if (pos)
355
 
                *pos = p;
356
 
 
357
 
        return arg;
358
 
}
359
 
 
360
 
/**
361
 
 * nih_config_next_arg:
362
 
 * @parent: parent object for returned argument,
363
 
 * @file: file or string to parse,
364
 
 * @len: length of @file,
365
 
 * @pos: offset within @file,
366
 
 * @lineno: line number.
367
 
 *
368
 
 * Extracts a single argument from @file, a dequoted token that is stopped
369
 
 * on any comment, space or newline character that is not quoted or escaped
370
 
 * with a backslash.  Any whitespace after the argument is also consumed,
371
 
 * but not returned, including that with escaped newlines within it.
372
 
 *
373
 
 * @file may be a memory mapped file, in which case @pos should be given
374
 
 * as the offset within and @len should be the length of the file as a
375
 
 * whole.
376
 
 *
377
 
 * If @pos is given then it will be used as the offset within @file to
378
 
 * begin (otherwise the start is assumed), and will be updated to point
379
 
 * to @delim or past the end of the file.
380
 
 *
381
 
 * If @lineno is given it will be incremented each time a new line is
382
 
 * discovered in the file.
383
 
 *
384
 
 * If @parent is not NULL, it should be a pointer to another object which
385
 
 * will be used as a parent for the returned argument.  When all parents
386
 
 * of the returned argument are freed, the returned argument will also be
387
 
 * freed.
388
 
 *
389
 
 * Returns: the argument found or NULL on raised error.
390
 
 **/
391
 
char *
392
 
nih_config_next_arg (const void *parent,
393
 
                     const char *file,
394
 
                     size_t      len,
395
 
                     size_t     *pos,
396
 
                     size_t     *lineno)
397
 
{
398
 
        nih_assert (file != NULL);
399
 
 
400
 
        return nih_config_next_token (parent, file, len, pos, lineno,
401
 
                                      NIH_CONFIG_CNLWS, TRUE);
402
 
}
403
 
 
404
 
/**
405
 
 * nih_config_next_line:
406
 
 * @file: file or string to parse,
407
 
 * @len: length of @file,
408
 
 * @pos: offset within @file,
409
 
 * @lineno: line number.
410
 
 *
411
 
 * Skips to the end of the current line in @file, ignoring any tokens,
412
 
 * comments, etc. along the way.  If you want to ensure that no arguments
413
 
 * are missed, use nih_config_skip_comment() instead.
414
 
 *
415
 
 * @file may be a memory mapped file, in which case @pos should be given
416
 
 * as the offset within and @len should be the length of the file as a
417
 
 * whole.
418
 
 *
419
 
 * @pos is used as the offset within @file to begin, and will be updated
420
 
 * to point to past the end of the line or file.
421
 
 *
422
 
 * If @lineno is given it will be incremented each time a new line is
423
 
 * discovered in the file.
424
 
 **/
425
 
void
426
 
nih_config_next_line (const char *file,
427
 
                      size_t      len,
428
 
                      size_t     *pos,
429
 
                      size_t     *lineno)
430
 
{
431
 
        nih_assert (file != NULL);
432
 
        nih_assert (pos != NULL);
433
 
 
434
 
        /* Spool forwards until the end of the line */
435
 
        while ((*pos < len) && (file[*pos] != '\n'))
436
 
                (*pos)++;
437
 
 
438
 
        /* Step over it */
439
 
        if (*pos < len) {
440
 
                if (lineno)
441
 
                        (*lineno)++;
442
 
                (*pos)++;
443
 
        }
444
 
}
445
 
 
446
 
 
447
 
/**
448
 
 * nih_config_skip_whitespace:
449
 
 * @file: file or string to parse,
450
 
 * @len: length of @file,
451
 
 * @pos: offset within @file,
452
 
 * @lineno: line number.
453
 
 *
454
 
 * Skips an amount of whitespace and finds either the next token or the end
455
 
 * of the current line in @file.  Escaped newlines within the whitespace
456
 
 * are treated as whitespace.
457
 
 *
458
 
 * @file may be a memory mapped file, in which case @pos should be given
459
 
 * as the offset within and @len should be the length of the file as a
460
 
 * whole.
461
 
 *
462
 
 * @pos is used as the offset within @file to begin, and will be updated
463
 
 * to point to past the end of the line or file.
464
 
 *
465
 
 * If @lineno is given it will be incremented each time a new line is
466
 
 * discovered in the file.
467
 
 **/
468
 
void
469
 
nih_config_skip_whitespace (const char *file,
470
 
                            size_t      len,
471
 
                            size_t     *pos,
472
 
                            size_t     *lineno)
473
 
{
474
 
        nih_assert (file != NULL);
475
 
        nih_assert (pos != NULL);
476
 
 
477
 
        /* Skip any amount of whitespace between them, we also need to
478
 
         * detect an escaped newline here.
479
 
         */
480
 
        while (*pos < len) {
481
 
                if (file[*pos] == '\\') {
482
 
                        /* Escape character, only continue scanning if
483
 
                         * the next character is newline
484
 
                         */
485
 
                        if ((len - *pos > 1) && (file[*pos + 1] == '\n')) {
486
 
                                (*pos)++;
487
 
                        } else {
488
 
                                break;
489
 
                        }
490
 
                } else if (! strchr (NIH_CONFIG_WS, file[*pos])) {
491
 
                        break;
492
 
                }
493
 
 
494
 
                if (file[*pos] == '\n')
495
 
                        if (lineno)
496
 
                                (*lineno)++;
497
 
 
498
 
                /* Whitespace characer */
499
 
                (*pos)++;
500
 
        }
501
 
}
502
 
 
503
 
/**
504
 
 * nih_config_skip_comment:
505
 
 * @file: file or string to parse,
506
 
 * @len: length of @file,
507
 
 * @pos: offset within @file,
508
 
 * @lineno: line number.
509
 
 *
510
 
 * Skips a comment and finds the end of the current line in @file.  If the
511
 
 * current position does not point to the end of a line, or a comment,
512
 
 * then an error is raised.
513
 
 *
514
 
 * @file may be a memory mapped file, in which case @pos should be given
515
 
 * as the offset within and @len should be the length of the file as a
516
 
 * whole.
517
 
 *
518
 
 * @pos is used as the offset within @file to begin, and will be updated
519
 
 * to point to past the end of the line or file.
520
 
 *
521
 
 * If @lineno is given it will be incremented each time a new line is
522
 
 * discovered in the file.
523
 
 *
524
 
 * Returns: zero on success, negative value on raised error.
525
 
 **/
526
 
int
527
 
nih_config_skip_comment (const char *file,
528
 
                         size_t      len,
529
 
                         size_t     *pos,
530
 
                         size_t     *lineno)
531
 
{
532
 
        nih_assert (file != NULL);
533
 
        nih_assert (pos != NULL);
534
 
 
535
 
        if (nih_config_has_token (file, len, pos, lineno)) {
536
 
                nih_error_raise (NIH_CONFIG_UNEXPECTED_TOKEN,
537
 
                                 _(NIH_CONFIG_UNEXPECTED_TOKEN_STR));
538
 
                return -1;
539
 
        }
540
 
 
541
 
        nih_config_next_line (file, len, pos, lineno);
542
 
 
543
 
        return 0;
544
 
}
545
 
 
546
 
 
547
 
/**
548
 
 * nih_config_parse_args:
549
 
 * @parent: parent object for returned array,
550
 
 * @file: file or string to parse,
551
 
 * @len: length of @file,
552
 
 * @pos: offset within @file,
553
 
 * @lineno: line number.
554
 
 *
555
 
 * Extracts a list of arguments from @file, each argument is separated
556
 
 * by whitespace and parsing is stopped when a newline is encountered
557
 
 * outside of a quoted string and not escaped using a backslash.
558
 
 *
559
 
 * @file may be a memory mapped file, in which case @pos should be given
560
 
 * as the offset within and @len should be the length of the file as a
561
 
 * whole.
562
 
 *
563
 
 * If @pos is given then it will be used as the offset within @file to
564
 
 * begin (otherwise the start is assumed), and will be updated to point
565
 
 * past the end of the line or the end of the file.
566
 
 *
567
 
 * If @lineno is given it will be incremented each time a new line is
568
 
 * discovered in the file.
569
 
 *
570
 
 * The arguments are returned as a NULL-terminated array, with each argument
571
 
 * dequoted before being returned.
572
 
 *
573
 
 * If @parent is not NULL, it should be a pointer to another object which
574
 
 * will be used as a parent for the returned array.  When all parents
575
 
 * of the returned array are freed, the returned array will also be
576
 
 * freed.
577
 
 *
578
 
 * Returns: the list of arguments found or NULL on raised error.
579
 
 **/
580
 
char **
581
 
nih_config_parse_args (const void *parent,
582
 
                       const char *file,
583
 
                       size_t      len,
584
 
                       size_t     *pos,
585
 
                       size_t     *lineno)
586
 
{
587
 
        char   **args;
588
 
        size_t   p, nargs;
589
 
 
590
 
        nih_assert (file != NULL);
591
 
 
592
 
        /* Begin with an empty array */
593
 
        nargs = 0;
594
 
        args = nih_str_array_new (parent);
595
 
        if (! args)
596
 
                nih_return_system_error (NULL);
597
 
 
598
 
        /* Loop through the arguments until we hit a comment or newline */
599
 
        p = (pos ? *pos : 0);
600
 
        while (nih_config_has_token (file, len, &p, lineno)) {
601
 
                char *arg;
602
 
 
603
 
                arg = nih_config_next_arg (args, file, len, &p, lineno);
604
 
                if (! arg) {
605
 
                        nih_free (args);
606
 
                        args = NULL;
607
 
                        goto finish;
608
 
                }
609
 
 
610
 
                if (! nih_str_array_addp (&args, parent, &nargs, arg)) {
611
 
                        nih_error_raise_system ();
612
 
                        nih_free (args);
613
 
                        return NULL;
614
 
                }
615
 
        }
616
 
 
617
 
        /* nih_config_has_token has returned FALSE, we must be either past
618
 
         * the end of the file, or at a comment or newline.
619
 
         */
620
 
        if (nih_config_skip_comment (file, len, &p, lineno) < 0)
621
 
                nih_assert_not_reached ();
622
 
 
623
 
finish:
624
 
        if (pos)
625
 
                *pos = p;
626
 
 
627
 
        return args;
628
 
}
629
 
 
630
 
/**
631
 
 * nih_config_parse_command:
632
 
 * @parent: parent object for returned string,
633
 
 * @file: file or string to parse,
634
 
 * @len: length of @file,
635
 
 * @pos: offset within @file,
636
 
 * @lineno: line number,
637
 
 *
638
 
 * Extracts a command and its arguments from @file, stopping when a
639
 
 * newline is encountered outside of a quoted string and not escaped
640
 
 * using a blackslash.
641
 
 *
642
 
 * @file may be a memory mapped file, in which case @pos should be given
643
 
 * as the offset within and @len should be the length of the file as a
644
 
 * whole.
645
 
 *
646
 
 * If @pos is given then it will be used as the offset within @file to
647
 
 * begin (otherwise the start is assumed), and will be updated to point
648
 
 * past the end of the line or the end of the file.
649
 
 *
650
 
 * If @lineno is given it will be incremented each time a new line is
651
 
 * discovered in the file.
652
 
 *
653
 
 * The command is returned as a string allocated with nih_alloc().
654
 
 *
655
 
 * If @parent is not NULL, it should be a pointer to another object which
656
 
 * will be used as a parent for the returned string.  When all parents
657
 
 * of the returned string are freed, the returned string will also be
658
 
 * freed.
659
 
 *
660
 
 * Returns: the command found or NULL on raised error.
661
 
 **/
662
 
char *
663
 
nih_config_parse_command (const void *parent,
664
 
                          const char *file,
665
 
                          size_t      len,
666
 
                          size_t     *pos,
667
 
                          size_t     *lineno)
668
 
{
669
 
        char   *cmd = NULL;
670
 
        size_t  p, cmd_start, cmd_len, cmd_end;
671
 
 
672
 
        nih_assert (file != NULL);
673
 
 
674
 
        /* Find the length of string up to the first unescaped comment
675
 
         * or newline.
676
 
         */
677
 
        p = (pos ? *pos : 0);
678
 
        cmd_start = p;
679
 
        if (nih_config_token (file, len, &p, lineno, NULL,
680
 
                              NIH_CONFIG_CNL, FALSE, &cmd_len) < 0)
681
 
                goto finish;
682
 
 
683
 
        cmd_end = p;
684
 
 
685
 
        /* nih_config_token will eat up to the end of the file, a comment
686
 
         * or a newline; so this must always succeed.
687
 
         */
688
 
        if (nih_config_skip_comment (file, len, &p, lineno) < 0)
689
 
                nih_assert_not_reached ();
690
 
 
691
 
        /* Now copy the string into the destination. */
692
 
        cmd = nih_alloc (parent, cmd_len + 1);
693
 
        if (! cmd)
694
 
                nih_return_system_error (NULL);
695
 
 
696
 
        if (nih_config_token (file + cmd_start, cmd_end - cmd_start, NULL,
697
 
                              NULL, cmd, NIH_CONFIG_CNL, FALSE, NULL) < 0)
698
 
                goto finish;
699
 
 
700
 
finish:
701
 
        if (pos)
702
 
                *pos = p;
703
 
 
704
 
        return cmd;
705
 
}
706
 
 
707
 
 
708
 
/**
709
 
 * nih_config_parse_block:
710
 
 * @parent: parent object for returned string,
711
 
 * @file: file or string to parse,
712
 
 * @len: length of @file,
713
 
 * @pos: offset within @file,
714
 
 * @lineno: line number,
715
 
 * @type: block identifier.
716
 
 *
717
 
 * Extracts a block of text from @line, stopping when the pharse "end @type"
718
 
 * is encountered without any quotes or blackslash escaping within it.
719
 
 *
720
 
 * @file may be a memory mapped file, in which case @pos should be given
721
 
 * as the offset within and @len should be the length of the file as a
722
 
 * whole.
723
 
 *
724
 
 * If @pos is given then it will be used as the offset within @file to
725
 
 * begin (otherwise the start is assumed), and will be updated to point
726
 
 * past the end of the block or the end of the file.
727
 
 *
728
 
 * Either @file or @pos should point to the start of the block, after the
729
 
 * opening stanza, rather than the start of the stanza that opens it.
730
 
 *
731
 
 * If @lineno is given it will be incremented each time a new line is
732
 
 * discovered in the file.
733
 
 *
734
 
 * The block is returned as a string allocated with nih_alloc().
735
 
 *
736
 
 * If @parent is not NULL, it should be a pointer to another object which
737
 
 * will be used as a parent for the returned string.  When all parents
738
 
 * of the returned string are freed, the returned string will also be
739
 
 * freed.
740
 
 *
741
 
 * Returns: the text contained within the block or NULL on raised error.
742
 
 **/
743
 
char *
744
 
nih_config_parse_block (const void *parent,
745
 
                        const char *file,
746
 
                        size_t      len,
747
 
                        size_t     *pos,
748
 
                        size_t     *lineno,
749
 
                        const char *type)
750
 
{
751
 
        char    *block = NULL;
752
 
        size_t   p, pp, sh_start, sh_len, sh_end, ws;
753
 
        int      lines;
754
 
 
755
 
        nih_assert (file != NULL);
756
 
        nih_assert (type != NULL);
757
 
 
758
 
        /* We need to find the end of the block which is a line that looks
759
 
         * like:
760
 
         *
761
 
         *      WS? end WS @type CNLWS?
762
 
         *
763
 
         * Just to make things more difficult for ourselves, we work out the
764
 
         * common whitespace on the start of the block lines and remember
765
 
         * not to copy those out later
766
 
         */
767
 
        p = (pos ? *pos : 0);
768
 
        sh_start = p;
769
 
        sh_end = 0;
770
 
        ws = 0;
771
 
        lines = 0;
772
 
 
773
 
        while (! nih_config_block_end (file, len, &p, lineno, type, &sh_end)) {
774
 
                size_t line_start;
775
 
 
776
 
                lines++;
777
 
                line_start = p;
778
 
 
779
 
                if (lines == 1) {
780
 
                        /* Count whitespace on the first line */
781
 
                        while ((p < len) && strchr (NIH_CONFIG_WS, file[p]))
782
 
                                p++;
783
 
 
784
 
                        ws = p - line_start;
785
 
                } else {
786
 
                        /* Compare how much whitespace matches the
787
 
                         * first line; and decrease the count if it's
788
 
                         * not as much.
789
 
                         */
790
 
                        while ((p < len) && (p - line_start < ws)
791
 
                               && (file[sh_start + p - line_start] == file[p]))
792
 
                                p++;
793
 
 
794
 
                        if (p - line_start < ws)
795
 
                                ws = p - line_start;
796
 
                }
797
 
 
798
 
                nih_config_next_line (file, len, &p, lineno);
799
 
 
800
 
                if (p >= len) {
801
 
                        nih_error_raise (NIH_CONFIG_UNTERMINATED_BLOCK,
802
 
                                         _(NIH_CONFIG_UNTERMINATED_BLOCK_STR));
803
 
                        goto finish;
804
 
                }
805
 
        }
806
 
 
807
 
        /* Copy the fragment into a string, removing common whitespace from
808
 
         * the start.  We can be less strict here because we already know
809
 
         * the contents, etc.
810
 
         */
811
 
        sh_len = sh_end - sh_start - (ws * lines);
812
 
        block = nih_alloc (parent, sh_len + 1);
813
 
        if (! block)
814
 
                nih_return_system_error (NULL);
815
 
 
816
 
        block[0] = '\0';
817
 
 
818
 
        pp = sh_start;
819
 
        while (pp < sh_end) {
820
 
                size_t line_start;
821
 
 
822
 
                pp += ws;
823
 
                line_start = pp;
824
 
 
825
 
                while (file[pp++] != '\n')
826
 
                        ;
827
 
 
828
 
                strncat (block, file + line_start, pp - line_start);
829
 
        }
830
 
 
831
 
finish:
832
 
        if (pos)
833
 
                *pos = p;
834
 
 
835
 
        return block;
836
 
}
837
 
 
838
 
/**
839
 
 * nih_config_skip_block:
840
 
 * @file: file or string to parse,
841
 
 * @len: length of @file,
842
 
 * @pos: offset within @file,
843
 
 * @lineno: line number,
844
 
 * @type: block identifier,
845
 
 * @endpos: pointer to end of block.
846
 
 *
847
 
 * Skips over a block of text from @file, stopping when the phrase
848
 
 * "end @type" is encountered without any quotes or blackslash escaping
849
 
 * within it.
850
 
 *
851
 
 * @file may be a memory mapped file, in which case @pos should be given
852
 
 * as the offset within and @len should be the length of the file as a
853
 
 * whole.
854
 
 *
855
 
 * If @pos is given then it will be used as the offset within @file to
856
 
 * begin (otherwise the start is assumed), and will be updated to point
857
 
 * past the end of the block and block marker or the end of the file.
858
 
 *
859
 
 * Either @file or @pos should point to the start of the block, after the
860
 
 * opening stanza, rather than the start of the stanza that opens it.
861
 
 *
862
 
 * If @lineno is given it will be incremented each time a new line is
863
 
 * discovered in the file.
864
 
 *
865
 
 * @endpos will be set to the end of the block and the start of the block
866
 
 * marker, this is useful for determining the length of the block skipped,
867
 
 * to parse it for example.
868
 
 *
869
 
 * Returns: zero on success, negative value on raised error.
870
 
 **/
871
 
int
872
 
nih_config_skip_block (const char *file,
873
 
                       size_t      len,
874
 
                       size_t     *pos,
875
 
                       size_t     *lineno,
876
 
                       const char *type,
877
 
                       size_t     *endpos)
878
 
{
879
 
        size_t p;
880
 
        int    ret = 0;
881
 
 
882
 
        nih_assert (file != NULL);
883
 
        nih_assert (type != NULL);
884
 
 
885
 
        p = (pos ? *pos : 0);
886
 
 
887
 
        while (! nih_config_block_end (file, len, &p, lineno, type, endpos)) {
888
 
                nih_config_next_line (file, len, &p, lineno);
889
 
 
890
 
                if (p >= len) {
891
 
                        nih_error_raise (NIH_CONFIG_UNTERMINATED_BLOCK,
892
 
                                         _(NIH_CONFIG_UNTERMINATED_BLOCK_STR));
893
 
                        ret = -1;
894
 
                        goto finish;
895
 
                }
896
 
        }
897
 
 
898
 
finish:
899
 
        if (pos)
900
 
                *pos = p;
901
 
 
902
 
        return ret;
903
 
}
904
 
 
905
 
/**
906
 
 * nih_config_block_end:
907
 
 * @file: file or string to parse,
908
 
 * @len: length of @file,
909
 
 * @pos: offset within @file,
910
 
 * @lineno: line number,
911
 
 * @type: block identifier,
912
 
 * @endpos: pointer to end of block.
913
 
 *
914
 
 * Determines whether the current line contains an end of block marker,
915
 
 * and if so, sets @endpos to the end  of the block.
916
 
 *
917
 
 * @file may be a memory mapped file, in which case @pos should be given
918
 
 * as the offset within and @len should be the length of the file.  @pos
919
 
 * will be updated to point past the end of the block and the end block
920
 
 * marker or the end of the file.
921
 
 *
922
 
 * @lineno will be incremented each time a new line is discovered.
923
 
 *
924
 
 * Returns: TRUE if at the end of the block, FALSE otherwise.
925
 
 **/
926
 
static int
927
 
nih_config_block_end (const char *file,
928
 
                      size_t      len,
929
 
                      size_t     *pos,
930
 
                      size_t     *lineno,
931
 
                      const char *type,
932
 
                      size_t     *endpos)
933
 
{
934
 
        size_t p;
935
 
 
936
 
        nih_assert (file != NULL);
937
 
        nih_assert (pos != NULL);
938
 
        nih_assert (type != NULL);
939
 
 
940
 
        p = *pos;
941
 
 
942
 
        /* Skip initial whitespace */
943
 
        while ((p < len) && strchr (NIH_CONFIG_WS, file[p]))
944
 
                p++;
945
 
 
946
 
        /* Check the first word (check we have at least 4 chars because of
947
 
         * the need for whitespace immediately after)
948
 
         */
949
 
        if ((len - p < 4) || strncmp (file + p, "end", 3))
950
 
                return FALSE;
951
 
 
952
 
        /* Must be whitespace after */
953
 
        if (! strchr (NIH_CONFIG_WS, file[p + 3]))
954
 
                return FALSE;
955
 
 
956
 
        /* Find the second word */
957
 
        p += 3;
958
 
        while ((p < len) && strchr (NIH_CONFIG_WS, file[p]))
959
 
                p++;
960
 
 
961
 
        /* Check the second word */
962
 
        if ((len - p < strlen (type))
963
 
            || strncmp (file + p, type, strlen (type)))
964
 
                return FALSE;
965
 
 
966
 
        /* May be followed by whitespace */
967
 
        p += strlen (type);
968
 
        while ((p < len) && strchr (NIH_CONFIG_WS, file[p]))
969
 
                p++;
970
 
 
971
 
        /* May be a comment, in which case eat up to the newline
972
 
         */
973
 
        if ((p < len) && (file[p] == '#')) {
974
 
                while ((p < len) && (file[p] != '\n'))
975
 
                        p++;
976
 
        }
977
 
 
978
 
        /* Should be end of string, or a newline */
979
 
        if ((p < len) && (file[p] != '\n'))
980
 
                return FALSE;
981
 
 
982
 
        /* Point past the new line */
983
 
        if (p < len) {
984
 
                if (lineno)
985
 
                        (*lineno)++;
986
 
                p++;
987
 
        }
988
 
 
989
 
        /* Set endpos to the beginning of the line (which is the end of the
990
 
         * script) but update pos to point past this line.
991
 
         */
992
 
        if (endpos)
993
 
                *endpos = *pos;
994
 
        *pos = p;
995
 
 
996
 
        return TRUE;
997
 
}
998
 
 
999
 
 
1000
 
/**
1001
 
 * nih_config_get_stanza:
1002
 
 * @name: name of stanza,
1003
 
 * @stanzas: table of stanza handlers.
1004
 
 *
1005
 
 * Locates the handler for the @name stanza in the @stanzas table.  The
1006
 
 * last entry in the table should have NULL for both the name and handler
1007
 
 * function pointers.
1008
 
 *
1009
 
 * If any entry exists with the stanza name "", this is returned instead
1010
 
 * of NULL if no specific entry is found.
1011
 
 *
1012
 
 * Returns: stanza found or NULL if no handler for @name.
1013
 
 **/
1014
 
static NihConfigStanza *
1015
 
nih_config_get_stanza (const char      *name,
1016
 
                       NihConfigStanza *stanzas)
1017
 
{
1018
 
        NihConfigStanza *stanza, *catch = NULL;
1019
 
 
1020
 
        for (stanza = stanzas; (stanza->name && stanza->handler); stanza++) {
1021
 
                if (! strlen (stanza->name))
1022
 
                        catch = stanza;
1023
 
 
1024
 
                if (! strcmp (stanza->name, name))
1025
 
                        return stanza;
1026
 
        }
1027
 
 
1028
 
        return catch;
1029
 
}
1030
 
 
1031
 
/**
1032
 
 * nih_config_parse_stanza:
1033
 
 * @file: file or string to parse,
1034
 
 * @len: length of @file,
1035
 
 * @pos: offset within @file,
1036
 
 * @lineno: line number,
1037
 
 * @stanzas: table of stanza handlers,
1038
 
 * @data: pointer to pass to stanza handler.
1039
 
 *
1040
 
 * Extracts a configuration stanza from @file and calls the handler
1041
 
 * function for that stanza found in the @stanzas table to handle the
1042
 
 * rest of the line from thereon in.
1043
 
 *
1044
 
 * @file may be a memory mapped file, in which case @pos should be given
1045
 
 * as the offset within and @len should be the length of the file as a
1046
 
 * whole.
1047
 
 *
1048
 
 * If @pos is given then it will be used as the offset within @file to
1049
 
 * begin (otherwise the start is assumed), and will be updated to point
1050
 
 * to @delim or past the end of the file.
1051
 
 *
1052
 
 * If @lineno is given it will be incremented each time a new line is
1053
 
 * discovered in the file.
1054
 
 *
1055
 
 * Returns: zero on success or negative value on raised error.
1056
 
 **/
1057
 
int
1058
 
nih_config_parse_stanza (const char      *file,
1059
 
                         size_t           len,
1060
 
                         size_t          *pos,
1061
 
                         size_t          *lineno,
1062
 
                         NihConfigStanza *stanzas,
1063
 
                         void            *data)
1064
 
{
1065
 
        NihConfigStanza *stanza;
1066
 
        nih_local char  *name = NULL;
1067
 
        size_t           p;
1068
 
        int              ret = -1;
1069
 
 
1070
 
        nih_assert (file != NULL);
1071
 
        nih_assert (stanzas != NULL);
1072
 
 
1073
 
        p = (pos ? *pos : 0);
1074
 
 
1075
 
        /* Get the next dequoted argument from the file */
1076
 
        name = nih_config_next_token (NULL, file, len, &p, lineno,
1077
 
                                      NIH_CONFIG_CNLWS, FALSE);
1078
 
        if (! name)
1079
 
                goto finish;
1080
 
 
1081
 
        /* Lookup the stanza for it */
1082
 
        stanza = nih_config_get_stanza (name, stanzas);
1083
 
        if (! stanza)
1084
 
                nih_return_error (-1, NIH_CONFIG_UNKNOWN_STANZA,
1085
 
                                  _(NIH_CONFIG_UNKNOWN_STANZA_STR));
1086
 
 
1087
 
        ret = stanza->handler (data, stanza, file, len, &p, lineno);
1088
 
 
1089
 
finish:
1090
 
        if (pos)
1091
 
                *pos = p;
1092
 
 
1093
 
        return ret;
1094
 
}
1095
 
 
1096
 
 
1097
 
/**
1098
 
 * nih_config_parse_file:
1099
 
 * @file: file or string to parse,
1100
 
 * @len: length of @file,
1101
 
 * @pos: offset within @file,
1102
 
 * @lineno: line number,
1103
 
 * @stanzas: table of stanza handlers,
1104
 
 * @data: pointer to pass to stanza handler.
1105
 
 *
1106
 
 * Parses configuration file lines from @file, skipping initial whitespace,
1107
 
 * blank lines and comments while calling nih_config_parse_stanza() for
1108
 
 * anything else.
1109
 
 *
1110
 
 * @file may be a memory mapped file, in which case @pos should be given
1111
 
 * as the offset within and @len should be the length of the file as a
1112
 
 * whole.
1113
 
 *
1114
 
 * If @pos is given then it will be used as the offset within @file to
1115
 
 * begin (otherwise the start is assumed), and will be updated to point
1116
 
 * to @delim or past the end of the file.
1117
 
 *
1118
 
 * If @lineno is given it will be incremented each time a new line is
1119
 
 * discovered in the file.
1120
 
 *
1121
 
 * Returns: zero on success, negative value on raised error.
1122
 
 **/
1123
 
int
1124
 
nih_config_parse_file (const char      *file,
1125
 
                       size_t           len,
1126
 
                       size_t          *pos,
1127
 
                       size_t          *lineno,
1128
 
                       NihConfigStanza *stanzas,
1129
 
                       void            *data)
1130
 
{
1131
 
        int    ret = -1;
1132
 
        size_t p;
1133
 
 
1134
 
        nih_assert (file != NULL);
1135
 
        nih_assert (stanzas != NULL);
1136
 
 
1137
 
        p = (pos ? *pos : 0);
1138
 
 
1139
 
        while (p < len) {
1140
 
                /* Skip initial whitespace */
1141
 
                while ((p < len) && strchr (NIH_CONFIG_WS, file[p]))
1142
 
                        p++;
1143
 
 
1144
 
                /* Skip lines with only comments in them; because has_token
1145
 
                 * returns FALSE we know we're either past the end of the
1146
 
                 * file, at a comment, or a newline.
1147
 
                 */
1148
 
                if (! nih_config_has_token (file, len, &p, lineno)) {
1149
 
                        if (nih_config_skip_comment (file, len,
1150
 
                                                     &p, lineno) < 0)
1151
 
                                nih_assert_not_reached ();
1152
 
 
1153
 
                        continue;
1154
 
                }
1155
 
 
1156
 
                /* Must have a stanza, parse it */
1157
 
                if (nih_config_parse_stanza (file, len, &p, lineno,
1158
 
                                                     stanzas, data) < 0)
1159
 
                        goto finish;
1160
 
        }
1161
 
 
1162
 
        ret = 0;
1163
 
 
1164
 
finish:
1165
 
        if (pos)
1166
 
                *pos = p;
1167
 
 
1168
 
        return ret;
1169
 
}
1170
 
 
1171
 
/**
1172
 
 * nih_config_parse:
1173
 
 * @filename: name of file to parse,
1174
 
 * @pos: offset within @file,
1175
 
 * @lineno: line number,
1176
 
 * @stanzas: table of stanza handlers,
1177
 
 * @data: pointer to pass to stanza handler.
1178
 
 *
1179
 
 * Reads @filename into memory and them parses configuration lines from it
1180
 
 * using nih_config_parse_file().
1181
 
 *
1182
 
 * If @pos is given then it will be used as the offset within @file to
1183
 
 * begin (otherwise the start is assumed), and will be updated to point
1184
 
 * to @delim or past the end of the file.
1185
 
 *
1186
 
 * If @lineno is given it will be incremented each time a new line is
1187
 
 * discovered in the file.
1188
 
 *
1189
 
 * Returns: zero on success, negative value on raised error.
1190
 
 **/
1191
 
int
1192
 
nih_config_parse (const char      *filename,
1193
 
                  size_t          *pos,
1194
 
                  size_t          *lineno,
1195
 
                  NihConfigStanza *stanzas,
1196
 
                  void            *data)
1197
 
{
1198
 
        nih_local char *file = NULL;
1199
 
        size_t          len;
1200
 
        int             ret;
1201
 
 
1202
 
        nih_assert (filename != NULL);
1203
 
 
1204
 
        file = nih_file_read (NULL, filename, &len);
1205
 
        if (! file)
1206
 
                return -1;
1207
 
 
1208
 
        if (lineno)
1209
 
                *lineno = 1;
1210
 
 
1211
 
        ret = nih_config_parse_file (file, len, pos, lineno, stanzas, data);
1212
 
 
1213
 
        return ret;
1214
 
}