Coverage Report

Created: 2023-06-07 06:22

/src/bind9/lib/isc/lex.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
/*! \file */
15
16
#include <ctype.h>
17
#include <errno.h>
18
#include <inttypes.h>
19
#include <stdbool.h>
20
#include <stdlib.h>
21
22
#include <isc/buffer.h>
23
#include <isc/file.h>
24
#include <isc/lex.h>
25
#include <isc/mem.h>
26
#include <isc/parseint.h>
27
#include <isc/stdio.h>
28
#include <isc/string.h>
29
#include <isc/util.h>
30
31
#include "errno2result.h"
32
33
typedef struct inputsource {
34
  isc_result_t result;
35
  bool is_file;
36
  bool need_close;
37
  bool at_eof;
38
  bool last_was_eol;
39
  isc_buffer_t *pushback;
40
  unsigned int ignored;
41
  void *input;
42
  char *name;
43
  unsigned long line;
44
  unsigned long saved_line;
45
  ISC_LINK(struct inputsource) link;
46
} inputsource;
47
48
0
#define LEX_MAGIC    ISC_MAGIC('L', 'e', 'x', '!')
49
#define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC)
50
51
struct isc_lex {
52
  /* Unlocked. */
53
  unsigned int magic;
54
  isc_mem_t *mctx;
55
  size_t max_token;
56
  char *data;
57
  unsigned int comments;
58
  bool comment_ok;
59
  bool last_was_eol;
60
  unsigned int brace_count;
61
  unsigned int paren_count;
62
  unsigned int saved_paren_count;
63
  isc_lexspecials_t specials;
64
  LIST(struct inputsource) sources;
65
};
66
67
static isc_result_t
68
0
grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
69
0
  char *tmp;
70
71
0
  tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
72
0
  memmove(tmp, lex->data, lex->max_token + 1);
73
0
  *currp = tmp + (*currp - lex->data);
74
0
  if (*prevp != NULL) {
75
0
    *prevp = tmp + (*prevp - lex->data);
76
0
  }
77
0
  isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
78
0
  lex->data = tmp;
79
0
  *remainingp += lex->max_token;
80
0
  lex->max_token *= 2;
81
0
  return (ISC_R_SUCCESS);
82
0
}
83
84
void
85
0
isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
86
0
  isc_lex_t *lex;
87
88
  /*
89
   * Create a lexer.
90
   */
91
0
  REQUIRE(lexp != NULL && *lexp == NULL);
92
93
0
  if (max_token == 0U) {
94
0
    max_token = 1;
95
0
  }
96
97
0
  lex = isc_mem_get(mctx, sizeof(*lex));
98
0
  lex->data = isc_mem_get(mctx, max_token + 1);
99
0
  lex->mctx = mctx;
100
0
  lex->max_token = max_token;
101
0
  lex->comments = 0;
102
0
  lex->comment_ok = true;
103
0
  lex->last_was_eol = true;
104
0
  lex->brace_count = 0;
105
0
  lex->paren_count = 0;
106
0
  lex->saved_paren_count = 0;
107
0
  memset(lex->specials, 0, 256);
108
0
  INIT_LIST(lex->sources);
109
0
  lex->magic = LEX_MAGIC;
110
111
0
  *lexp = lex;
112
0
}
113
114
void
115
0
isc_lex_destroy(isc_lex_t **lexp) {
116
0
  isc_lex_t *lex;
117
118
  /*
119
   * Destroy the lexer.
120
   */
121
122
0
  REQUIRE(lexp != NULL);
123
0
  lex = *lexp;
124
0
  *lexp = NULL;
125
0
  REQUIRE(VALID_LEX(lex));
126
127
0
  while (!EMPTY(lex->sources)) {
128
0
    RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
129
0
  }
130
0
  if (lex->data != NULL) {
131
0
    isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
132
0
  }
133
0
  lex->magic = 0;
134
0
  isc_mem_put(lex->mctx, lex, sizeof(*lex));
135
0
}
136
137
unsigned int
138
0
isc_lex_getcomments(isc_lex_t *lex) {
139
  /*
140
   * Return the current lexer commenting styles.
141
   */
142
143
0
  REQUIRE(VALID_LEX(lex));
144
145
0
  return (lex->comments);
146
0
}
147
148
void
149
0
isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
150
  /*
151
   * Set allowed lexer commenting styles.
152
   */
153
154
0
  REQUIRE(VALID_LEX(lex));
155
156
0
  lex->comments = comments;
157
0
}
158
159
void
160
0
isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
161
  /*
162
   * Put the current list of specials into 'specials'.
163
   */
164
165
0
  REQUIRE(VALID_LEX(lex));
166
167
0
  memmove(specials, lex->specials, 256);
