/* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. * * See the COPYRIGHT file distributed with this work for additional * information regarding copyright ownership. */ /*! \file */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct inputsource { isc_result_t result; bool is_file; bool need_close; bool at_eof; bool last_was_eol; isc_buffer_t *pushback; unsigned int ignored; void *input; char *name; unsigned long line; unsigned long saved_line; ISC_LINK(struct inputsource) link; } inputsource; #define LEX_MAGIC ISC_MAGIC('L', 'e', 'x', '!') #define VALID_LEX(l) ISC_MAGIC_VALID(l, LEX_MAGIC) struct isc_lex { /* Unlocked. */ unsigned int magic; isc_mem_t *mctx; size_t max_token; char *data; unsigned int comments; bool comment_ok; bool last_was_eol; unsigned int brace_count; unsigned int paren_count; unsigned int saved_paren_count; isc_lexspecials_t specials; LIST(struct inputsource) sources; }; static isc_result_t grow_data(isc_lex_t *lex, size_t *remainingp, char **currp, char **prevp) { char *tmp; tmp = isc_mem_get(lex->mctx, lex->max_token * 2 + 1); memmove(tmp, lex->data, lex->max_token + 1); *currp = tmp + (*currp - lex->data); if (*prevp != NULL) { *prevp = tmp + (*prevp - lex->data); } isc_mem_put(lex->mctx, lex->data, lex->max_token + 1); lex->data = tmp; *remainingp += lex->max_token; lex->max_token *= 2; return (ISC_R_SUCCESS); } isc_result_t isc_lex_create(isc_mem_t *mctx, size_t max_token, isc_lex_t **lexp) { isc_lex_t *lex; /* * Create a lexer. */ REQUIRE(lexp != NULL && *lexp == NULL); if (max_token == 0U) { max_token = 1; } lex = isc_mem_get(mctx, sizeof(*lex)); lex->data = isc_mem_get(mctx, max_token + 1); lex->mctx = mctx; lex->max_token = max_token; lex->comments = 0; lex->comment_ok = true; lex->last_was_eol = true; lex->brace_count = 0; lex->paren_count = 0; lex->saved_paren_count = 0; memset(lex->specials, 0, 256); INIT_LIST(lex->sources); lex->magic = LEX_MAGIC; *lexp = lex; return (ISC_R_SUCCESS); } void isc_lex_destroy(isc_lex_t **lexp) { isc_lex_t *lex; /* * Destroy the lexer. */ REQUIRE(lexp != NULL); lex = *lexp; *lexp = NULL; REQUIRE(VALID_LEX(lex)); while (!EMPTY(lex->sources)) { RUNTIME_CHECK(isc_lex_close(lex) == ISC_R_SUCCESS); } if (lex->data != NULL) { isc_mem_put(lex->mctx, lex->data, lex->max_token + 1); } lex->magic = 0; isc_mem_put(lex->mctx, lex, sizeof(*lex)); } unsigned int isc_lex_getcomments(isc_lex_t *lex) { /* * Return the current lexer commenting styles. */ REQUIRE(VALID_LEX(lex)); return (lex->comments); } void isc_lex_setcomments(isc_lex_t *lex, unsigned int comments) { /* * Set allowed lexer commenting styles. */ REQUIRE(VALID_LEX(lex)); lex->comments = comments; } void isc_lex_getspecials(isc_lex_t *lex, isc_lexspecials_t specials) { /* * Put the current list of specials into 'specials'. */ REQUIRE(VALID_LEX(lex)); memmove(specials, lex->specials, 256); } void isc_lex_setspecials(isc_lex_t *lex, isc_lexspecials_t specials) { /* * The characters in 'specials' are returned as tokens. Along with * whitespace, they delimit strings and numbers. */ REQUIRE(VALID_LEX(lex)); memmove(lex->specials, specials, 256); } static isc_result_t new_source(isc_lex_t *lex, bool is_file, bool need_close, void *input, const char *name) { inputsource *source; source = isc_mem_get(lex->mctx, sizeof(*source)); source->result = ISC_R_SUCCESS; source->is_file = is_file; source->need_close = need_close; source->at_eof = false; source->last_was_eol = lex->last_was_eol; source->input = input; source->name = isc_mem_strdup(lex->mctx, name); source->pushback = NULL; isc_buffer_allocate(lex->mctx, &source->pushback, (unsigned int)lex->max_token); source->ignored = 0; source->line = 1; ISC_LIST_INITANDPREPEND(lex->sources, source, link); return (ISC_R_SUCCESS); } isc_result_t isc_lex_openfile(isc_lex_t *lex, const char *filename) { isc_result_t result; FILE *stream = NULL; /* * Open 'filename' and make it the current input source for 'lex'. */ REQUIRE(VALID_LEX(lex)); result = isc_stdio_open(filename, "r", &stream); if (result != ISC_R_SUCCESS) { return (result); } result = new_source(lex, true, true, stream, filename); if (result != ISC_R_SUCCESS) { (void)fclose(stream); } return (result); } isc_result_t isc_lex_openstream(isc_lex_t *lex, FILE *stream) { char name[128]; /* * Make 'stream' the current input source for 'lex'. */ REQUIRE(VALID_LEX(lex)); snprintf(name, sizeof(name), "stream-%p", stream); return (new_source(lex, true, false, stream, name)); } isc_result_t isc_lex_openbuffer(isc_lex_t *lex, isc_buffer_t *buffer) { char name[128]; /* * Make 'buffer' the current input source for 'lex'. */ REQUIRE(VALID_LEX(lex)); snprintf(name, sizeof(name), "buffer-%p", buffer); return (new_source(lex, false, false, buffer, name)); } isc_result_t isc_lex_close(isc_lex_t *lex) { inputsource *source; /* * Close the most recently opened object (i.e. file or buffer). */ REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (ISC_R_NOMORE); } ISC_LIST_UNLINK(lex->sources, source, link); lex->last_was_eol = source->last_was_eol; if (source->is_file) { if (source->need_close) { (void)fclose((FILE *)(source->input)); } } isc_mem_free(lex->mctx, source->name); isc_buffer_free(&source->pushback); isc_mem_put(lex->mctx, source, sizeof(*source)); return (ISC_R_SUCCESS); } typedef enum { lexstate_start, lexstate_crlf, lexstate_string, lexstate_number, lexstate_maybecomment, lexstate_ccomment, lexstate_ccommentend, lexstate_eatline, lexstate_qstring, lexstate_btext, lexstate_vpair, lexstate_vpairstart, lexstate_qvpair, } lexstate; #define IWSEOL (ISC_LEXOPT_INITIALWS | ISC_LEXOPT_EOL) static void pushback(inputsource *source, int c) { REQUIRE(source->pushback->current > 0); if (c == EOF) { source->at_eof = false; return; } source->pushback->current--; if (c == '\n') { source->line--; } } static isc_result_t pushandgrow(isc_lex_t *lex, inputsource *source, int c) { if (isc_buffer_availablelength(source->pushback) == 0) { isc_buffer_t *tbuf = NULL; unsigned int oldlen; isc_region_t used; isc_result_t result; oldlen = isc_buffer_length(source->pushback); isc_buffer_allocate(lex->mctx, &tbuf, oldlen * 2); isc_buffer_usedregion(source->pushback, &used); result = isc_buffer_copyregion(tbuf, &used); INSIST(result == ISC_R_SUCCESS); tbuf->current = source->pushback->current; isc_buffer_free(&source->pushback); source->pushback = tbuf; } isc_buffer_putuint8(source->pushback, (uint8_t)c); return (ISC_R_SUCCESS); } isc_result_t isc_lex_gettoken(isc_lex_t *lex, unsigned int options, isc_token_t *tokenp) { inputsource *source; int c; bool done = false; bool no_comments = false; bool escaped = false; lexstate state = lexstate_start; lexstate saved_state = lexstate_start; isc_buffer_t *buffer; FILE *stream; char *curr, *prev; size_t remaining; uint32_t as_ulong; unsigned int saved_options; isc_result_t result; /* * Get the next token. */ REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); REQUIRE(tokenp != NULL); if (source == NULL) { if ((options & ISC_LEXOPT_NOMORE) != 0) { tokenp->type = isc_tokentype_nomore; return (ISC_R_SUCCESS); } return (ISC_R_NOMORE); } if (source->result != ISC_R_SUCCESS) { return (source->result); } lex->saved_paren_count = lex->paren_count; source->saved_line = source->line; if (isc_buffer_remaininglength(source->pushback) == 0 && source->at_eof) { if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count != 0) { lex->paren_count = 0; return (ISC_R_UNBALANCED); } if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0) { lex->brace_count = 0; return (ISC_R_UNBALANCED); } if ((options & ISC_LEXOPT_EOF) != 0) { tokenp->type = isc_tokentype_eof; return (ISC_R_SUCCESS); } return (ISC_R_EOF); } isc_buffer_compact(source->pushback); saved_options = options; if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count > 0) { options &= ~IWSEOL; } curr = lex->data; *curr = '\0'; prev = NULL; remaining = lex->max_token; #ifdef HAVE_FLOCKFILE if (source->is_file) { flockfile(source->input); } #endif /* ifdef HAVE_FLOCKFILE */ do { if (isc_buffer_remaininglength(source->pushback) == 0) { if (source->is_file) { stream = source->input; #if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) c = getc_unlocked(stream); #else /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */ c = getc(stream); #endif /* if defined(HAVE_FLOCKFILE) && defined(HAVE_GETC_UNLOCKED) */ if (c == EOF) { if (ferror(stream)) { source->result = ISC_R_IOERROR; result = source->result; goto done; } source->at_eof = true; } } else { buffer = source->input; if (buffer->current == buffer->used) { c = EOF; source->at_eof = true; } else { c = *((unsigned char *)buffer->base + buffer->current); buffer->current++; } } if (c != EOF) { source->result = pushandgrow(lex, source, c); if (source->result != ISC_R_SUCCESS) { result = source->result; goto done; } } } if (!source->at_eof) { if (state == lexstate_start) { /* Token has not started yet. */ source->ignored = isc_buffer_consumedlength( source->pushback); } c = isc_buffer_getuint8(source->pushback); } else { c = EOF; } if (c == '\n') { source->line++; } if (lex->comment_ok && !no_comments) { if (!escaped && c == ';' && ((lex->comments & ISC_LEXCOMMENT_DNSMASTERFILE) != 0)) { saved_state = state; state = lexstate_eatline; no_comments = true; continue; } else if (c == '/' && (lex->comments & (ISC_LEXCOMMENT_C | ISC_LEXCOMMENT_CPLUSPLUS)) != 0) { saved_state = state; state = lexstate_maybecomment; no_comments = true; continue; } else if (c == '#' && ((lex->comments & ISC_LEXCOMMENT_SHELL) != 0)) { saved_state = state; state = lexstate_eatline; no_comments = true; continue; } } no_read: /* INSIST(c == EOF || (c >= 0 && c <= 255)); */ switch (state) { case lexstate_start: if (c == EOF) { lex->last_was_eol = false; if ((options & ISC_LEXOPT_DNSMULTILINE) != 0 && lex->paren_count != 0) { lex->paren_count = 0; result = ISC_R_UNBALANCED; goto done; } if ((options & ISC_LEXOPT_BTEXT) != 0 && lex->brace_count != 0) { lex->brace_count = 0; result = ISC_R_UNBALANCED; goto done; } if ((options & ISC_LEXOPT_EOF) == 0) { result = ISC_R_EOF; goto done; } tokenp->type = isc_tokentype_eof; done = true; } else if (c == ' ' || c == '\t') { if (lex->last_was_eol && (options & ISC_LEXOPT_INITIALWS) != 0) { lex->last_was_eol = false; tokenp->type = isc_tokentype_initialws; tokenp->value.as_char = c; done = true; } } else if (c == '\n') { if ((options & ISC_LEXOPT_EOL) != 0) { tokenp->type = isc_tokentype_eol; done = true; } lex->last_was_eol = true; } else if (c == '\r') { if ((options & ISC_LEXOPT_EOL) != 0) { state = lexstate_crlf; } } else if (c == '"' && (options & ISC_LEXOPT_QSTRING) != 0) { lex->last_was_eol = false; no_comments = true; state = lexstate_qstring; } else if (lex->specials[c]) { lex->last_was_eol = false; if ((c == '(' || c == ')') && (options & ISC_LEXOPT_DNSMULTILINE) != 0) { if (c == '(') { if (lex->paren_count == 0) { options &= ~IWSEOL; } lex->paren_count++; } else { if (lex->paren_count == 0) { result = ISC_R_UNBALANCED; goto done; } lex->paren_count--; if (lex->paren_count == 0) { options = saved_options; } } continue; } else if (c == '{' && (options & ISC_LEXOPT_BTEXT) != 0) { if (lex->brace_count != 0) { result = ISC_R_UNBALANCED; goto done; } lex->brace_count++; options &= ~IWSEOL; state = lexstate_btext; no_comments = true; continue; } tokenp->type = isc_tokentype_special; tokenp->value.as_char = c; done = true; } else if (isdigit((unsigned char)c) && (options & ISC_LEXOPT_NUMBER) != 0) { lex->last_was_eol = false; if ((options & ISC_LEXOPT_OCTAL) != 0 && (c == '8' || c == '9')) { state = lexstate_string; } else { state = lexstate_number; } goto no_read; } else { lex->last_was_eol = false; state = lexstate_string; goto no_read; } break; case lexstate_crlf: if (c != '\n') { pushback(source, c); } tokenp->type = isc_tokentype_eol; done = true; lex->last_was_eol = true; break; case lexstate_number: if (c == EOF || !isdigit((unsigned char)c)) { if (c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == EOF || lex->specials[c]) { int base; if ((options & ISC_LEXOPT_OCTAL) != 0) { base = 8; } else if ((options & ISC_LEXOPT_CNUMBER) != 0) { base = 0; } else { base = 10; } pushback(source, c); result = isc_parse_uint32( &as_ulong, lex->data, base); if (result == ISC_R_SUCCESS) { tokenp->type = isc_tokentype_number; tokenp->value.as_ulong = as_ulong; } else if (result == ISC_R_BADNUMBER) { isc_tokenvalue_t *v; tokenp->type = isc_tokentype_string; v = &(tokenp->value); v->as_textregion.base = lex->data; v->as_textregion.length = (unsigned int)(lex->max_token - remaining); } else { goto done; } done = true; continue; } else if ((options & ISC_LEXOPT_CNUMBER) == 0 || ((c != 'x' && c != 'X') || (curr != &lex->data[1]) || (lex->data[0] != '0'))) { /* Above test supports hex numbers */ state = lexstate_string; } } else if ((options & ISC_LEXOPT_OCTAL) != 0 && (c == '8' || c == '9')) { state = lexstate_string; } if (remaining == 0U) { result = grow_data(lex, &remaining, &curr, &prev); if (result != ISC_R_SUCCESS) { goto done; } } INSIST(remaining > 0U); *curr++ = c; *curr = '\0'; remaining--; break; case lexstate_string: if (!escaped && c == '=' && (options & ISC_LEXOPT_VPAIR) != 0) { if (remaining == 0U) { result = grow_data(lex, &remaining, &curr, &prev); if (result != ISC_R_SUCCESS) { goto done; } } INSIST(remaining > 0U); *curr++ = c; *curr = '\0'; remaining--; state = lexstate_vpairstart; break; } FALLTHROUGH; case lexstate_vpairstart: if (state == lexstate_vpairstart) { if (c == '"' && (options & ISC_LEXOPT_QVPAIR) != 0) { no_comments = true; state = lexstate_qvpair; break; } state = lexstate_vpair; } FALLTHROUGH; case lexstate_vpair: /* * EOF needs to be checked before lex->specials[c] * as lex->specials[EOF] is not a good idea. */ if (c == '\r' || c == '\n' || c == EOF || (!escaped && (c == ' ' || c == '\t' || lex->specials[c]))) { pushback(source, c); if (source->result != ISC_R_SUCCESS) { result = source->result; goto done; } if (escaped && c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; } tokenp->type = (state == lexstate_string) ? isc_tokentype_string : isc_tokentype_vpair; tokenp->value.as_textregion.base = lex->data; tokenp->value.as_textregion.length = (unsigned int)(lex->max_token - remaining); done = true; continue; } if ((options & ISC_LEXOPT_ESCAPE) != 0) { escaped = (!escaped && c == '\\') ? true : false; } if (remaining == 0U) { result = grow_data(lex, &remaining, &curr, &prev); if (result != ISC_R_SUCCESS) { goto done; } } INSIST(remaining > 0U); *curr++ = c; *curr = '\0'; remaining--; break; case lexstate_maybecomment: if (c == '*' && (lex->comments & ISC_LEXCOMMENT_C) != 0) { state = lexstate_ccomment; continue; } else if (c == '/' && (lex->comments & ISC_LEXCOMMENT_CPLUSPLUS) != 0) { state = lexstate_eatline; continue; } pushback(source, c); c = '/'; no_comments = false; state = saved_state; goto no_read; case lexstate_ccomment: if (c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; } if (c == '*') { state = lexstate_ccommentend; } break; case lexstate_ccommentend: if (c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; } if (c == '/') { /* * C-style comments become a single space. * We do this to ensure that a comment will * act as a delimiter for strings and * numbers. */ c = ' '; no_comments = false; state = saved_state; goto no_read; } else if (c != '*') { state = lexstate_ccomment; } break; case lexstate_eatline: if ((c == '\n') || (c == EOF)) { no_comments = false; state = saved_state; goto no_read; } break; case lexstate_qstring: case lexstate_qvpair: if (c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; } if (c == '"') { if (escaped) { escaped = false; /* * Overwrite the preceding backslash. */ INSIST(prev != NULL); *prev = '"'; } else { tokenp->type = (state == lexstate_qstring) ? isc_tokentype_qstring : isc_tokentype_qvpair; tokenp->value.as_textregion.base = lex->data; tokenp->value.as_textregion.length = (unsigned int)(lex->max_token - remaining); no_comments = false; done = true; } } else { if (c == '\n' && !escaped && (options & ISC_LEXOPT_QSTRINGMULTILINE) == 0) { pushback(source, c); result = ISC_R_UNBALANCEDQUOTES; goto done; } if (c == '\\' && !escaped) { escaped = true; } else { escaped = false; } if (remaining == 0U) { result = grow_data(lex, &remaining, &curr, &prev); if (result != ISC_R_SUCCESS) { goto done; } } INSIST(remaining > 0U); prev = curr; *curr++ = c; *curr = '\0'; remaining--; } break; case lexstate_btext: if (c == EOF) { result = ISC_R_UNEXPECTEDEND; goto done; } if (c == '{') { if (escaped) { escaped = false; } else { lex->brace_count++; } } else if (c == '}') { if (escaped) { escaped = false; } else { INSIST(lex->brace_count > 0); lex->brace_count--; } if (lex->brace_count == 0) { tokenp->type = isc_tokentype_btext; tokenp->value.as_textregion.base = lex->data; tokenp->value.as_textregion.length = (unsigned int)(lex->max_token - remaining); no_comments = false; done = true; break; } } if (c == '\\' && !escaped) { escaped = true; } else { escaped = false; } if (remaining == 0U) { result = grow_data(lex, &remaining, &curr, &prev); if (result != ISC_R_SUCCESS) { goto done; } } INSIST(remaining > 0U); prev = curr; *curr++ = c; *curr = '\0'; remaining--; break; default: FATAL_ERROR(__FILE__, __LINE__, "Unexpected state %d", state); } } while (!done); result = ISC_R_SUCCESS; done: #ifdef HAVE_FLOCKFILE if (source->is_file) { funlockfile(source->input); } #endif /* ifdef HAVE_FLOCKFILE */ return (result); } isc_result_t isc_lex_getmastertoken(isc_lex_t *lex, isc_token_t *token, isc_tokentype_t expect, bool eol) { unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE; isc_result_t result; if (expect == isc_tokentype_vpair) { options |= ISC_LEXOPT_VPAIR; } else if (expect == isc_tokentype_qvpair) { options |= ISC_LEXOPT_VPAIR; options |= ISC_LEXOPT_QVPAIR; } else if (expect == isc_tokentype_qstring) { options |= ISC_LEXOPT_QSTRING; } else if (expect == isc_tokentype_number) { options |= ISC_LEXOPT_NUMBER; } result = isc_lex_gettoken(lex, options, token); if (result == ISC_R_RANGE) { isc_lex_ungettoken(lex, token); } if (result != ISC_R_SUCCESS) { return (result); } if (eol && ((token->type == isc_tokentype_eol) || (token->type == isc_tokentype_eof))) { return (ISC_R_SUCCESS); } if (token->type == isc_tokentype_string && (expect == isc_tokentype_qstring || expect == isc_tokentype_qvpair)) { return (ISC_R_SUCCESS); } if (token->type == isc_tokentype_vpair && expect == isc_tokentype_qvpair) { return (ISC_R_SUCCESS); } if (token->type != expect) { isc_lex_ungettoken(lex, token); if (token->type == isc_tokentype_eol || token->type == isc_tokentype_eof) { return (ISC_R_UNEXPECTEDEND); } if (expect == isc_tokentype_number) { return (ISC_R_BADNUMBER); } return (ISC_R_UNEXPECTEDTOKEN); } return (ISC_R_SUCCESS); } isc_result_t isc_lex_getoctaltoken(isc_lex_t *lex, isc_token_t *token, bool eol) { unsigned int options = ISC_LEXOPT_EOL | ISC_LEXOPT_EOF | ISC_LEXOPT_DNSMULTILINE | ISC_LEXOPT_ESCAPE | ISC_LEXOPT_NUMBER | ISC_LEXOPT_OCTAL; isc_result_t result; result = isc_lex_gettoken(lex, options, token); if (result == ISC_R_RANGE) { isc_lex_ungettoken(lex, token); } if (result != ISC_R_SUCCESS) { return (result); } if (eol && ((token->type == isc_tokentype_eol) || (token->type == isc_tokentype_eof))) { return (ISC_R_SUCCESS); } if (token->type != isc_tokentype_number) { isc_lex_ungettoken(lex, token); if (token->type == isc_tokentype_eol || token->type == isc_tokentype_eof) { return (ISC_R_UNEXPECTEDEND); } return (ISC_R_BADNUMBER); } return (ISC_R_SUCCESS); } void isc_lex_ungettoken(isc_lex_t *lex, isc_token_t *tokenp) { inputsource *source; /* * Unget the current token. */ REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); REQUIRE(source != NULL); REQUIRE(tokenp != NULL); REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 || tokenp->type == isc_tokentype_eof); UNUSED(tokenp); isc_buffer_first(source->pushback); lex->paren_count = lex->saved_paren_count; source->line = source->saved_line; source->at_eof = false; } void isc_lex_getlasttokentext(isc_lex_t *lex, isc_token_t *tokenp, isc_region_t *r) { inputsource *source; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); REQUIRE(source != NULL); REQUIRE(tokenp != NULL); REQUIRE(isc_buffer_consumedlength(source->pushback) != 0 || tokenp->type == isc_tokentype_eof); UNUSED(tokenp); INSIST(source->ignored <= isc_buffer_consumedlength(source->pushback)); r->base = (unsigned char *)isc_buffer_base(source->pushback) + source->ignored; r->length = isc_buffer_consumedlength(source->pushback) - source->ignored; } char * isc_lex_getsourcename(isc_lex_t *lex) { inputsource *source; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (NULL); } return (source->name); } unsigned long isc_lex_getsourceline(isc_lex_t *lex) { inputsource *source; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (0); } return (source->line); } isc_result_t isc_lex_setsourcename(isc_lex_t *lex, const char *name) { inputsource *source; char *newname; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (ISC_R_NOTFOUND); } newname = isc_mem_strdup(lex->mctx, name); isc_mem_free(lex->mctx, source->name); source->name = newname; return (ISC_R_SUCCESS); } isc_result_t isc_lex_setsourceline(isc_lex_t *lex, unsigned long line) { inputsource *source; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (ISC_R_NOTFOUND); } source->line = line; return (ISC_R_SUCCESS); } bool isc_lex_isfile(isc_lex_t *lex) { inputsource *source; REQUIRE(VALID_LEX(lex)); source = HEAD(lex->sources); if (source == NULL) { return (false); } return (source->is_file); }