Coverage Report

Created: 2025-12-14 06:31

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.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.41k
grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) {
69
9.41k
  char *tmp;
70
71
9.41k
  tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1);
72
9.41k
  memmove(tmp, lex->data, lex->max_token + 1);
73
9.41k
  *currp = tmp + (*currp - lex->data);
74
9.41k
  if (*prevp != NULL) {
75
243
    *prevp = tmp + (*prevp - lex->data);
76
243
  }
77
9.41k
  isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
78
9.41k
  lex->data = tmp;
79
9.41k
  *remainingp += lex->max_token;
80
9.41k
  lex->max_token *= 2;
81
9.41k
}
82
83
void
84
34.9k
isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) {
85
34.9k
  isc_lex_t *lex;
86
87
  /*
88
   * Create a lexer.
89
   */
90
34.9k
  REQUIRE(lexp != NULL && *lexp == NULL);
91
92
34.9k
  if (max_token == 0U) {
93
0
    max_token = 1;
94
0
  }
95
96
34.9k
  lex = isc_mem_get(mctx, sizeof(*lex));
97
34.9k
  lex->data = isc_mem_get(mctx, max_token + 1);
98
34.9k
  lex->mctx = mctx;
99
34.9k
  lex->max_token = max_token;
100
34.9k
  lex->comments = 0;
101
34.9k
  lex->comment_ok = true;
102
34.9k
  lex->last_was_eol = true;
103
34.9k
  lex->brace_count = 0;
104
34.9k
  lex->paren_count = 0;
105
34.9k
  lex->saved_paren_count = 0;
106
34.9k
  memset(lex->specials, 0, 256);
107
34.9k
  ISC_LIST_INIT(lex->sources);
108
34.9k
  lex->magic = LEX_MAGIC;
109
110
34.9k
  *lexp = lex;
111
34.9k
}
112
113
void
114
34.9k
isc_lex_destroy(isc_lex_t **lexp) {
115
34.9k
  isc_lex_t *lex;
116
117
  /*
118
   * Destroy the lexer.
119
   */
120
121
34.9k
  REQUIRE(lexp != NULL);
122
34.9k
  lex = *lexp;
123
34.9k
  *lexp = NULL;
124
34.9k
  REQUIRE(VALID_LEX(lex));
125
126
53.4k
  while (!ISC_LIST_EMPTY(lex->sources)) {
127
18.5k
    RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS);
128
18.5k
  }
129
34.9k
  if (lex->data != NULL) {
130
34.9k
    isc_mem_put(lex->mctx, lex->data, lex->max_token + 1);
131
34.9k
  }
132
34.9k
  lex->magic = 0;
133
34.9k
  isc_mem_put(lex->mctx, lex, sizeof(*lex));