168
0
}
169
170
void
171
0
isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) {
172
  /*
173
   * The characters in 'specials' are returned as tokens.  Along with
174
   * whitespace, they delimit strings and numbers.
175
   */
176
177
0
  REQUIRE(VALID_LEX(lex));
178
179
0
  memmove(lex->specials, specials, 256);
180
0
}
181
182
static isc_result_t
183
new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
184
0
     const char *name) {
185
0
  inputsource *source;
186
187
0
  source = isc_mem_get(lex->mctx, sizeof(*source));
188
0
  source->result = ISC_R_SUCCESS;
189
0
  source->is_file = is_file;
190
0
  source->need_close = need_close;
191
0
  source->at_eof = false;
192
0
  source->last_was_eol = lex->last_was_eol;
193
0
  source->input = input;
194
0
  source->name = isc_mem_strdup(lex->mctx, name);
195
0
  source->pushback = NULL;
196
0
  isc_buffer_allocate(lex->mctx, &source->pushback,
197
0
          (unsigned int)lex->max_token);
198
0
  source->ignored = 0;
199
0
  source->line = 1;
200
0
  ISC_LIST_INITANDPREPEND(lex->sources, source, link);
201
202
0
  return (ISC_R_SUCCESS);
203
0
}
204
205
isc_result_t
206
0
isc_lex_openfile(isc_lex_t *lex, const char *filename) {
207
0
  isc_result_t result;
208
0
  FILE *stream = NULL;
209
210
  /*
211
   * Open 'filename' and make it the current input source for 'lex'.
212
   */
213
214
0
  REQUIRE(VALID_LEX(lex));
215
216
0
  result = isc_stdio_open(filename, "r", &stream);
217
0
  if (result != ISC_R_SUCCESS) {
218
0
    return (result);
219
0
  }
220
221
0
  result = new_source(lex, true, true, stream, filename);
222
0
  if (result != ISC_R_SUCCESS) {
223
0
    (void)fclose(stream);
224
0
  }
225
0
  return (result);
226
0
}
227
228
isc_result_t
229
0
isc_lex_openstream(isc_lex_t *lex, FILE *stream) {
230
0
  char name[128];
231
232
  /*
233
   * Make 'stream' the current input source for 'lex'.
234
   */
235
236
0
  REQUIRE(VALID_LEX(lex));
237
238
0
  snprintf(name, sizeof(name), "stream-%p", stream);
239
240
0
  return (new_source(lex, true, false, stream, name));
241
0
}
242
243
isc_result_t
244
0
isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
245
0
  char name[128];
246
247
  /*
248
   * Make 'buffer' the current input source for 'lex'.
249
   */
250
251
0
  REQUIRE(VALID_LEX(lex));
252
253
0
  snprintf(name, sizeof(name), "buffer-%p", buffer);
254
255
0
  return (new_source(lex, false, false, buffer, name));
256
0
}
257
258
isc_result_t
259
0
isc_lex_close(isc_lex_t *lex) {
260
0
  inputsource *source;
261
262
  /*
263
   * Close the most recently opened object (i.e. file or buffer).
264
   */
265
266
0
  REQUIRE(VALID_LEX(lex));
267
268
0
  source = HEAD(lex->sources);
269
0
  if (source == NULL) {
270
0
    return (ISC_R_NOMORE);
271
0
  }
272
273
0
  ISC_LIST_UNLINK(lex->sources, source, link);
274
0
  lex->last_was_eol = source->last_was_eol;
275
0
  if (source->is_file) {
276
0
    if (source->need_close) {
277
0
      (void)fclose((FILE *)(source->input));
278
0
    }
279
0
  }
280
0
  isc_mem_free(lex->mctx, source->name);
281
0
  isc_buffer_free(&source->pushback);
282
0
  isc_mem_put(lex->mctx, source, sizeof(*source));
283
284
0
  return (ISC_R_SUCCESS);
285
0
}
286
287
typedef enum {
288
  lexstate_start,
289
  lexstate_crlf,
290
  lexstate_string,
291
  lexstate_number,
292
  lexstate_maybecomment,
293
  lexstate_ccomment,
294
  lexstate_ccommentend,
295
  lexstate_eatline,
296
  lexstate_qstring,
297
  lexstate_btext,
298
  lexstate_vpair,
299
  lexstate_vpairstart,
300
  lexstate_qvpair,
301
} lexstate;
302
303
0
#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
304
305
static void
306
0
pushback(inputsource *source, int c) {
307
0
  REQUIRE(source->pushback->current > 0);
308
0
  if (c == EOF) {
309
0
    source->at_eof = false;
310
0
    return;
311
0
  }
312
0
  source->pushback->current--;
313
0
  if (c == '\n') {
314
0
    source->line--;
315
0
  }
316
0
}
317
318
static isc_result_t
319
0
pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
320
0
  if (isc_buffer_availablelength(source->pushback) == 0) {
321
0
    isc_buffer_t *tbuf = NULL;
322
0
    unsigned int oldlen;
323
0
    isc_region_t used;
324
0
    isc_result_t result;
325
326
0
    oldlen = isc_buffer_length(source->pushback);
327
0
    isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
328
0
    isc_buffer_usedregion(source->pushback, &used);
329
0
    result = isc_buffer_copyregion(tbuf, &used);
330
0
    INSIST(result == ISC_R_SUCCESS);
331
0
    tbuf->current = source->pushback->current;
332
0
    isc_buffer_free(&source->pushback);
333
0
    source->pushback = tbuf;
334
0
  }
