Coverage Report

Created: 2025-08-03 06:33

/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
32.9k
#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.96k
grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
69
9.96k
  char *tmp;
70
71
9.96k
  tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
72
9.96k
  memmove(tmp, lex->data, lex->max_token + 1);
73
9.96k
  *currp = tmp + (*currp - lex->data);
74
9.96k
  if (*prevp != NULL) {
75
324
    *prevp = tmp + (*prevp - lex->data);
76
324
  }
77
9.96k
  isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
78
9.96k
  lex->data = tmp;
79
9.96k
  *remainingp += lex->max_token;
80
9.96k
  lex->max_token *= 2;
81
9.96k
}
82
83
void
84
32.9k
isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
85
32.9k
  isc_lex_t *lex;
86
87
  /*
88
   * Create a lexer.
89
   */
90
32.9k
  REQUIRE(lexp != NULL && *lexp == NULL);
91
92
32.9k
  if (max_token == 0U) {
93
0
    max_token = 1;
94
0
  }
95
96
32.9k
  lex = isc_mem_get(mctx, sizeof(*lex));
97
32.9k
  lex->data = isc_mem_get(mctx, max_token + 1);
98
32.9k
  lex->mctx = mctx;
99
32.9k
  lex->max_token = max_token;
100
32.9k
  lex->comments = 0;
101
32.9k
  lex->comment_ok = true;
102
32.9k
  lex->last_was_eol = true;
103
32.9k
  lex->brace_count = 0;
104
32.9k
  lex->paren_count = 0;
105
32.9k
  lex->saved_paren_count = 0;
106
32.9k
  memset(lex->specials, 0, 256);
107
32.9k
  ISC_LIST_INIT(lex->sources);
108
32.9k
  lex->magic = LEX_MAGIC;
109
110
32.9k
  *lexp = lex;
111
32.9k
}
112
113
void
114
32.9k
isc_lex_destroy(isc_lex_t **lexp) {
115
32.9k
  isc_lex_t *lex;
116
117
  /*
118
   * Destroy the lexer.
119
   */
120
121
32.9k
  REQUIRE(lexp != NULL);
122
32.9k
  lex = *lexp;
123
32.9k
  *lexp = NULL;
124
32.9k
  REQUIRE(VALID_LEX(lex));
125
126
48.9k
  while (!ISC_LIST_EMPTY(lex->sources)) {
127
15.9k
    RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
128
15.9k
  }
129
32.9k
  if (lex->data != NULL) {
130
32.9k
    isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
131
32.9k
  }
132
32.9k
  lex->magic = 0;
133
32.9k
  isc_mem_put(lex->mctx, lex, sizeof(*lex));
134
32.9k
}
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
32.9k
isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
149
  /*
150
   * Set allowed lexer commenting styles.
151
   */
152
153
32.9k
  REQUIRE(VALID_LEX(lex));
154
155
32.9k
  lex->comments = comments;
156
32.9k
}
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
32.9k
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
32.9k
  REQUIRE(VALID_LEX(lex));
177
178
32.9k
  memmove(lex->specials, specials, 256);
179
32.9k
}
180
181
static void
182
new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
183
5.87M
     const char *name) {
184
5.87M
  inputsource *source;
185
186
5.87M
  source = isc_mem_get(lex->mctx, sizeof(*source));
187
5.87M
  *source = (inputsource){
188
5.87M
    .is_file = is_file,
189
5.87M
    .need_close = need_close,
190
5.87M
    .last_was_eol = lex->last_was_eol,
191
5.87M
    .input = input,
192
5.87M
    .name = isc_mem_strdup(lex->mctx, name),
193
5.87M
    .line = 1,
194
5.87M
    .link = ISC_LINK_INITIALIZER,
195
5.87M
  };
196
5.87M
  isc_buffer_allocate(lex->mctx, &source->pushback,
197
5.87M
          (unsigned int)lex->max_token);
198
5.87M
  ISC_LIST_PREPEND(lex->sources, source, link);
199
5.87M
}
200
201
isc_result_t
202
17
isc_lex_openfile(isc_lex_t *lex, const char *filename) {
203
17
  isc_result_t result;
204
17
  FILE *stream = NULL;
205
206
  /*
207
   * Open 'filename' and make it the current input source for 'lex'.
208
   */
209
210
17
  REQUIRE(VALID_LEX(lex));
211
212
17
  result = isc_stdio_open(filename, "r", &stream);
213
17
  if (result != ISC_R_SUCCESS) {
214
13
    return result;
215
13
  }
216
217
4
  new_source(lex, true, true, stream, filename);
218
4
  return ISC_R_SUCCESS;
219
17
}
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
5.87M
isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
238
5.87M
  char name[128];
