8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-30 18:43:03 +01:00
firebird-mirror/src/qli/lex.cpp

1275 lines
27 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Command Oriented Query Language
* MODULE: lex.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Lexical analyser
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
*
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-04-29 00:36:29 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "../qli/dtr.h"
#include "../qli/parse.h"
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#include "../qli/all_proto.h"
#include "../qli/err_proto.h"
#include "../qli/hsh_proto.h"
#include "../qli/lex_proto.h"
#include "../qli/proc_proto.h"
#include "../jrd/utl_proto.h"
#include "../jrd/gdsassert.h"
#include "../common/utils_proto.h"
#include "../common/classes/TempFile.h"
2001-05-23 15:26:42 +02:00
using MsgFormat::SafeArg;
2001-07-12 07:46:06 +02:00
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
//#ifdef HAVE_CTYPES_H
//#include <ctypes.h>
//#endif
2001-07-12 07:46:06 +02:00
2001-05-23 15:26:42 +02:00
#ifdef VMS
#include <descrip.h>
2004-05-16 03:42:11 +02:00
const SLONG LIB$_INPSTRTRU = 0x15821c;
2001-05-23 15:26:42 +02:00
#endif
#if (defined WIN_NT)
#include <io.h> // isatty
2001-05-23 15:26:42 +02:00
#endif
2004-05-07 09:57:46 +02:00
static const char* SCRATCH = "fb_query_";
2003-07-06 09:04:03 +02:00
2004-05-16 03:42:11 +02:00
const char* FOPEN_INPUT_TYPE = "r";
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
static bool get_line(FILE *, TEXT *, USHORT);
static int nextchar(const bool);
static void next_line(const bool);
2003-10-17 14:55:17 +02:00
static void retchar();
2003-09-10 19:52:12 +02:00
static bool scan_number(SSHORT, TEXT **);
2001-05-23 15:26:42 +02:00
static int skip_white(void);
2004-02-02 12:02:12 +01:00
static qli_lls* QLI_statements;
2001-05-23 15:26:42 +02:00
static int QLI_position;
2004-04-29 00:36:29 +02:00
static FILE *input_file = NULL, *trace_file = NULL;
static char trace_file_name[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
static SLONG trans_limit;
2004-05-16 03:42:11 +02:00
const SLONG TRANS_LIMIT = 10;
2001-05-23 15:26:42 +02:00
const char CHR_ident = char(1);
const char CHR_letter = char(2);
const char CHR_digit = char(4);
const char CHR_quote = char(8);
const char CHR_white = char(16);
const char CHR_eol = char(32);
const char CHR_IDENT = CHR_ident;
const char CHR_LETTER = CHR_letter | CHR_ident;
const char CHR_DIGIT = CHR_digit | CHR_ident;
const char CHR_QUOTE = CHR_quote;
const char CHR_WHITE = CHR_white;
const char CHR_EOL = CHR_eol;
static const char* const eol_string = "end of line";
static const char* const eof_string = "*end_of_file*";
2001-05-23 15:26:42 +02:00
// Do not reference the array directly; use the functions below.
static const char classes_array[256] =
{
2001-05-23 15:26:42 +02:00
0, 0, 0, 0, 0, 0, 0, 0,
0, CHR_WHITE, CHR_EOL, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
CHR_WHITE, 0, CHR_QUOTE, CHR_IDENT, CHR_LETTER, 0, 0, CHR_QUOTE,
0, 0, 0, 0, 0, 0, 0, 0,
CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT, CHR_DIGIT,
CHR_DIGIT, CHR_DIGIT,
CHR_DIGIT, CHR_DIGIT, 0, 0, 0, 0, 0, 0,
0, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, 0, 0, 0, 0, CHR_IDENT,
0, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER,
CHR_LETTER, CHR_LETTER, CHR_LETTER, 0
};
inline char classes(int idx)
{
return classes_array[(UCHAR) idx];
}
inline char classes(UCHAR idx)
{
return classes_array[idx];
}
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
bool LEX_active_procedure(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ a c t i v e _ p r o c e d u r e
*
**************************************
*
* Functional description
2003-09-10 19:52:12 +02:00
* Return true if we're running out of a
* procedure and false otherwise. Somebody
2001-05-23 15:26:42 +02:00
* somewhere may care.
*
**************************************/
2003-09-10 19:52:12 +02:00
return (QLI_line->line_type == line_blob);
2001-05-23 15:26:42 +02:00
}
2006-07-03 00:42:12 +02:00
void LEX_edit(SLONG start, SLONG stop)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ e d i t
*
**************************************
*
* Functional description
* Dump the last full statement into a scratch file, then
* push the scratch file on the input stack.
*
**************************************/
const Firebird::PathName filename = TempFile::create(SCRATCH);
FILE* scratch = fopen(filename.c_str(), "w+b");
if (!scratch)
IBERROR(61); // Msg 61 couldn't open scratch file
2006-07-03 00:42:12 +02:00
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
stop--;
#endif
2004-04-29 00:36:29 +02:00
if (fseek(trace_file, start, 0)) {
fseek(trace_file, 0, 2);
2004-04-29 00:36:29 +02:00
IBERROR(59); // Msg 59 fseek failed
2001-05-23 15:26:42 +02:00
}
while (++start <= stop) {
2004-04-29 00:36:29 +02:00
const SSHORT c = getc(trace_file);
2001-05-23 15:26:42 +02:00
if (c == EOF)
break;
2004-04-29 00:36:29 +02:00
putc(c, scratch);
2001-05-23 15:26:42 +02:00
}
2004-04-29 00:36:29 +02:00
fclose(scratch);
2001-05-23 15:26:42 +02:00
if (gds__edit(filename.c_str(), TRUE))
LEX_push_file(filename.c_str(), true);
2001-05-23 15:26:42 +02:00
unlink(filename.c_str());
2001-05-23 15:26:42 +02:00
fseek(trace_file, 0, 2);
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
qli_tok* LEX_edit_string(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ e d i t _ s t r i n g
*
**************************************
*
* Functional description
* Parse the next token as an edit_string.
*
**************************************/
SSHORT c;
2004-02-02 12:02:12 +01:00
qli_tok* token = QLI_token;
2001-05-23 15:26:42 +02:00
do {
2001-05-23 15:26:42 +02:00
c = skip_white();
} while (c == '\n');
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
*p = c;
if (!QLI_line) {
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
return NULL;
}
while (!(classes(c) & (CHR_white | CHR_eol))) {
2001-05-23 15:26:42 +02:00
*p++ = c;
if (classes(c) & CHR_quote)
2001-05-23 15:26:42 +02:00
for (;;) {
const SSHORT d = nextchar(false);
if (d == '\n') {
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
break;
}
*p++ = d;
if (d == c)
break;
}
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
2003-10-20 12:08:01 +02:00
if (p[-1] == ',') {
2003-10-17 14:55:17 +02:00
retchar();
2003-10-20 12:08:01 +02:00
--p;
2003-10-17 14:55:17 +02:00
}
2001-05-23 15:26:42 +02:00
token->tok_length = p - token->tok_string;
*p = 0;
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
if (sw_trace)
2004-04-29 00:36:29 +02:00
puts(token->tok_string);
2001-05-23 15:26:42 +02:00
return token;
}
2004-02-02 12:02:12 +01:00
qli_tok* LEX_filename(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ f i l e n a m e
*
**************************************
*
* Functional description
* Parse the next token as a filename.
*
**************************************/
SSHORT c;
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_tok* token = QLI_token;
2001-05-23 15:26:42 +02:00
do {
2001-05-23 15:26:42 +02:00
c = skip_white();
} while (c == '\n');
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
*p++ = c;
// If there isn't a line, we're all done
2001-05-23 15:26:42 +02:00
if (!QLI_line) {
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
return NULL;
}
// notice if this looks like a quoted file name
2001-05-23 15:26:42 +02:00
SSHORT save = 0;
if (classes(c) & CHR_quote) {
2001-05-23 15:26:42 +02:00
token->tok_type = tok_quoted;
save = c;
}
// Look for white space or end of line, allowing embedded quoted strings.
2001-05-23 15:26:42 +02:00
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
char char_class = classes(c);
2001-05-23 15:26:42 +02:00
if (c == '"' && c != save) {
*p++ = c;
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
char_class = classes(c);
if ((char_class & CHR_eol) || c == '"')
2001-05-23 15:26:42 +02:00
break;
*p++ = c;
}
}
if (char_class & (CHR_white | CHR_eol))
2001-05-23 15:26:42 +02:00
break;
*p++ = c;
}
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
// Drop trailing semi-colon to avoid confusion
2001-05-23 15:26:42 +02:00
if (p[-1] == ';') {
2003-10-17 14:55:17 +02:00
retchar();
2003-10-20 12:08:01 +02:00
--p;
2001-05-23 15:26:42 +02:00
}
// complain on unterminated quoted string
2001-05-23 15:26:42 +02:00
if ((token->tok_type == tok_quoted) && (p[-1] != save))
IBERROR(60); // Msg 60 unterminated quoted string
2001-05-23 15:26:42 +02:00
token->tok_length = p - token->tok_string;
*p = 0;
token->tok_symbol = NULL;
token->tok_keyword = KW_none;
if (sw_trace)
2004-04-29 00:36:29 +02:00
puts(token->tok_string);
2001-05-23 15:26:42 +02:00
return token;
}
void LEX_fini(void)
{
/**************************************
*
* L E X _ f i n i
*
**************************************
*
* Functional description
* Shut down LEX in a more or less clean way.
*
**************************************/
if (trace_file) {
2004-04-29 00:36:29 +02:00
fclose(trace_file);
unlink(trace_file_name);
2001-05-23 15:26:42 +02:00
}
}
void LEX_flush(void)
{
/**************************************
*
* L E X _ f l u s h
*
**************************************
*
* Functional description
* Flush the input stream after an error.
2001-05-23 15:26:42 +02:00
*
**************************************/
trans_limit = 0;
if (!QLI_line)
return;
// Pop off line sources until we're down to the last one.
2001-05-23 15:26:42 +02:00
while (QLI_line->line_next)
LEX_pop_line();
// Look for a semi-colon
2001-05-23 15:26:42 +02:00
if (QLI_semi)
while (QLI_line && QLI_token->tok_keyword != KW_SEMI)
LEX_token();
else
while (QLI_line && QLI_token->tok_type != tok_eol)
LEX_token();
}
2004-05-16 03:42:11 +02:00
#if defined(UNIX) || defined(WIN_NT)
bool LEX_get_line(const TEXT* prompt,
2003-09-10 19:52:12 +02:00
TEXT * buffer,
int size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ g e t _ l i n e ( U N I X )
*
**************************************
*
* Functional description
* Give a prompt and read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
// UNIX flavor
2001-05-23 15:26:42 +02:00
if (prompt)
2004-04-29 00:36:29 +02:00
printf(prompt);
2001-05-23 15:26:42 +02:00
errno = 0;
TEXT* p = buffer;
2001-05-23 15:26:42 +02:00
bool overflow_flag = false;
SSHORT c;
2003-09-10 19:52:12 +02:00
while (true) {
2004-04-29 00:36:29 +02:00
c = getc(input_file);
2001-05-23 15:26:42 +02:00
if (c == EOF) {
if (SYSCALL_INTERRUPTED(errno) && !QLI_abort) {
errno = 0;
continue;
}
// The check for this actually being a terminal that is at
// end of file is to prevent looping through a redirected
2004-04-29 00:36:29 +02:00
// stdin (e.g., a script file).
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
if (prompt && isatty(fileno(stdin))) {
rewind(stdin);
putchar('\n');
2001-05-23 15:26:42 +02:00
}
if (QLI_abort)
continue;
else
break;
}
if (--size > 0)
*p++ = c;
else if (c != ' ' && c != '\n')
2003-09-10 19:52:12 +02:00
overflow_flag = true;
2001-05-23 15:26:42 +02:00
if (c == '\n')
break;
}
*p = 0;
if (c == EOF)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (overflow_flag) {
buffer[0] = 0;
IBERROR(476); // Msg 476 input line too long
2001-05-23 15:26:42 +02:00
}
if (sw_verify)
2004-04-29 00:36:29 +02:00
fputs(buffer, stdout);
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef VMS
bool LEX_get_line(const TEXT* prompt,
2003-09-10 19:52:12 +02:00
TEXT * buffer,
int size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ g e t _ l i n e ( V M S )
*
**************************************
*
* Functional description
* Give a prompt and read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
struct dsc$descriptor_s line_desc, prompt_desc;
SLONG status;
SSHORT length;
// We're going to add a null to the end, so don't read too much
2001-05-23 15:26:42 +02:00
--size;
line_desc.dsc$b_class = DSC$K_CLASS_S;
line_desc.dsc$b_dtype = DSC$K_DTYPE_T;
line_desc.dsc$w_length = size;
line_desc.dsc$a_pointer = buffer;
if (prompt) {
prompt_desc.dsc$b_class = DSC$K_CLASS_S;
prompt_desc.dsc$b_dtype = DSC$K_DTYPE_T;
prompt_desc.dsc$w_length = strlen(prompt);
prompt_desc.dsc$a_pointer = const_cast<TEXT*>(prompt); // safe cast
2001-05-23 15:26:42 +02:00
status = lib$get_input(&line_desc, &prompt_desc, &length);
}
else
status = lib$get_input(&line_desc, NULL, &length);
SCHAR* p = buffer + length;
2001-05-23 15:26:42 +02:00
if (!(status & 1)) {
if (status != LIB$_INPSTRTRU)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
buffer[0] = 0;
IBERROR(476); // Msg 476 input line too long
2001-05-23 15:26:42 +02:00
}
else if (length < size)
*p++ = '\n';
*p = 0;
if (sw_verify) {
line_desc.dsc$w_length = length;
lib$put_output(&line_desc);
}
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
#endif
void LEX_init(void)
{
/**************************************
*
* L E X _ i n i t
*
**************************************
*
* Functional description
* Initialize for lexical scanning. While we're at it, open a
* scratch trace file to keep all input.
*
**************************************/
const Firebird::PathName filename = TempFile::create(SCRATCH);
strcpy(trace_file_name, filename.c_str());
trace_file = fopen(trace_file_name, "w+b");
if (!trace_file)
IBERROR(61); // Msg 61 couldn't open scratch file
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
QLI_token = (qli_tok*) ALLOCPV(type_tok, MAXSYMLEN);
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
QLI_line = (qli_line*) ALLOCPV(type_line, 0);
2001-05-23 15:26:42 +02:00
QLI_line->line_size = sizeof(QLI_line->line_data);
QLI_line->line_ptr = QLI_line->line_data;
QLI_line->line_type = line_stdin;
2004-05-03 01:06:37 +02:00
QLI_line->line_source_file = stdin;
2001-05-23 15:26:42 +02:00
QLI_semi = false;
2004-04-29 00:36:29 +02:00
input_file = stdin;
2001-05-23 15:26:42 +02:00
HSH_init();
}
void LEX_mark_statement(void)
{
/**************************************
*
* L E X _ m a r k _ s t a t e m e n t
*
**************************************
*
* Functional description
* Push a position in the trace file onto
* the statement stack.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_line* temp;
2001-05-23 15:26:42 +02:00
for (temp = QLI_line;
temp->line_next && QLI_statements;
temp = temp->line_next)
{
2004-03-14 06:51:54 +01:00
if (temp->line_next->line_position == (IPTR) QLI_statements->lls_object)
return;
}
2001-05-23 15:26:42 +02:00
2004-02-02 12:02:12 +01:00
qli_lls* statement = (qli_lls*) ALLOCP(type_lls);
2004-03-14 06:51:54 +01:00
statement->lls_object = (BLK)(IPTR) temp->line_position;
2001-05-23 15:26:42 +02:00
statement->lls_next = QLI_statements;
QLI_statements = statement;
}
void LEX_pop_line(void)
{
/**************************************
*
* L E X _ p o p _ l i n e
*
**************************************
*
* Functional description
* We're done with the current line source. Close it out
* and release the line block.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_line* temp = QLI_line;
2001-05-23 15:26:42 +02:00
QLI_line = temp->line_next;
if (temp->line_type == line_blob)
2004-05-03 01:06:37 +02:00
PRO_close(temp->line_database, temp->line_source_blob);
2001-05-23 15:26:42 +02:00
else if (temp->line_type == line_file)
2004-05-03 01:06:37 +02:00
fclose(temp->line_source_file);
2001-05-23 15:26:42 +02:00
2004-05-16 03:42:11 +02:00
ALLQ_release((FRB) temp);
2001-05-23 15:26:42 +02:00
}
2004-05-03 01:06:37 +02:00
void LEX_procedure( DBB database, FB_API_HANDLE blob)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p r o c e d u r e
*
**************************************
*
* Functional description
* Push a blob-resident procedure onto the input line source
* stack;
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_line* temp = (qli_line*) ALLOCPV(type_line, QLI_token->tok_length);
2004-05-03 01:06:37 +02:00
temp->line_source_blob = blob;
2001-05-23 15:26:42 +02:00
strncpy(temp->line_source_name, QLI_token->tok_string,
QLI_token->tok_length);
temp->line_type = line_blob;
temp->line_database = database;
temp->line_size = sizeof(temp->line_data);
temp->line_ptr = temp->line_data;
temp->line_position = QLI_position;
temp->line_next = QLI_line;
QLI_line = temp;
}
bool LEX_push_file(const TEXT* filename,
const bool error_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u s h _ f i l e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Open a command file. If the open succeedes, push the input
* source onto the source stack. If the open fails, give an error
* if the error flag is set, otherwise return quietly.
*
**************************************/
2004-04-29 00:36:29 +02:00
FILE *file = fopen(filename, FOPEN_INPUT_TYPE);
if (!file) {
TEXT buffer[64];
2001-05-23 15:26:42 +02:00
sprintf(buffer, "%s.com", filename);
2004-04-29 00:36:29 +02:00
if (!(file = fopen(buffer, FOPEN_INPUT_TYPE))) {
2001-05-23 15:26:42 +02:00
if (error_flag)
ERRQ_msg_put(67, filename);
// Msg 67 can't open command file \"%s\"\n
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
}
2004-02-02 12:02:12 +01:00
qli_line* line = (qli_line*) ALLOCPV(type_line, strlen(filename));
2001-05-23 15:26:42 +02:00
line->line_type = line_file;
2004-05-03 01:06:37 +02:00
line->line_source_file = file;
2001-05-23 15:26:42 +02:00
line->line_size = sizeof(line->line_data);
line->line_ptr = line->line_data;
*line->line_ptr = 0;
strcpy(line->line_source_name, filename);
line->line_next = QLI_line;
QLI_line = line;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
bool LEX_push_string(const TEXT* const string)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u s h _ s t r i n g
*
**************************************
*
* Functional description
* Push a simple string on as an input source.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_line* line = (qli_line*) ALLOCPV(type_line, 0);
2001-05-23 15:26:42 +02:00
line->line_type = line_string;
line->line_size = strlen(string);
line->line_ptr = line->line_data;
strcpy(line->line_data, string);
line->line_data[line->line_size] = '\n';
line->line_next = QLI_line;
QLI_line = line;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
2004-05-03 01:06:37 +02:00
void LEX_put_procedure(FB_API_HANDLE blob, SLONG start, SLONG stop)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ p u t _ p r o c e d u r e
*
**************************************
*
* Functional description
* Write text from the scratch trace file into a blob.
*
**************************************/
2003-04-16 12:18:51 +02:00
ISC_STATUS_ARRAY status_vector;
2001-05-23 15:26:42 +02:00
2004-04-29 00:36:29 +02:00
if (fseek(trace_file, start, 0)) {
fseek(trace_file, 0, 2);
2004-04-29 00:36:29 +02:00
IBERROR(62); // Msg 62 fseek failed
2001-05-23 15:26:42 +02:00
}
int length = stop - start;
TEXT buffer[1024];
2001-05-23 15:26:42 +02:00
while (length) {
TEXT* p = buffer;
2001-05-23 15:26:42 +02:00
while (length) {
--length;
2004-04-29 00:36:29 +02:00
const SSHORT c = getc(trace_file);
*p++ = c;
2001-05-23 15:26:42 +02:00
if (c == '\n') {
2004-05-16 03:42:11 +02:00
#ifdef WIN_NT
// account for the extra line-feed on OS/2 and Windows NT
2001-05-23 15:26:42 +02:00
if (length)
--length;
#endif
break;
}
}
const SSHORT l = p - buffer;
if (l)
2003-11-08 17:40:17 +01:00
if (isc_put_segment(status_vector, &blob, l, buffer))
2004-05-16 03:42:11 +02:00
ERRQ_bugcheck(58); // Msg 58 isc_put_segment failed
2001-05-23 15:26:42 +02:00
}
fseek(trace_file, 0, 2);
2001-05-23 15:26:42 +02:00
}
void LEX_real(void)
{
/**************************************
*
* L E X _ r e a l
*
**************************************
*
* Functional description
* If the token is an end of line, get the next token.
*
**************************************/
while (QLI_token->tok_type == tok_eol)
LEX_token();
}
2004-02-02 12:02:12 +01:00
qli_lls* LEX_statement_list(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ s t a t e m e n t _ l i s t
*
**************************************
*
* Functional description
* Somebody outside needs to know
* where the top of the statement list
* is.
*
**************************************/
return QLI_statements;
}
2004-02-02 12:02:12 +01:00
qli_tok* LEX_token(void)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* L E X _ t o k e n
*
**************************************
*
* Functional description
* Parse and return the next token.
*
**************************************/
2004-02-02 12:02:12 +01:00
qli_tok* token = QLI_token;
TEXT* p = token->tok_string;
2001-05-23 15:26:42 +02:00
// Get next significant byte. If it's the last EOL of a blob, throw it away
2001-05-23 15:26:42 +02:00
SSHORT c;
2001-05-23 15:26:42 +02:00
for (;;) {
c = skip_white();
if (c != '\n' || QLI_line->line_type != line_blob)
break;
2004-02-02 12:02:12 +01:00
qli_line* prior = QLI_line;
2003-09-10 19:52:12 +02:00
next_line(true);
2001-05-23 15:26:42 +02:00
if (prior == QLI_line)
break;
}
// If we hit end of file, make up a phoney token
2001-05-23 15:26:42 +02:00
if (!QLI_line) {
const TEXT* q = eof_string;
2001-05-23 15:26:42 +02:00
while (*p++ = *q++);
token->tok_type = tok_eof;
token->tok_keyword = KW_none;
return NULL;
}
*p++ = c;
QLI_token->tok_position = QLI_line->line_position +
QLI_line->line_ptr - QLI_line->line_data - 1;
// On end of file, generate furious but phone end of line tokens
2001-05-23 15:26:42 +02:00
char char_class = classes(c);
2001-05-23 15:26:42 +02:00
if (char_class & CHR_letter) {
for (c = nextchar(true); classes(c) & CHR_ident; c = nextchar(true))
2001-05-23 15:26:42 +02:00
*p++ = c;
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
token->tok_type = tok_ident;
}
else if (((char_class & CHR_digit) || c == '.') && scan_number(c, &p))
2001-05-23 15:26:42 +02:00
token->tok_type = tok_number;
else if (char_class & CHR_quote) {
2001-05-23 15:26:42 +02:00
token->tok_type = tok_quoted;
2003-09-10 19:52:12 +02:00
while (true) {
const SSHORT next = nextchar(false);
if (!next || next == '\n') {
2003-10-17 14:55:17 +02:00
retchar();
IBERROR(63); // Msg 63 unterminated quoted string
2001-05-23 15:26:42 +02:00
break;
}
*p++ = next;
if ((p - token->tok_string) >= MAXSYMLEN)
ERRQ_msg_put(470, SafeArg() << MAXSYMLEN); // Msg 470 literal too long
2001-05-23 15:26:42 +02:00
// If there are 2 quotes in a row, interpret 2nd as a literal
2001-05-23 15:26:42 +02:00
if (next == c) {
const SSHORT peek = nextchar(false);
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
if (peek != c)
break;
2003-09-10 19:52:12 +02:00
nextchar(false);
2001-05-23 15:26:42 +02:00
}
}
}
else if (c == '\n') {
// end of line, signal it properly with a phoney token.
2001-05-23 15:26:42 +02:00
token->tok_type = tok_eol;
--p;
const TEXT* q = eol_string;
2001-05-23 15:26:42 +02:00
while (*q)
*p++ = *q++;
}
else {
token->tok_type = tok_punct;
2003-09-10 19:52:12 +02:00
*p++ = nextchar(true);
2001-05-23 15:26:42 +02:00
if (!HSH_lookup(token->tok_string, 2))
2003-10-17 14:55:17 +02:00
{
retchar();
2003-10-20 12:08:01 +02:00
--p;
2003-10-17 14:55:17 +02:00
}
2001-05-23 15:26:42 +02:00
}
token->tok_length = p - token->tok_string;
*p = '\0';
if (token->tok_string[0] == '$' && trans_limit < TRANS_LIMIT)
{
Firebird::string s;
if (fb_utils::readenv(token->tok_string + 1, s))
{
LEX_push_string(s.c_str());
++trans_limit;
token = LEX_token();
--trans_limit;
return token;
}
2001-05-23 15:26:42 +02:00
}
2004-02-02 12:02:12 +01:00
qli_symbol* symbol = HSH_lookup(token->tok_string, token->tok_length);
token->tok_symbol = symbol;
2001-05-23 15:26:42 +02:00
if (symbol && symbol->sym_type == SYM_keyword)
token->tok_keyword = (KWWORDS) symbol->sym_keyword;
else
token->tok_keyword = KW_none;
if (sw_trace)
2004-04-29 00:36:29 +02:00
puts(token->tok_string);
2001-05-23 15:26:42 +02:00
return token;
}
2004-04-29 00:36:29 +02:00
static bool get_line(FILE * file,
2003-09-10 19:52:12 +02:00
TEXT * buffer,
USHORT size)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ l i n e
*
**************************************
*
* Functional description
* Read a line. If the line is terminated by
2003-09-10 19:52:12 +02:00
* an EOL, return true. If the buffer is exhausted and non-blanks
2001-05-23 15:26:42 +02:00
* would be discarded, return an error. If EOF is detected,
2003-09-10 19:52:12 +02:00
* return false. Regardless, a null terminated string is returned.
2001-05-23 15:26:42 +02:00
*
**************************************/
2003-09-10 19:52:12 +02:00
bool overflow_flag = false;
2001-05-23 15:26:42 +02:00
SSHORT c;
errno = 0;
TEXT* p = buffer;
SLONG length = size;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
while (true) {
2004-04-29 00:36:29 +02:00
c = getc(file);
2001-05-23 15:26:42 +02:00
if (c == EOF) {
if (SYSCALL_INTERRUPTED(errno) && !QLI_abort) {
errno = 0;
continue;
}
if (QLI_abort)
continue;
else
break;
}
if (--length > 0)
*p++ = c;
else if (c != ' ' && c != '\n')
2003-09-10 19:52:12 +02:00
overflow_flag = true;
2001-05-23 15:26:42 +02:00
if (c == '\n')
break;
}
*p = 0;
if (c == EOF)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
if (overflow_flag)
IBERROR(477); // Msg 477 input line too long
2001-05-23 15:26:42 +02:00
if (sw_verify)
2004-04-29 00:36:29 +02:00
fputs(buffer, stdout);
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static int nextchar(const bool eof_ok)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e x t c h a r
*
**************************************
*
* Functional description
* Get the next character from the input stream.
*
**************************************/
// Get the next character in the current line. If we run out,
// get the next line. If the line source runs out, pop the
// line source. If we run out of line sources, we are distinctly
// at end of file.
2001-05-23 15:26:42 +02:00
while (QLI_line) {
const SSHORT c = *QLI_line->line_ptr++;
if (c)
2001-05-23 15:26:42 +02:00
return c;
next_line(eof_ok);
}
return 0;
}
static void next_line(const bool eof_ok)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n e x t _ l i n e
*
**************************************
*
* Functional description
* Get the next line from the input stream.
*
**************************************/
TEXT *p;
2001-05-23 15:26:42 +02:00
while (QLI_line) {
bool flag = false;
2001-05-23 15:26:42 +02:00
// Get next line from where ever. If it comes from either the terminal
// or command file, check for another command file.
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob) {
// If the current blob segment contains another line, use it
2001-05-23 15:26:42 +02:00
if ((p = QLI_line->line_ptr) != QLI_line->line_data
&& p[-1] == '\n' && *p)
{
2003-09-10 19:52:12 +02:00
flag = true;
}
2001-05-23 15:26:42 +02:00
else {
// Initialize line block for retrieval
2001-05-23 15:26:42 +02:00
p = QLI_line->line_data;
QLI_line->line_ptr = QLI_line->line_data;
2004-05-03 01:06:37 +02:00
flag = PRO_get_line(QLI_line->line_source_blob, p,
2001-05-23 15:26:42 +02:00
QLI_line->line_size);
if (flag && QLI_echo)
2004-04-29 00:36:29 +02:00
printf("%s", QLI_line->line_data);
2001-05-23 15:26:42 +02:00
}
}
else {
// Initialize line block for retrieval
2001-05-23 15:26:42 +02:00
QLI_line->line_ptr = QLI_line->line_data;
p = QLI_line->line_data;
if (QLI_line->line_type == line_stdin)
flag = LEX_get_line(QLI_prompt, p, (int) QLI_line->line_size);
else if (QLI_line->line_type == line_file) {
2004-05-03 01:06:37 +02:00
flag = get_line(QLI_line->line_source_file, p, QLI_line->line_size);
2001-05-23 15:26:42 +02:00
if (QLI_echo)
2004-04-29 00:36:29 +02:00
printf("%s", QLI_line->line_data);
2001-05-23 15:26:42 +02:00
}
if (flag) {
TEXT* q;
for (q = p; classes(*q) & CHR_white; q++);
2001-05-23 15:26:42 +02:00
if (*q == '@') {
TEXT filename[MAXPATHLEN];
2001-05-23 15:26:42 +02:00
for (p = q + 1, q = filename; *p && *p != '\n';)
*q++ = *p++;
*q = 0;
QLI_line->line_ptr = p;
2003-09-10 19:52:12 +02:00
LEX_push_file(filename, true);
2001-05-23 15:26:42 +02:00
continue;
}
}
}
// If get got a line, we're almost done
2001-05-23 15:26:42 +02:00
if (flag)
break;
// We hit end of file. Either complain about the circumstances
// or just close out the current input source. Don't close the
// input source if it's the terminal and we're at a continuation
// prompt.
2001-05-23 15:26:42 +02:00
if (eof_ok && (QLI_line->line_next || QLI_prompt != QLI_cont_string)) {
LEX_pop_line();
return;
}
// this is an unexpected end of file
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob)
2007-04-01 02:35:59 +02:00
{
ERRQ_print_error(64, QLI_line->line_source_name);
2007-04-01 02:35:59 +02:00
// Msg 64 unexpected end of procedure in procedure %s
}
2001-05-23 15:26:42 +02:00
else if (QLI_line->line_type == line_file)
2007-04-01 02:35:59 +02:00
{
ERRQ_print_error(65, QLI_line->line_source_name);
2007-04-01 02:35:59 +02:00
// Msg 65 unexpected end of file in file %s
}
else
{
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_string)
LEX_pop_line();
IBERROR(66); // Msg 66 unexpected eof
2001-05-23 15:26:42 +02:00
}
}
if (!QLI_line)
return;
QLI_line->line_position = QLI_position;
// Dump output to the trace file
2001-05-23 15:26:42 +02:00
if (QLI_line->line_type == line_blob)
while (*p)
p++;
else {
while (*p)
2004-04-29 00:36:29 +02:00
putc(*p++, trace_file);
2001-05-23 15:26:42 +02:00
QLI_position += (TEXT *) p - QLI_line->line_data;
2004-05-16 03:42:11 +02:00
#ifdef WIN_NT
// account for the extra line-feed on OS/2 and Windows NT
// to determine file position
2001-05-23 15:26:42 +02:00
QLI_position++;
#endif
}
QLI_line->line_length = (TEXT *) p - QLI_line->line_data;
}
2003-10-17 14:55:17 +02:00
static void retchar()
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e t c h a r
*
**************************************
*
* Functional description
* Return a character to the input stream.
*
**************************************/
// CVC: Too naive implementation: what if the pointer is at the beginning?
2003-10-17 14:55:17 +02:00
fb_assert(QLI_line)
2001-05-23 15:26:42 +02:00
--QLI_line->line_ptr;
}
2003-09-10 19:52:12 +02:00
static bool scan_number(SSHORT c,
TEXT** ptr)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s c a n _ n u m b e r
*
**************************************
*
* Functional description
* Pass on a possible numeric literal.
*
**************************************/
2003-09-10 19:52:12 +02:00
bool dot = false;
2001-05-23 15:26:42 +02:00
TEXT* p = *ptr;
2001-05-23 15:26:42 +02:00
// If this is a leading decimal point, check that the next
// character is really a digit, otherwise backout
2001-05-23 15:26:42 +02:00
if (c == '.') {
2003-10-17 14:55:17 +02:00
c = nextchar(true);
2003-10-20 12:08:01 +02:00
retchar();
if (!(classes(c) & CHR_digit))
2003-09-10 19:52:12 +02:00
return false;
dot = true;
2001-05-23 15:26:42 +02:00
}
// Gobble up digits up to a single decimal point
2001-05-23 15:26:42 +02:00
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
if (classes(c) & CHR_digit)
2001-05-23 15:26:42 +02:00
*p++ = c;
else if (!dot && c == '.') {
*p++ = c;
2003-09-10 19:52:12 +02:00
dot = true;
2001-05-23 15:26:42 +02:00
}
else
break;
}
// If this is an exponential, eat the exponent sign and digits
2001-05-23 15:26:42 +02:00
if (UPPER(c) == 'E') {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
if (c == '+' || c == '-') {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
while (classes(c) & CHR_digit) {
2001-05-23 15:26:42 +02:00
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
}
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
*ptr = p;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
static int skip_white(void)
{
/**************************************
*
* s k i p _ w h i t e
*
**************************************
*
* Functional description
* Skip over while space and comments in input stream
*
**************************************/
SSHORT c;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
while (true) {
c = nextchar(true);
const char char_class = classes(c);
if (char_class & CHR_white)
2001-05-23 15:26:42 +02:00
continue;
if (c == '/') {
SSHORT next = nextchar(true);
if (next != '*') {
2003-10-17 14:55:17 +02:00
retchar();
2001-05-23 15:26:42 +02:00
return c;
}
2003-09-10 19:52:12 +02:00
c = nextchar(false);
while ((next = nextchar(false)) && !(c == '*' && next == '/'))
2001-05-23 15:26:42 +02:00
c = next;
continue;
}
break;
}
return c;
}