335
0
  isc_buffer_putuint8(source->pushback, (uint8_t)c);
336
0
  return (ISC_R_SUCCESS);
337
0
}
338
339
isc_result_t
340
0
isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
341
0
  inputsource *source;
342
0
  int c;
343
0
  bool done = false;
344
0
  bool no_comments = false;
345
0
  bool escaped = false;
346
0
  lexstate state = lexstate_start;
347
0
  lexstate saved_state = lexstate_start;
348
0
  isc_buffer_t *buffer;
349
0
  FILE *stream;
350
0
  char *curr, *prev;
351
0
  size_t remaining;
352
0
  uint32_t as_ulong;
353
0
  unsigned int saved_options;
354
0
  isc_result_t result;
355
356
  /*
357
   * Get the next token.
358
   */
359
360
0
  REQUIRE(VALID_LEX(lex));
361
0
  source = HEAD(lex->sources);
362
0
  REQUIRE(tokenp != NULL);
363
364
0
  if (source == NULL) {
365
0
    if ((options & ISC_LEXOPT_NOMORE) != 0) {
366
0
      tokenp->type = isc_tokentype_nomore;
367
0
      return (ISC_R_SUCCESS);
368
0
    }
369
0
    return (ISC_R_NOMORE);
370
0
  }
371
372
0
  if (source->result != ISC_R_SUCCESS) {
373
0
    return (source->result);
374
0
  }
375
376
0
  lex->saved_paren_count = lex->paren_count;
377
0
  source->saved_line = source->line;
378
379
0
  if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
380
0
  {
381
0
    if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
382
0
        lex->paren_count != 0)
383
0
    {
384
0
      lex->paren_count = 0;
385
0
      return (ISC_R_UNBALANCED);
386
0
    }
387
0
    if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0)
388
0
    {
389
0
      lex->brace_count = 0;
390
0
      return (ISC_R_UNBALANCED);
391
0
    }
392
0
    if ((options & ISC_LEXOPT_EOF) != 0) {
393
0
      tokenp->type = isc_tokentype_eof;
394
0
      return (ISC_R_SUCCESS);
395
0
    }
396
0
    return (ISC_R_EOF);
397
0
  }
398
399
0
  isc_buffer_compact(source->pushback);
400
401
0
  saved_options = options;
402
0
  if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
403
0
    options &= ~IWSEOL;
404
0
  }
405
406
0
  curr = lex->data;
407
0
  *curr = '\0';
408
409
0
  prev = NULL;
410
0
  remaining = lex->max_token;
411
412
0
#ifdef HAVE_FLOCKFILE
413
0
  if (source->is_file) {
414
0
    flockfile(source->input);
415
0
  }
416
0
#endif /* ifdef HAVE_FLOCKFILE */
417
418
0
  do {
419
0
    if (isc_buffer_remaininglength(source->pushback) == 0) {
420
0
      if (source->is_file) {
421
0
        stream = source->input;
422
423
0
#if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
424
0
        c = getc_unlocked(stream);
425
#else  /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
426
        c = getc(stream);
427
#endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */
428
0
        if (c == EOF) {
429
0
          if (ferror(stream)) {
430
0
            source->result =
431
0
              isc__errno2result(
432
0
                errno);
433
0
            result = source->result;
434
0
            goto done;
435
0
          }
436
0
          source->at_eof = true;
437
0
        }
438
0
      } else {
439
0
        buffer = source->input;
440
441
0
        if (buffer->current == buffer->used) {
442
0
          c = EOF;
443
0
          source->at_eof = true;
444
0
        } else {
445
0
          c = *((unsigned char *)buffer->base +
446
0
                buffer->current);
447
0
          buffer->current++;
448
0
        }
449
0
      }
450
0
      if (c != EOF) {
451
0
        source->result = pushandgrow(lex, source, c);
452
0
        if (source->result != ISC_R_SUCCESS) {
453
0
          result = source->result;
454
0
          goto done;
455
0
        }
456
0
      }
457
0
    }
458
459
0
    if (!source->at_eof) {
460
0
      if (state == lexstate_start) {
461
        /* Token has not started yet. */
462
0
        source->ignored = isc_buffer_consumedlength(
463
0
          source->pushback);
464
0
      }
465
0
      c = isc_buffer_getuint8(source->pushback);
466
0
    } else {
467
0
      c = EOF;
468
0
    }
469
470
0
    if (c == '\n') {
471
0
      source->line++;
472
0
    }
473
474
0
    if (lex->comment_ok && !no_comments) {
475
0
      if (!escaped && c == ';' &&
476
0
          ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
477
0
           0))
478
0
      {
479
0
        saved_state = state;
480
0
        state = lexstate_eatline;
481
0
        no_comments = true;
482
0
        continue;
483
0
      } else if (c == '/' &&
484
0
           (lex->comments &
485
0
            (ISC_LEXCOMMENT_C |
486
0
             ISC_LEXCOMMENT_CPLUSPLUS)) != 0)
487
0
      {
488
0
        saved_state = state;
489
0
        state = lexstate_maybecomment;
490
0
        no_comments = true;
491
0
        continue;
492
0
      } else if (c == '#' && ((lex->comments &
493
0
             ISC_LEXCOMMENT_SHELL) != 0))
494
0
      {
495
0
        saved_state = state;
496
0
        state = lexstate_eatline;
497
0
        no_comments = true;
498
0
        continue;
499
0
      }
500
0
    }