239
240
  /*
241
   * Make 'buffer' the current input source for 'lex'.
242
   */
243
244
5.87M
  REQUIRE(VALID_LEX(lex));
245
246
5.87M
  snprintf(name, sizeof(name), "buffer-%p", buffer);
247
248
5.87M
  new_source(lex, false, false, buffer, name);
249
5.87M
  return ISC_R_SUCCESS;
250
5.87M
}
251
252
isc_result_t
253
5.86M
isc_lex_close(isc_lex_t *lex) {
254
5.86M
  inputsource *source;
255
256
  /*
257
   * Close the most recently opened object (i.e. file or buffer).
258
   */
259
260
5.86M
  REQUIRE(VALID_LEX(lex));
261
262
5.86M
  source = ISC_LIST_HEAD(lex->sources);
263
5.86M
  if (source == NULL) {
264
0
    return ISC_R_NOMORE;
265
0
  }
266
267
5.86M
  ISC_LIST_UNLINK(lex->sources, source, link);
268
5.86M
  lex->last_was_eol = source->last_was_eol;
269
5.86M
  if (source->is_file) {
270
4
    if (source->need_close) {
271
4
      (void)fclose((FILE *)(source->input));
272
4
    }
273
4
  }
274
5.86M
  isc_mem_free(lex->mctx, source->name);
275
5.86M
  isc_buffer_free(&source->pushback);
276
5.86M
  isc_mem_put(lex->mctx, source, sizeof(*source));
277
278
5.86M
  return ISC_R_SUCCESS;
279
5.86M
}
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.47M
#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
298
299
static void
300
15.6M
pushback(inputsource *source, int c) {
301
15.6M
  REQUIRE(source->pushback->current > 0);
302
15.6M
  if (c == EOF) {
303
11.5M
    source->at_eof = false;
304
11.5M
    return;
305
11.5M
  }
306
4.05M
  source->pushback->current--;
307
4.05M
  if (c == '\n') {
308
435k
    source->line--;
309
435k
  }
310
4.05M
}
311
312
static isc_result_t
313
390M
pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
314
390M
  if (isc_buffer_availablelength(source->pushback) == 0) {
315
10.1k
    isc_buffer_t *tbuf = NULL;
316
10.1k
    unsigned int oldlen;
317
10.1k
    isc_region_t used;
318
10.1k
    isc_result_t result;
319
320
10.1k
    oldlen = isc_buffer_length(source->pushback);
321
10.1k
    isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
322
10.1k
    isc_buffer_usedregion(source->pushback, &used);
323
10.1k
    result = isc_buffer_copyregion(tbuf, &used);
324
10.1k
    INSIST(result == ISC_R_SUCCESS);
325
10.1k
    tbuf->current = source->pushback->current;
326
10.1k
    isc_buffer_free(&source->pushback);
327
10.1k
    source->pushback = tbuf;
328
10.1k
  }
329
390M
  isc_buffer_putuint8(source->pushback, (uint8_t)c);
330
390M
  return ISC_R_SUCCESS;
331
390M
}
332
333
isc_result_t
334
22.9M
isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) {
335
22.9M
  inputsource *source;
336
22.9M
  int c;
337
22.9M
  bool done = false;
338
22.9M
  bool no_comments = false;
339
22.9M
  bool escaped = false;
340
22.9M
  lexstate state = lexstate_start;
341
22.9M
  lexstate saved_state = lexstate_start;
342
22.9M
  isc_buffer_t *buffer;
343
22.9M
  FILE *stream;
344
22.9M
  char *curr, *prev;
345
22.9M
  size_t remaining;
346
22.9M
  uint32_t as_ulong;
347
22.9M
  unsigned int saved_options;
348
22.9M
  isc_result_t result;
349
350
  /*
351
   * Get the next token.
352
   */
353
354
22.9M
  REQUIRE(VALID_LEX(lex));
355
22.9M
  source = ISC_LIST_HEAD(lex->sources);
356
22.9M
  REQUIRE(tokenp != NULL);
357
358
22.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
22.9M
  if (source->result != ISC_R_SUCCESS) {
367
0
    return source->result;
368
0
  }
369
370
22.9M
  lex->saved_paren_count = lex->paren_count;
371
22.9M
  source->saved_line = source->line;
372
373
22.9M
  if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof)
