Coverage Report

Created: 2026-03-07 06:55

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