501
502
0
  no_read:
503
    /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
504
0
    switch (state) {
505
0
    case lexstate_start:
506
0
      if (c == EOF) {
507
0
        lex->last_was_eol = false;
508
0
        if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
509
0
            lex->paren_count != 0)
510
0
        {
511
0
          lex->paren_count = 0;
512
0
          result = ISC_R_UNBALANCED;
513
0
          goto done;
514
0
        }
515
0
        if ((options & ISC_LEXOPT_BTEXT) != 0 &&
516
0
            lex->brace_count != 0)
517
0
        {
518
0
          lex->brace_count = 0;
519
0
          result = ISC_R_UNBALANCED;
520
0
          goto done;
521
0
        }
522
0
        if ((options & ISC_LEXOPT_EOF) == 0) {
523
0
          result = ISC_R_EOF;
524
0
          goto done;
525
0
        }
526
0
        tokenp->type = isc_tokentype_eof;
527
0
        done = true;
528
0
      } else if (c == ' ' || c == '\t') {
529
0
        if (lex->last_was_eol &&
530
0
            (options & ISC_LEXOPT_INITIALWS) != 0)
531
0
        {
532
0
          lex->last_was_eol = false;
533
0
          tokenp->type = isc_tokentype_initialws;
534
0
          tokenp->value.as_char = c;
535
0
          done = true;
536
0
        }
537
0
      } else if (c == '\n') {
538
0
        if ((options & ISC_LEXOPT_EOL) != 0) {
539
0
          tokenp->type = isc_tokentype_eol;
540
0
          done = true;
541
0
        }
542
0
        lex->last_was_eol = true;
543
0
      } else if (c == '\r') {
544
0
        if ((options & ISC_LEXOPT_EOL) != 0) {
545
0
          state = lexstate_crlf;
546
0
        }
547
0
      } else if (c == '"' &&
548
0
           (options & ISC_LEXOPT_QSTRING) != 0)
549
0
      {
550
0
        lex->last_was_eol = false;
551
0
        no_comments = true;
552
0
        state = lexstate_qstring;
553
0
      } else if (lex->specials[c]) {
554
0
        lex->last_was_eol = false;
555
0
        if ((c == '(' || c == ')') &&
556
0
            (options & ISC_LEXOPT_DNSMULTILINE) != 0)
557
0
        {
558
0
          if (c == '(') {
559
0
            if (lex->paren_count == 0) {
560
0
              options &= ~IWSEOL;
561
0
            }
562
0
            lex->paren_count++;
563
0
          } else {
564
0
            if (lex->paren_count == 0) {
565
0
              result =
566
0
                ISC_R_UNBALANCED;
567
0
              goto done;
568
0
            }
569
0
            lex->paren_count--;
570
0
            if (lex->paren_count == 0) {
571
0
              options = saved_options;
572
0
            }
573
0
          }
574
0
          continue;
575
0
        } else if (c == '{' &&
576
0
             (options & ISC_LEXOPT_BTEXT) != 0)
577
0
        {
578
0
          if (lex->brace_count != 0) {
579
0
            result = ISC_R_UNBALANCED;
580
0
            goto done;
581
0
          }
582
0
          lex->brace_count++;
583
0
          options &= ~IWSEOL;
584
0
          state = lexstate_btext;
585
0
          no_comments = true;
586
0
          continue;
587
0
        }
588
0
        tokenp->type = isc_tokentype_special;
589
0
        tokenp->value.as_char = c;
590
0
        done = true;
591
0
      } else if (isdigit((unsigned char)c) &&
592
0
           (options & ISC_LEXOPT_NUMBER) != 0)