374
3.73k
  {
375
3.73k
    if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
376
3.73k
        lex->paren_count != 0)
377
13
    {
378
13
      lex->paren_count = 0;
379
13
      return ISC_R_UNBALANCED;
380
13
    }
381
3.72k
    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.72k
    if ((options & ISC_LEXOPT_EOF) != 0) {
387
3.72k
      tokenp->type = isc_tokentype_eof;
388
3.72k
      return ISC_R_SUCCESS;
389
3.72k
    }
390
0
    return ISC_R_EOF;
391
3.72k
  }
392
393
22.9M
  isc_buffer_compact(source->pushback);
394
395
22.9M
  saved_options = options;
396
22.9M
  if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
397
1.26M
    options &= ~IWSEOL;
398
1.26M
  }
399
400
22.9M
  curr = lex->data;
401
22.9M
  *curr = '\0';
402
403
22.9M
  prev = NULL;
404
22.9M
  remaining = lex->max_token;
405
406
22.9M
#ifdef HAVE_FLOCKFILE
407
22.9M
  if (source->is_file) {
408
76
    flockfile(source->input);
409
76
  }
410
22.9M
#endif /* ifdef HAVE_FLOCKFILE */
411
412
539M
  do {
413
539M
    if (isc_buffer_remaininglength(source->pushback) == 0) {
414
408M
      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
408M
      } else {
433
408M
        buffer = source->input;
434
435
408M
        if (buffer->current == buffer->used) {
436
18.3M
          c = EOF;
437
18.3M
          source->at_eof = true;
438
390M
        } else {
439
390M
          c = *((unsigned char *)buffer->base +
440
390M
                buffer->current);
441
390M
          buffer->current++;
442
390M
        }
443
408M
      }
444
408M
      if (c != EOF) {
445
390M
        source->result = pushandgrow(lex, source, c);
446
390M
        if (source->result != ISC_R_SUCCESS) {
447
0
          result = source->result;
448
0
          goto done;
449
0
        }
450
390M
      }
451
408M
    }
452
453
539M
    if (!source->at_eof) {
454
520M
      if (state == lexstate_start) {
455
        /* Token has not started yet. */
456
26.3M
        source->ignored = isc_buffer_consumedlength(
457
26.3M
          source->pushback);
458
26.3M
      }
459
520M
      c = isc_buffer_getuint8(source->pushback);
460
520M
    } else {
461
18.3M
      c = EOF;
462
18.3M
    }
463
464
539M
    if (c == '\n') {
465
2.00M
      source->line++;
466
2.00M
    }
467
468
539M
    if (lex->comment_ok && !no_comments) {
469
504M
      if (!escaped && c == ';' &&
470
504M
          ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
471
816k
           0))