134
34.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
34.9k
isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) {
149
  /*
150
   * Set allowed lexer commenting styles.
151
   */
152
153
34.9k
  REQUIRE(VALID_LEX(lex));
154
155
34.9k
  lex->comments = comments;
156
34.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
34.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
34.9k
  REQUIRE(VALID_LEX(lex));
177
178
34.9k
  memmove(lex->specials, specials, 256);
179
34.9k
}
180
181
static void
182
new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input,
183
8.20M
     const char *name) {
184
8.20M
  inputsource *source;
185
186
8.20M
  source = isc_mem_get(lex->mctx, sizeof(*source));
187
8.20M
  *source = (inputsource){
188
8.20M
    .is_file = is_file,
189
8.20M
    .need_close = need_close,
190
8.20M
    .last_was_eol = lex->last_was_eol,
191
8.20M
    .input = input,
192
8.20M
    .name = isc_mem_strdup(lex->mctx, name),
193
8.20M
    .line = 1,
194
8.20M
    .link = ISC_LINK_INITIALIZER,
195
8.20M
  };
196
8.20M
  isc_buffer_allocate(lex->mctx, &source->pushback,
197
8.20M
          (unsigned int)lex->max_token);
198
8.20M
  ISC_LIST_PREPEND(lex->sources, source, link);
199
8.20M
}
200
201
isc_result_t
202
18
isc_lex_openfile(isc_lex_t *lex, const char *filename) {
203
18
  FILE *stream = NULL;
204
205
  /*
206
   * Open 'filename' and make it the current input source for 'lex'.
207
   */
208
209
18
  REQUIRE(VALID_LEX(lex));
210
211
18
  RETERR(isc_stdio_open(filename, "r", &stream));
212
213
5
  new_source(lex, true, true, stream, filename);
214
5
  return ISC_R_SUCCESS;
215
18
}
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
8.20M
isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) {
234
8.20M
  char name[128];
235
236
  /*
237
   * Make 'buffer' the current input source for 'lex'.
238
   */
239
240
8.20M
  REQUIRE(VALID_LEX(lex));
241
242
8.20M
  snprintf(name, sizeof(name), "buffer-%p", buffer);
243
244
8.20M
  new_source(lex, false, false, buffer, name);
245
8.20M
  return ISC_R_SUCCESS;
246
8.20M
}
247
248
isc_result_t
249
8.19M
isc_lex_close(isc_lex_t *lex) {
250
8.19M
  inputsource *source;
251
252
  /*
253
   * Close the most recently opened object (i.e. file or buffer).
254
   */
255
256
8.19M
  REQUIRE(VALID_LEX(lex));
257
258
8.19M
  source = ISC_LIST_HEAD(lex->sources);
259
8.19M
  if (source == NULL) {
260
0
    return ISC_R_NOMORE;
261
0
  }
262
263
8.19M
  ISC_LIST_UNLINK(lex->sources, source, link);
264
8.19M
  lex->last_was_eol = source->last_was_eol;
265
8.19M
  if (source->is_file) {
266
5
    if (source->need_close) {
267
5
      (void)fclose((FILE *)(source->input));
268
5
    }
269
5
  }
270
8.19M
  isc_mem_free(lex->mctx, source->name);
271
8.19M
  isc_buffer_free(&source->pushback);
272
8.19M
  isc_mem_put(lex->mctx, source, sizeof(*source));
273
274
8.19M
  return ISC_R_SUCCESS;
275
8.19M
}
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.09M
#define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL)
294
295
static void
296
19.1M
pushback(inputsource *source, int c) {
297
19.1M
  REQUIRE(source->pushback->current > 0);
298
19.1M
  if (c == EOF) {
299
15.9M
    source->at_eof = false;
300
15.9M
    return;
301
15.9M
  }
302
3.15M
  source->pushback->current--;
303
3.15M
  if (c == '\n') {
304
476k
    source->line--;
305
476k
  }
306
3.15M
}
307
308
static isc_result_t
309
382M
pushandgrow(isc_lex_t *lex, inputsource *source, int c) {
310
382M
  if (isc_buffer_availablelength(source->pushback) == 0) {
311
10.0k
    isc_buffer_t *tbuf = NULL;
312
10.0k
    unsigned int oldlen;
313
10.0k
    isc_region_t used;
314
10.0k
    isc_result_t result;
315
316
10.0k
    oldlen = isc_buffer_length(source->pushback);
317
10.0k
    isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2);
318
10.0k
    isc_buffer_usedregion(source->pushback, &used);
319
10.0k
    result = isc_buffer_copyregion(tbuf, &used);
320
10.0k
    INSIST(result == ISC_R_SUCCESS);
321
10.0k
    tbuf->current = source->pushback->current;
322
10.0k
    isc_buffer_free(&source->pushback);
323
10.0k
    source->pushback = tbuf;
324
10.0k
  }
325
382M
  isc_buffer_putuint8(source->pushback, (uint8_t)c);
326
382M
  return ISC_R_SUCCESS;
327
382M
}
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.08k
  {
371
4.08k
    if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
372
4.08k
        lex->paren_count != 0)
373
6
    {
374
6
      lex->paren_count = 0;
375
6
      return ISC_R_UNBALANCED;
376
6
    }
377
4.07k
    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.07k
    if ((options & ISC_LEXOPT_EOF) != 0) {
383
4.07k
      tokenp->type = isc_tokentype_eof;
384
4.07k
      return ISC_R_SUCCESS;
385
4.07k
    }