593
0
      {
594
0
        lex->last_was_eol = false;
595
0
        if ((options & ISC_LEXOPT_OCTAL) != 0 &&
596
0
            (c == '8' || c == '9'))
597
0
        {
598
0
          state = lexstate_string;
599
0
        } else {
600
0
          state = lexstate_number;
601
0
        }
602
0
        goto no_read;
603
0
      } else {
604
0
        lex->last_was_eol = false;
605
0
        state = lexstate_string;
606
0
        goto no_read;
607
0
      }
608
0
      break;
609
0
    case lexstate_crlf:
610
0
      if (c != '\n') {
611
0
        pushback(source, c);
612
0
      }
613
0
      tokenp->type = isc_tokentype_eol;
614
0
      done = true;
615
0
      lex->last_was_eol = true;
616
0
      break;
617
0
    case lexstate_number:
618
0
      if (c == EOF || !isdigit((unsigned char)c)) {
619
0
        if (c == ' ' || c == '\t' || c == '\r' ||
620
0
            c == '\n' || c == EOF || lex->specials[c])
621
0
        {
622
0
          int base;
623
0
          if ((options & ISC_LEXOPT_OCTAL) != 0) {
624
0
            base = 8;
625
0
          } else if ((options &
626
0
                ISC_LEXOPT_CNUMBER) != 0)
627
0
          {
628
0
            base = 0;
629
0
          } else {
630
0
            base = 10;
631
0
          }
632
0
          pushback(source, c);
633
634
0
          result = isc_parse_uint32(
635
0
            &as_ulong, lex->data, base);
636
0
          if (result == ISC_R_SUCCESS) {
637
0
            tokenp->type =
638
0
              isc_tokentype_number;
639
0
            tokenp->value.as_ulong =
640
0
              as_ulong;
641
0
          } else if (result == ISC_R_BADNUMBER) {
642
0
            isc_tokenvalue_t *v;
643
644
0
            tokenp->type =
645
0
              isc_tokentype_string;
646
0
            v = &(tokenp->value);
647
0
            v->as_textregion.base =
648
0
              lex->data;
649
0
            v->as_textregion.length =
650
0
              (unsigned int)(lex->max_token -
651
0
                       remaining);
652
0
          } else {
653
0
            goto done;
654
0
          }
655
0
          done = true;
656
0
          continue;
657
0
        } else if ((options & ISC_LEXOPT_CNUMBER) ==
658
0
               0 ||
659
0
             ((c != 'x' && c != 'X') ||
660
0
              (curr != &lex->data[1]) ||
661
0
              (lex->data[0] != '0')))
662
0
        {
663
          /* Above test supports hex numbers */
664
0
          state = lexstate_string;
665
0
        }
666
0
      } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
667
0
           (c == '8' || c == '9'))
668
0
      {
669
0
        state = lexstate_string;
670
0
      }
671
0
      if (remaining == 0U) {
672
0
        result = grow_data(lex, &remaining, &curr,
673
0
               &prev);
674
0
        if (result != ISC_R_SUCCESS) {
675
0
          goto done;
676
0
        }
677
0
      }
678
0
      INSIST(remaining > 0U);
679
0
      *curr++ = c;
680
0
      *curr = '\0';
681
0
      remaining--;
682
0
      break;
683
0
    case lexstate_string:
684
0
      if (!escaped && c == '=' &&
685
0
          (options & ISC_LEXOPT_VPAIR) != 0)
686
0
      {
687
0
        if (remaining == 0U) {
688
0
          result = grow_data(lex, &remaining,
689
0
                 &curr, &prev);
690
0
          if (result != ISC_R_SUCCESS) {
691
0
            goto done;
692
0
          }
693
0
        }
694
0
        INSIST(remaining > 0U);
695
0
        *curr++ = c;
696
0
        *curr = '\0';
697
0
        remaining--;
698
0
        state = lexstate_vpairstart;
699
0
        break;
700
0
      }
701
0
      FALLTHROUGH;
702
0
    case lexstate_vpairstart:
703
0
      if (state == lexstate_vpairstart) {
704
0
        if (c == '"' &&
705
0
            (options & ISC_LEXOPT_QVPAIR) != 0)
706
0
        {
707
0
          no_comments = true;
708
0
          state = lexstate_qvpair;
709
0
          break;
710
0
        }
711
0
        state = lexstate_vpair;
712
0
      }
713
0
      FALLTHROUGH;
714
0
    case lexstate_vpair:
715
      /*
716
       * EOF needs to be checked before lex->specials[c]
717
       * as lex->specials[EOF] is not a good idea.
718
       */
719
0
      if (c == '\r' || c == '\n' || c == EOF ||
720
0
          (!escaped &&
721
0
           (c == ' ' || c == '\t' || lex->specials[c])))