472
266k
      {
473
266k
        saved_state = state;
474
266k
        state = lexstate_eatline;
475
266k
        no_comments = true;
476
266k
        continue;
477
504M
      } else if (c == '/' &&
478
504M
           (lex->comments &
479
177k
            (ISC_LEXCOMMENT_C |
480
177k
             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
504M
      } else if (c == '#' && ((lex->comments &
487
648k
             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
504M
    }
495
496
554M
  no_read:
497
    /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
498
554M
    switch (state) {
499
33.0M
    case lexstate_start:
500
33.0M
      if (c == EOF) {
501
6.73M
        lex->last_was_eol = false;
502
6.73M
        if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
503
6.73M
            lex->paren_count != 0)
504
6.30k
        {
505
6.30k
          lex->paren_count = 0;
506
6.30k
          result = ISC_R_UNBALANCED;
507
6.30k
          goto done;
508
6.30k
        }
509
6.72M
        if ((options & ISC_LEXOPT_BTEXT) != 0 &&
510
6.72M
            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
6.72M
        if ((options & ISC_LEXOPT_EOF) == 0) {
517
343
          result = ISC_R_EOF;
518
343
          goto done;
519
343
        }
520
6.72M
        tokenp->type = isc_tokentype_eof;
521
6.72M
        done = true;
522
26.3M
      } else if (c == ' ' || c == '\t') {
523
3.15M
        if (lex->last_was_eol &&
524
3.15M
            (options & ISC_LEXOPT_INITIALWS) != 0)
525
126k
        {
526
126k
          lex->last_was_eol = false;
527
126k
          tokenp->type = isc_tokentype_initialws;
528
126k
          tokenp->value.as_char = c;
529
126k
          done = true;
530
126k
        }
531
23.1M
      } else if (c == '\n') {
532
1.56M
        if ((options & ISC_LEXOPT_EOL) != 0) {
533
401k
          tokenp->type = isc_tokentype_eol;
534
401k
          done = true;
535
401k
        }
536
1.56M
        lex->last_was_eol = true;
537
21.6M
      } else if (c == '\r') {
538
14.9k
        if ((options & ISC_LEXOPT_EOL) != 0) {
539
11.0k
          state = lexstate_crlf;
540
11.0k
        }
541
21.6M
      } else if (c == '"' &&
542
21.6M
           (options & ISC_LEXOPT_QSTRING) != 0)
543
26.0k
      {
544
26.0k
        lex->last_was_eol = false;
545
26.0k
        no_comments = true;
546
26.0k
        state = lexstate_qstring;
547
21.5M
      } else if (lex->specials[c]) {
548
5.96M
        lex->last_was_eol = false;
549
5.96M
        if ((c == '(' || c == ')') &&
550
5.96M
            (options & ISC_LEXOPT_DNSMULTILINE) != 0)
551
5.93M
        {
552
5.93M
          if (c == '(') {
553
5.70M
            if (lex->paren_count == 0) {
554
209k
              options &= ~IWSEOL;
555
209k
            }
556
5.70M
            lex->paren_count++;
557
5.70M
          } else {
558
236k
            if (lex->paren_count == 0) {
559
455
              result =
560
455
                ISC_R_UNBALANCED;
561
455
              goto done;
562
455
            }
563
236k
            lex->paren_count--;
564
236k
            if (lex->paren_count == 0) {
565
173k
              options = saved_options;
566
173k
            }
567
236k
          }
568
5.93M
          continue;
569
5.93M
        } else if (c == '{' &&
570
25.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
25.1k
        tokenp->type = isc_tokentype_special;
583
25.1k
        tokenp->value.as_char = c;
584
25.1k
        done = true;
585
15.6M
      } else if (isdigit((unsigned char)c) &&
586
15.6M
           (options & ISC_LEXOPT_NUMBER) != 0)
587
389k
      {
588
389k
        lex->last_was_eol = false;
589
389k
        if ((options & ISC_LEXOPT_OCTAL) != 0 &&
590
389k
            (c == '8' || c == '9'))
591
2
        {
592
2
          state = lexstate_string;
593
389k
        } else {
594
389k
          state = lexstate_number;
595
389k
        }
596
389k
        goto no_read;
597
15.2M
      } else {
598
15.2M
        lex->last_was_eol = false;
599
15.2M
        state = lexstate_string;
600
15.2M
        goto no_read;
601
15.2M
      }
602
11.5M
      break;
603
11.5M
    case lexstate_crlf:
604
11.0k
      if (c != '\n') {
605
10.5k
        pushback(source, c);
606
10.5k
      }
607
11.0k
      tokenp->type = isc_tokentype_eol;
608
11.0k
      done = true;
609
11.0k
      lex->last_was_eol = true;
610
11.0k
      break;
611
3.70M
    case lexstate_number:
612
3.70M
      if (c == EOF || !isdigit((unsigned char)c)) {
613
389k
        if (c == ' ' || c == '\t' || c == '\r' ||
614
389k
            c == '\n' || c == EOF || lex->specials[c])
615
387k
        {
616
387k
          int base;
617
387k
          if ((options & ISC_LEXOPT_OCTAL) != 0) {
618
111
            base = 8;
619
387k
          } else if ((options &
620
387k
                ISC_LEXOPT_CNUMBER) != 0)
621
0
          {
622
0
            base = 0;
623
387k
          } else {
624
387k
            base = 10;
625
387k
          }
626
387k
          pushback(source, c);
627
628
387k
          result = isc_parse_uint32(
629
387k
            &as_ulong, lex->data, base);
630
387k
          if (result == ISC_R_SUCCESS) {
631
386k
            tokenp->type =
632
386k
              isc_tokentype_number;
633
386k
            tokenp->value.as_ulong =
634
386k
              as_ulong;
635
386k
          } 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
1.15k
          } else {
647
1.15k
            goto done;
648
1.15k
          }
649
386k
          done = true;
650
386k
          continue;
651
387k
        } else if ((options & ISC_LEXOPT_CNUMBER) ==
652
1.43k
               0 ||
653
1.43k
             ((c != 'x' && c != 'X') ||
654
0
              (curr != &lex->data[1]) ||
655
0
              (lex->data[0] != '0')))
656
1.43k
        {
657
          /* Above test supports hex numbers */
658
1.43k
          state = lexstate_string;
659
1.43k
        }
660
3.31M
      } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
661
3.31M
           (c == '8' || c == '9'))
662
2
      {
663
2
        state = lexstate_string;
664
2
      }
665
3.31M
      if (remaining == 0U) {
666
44
        grow_data(lex, &remaining, &curr, &prev);
667
44
      }
668
3.31M
      INSIST(remaining > 0U);
669
3.31M
      *curr++ = c;
670
3.31M
      *curr = '\0';
671
3.31M
      remaining--;
672
3.31M
      break;
673
450M
    case lexstate_string:
674
450M
      if (!escaped && c == '=' &&
675
450M
          (options & ISC_LEXOPT_VPAIR) != 0)
676
32.5k
      {
677
32.5k
        if (remaining == 0U) {
678
5
          grow_data(lex, &remaining, &curr,
679
5
              &prev);
680
5
        }
681
32.5k
        INSIST(remaining > 0U);
682
32.5k
        *curr++ = c;
683
32.5k
        *curr = '\0';
684
32.5k
        remaining--;
685
32.5k
        state = lexstate_vpairstart;
686
32.5k
        break;
687
32.5k
      }
688
450M
      FALLTHROUGH;
689
450M
    case lexstate_vpairstart:
690
450M
      if (state == lexstate_vpairstart) {
691
32.5k
        if (c == '"' &&
692
32.5k
            (options & ISC_LEXOPT_QVPAIR) != 0)
693
1.32k
        {
694
1.32k
          no_comments = true;
695
1.32k
          state = lexstate_qvpair;
696
1.32k
          break;
697
1.32k
        }
698
31.2k
        state = lexstate_vpair;
699
31.2k
      }
700
450M
      FALLTHROUGH;
701
483M
    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
483M
      if (c == '\r' || c == '\n' || c == EOF ||
707
483M
          (!escaped &&
708
471M
           (c == ' ' || c == '\t' || lex->specials[c])))
709
15.2M
      {
710
15.2M
        pushback(source, c);
711
15.2M
        if (source->result != ISC_R_SUCCESS) {
712
0
          result = source->result;
713
0
          goto done;
714
0
        }
715
15.2M
        if (escaped && c == EOF) {
716
44
          result = ISC_R_UNEXPECTEDEND;
717
44
          goto done;
718
44
        }
719
15.2M
        tokenp->type = (state == lexstate_string)
720
15.2M
                   ? isc_tokentype_string
721
15.2M
                   : isc_tokentype_vpair;
722
15.2M
        tokenp->value.as_textregion.base = lex->data;
723
15.2M
        tokenp->value.as_textregion.length =
724
15.2M
          (unsigned int)(lex->max_token -
725
15.2M
                   remaining);
726
15.2M
        done = true;
727
15.2M
        continue;
728
15.2M
      }
729
467M
      if ((options & ISC_LEXOPT_ESCAPE) != 0) {
730
299M
        escaped = (!escaped && c == '\\') ? true
731
299M
                  : false;
732
299M
      }
733
467M
      if (remaining == 0U) {
734
9.59k
        grow_data(lex, &remaining, &curr, &prev);
735
9.59k
      }
736
467M
      INSIST(remaining > 0U);
737
467M
      *curr++ = c;
738
467M
      *curr = '\0';
739
467M
      remaining--;
740
467M
      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
21.6M
    case lexstate_eatline:
787
21.6M
      if ((c == '\n') || (c == EOF)) {
788
266k
        no_comments = false;
789
266k
        state = saved_state;
790
266k
        goto no_read;
791
266k
      }
792
21.3M
      break;
793
21.3M
    case lexstate_qstring:
794
13.0M
    case lexstate_qvpair:
795
13.0M
      if (c == EOF) {
796
247
        result = ISC_R_UNEXPECTEDEND;
797
247
        goto done;
798
247
      }
799
13.0M
      if (c == '"') {
800
31.9k
        if (escaped) {
801
4.84k
          escaped = false;
802
          /*
803
           * Overwrite the preceding backslash.
804
           */
805
4.84k
          INSIST(prev != NULL);
806
4.84k
          *prev = '"';
807
27.0k
        } else {
808
27.0k
          tokenp->type =
809
27.0k
            (state == lexstate_qstring)
810
27.0k
              ? isc_tokentype_qstring
811
27.0k
              : isc_tokentype_qvpair;
812
27.0k
          tokenp->value.as_textregion.base =
813
27.0k
            lex->data;
814
27.0k
          tokenp->value.as_textregion.length =
815
27.0k
            (unsigned int)(lex->max_token -
816
27.0k
                     remaining);
817
27.0k
          no_comments = false;
818
27.0k
          done = true;
819
27.0k
        }
820
13.0M
      } else {
821
13.0M
        if (c == '\n' && !escaped &&
822
13.0M
            (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
823
34
              0)
824
34
        {
825
34
          pushback(source, c);
826
34
          result = ISC_R_UNBALANCEDQUOTES;
827
34
          goto done;
828
34
        }
829
13.0M
        if (c == '\\' && !escaped) {
830
160k
          escaped = true;
831
12.8M
        } else {
832
12.8M
          escaped = false;
833
12.8M
        }
834
13.0M
        if (remaining == 0U) {
835
325
          grow_data(lex, &remaining, &curr,
836
325
              &prev);
837
325
        }
838
13.0M
        INSIST(remaining > 0U);
839
13.0M
        prev = curr;
840
13.0M
        *curr++ = c;
841
13.0M
        *curr = '\0';
842
13.0M
        remaining--;
843
13.0M
      }
844
13.0M
      break;
845
13.0M
    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
554M
    }
895
554M
  } while (!done);
896
897
22.9M
  result = ISC_R_SUCCESS;
898
22.9M
done:
899
22.9M
#ifdef HAVE_FLOCKFILE
900
22.9M
  if (source->is_file) {
901
76
    funlockfile(source->input);
902
76
  }
903
22.9M
#endif /* ifdef HAVE_FLOCKFILE */
904
22.9M
  return result;
905
22.9M
}
906
907
isc_result_t
908
isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
909
15.5M
           isc_tokentype_t expect, bool eol) {
910
15.5M
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
911
15.5M
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
912
15.5M
  isc_result_t result;
913
914
15.5M
  if (expect == isc_tokentype_vpair) {
915
1.24k
    options |= ISC_LEXOPT_VPAIR;
916
15.5M
  } else if (expect == isc_tokentype_qvpair) {
917
50.5k
    options |= ISC_LEXOPT_VPAIR;
918
50.5k
    options |= ISC_LEXOPT_QVPAIR;
919
15.5M
  } else if (expect == isc_tokentype_qstring) {
920
8.63M
    options |= ISC_LEXOPT_QSTRING;
921
8.63M
  } else if (expect == isc_tokentype_number) {
922
401k
    options |= ISC_LEXOPT_NUMBER;
923
401k
  }
924
15.5M
  result = isc_lex_gettoken(lex, options, token);
925
15.5M
  if (result == ISC_R_RANGE) {
926
1.05k
    isc_lex_ungettoken(lex, token);
927
1.05k
  }
928
15.5M
  if (result != ISC_R_SUCCESS) {
929
3.22k
    return result;
930
3.22k
  }
931
932
15.5M
  if (eol && ((token->type == isc_tokentype_eol) ||
933
9.60M
        (token->type == isc_tokentype_eof)))
934
934k
  {
935
934k
    return ISC_R_SUCCESS;
936
934k
  }
937
14.6M
  if (token->type == isc_tokentype_string &&
938
14.6M
      (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
939
7.80M
  {
940
7.80M
    return ISC_R_SUCCESS;
941
7.80M
  }
942
6.80M
  if (token->type == isc_tokentype_vpair &&
943
6.80M
      expect == isc_tokentype_qvpair)
944
30.8k
  {
945
30.8k
    return ISC_R_SUCCESS;
946
30.8k
  }
947
6.77M
  if (token->type != expect) {
948
42.9k
    isc_lex_ungettoken(lex, token);
949
42.9k
    if (token->type == isc_tokentype_eol ||
950
42.9k
        token->type == isc_tokentype_eof)
951
41.1k
    {
952
41.1k
      return ISC_R_UNEXPECTEDEND;
953
41.1k
    }
954
1.80k
    if (expect == isc_tokentype_number) {
955
1.28k
      return ISC_R_BADNUMBER;
956
1.28k
    }
957
524
    return ISC_R_UNEXPECTEDTOKEN;
958
1.80k
  }
959
6.73M
  return ISC_R_SUCCESS;
960
6.77M
}
961
962
isc_result_t
963
128
isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
964
128
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
965
128
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
966
128
             ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
967
128
  isc_result_t result;
968
969
128
  result = isc_lex_gettoken(lex, options, token);
970
128
  if (result == ISC_R_RANGE) {
971
4
    isc_lex_ungettoken(lex, token);
972
4
  }
973
128
  if (result != ISC_R_SUCCESS) {
974
6
    return result;
975
6
  }
976
977
122
  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
122
  if (token->type != isc_tokentype_number) {
983
15
    isc_lex_ungettoken(lex, token);
984
15
    if (token->type == isc_tokentype_eol ||
985
15
        token->type == isc_tokentype_eof)
986
7
    {
987
7
      return ISC_R_UNEXPECTEDEND;
988
7
    }
989
8
    return ISC_R_BADNUMBER;
990
15
  }
991
107
  return ISC_R_SUCCESS;
992
122
}
993
994
void
995
6.98M
isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
996
6.98M
  inputsource *source;
997
  /*
998
   * Unget the current token.
999
   */
1000
1001
6.98M
  REQUIRE(VALID_LEX(lex));
1002
6.98M
  source = ISC_LIST_HEAD(lex->sources);
1003
6.98M
  REQUIRE(source != NULL);
1004
6.98M
  REQUIRE(tokenp != NULL);
1005
6.98M
  REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
1006
6.98M
    tokenp->type == isc_tokentype_eof);
1007
1008
6.98M
  UNUSED(tokenp);
1009
1010
6.98M
  isc_buffer_first(source->pushback);
1011
6.98M
  lex->paren_count = lex->saved_paren_count;
1012
6.98M
  source->line = source->saved_line;
1013
6.98M
  source->at_eof = false;
1014
6.98M
}
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
6.67M
isc_lex_getsourcename(isc_lex_t *lex) {
1038
6.67M
  inputsource *source;
1039
1040
6.67M
  REQUIRE(VALID_LEX(lex));
1041
6.67M
  source = ISC_LIST_HEAD(lex->sources);
1042
1043
6.67M
  if (source == NULL) {
1044
0
    return NULL;
1045
0
  }
1046
1047
6.67M
  return source->name;
1048
6.67M
}
1049
1050
unsigned long
1051
7.30M
isc_lex_getsourceline(isc_lex_t *lex) {
1052
7.30M
  inputsource *source;
1053
1054
7.30M
  REQUIRE(VALID_LEX(lex));
1055
7.30M
  source = ISC_LIST_HEAD(lex->sources);
1056
1057
7.30M
  if (source == NULL) {
1058
0
    return 0;
1059
0
  }
1060
1061
7.30M
  return source->line;
1062
7.30M
}
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
5.85M
isc_lex_isfile(isc_lex_t *lex) {
1098
5.85M
  inputsource *source;
1099
1100
5.85M
  REQUIRE(VALID_LEX(lex));
1101
1102
5.85M
  source = ISC_LIST_HEAD(lex->sources);
1103
1104
5.85M
  if (source == NULL) {
1105
0
    return false;
1106
0
  }
1107
1108
5.85M
  return source->is_file;
1109
5.85M
}