386
0
    return ISC_R_EOF;
387
4.07k
  }
388
389
28.2M
  isc_buffer_compact(source->pushback);
390
391
28.2M
  saved_options = options;
392
28.2M
  if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) {
393
827k
    options &= ~IWSEOL;
394
827k
  }
395
396
28.2M
  curr = lex->data;
397
28.2M
  *curr = '\0';
398
399
28.2M
  prev = NULL;
400
28.2M
  remaining = lex->max_token;
401
402
28.2M
#ifdef HAVE_FLOCKFILE
403
28.2M
  if (source->is_file) {
404
77
    flockfile(source->input);
405
77
  }
406
28.2M
#endif /* ifdef HAVE_FLOCKFILE */
407
408
530M
  do {
409
530M
    if (isc_buffer_remaininglength(source->pushback) == 0) {
410
407M
      if (source->is_file) {
411
869
        stream = source->input;
412
413
869
#if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED)
414
869
        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
869
        if (c == EOF) {
419
5
          if (ferror(stream)) {
420
3
            source->result =
421
3
              isc__errno2result(
422
3
                errno);
423
3
            result = source->result;
424
3
            goto done;
425
3
          }
426
2
          source->at_eof = true;
427
2
        }
428
407M
      } else {
429
407M
        buffer = source->input;
430
431
407M
        if (buffer->current == buffer->used) {
432
24.5M
          c = EOF;
433
24.5M
          source->at_eof = true;
434
382M
        } else {
435
382M
          c = *((unsigned char *)buffer->base +
436
382M
                buffer->current);
437
382M
          buffer->current++;
438
382M
        }
439
407M
      }
440
407M
      if (c != EOF) {
441
382M
        source->result = pushandgrow(lex, source, c);
442
382M
        if (source->result != ISC_R_SUCCESS) {
443
0
          result = source->result;
444
0
          goto done;
445
0
        }
446
382M
      }
447
407M
    }
448
449
530M
    if (!source->at_eof) {
450
505M
      if (state == lexstate_start) {
451
        /* Token has not started yet. */
452
26.3M
        source->ignored = isc_buffer_consumedlength(
453
26.3M
          source->pushback);
454
26.3M
      }
455
505M
      c = isc_buffer_getuint8(source->pushback);
456
505M
    } else {
457
24.5M
      c = EOF;
458
24.5M
    }
459
460
530M
    if (c == '\n') {
461
974k
      source->line++;
462
974k
    }
463
464
530M
    if (lex->comment_ok && !no_comments) {
465
500M
      if (!escaped && c == ';' &&
466
243k
          ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) !=
467
243k
           0))