722
0
      {
723
0
        pushback(source, c);
724
0
        if (source->result != ISC_R_SUCCESS) {
725
0
          result = source->result;
726
0
          goto done;
727
0
        }
728
0
        if (escaped && c == EOF) {
729
0
          result = ISC_R_UNEXPECTEDEND;
730
0
          goto done;
731
0
        }
732
0
        tokenp->type = (state == lexstate_string)
733
0
                   ? isc_tokentype_string
734
0
                   : isc_tokentype_vpair;
735
0
        tokenp->value.as_textregion.base = lex->data;
736
0
        tokenp->value.as_textregion.length =
737
0
          (unsigned int)(lex->max_token -
738
0
                   remaining);
739
0
        done = true;
740
0
        continue;
741
0
      }
742
0
      if ((options & ISC_LEXOPT_ESCAPE) != 0) {
743
0
        escaped = (!escaped && c == '\\') ? true
744
0
                  : false;
745
0
      }
746
0
      if (remaining == 0U) {
747
0
        result = grow_data(lex, &remaining, &curr,
748
0
               &prev);
749
0
        if (result != ISC_R_SUCCESS) {
750
0
          goto done;
751
0
        }
752
0
      }
753
0
      INSIST(remaining > 0U);
754
0
      *curr++ = c;
755
0
      *curr = '\0';
756
0
      remaining--;
757
0
      break;
758
0
    case lexstate_maybecomment:
759
0
      if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0)
760
0
      {
761
0
        state = lexstate_ccomment;
762
0
        continue;
763
0
      } else if (c == '/' && (lex->comments &
764
0
            ISC_LEXCOMMENT_CPLUSPLUS) != 0)
765
0
      {
766
0
        state = lexstate_eatline;
767
0
        continue;
768
0
      }
769
0
      pushback(source, c);
770
0
      c = '/';
771
0
      no_comments = false;
772
0
      state = saved_state;
773
0
      goto no_read;
774
0
    case lexstate_ccomment:
775
0
      if (c == EOF) {
776
0
        result = ISC_R_UNEXPECTEDEND;
777
0
        goto done;
778
0
      }
779
0
      if (c == '*') {
780
0
        state = lexstate_ccommentend;
781
0
      }
782
0
      break;
783
0
    case lexstate_ccommentend:
784
0
      if (c == EOF) {
785
0
        result = ISC_R_UNEXPECTEDEND;
786
0
        goto done;
787
0
      }
788
0
      if (c == '/') {
789
        /*
790
         * C-style comments become a single space.
791
         * We do this to ensure that a comment will
792
         * act as a delimiter for strings and
793
         * numbers.
794
         */
795
0
        c = ' ';
796
0
        no_comments = false;
797
0
        state = saved_state;
798
0
        goto no_read;
799
0
      } else if (c != '*') {
800
0
        state = lexstate_ccomment;
801
0
      }
802
0
      break;
803
0
    case lexstate_eatline:
804
0
      if ((c == '\n') || (c == EOF)) {
805
0
        no_comments = false;
806
0
        state = saved_state;
807
0
        goto no_read;
808
0
      }
809
0
      break;
810
0
    case lexstate_qstring:
811
0
    case lexstate_qvpair:
812
0
      if (c == EOF) {
813
0
        result = ISC_R_UNEXPECTEDEND;
814
0
        goto done;
815
0
      }
