Coverage Report

Created: 2025-08-26 06:59

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