468
215k
      {
469
215k
        saved_state = state;
470
215k
        state = lexstate_eatline;
471
215k
        no_comments = true;
472
215k
        continue;
473
500M
      } else if (c == '/' &&
474
108k
           (lex->comments &
475
108k
            (ISC_LEXCOMMENT_C |
476
108k
             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
500M
      } else if (c == '#' && ((lex->comments &
483
418k
             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
500M
    }
491
492
549M
  no_read:
493
    /* INSIST(c == EOF || (c >= 0 && c <= 255)); */
494
549M
    switch (state) {
495
34.8M
    case lexstate_start:
496
34.8M
      if (c == EOF) {
497
8.56M
        lex->last_was_eol = false;
498
8.56M
        if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 &&
499
8.56M
            lex->paren_count != 0)
500
7.24k
        {
501
7.24k
          lex->paren_count = 0;
502
7.24k
          result = ISC_R_UNBALANCED;
503
7.24k
          goto done;
504
7.24k
        }
505
8.55M
        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.55M
        if ((options & ISC_LEXOPT_EOF) == 0) {
513
333
          result = ISC_R_EOF;
514
333
          goto done;
515
333
        }
516
8.55M
        tokenp->type = isc_tokentype_eof;
517
8.55M
        done = true;
518
26.3M
      } else if (c == ' ' || c == '\t') {
519
2.72M
        if (lex->last_was_eol &&
520
184k
            (options & ISC_LEXOPT_INITIALWS) != 0)
521
158k
        {
522
158k
          lex->last_was_eol = false;
523
158k
          tokenp->type = isc_tokentype_initialws;
524
158k
          tokenp->value.as_char = c;
525
158k
          done = true;
526
158k
        }
527
23.5M
      } else if (c == '\n') {
528
493k
        if ((options & ISC_LEXOPT_EOL) != 0) {
529
392k
          tokenp->type = isc_tokentype_eol;
530
392k
          done = true;
531
392k
        }
532
493k
        lex->last_was_eol = true;
533
23.0M
      } else if (c == '\r') {
534
91.7k
        if ((options & ISC_LEXOPT_EOL) != 0) {
535
82.1k
          state = lexstate_crlf;
536
82.1k
        }
537
23.0M
      } else if (c == '"' &&
538
25.6k
           (options & ISC_LEXOPT_QSTRING) != 0)
539
22.1k
      {
540
22.1k
        lex->last_was_eol = false;
541
22.1k
        no_comments = true;
542
22.1k
        state = lexstate_qstring;
543
22.9M
      } else if (lex->specials[c]) {
544
3.91M
        lex->last_was_eol = false;
545
3.91M
        if ((c == '(' || c == ')') &&
546
3.90M
            (options & ISC_LEXOPT_DNSMULTILINE) != 0)
547
3.90M
        {
548
3.90M
          if (c == '(') {
549
3.64M
            if (lex->paren_count == 0) {
550
264k
              options &= ~IWSEOL;
551
264k
            }
552
3.64M
            lex->paren_count++;
553
3.64M
          } else {
554
259k
            if (lex->paren_count == 0) {
555
364
              result =
556
364
                ISC_R_UNBALANCED;
557
364
              goto done;
558
364
            }
559
258k
            lex->paren_count--;
560
258k
            if (lex->paren_count == 0) {
561
244k
              options = saved_options;
562
244k
            }
563
258k
          }
564
3.90M
          continue;
565
3.90M
        } 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
8.14k
        tokenp->type = isc_tokentype_special;
579
8.14k
        tokenp->value.as_char = c;
580
8.14k
        done = true;
581
19.0M
      } else if (isdigit((unsigned char)c) &&
582
2.52M
           (options & ISC_LEXOPT_NUMBER) != 0)
583
443k
      {
584
443k
        lex->last_was_eol = false;
585
443k
        if ((options & ISC_LEXOPT_OCTAL) != 0 &&
586
87
            (c == '8' || c == '9'))
587
2
        {
588
2
          state = lexstate_string;
589
443k
        } else {
590
443k
          state = lexstate_number;
591
443k
        }
592
443k
        goto no_read;
593
18.6M
      } else {
594
18.6M
        lex->last_was_eol = false;
595
18.6M
        state = lexstate_string;
596
18.6M
        goto no_read;
597
18.6M
      }
598
11.8M
      break;
599
11.8M
    case lexstate_crlf:
600
82.1k
      if (c != '\n') {
601
79.3k
        pushback(source, c);
602
79.3k
      }
603
82.1k
      tokenp->type = isc_tokentype_eol;
604
82.1k
      done = true;
605
82.1k
      lex->last_was_eol = true;
606
82.1k
      break;
607
4.64M
    case lexstate_number:
608
4.64M
      if (c == EOF || !isdigit((unsigned char)c)) {
609
443k
        if (c == ' ' || c == '\t' || c == '\r' ||
610
218k
            c == '\n' || c == EOF || lex->specials[c])
611
441k
        {
612
441k
          int base;
613
441k
          if ((options & ISC_LEXOPT_OCTAL) != 0) {
614
82
            base = 8;
615
441k
          } else if ((options &
616
441k
                ISC_LEXOPT_CNUMBER) != 0)
617
0
          {
618
0
            base = 0;
619
441k
          } else {
620
441k
            base = 10;
621
441k
          }
622
441k
          pushback(source, c);
623
624
441k
          result = isc_parse_uint32(
625
441k
            &as_ulong, lex->data, base);
626
441k
          if (result == ISC_R_SUCCESS) {
627
440k
            tokenp->type =
628
440k
              isc_tokentype_number;
629
440k
            tokenp->value.as_ulong =
630
440k
              as_ulong;
631
440k
          } 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.26k
          } else {
643
1.26k
            goto done;
644
1.26k
          }
645
440k
          done = true;
646
440k
          continue;
647
441k
        } else if ((options & ISC_LEXOPT_CNUMBER) ==
648
1.53k
               0 ||
649
0
             ((c != 'x' && c != 'X') ||
650
0
              (curr != &lex->data[1]) ||
651
0
              (lex->data[0] != '0')))
652
1.53k
        {
653
          /* Above test supports hex numbers */
654
1.53k
          state = lexstate_string;
655
1.53k
        }
656
4.19M
      } else if ((options & ISC_LEXOPT_OCTAL) != 0 &&
657
561
           (c == '8' || c == '9'))
658
2
      {
659
2
        state = lexstate_string;
660
2
      }
661
4.20M
      if (remaining == 0U) {
662
66
        grow_data(lex, &remaining, &curr, &prev);
663
66
      }
664
4.20M
      INSIST(remaining > 0U);
665
4.20M
      *curr++ = c;
666
4.20M
      *curr = '\0';
667
4.20M
      remaining--;
668
4.20M
      break;
669
451M
    case lexstate_string:
670
451M
      if (!escaped && c == '=' &&
671
807k
          (options & ISC_LEXOPT_VPAIR) != 0)
672
23.5k
      {
673
23.5k
        if (remaining == 0U) {
674
7
          grow_data(lex, &remaining, &curr,
675
7
              &prev);
676
7
        }
677
23.5k
        INSIST(remaining > 0U);
678
23.5k
        *curr++ = c;
679
23.5k
        *curr = '\0';
680
23.5k
        remaining--;
681
23.5k
        state = lexstate_vpairstart;
682
23.5k
        break;
683
23.5k
      }
684
451M
      FALLTHROUGH;
685
451M
    case lexstate_vpairstart:
686
451M
      if (state == lexstate_vpairstart) {
687
23.5k
        if (c == '"' &&
688
1.89k
            (options & ISC_LEXOPT_QVPAIR) != 0)
689
1.70k
        {
690
1.70k
          no_comments = true;
691
1.70k
          state = lexstate_qvpair;
692
1.70k
          break;
693
1.70k
        }
694
21.8k
        state = lexstate_vpair;
695
21.8k
      }
696
451M
      FALLTHROUGH;
697
479M
    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
479M
      if (c == '\r' || c == '\n' || c == EOF ||
703
463M
          (!escaped &&
704
462M
           (c == ' ' || c == '\t' || lex->specials[c])))
705
18.6M
      {
706
18.6M
        pushback(source, c);
707
18.6M
        if (source->result != ISC_R_SUCCESS) {
708
0
          result = source->result;
709
0
          goto done;
710
0
        }
711
18.6M
        if (escaped && c == EOF) {
712
35
          result = ISC_R_UNEXPECTEDEND;
713
35
          goto done;
714
35
        }
715
18.6M
        tokenp->type = (state == lexstate_string)
716
18.6M
                   ? isc_tokentype_string
717
18.6M
                   : isc_tokentype_vpair;
718
18.6M
        tokenp->value.as_textregion.base = lex->data;
719
18.6M
        tokenp->value.as_textregion.length =
720
18.6M
          (unsigned int)(lex->max_token -
721
18.6M
                   remaining);
722
18.6M
        done = true;
723
18.6M
        continue;
724
18.6M
      }
725
461M
      if ((options & ISC_LEXOPT_ESCAPE) != 0) {
726
299M
        escaped = (!escaped && c == '\\') ? true
727
299M
                  : false;
728
299M
      }
729
461M
      if (remaining == 0U) {
730
9.10k
        grow_data(lex, &remaining, &curr, &prev);
731
9.10k
      }
732
461M
      INSIST(remaining > 0U);
733
461M
      *curr++ = c;
734
461M
      *curr = '\0';
735
461M
      remaining--;
736
461M
      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
19.4M
    case lexstate_eatline:
783
19.4M
      if ((c == '\n') || (c == EOF)) {
784
215k
        no_comments = false;
785
215k
        state = saved_state;
786
215k
        goto no_read;
787
215k
      }
788
19.2M
      break;
789
19.2M
    case lexstate_qstring:
790
10.5M
    case lexstate_qvpair:
791
10.5M
      if (c == EOF) {
792
234
        result = ISC_R_UNEXPECTEDEND;
793
234
        goto done;
794
234
      }
795
10.5M
      if (c == '"') {
796
27.2k
        if (escaped) {
797
3.62k
          escaped = false;
798
          /*
799
           * Overwrite the preceding backslash.
800
           */
801
3.62k
          INSIST(prev != NULL);
802
3.62k
          *prev = '"';
803
23.5k
        } else {
804
23.5k
          tokenp->type =
805
23.5k
            (state == lexstate_qstring)
806
23.5k
              ? isc_tokentype_qstring
807
23.5k
              : isc_tokentype_qvpair;
808
23.5k
          tokenp->value.as_textregion.base =
809
23.5k
            lex->data;
810
23.5k
          tokenp->value.as_textregion.length =
811
23.5k
            (unsigned int)(lex->max_token -
812
23.5k
                     remaining);
813
23.5k
          no_comments = false;
814
23.5k
          done = true;
815
23.5k
        }
816
10.5M
      } else {
817
10.5M
        if (c == '\n' && !escaped &&
818
44
            (options & ISC_LEXOPT_QSTRINGMULTILINE) ==
819
44
              0)
820
44
        {
821
44
          pushback(source, c);
822
44
          result = ISC_R_UNBALANCEDQUOTES;
823
44
          goto done;
824
44
        }
825
10.5M
        if (c == '\\' && !escaped) {
826
119k
          escaped = true;
827
10.4M
        } else {
828
10.4M
          escaped = false;
829
10.4M
        }
830
10.5M
        if (remaining == 0U) {
831
244
          grow_data(lex, &remaining, &curr,
832
244
              &prev);
833
244
        }
834
10.5M
        INSIST(remaining > 0U);
835
10.5M
        prev = curr;
836
10.5M
        *curr++ = c;
837
10.5M
        *curr = '\0';
838
10.5M
        remaining--;
839
10.5M
      }
840
10.5M
      break;
841
10.5M
    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.2M
  result = ISC_R_SUCCESS;
894
28.2M
done:
895
28.2M
#ifdef HAVE_FLOCKFILE
896
28.2M
  if (source->is_file) {
897
77
    funlockfile(source->input);
898
77
  }
899
28.2M
#endif /* ifdef HAVE_FLOCKFILE */
900
28.2M
  return result;
901
28.2M
}
902
903
isc_result_t
904
isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token,
905
19.0M
           isc_tokentype_t expect, bool eol) {
906
19.0M
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
907
19.0M
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE;
908
19.0M
  isc_result_t result;
909
910
19.0M
  if (expect == isc_tokentype_vpair) {
911
931
    options |= ISC_LEXOPT_VPAIR;
912
19.0M
  } else if (expect == isc_tokentype_qvpair) {
913
38.9k
    options |= ISC_LEXOPT_VPAIR;
914
38.9k
    options |= ISC_LEXOPT_QVPAIR;
915
18.9M
  } else if (expect == isc_tokentype_qstring) {
916
9.21M
    options |= ISC_LEXOPT_QSTRING;
917
9.77M
  } else if (expect == isc_tokentype_number) {
918
471k
    options |= ISC_LEXOPT_NUMBER;
919
471k
  }
920
19.0M
  result = isc_lex_gettoken(lex, options, token);
921
19.0M
  if (result == ISC_R_RANGE) {
922
1.16k
    isc_lex_ungettoken(lex, token);
923
1.16k
  }
924
19.0M
  if (result != ISC_R_SUCCESS) {
925
3.99k
    return result;
926
3.99k
  }
927
928
19.0M
  if (eol && ((token->type == isc_tokentype_eol) ||
929
10.0M
        (token->type == isc_tokentype_eof)))
930
474k
  {
931
474k
    return ISC_R_SUCCESS;
932
474k
  }
933
18.5M
  if (token->type == isc_tokentype_string &&
934
18.0M
      (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair))
935
8.92M
  {
936
8.92M
    return ISC_R_SUCCESS;
937
8.92M
  }
938
9.62M
  if (token->type == isc_tokentype_vpair &&
939
21.8k
      expect == isc_tokentype_qvpair)
940
21.4k
  {
941
21.4k
    return ISC_R_SUCCESS;
942
21.4k
  }
943
9.60M
  if (token->type != expect) {
944
57.6k
    isc_lex_ungettoken(lex, token);
945
57.6k
    if (token->type == isc_tokentype_eol ||
946
57.3k
        token->type == isc_tokentype_eof)
947
55.7k
    {
948
55.7k
      return ISC_R_UNEXPECTEDEND;
949
55.7k
    }
950
1.86k
    if (expect == isc_tokentype_number) {
951
1.44k
      return ISC_R_BADNUMBER;
952
1.44k
    }
953
419
    return ISC_R_UNEXPECTEDTOKEN;
954
1.86k
  }
955
9.54M
  return ISC_R_SUCCESS;
956
9.60M
}
957
958
isc_result_t
959
104
isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) {
960
104
  unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF |
961
104
             ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE |
962
104
             ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL;
963
104
  isc_result_t result;
964
965
104
  result = isc_lex_gettoken(lex, options, token);
966
104
  if (result == ISC_R_RANGE) {
967
4
    isc_lex_ungettoken(lex, token);
968
4
  }
969
104
  if (result != ISC_R_SUCCESS) {
970
6
    return result;
971
6
  }
972
973
98
  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
98
  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
78
  return ISC_R_SUCCESS;
988
98
}
989
990
void
991
8.75M
isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) {
992
8.75M
  inputsource *source;
993
  /*
994
   * Unget the current token.
995
   */
996
997
8.75M
  REQUIRE(VALID_LEX(lex));
998
8.75M
  source = ISC_LIST_HEAD(lex->sources);
999
8.75M
  REQUIRE(source != NULL);
1000
8.75M
  REQUIRE(tokenp != NULL);
1001
8.75M
  REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 ||
1002
8.75M
    tokenp->type == isc_tokentype_eof);