816
0
      if (c == '"') {
817
0
        if (escaped) {
818
0
          escaped = false;
819
          /*
820
           * Overwrite the preceding backslash.
821
           */
822
0
          INSIST(prev != NULL);
823
0
          *prev = '"';
824
0
        } else {
825
0
          tokenp->type =
826
0
            (state == lexstate_qstring)
827
0
              ? isc_tokentype_qstring
828
0
              : isc_tokentype_qvpair;
829
0
          tokenp->value.as_textregion.base =
830
0
            lex->data;
831
0
          tokenp->value.as_textregion.length =
832
0
            (unsigned int)(lex->max_token -
833
0
                     remaining);
834
0
          no_comments = false;
835
0
          done = true;
836
0
        }
837
0
      } else {
838
0
        if (c == '\n' && !escaped &&
839
0
            (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
840
0
              0)
841
0
        {
842
0
          pushback(source, c);
843
0
          result = ISC_R_UNBALANCEDQUOTES;
844
0
          goto done;
845
0
        }
846
0
        if (c == '\\' && !escaped) {
847
0
          escaped = true;
848
0
        } else {
849
0
          escaped = false;
850
0
        }
851
0
        if (remaining == 0U) {
852
0
          result = grow_data(lex, &remaining,
853
0
                 &curr, &prev);
854
0
          if (result != ISC_R_SUCCESS) {
855
0
            goto done;
856
0
          }
857
0
        }
858
0
        INSIST(remaining > 0U);
859
0
        prev = curr;
860
0
        *curr++ = c;
861
0
        *curr = '\0';
862
0
        remaining--;
863
0
      }
864
0
      break;
865
0
    case lexstate_btext:
866
0
      if (c == EOF) {
867
0
        result = ISC_R_UNEXPECTEDEND;
868
0
        goto done;
869
0
      }
870
0
      if (c == '{') {
871
0
        if (escaped) {
872
0
          escaped = false;
873
0
        } else {
874
0
          lex->brace_count++;
875
0
        }
876
0
      } else if (c == '}') {
877
0
        if (escaped) {
878
0
          escaped = false;
879
0
        } else {
880
0
          INSIST(lex->brace_count > 0);
881
0
          lex->brace_count--;
882
0
        }
883
884
0
        if (lex->brace_count == 0) {
885
0
          tokenp->type = isc_tokentype_btext;
886
0
          tokenp->value.as_textregion.base =
887
0
            lex->data;
888
0
          tokenp->value.as_textregion.length =
889
0
            (unsigned int)(lex->max_token -
890
0
                     remaining);
891
0
          no_comments = false;
892
0
          done = true;
893
0
          break;
894
0
        }
895
0
      }
896
897
0
      if (c == '\\' && !escaped) {
898
0
        escaped = true;
899
0
      } else {
900
0
        escaped = false;
901
0
      }
902
903
0
      if (remaining == 0U) {
904
0
        result = grow_data(lex, &remaining, &curr,
905
0
               &prev);
906
0
        if (result != ISC_R_SUCCESS) {
907
0
          goto done;
908
0
        }
909
0
      }
910
0
      INSIST(remaining > 0U);
911
0
      prev = curr;
912
0
      *curr++ = c;
913
0
      *curr = '\0';
914
0
      remaining--;
915
0
      break;
916
0
    default:
917
0
      FATAL_ERROR("Unexpected state %d", state);
918
0
    }
919
0
  } while (!done);
920
921
0
  result = ISC_R_SUCCESS;
922
0
done:
923
0
#ifdef HAVE_FLOCKFILE
924
0
  if (source->is_file) {
925
0
    funlockfile(source->input);
926
0
  }
927
0
#endif /* ifdef HAVE_FLOCKFILE */
928
0
  return (result);
929
0
}
930
931
isc_result_t
932
isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
933
0
           isc_tokentype_t expect, bool eol) {
934
0
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
935
0
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
936
0
  isc_result_t result;
937
938
0
  if (expect == isc_tokentype_vpair) {
939
0
    options |= ISC_LEXOPT_VPAIR;
940
0
  } else if (expect == isc_tokentype_qvpair) {
941
0
    options |= ISC_LEXOPT_VPAIR;
942
0
    options |= ISC_LEXOPT_QVPAIR;
943
0
  } else if (expect == isc_tokentype_qstring) {
944
0
    options |= ISC_LEXOPT_QSTRING;
945
0
  } else if (expect == isc_tokentype_number) {
946
0
    options |= ISC_LEXOPT_NUMBER;
947
0
  }
948
0
  result = isc_lex_gettoken(lex, options, token);
949
0
  if (result == ISC_R_RANGE) {
950
0
    isc_lex_ungettoken(lex, token);
951
0
  }
952
0
  if (result != ISC_R_SUCCESS) {
953
0
    return (result);
954
0
  }
955
956
0
  if (eol && ((token->type == isc_tokentype_eol) ||
957
0
        (token->type == isc_tokentype_eof)))
958
0
  {
959
0
    return (ISC_R_SUCCESS);
960
0
  }
961
0
  if (token->type == isc_tokentype_string &&
962
0
      (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
963
0
  {
964
0
    return (ISC_R_SUCCESS);
965
0
  }
966
0
  if (token->type == isc_tokentype_vpair &&
967
0
      expect == isc_tokentype_qvpair)
968
0
  {
969
0
    return (ISC_R_SUCCESS);
970
0
  }
971
0
  if (token->type != expect) {
972
0
    isc_lex_ungettoken(lex, token);
973
0
    if (token->type == isc_tokentype_eol ||
974
0
        token->type == isc_tokentype_eof)
975
0
    {
976
0
      return (ISC_R_UNEXPECTEDEND);
977
0
    }
978
0
    if (expect == isc_tokentype_number) {
979
0
      return (ISC_R_BADNUMBER);
980
0
    }
981
0
    return (ISC_R_UNEXPECTEDTOKEN);
982
0
  }
983
0
  return (ISC_R_SUCCESS);
984
0
}
985
986
isc_result_t
987
0
isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
988
0
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
989
0
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
990
0
             ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
991
0
  isc_result_t result;
992
993
0
  result = isc_lex_gettoken(lex, options, token);
994
0
  if (result == ISC_R_RANGE) {
995
0
    isc_lex_ungettoken(lex, token);
996
0
  }
997
0
  if (result != ISC_R_SUCCESS) {
998
0
    return (result);
999
0
  }
1000
1001
0
  if (eol && ((token->type == isc_tokentype_eol) ||
1002
0
        (token->type == isc_tokentype_eof)))
1003
0
  {
1004
0
    return (ISC_R_SUCCESS);
1005
0
  }
1006
0
  if (token->type != isc_tokentype_number) {
1007
0
    isc_lex_ungettoken(lex, token);
1008
0
    if (token->type == isc_tokentype_eol ||
1009
0
        token->type == isc_tokentype_eof)
1010
0
    {
1011
0
      return (ISC_R_UNEXPECTEDEND);
1012
0
    }
1013
0
    return (ISC_R_BADNUMBER);
1014
0
  }
1015
0
  return (ISC_R_SUCCESS);
1016
0
}
1017
1018
void
1019
0
isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
1020
0
  inputsource *source;
1021
  /*
1022
   * Unget the current token.
1023
   */
1024
1025
0
  REQUIRE(VALID_LEX(lex));
1026
0
  source = HEAD(lex->sources);
1027
0
  REQUIRE(source != NULL);
1028
0
  REQUIRE(tokenp != NULL);
1029
0
  REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
1030
0
    tokenp->type == isc_tokentype_eof);