1003
1004
8.75M
  UNUSED(tokenp);
1005
1006
8.75M
  isc_buffer_first(source->pushback);
1007
8.75M
  lex->paren_count = lex->saved_paren_count;
1008
8.75M
  source->line = source->saved_line;
1009
8.75M
  source->at_eof = false;
1010
8.75M
}
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
8.55M
isc_lex_getsourcename(isc_lex_t *lex) {
1034
8.55M
  inputsource *source;
1035
1036
8.55M
  REQUIRE(VALID_LEX(lex));
1037
8.55M
  source = ISC_LIST_HEAD(lex->sources);
1038
1039
8.55M
  if (source == NULL) {
1040
0
    return NULL;
1041
0
  }
1042
1043
8.55M
  return source->name;
1044
8.55M
}
1045
1046
unsigned long
1047
9.20M
isc_lex_getsourceline(isc_lex_t *lex) {
1048
9.20M
  inputsource *source;
1049
1050
9.20M
  REQUIRE(VALID_LEX(lex));
1051
9.20M
  source = ISC_LIST_HEAD(lex->sources);
1052
1053
9.20M
  if (source == NULL) {
1054
0
    return 0;
1055
0
  }
1056
1057
9.20M
  return source->line;
1058
9.20M
}
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
8.12M
isc_lex_isfile(isc_lex_t *lex) {
1094
8.12M
  inputsource *source;
1095
1096
8.12M
  REQUIRE(VALID_LEX(lex));
1097
1098
8.12M
  source = ISC_LIST_HEAD(lex->sources);
1099
1100
8.12M
  if (source == NULL) {
1101
0
    return false;
1102
0
  }
1103
1104
8.12M
  return source->is_file;
1105
8.12M
}