1031
1032
0
  UNUSED(tokenp);
1033
1034
0
  isc_buffer_first(source->pushback);
1035
0
  lex->paren_count = lex->saved_paren_count;
1036
0
  source->line = source->saved_line;
1037
0
  source->at_eof = false;
1038
0
}
1039
1040
void
1041
0
isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) {
1042
0
  inputsource *source;
1043
1044
0
  REQUIRE(VALID_LEX(lex));
1045
0
  source = HEAD(lex->sources);
1046
0
  REQUIRE(source != NULL);
1047
0
  REQUIRE(tokenp != NULL);
1048
0
  REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
1049
0
    tokenp->type == isc_tokentype_eof);
1050
1051
0
  UNUSED(tokenp);
1052
1053
0
  INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback));
1054
0
  r->base = (unsigned char *)isc_buffer_base(source->pushback) +
1055
0
      source->ignored;
1056
0
  r->length = isc_buffer_consumedlength(source->pushback) -
1057
0
        source->ignored;
1058
0
}
1059
1060
char *
1061
0
isc_lex_getsourcename(isc_lex_t *lex) {
1062
0
  inputsource *source;
1063
1064
0
  REQUIRE(VALID_LEX(lex));
1065
0
  source = HEAD(lex->sources);
1066
1067
0
  if (source == NULL) {
1068
0
    return (NULL);
1069
0
  }
1070
1071
0
  return (source->name);
1072
0
}
1073
1074
unsigned long
1075
0
isc_lex_getsourceline(isc_lex_t *lex) {
1076
0
  inputsource *source;
1077
1078
0
  REQUIRE(VALID_LEX(lex));
1079
0
  source = HEAD(lex->sources);
1080
1081
0
  if (source == NULL) {
1082
0
    return (0);
1083
0
  }
1084
1085
0
  return (source->line);
1086
0
}
1087
1088
isc_result_t
1089
0
isc_lex_setsourcename(isc_lex_t *lex, const char *name) {
1090
0
  inputsource *source;
1091
0
  char *newname;
1092
1093
0
  REQUIRE(VALID_LEX(lex));
1094
0
  source = HEAD(lex->sources);
1095
1096
0
  if (source == NULL) {
1097
0
    return (ISC_R_NOTFOUND);
1098
0
  }
1099
0
  newname = isc_mem_strdup(lex->mctx, name);
1100
0
  isc_mem_free(lex->mctx, source->name);
1101
0
  source->name = newname;
1102
0
  return (ISC_R_SUCCESS);
1103
0
}
1104
1105
isc_result_t
1106
0
isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) {
1107
0
  inputsource *source;
1108
1109
0
  REQUIRE(VALID_LEX(lex));
1110
0
  source = HEAD(lex->sources);
1111
1112
0
  if (source == NULL) {
1113
0
    return (ISC_R_NOTFOUND);
1114
0
  }
1115
1116
0
  source->line = line;
1117
0
  return (ISC_R_SUCCESS);
1118
0
}
1119
1120
bool
1121
0
isc_lex_isfile(isc_lex_t *lex) {
1122
0
  inputsource *source;
1123
1124
0
  REQUIRE(VALID_LEX(lex));
1125
1126
0
  source = HEAD(lex->sources);
1127
1128
0
  if (source == NULL) {
1129
0
    return (false);
1130
0
  }
1131
1132
0
  return (source->is_file);
1133
0
}