mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 12:03:02 +01:00
1267 lines
26 KiB
C++
1267 lines
26 KiB
C++
/*
|
|
* PROGRAM: JRD Command Oriented Query Language
|
|
* MODULE: lex.c
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "../qli/dtr.h"
|
|
#include "../qli/parse.h"
|
|
#include "../jrd/gds.h"
|
|
#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/gds_proto.h"
|
|
#include "../jrd/utl_proto.h"
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_CTYPES_H
|
|
#include <ctypes.h>
|
|
#endif
|
|
|
|
#ifdef VMS
|
|
#include <descrip.h>
|
|
#define SCRATCH "Fb_query"
|
|
#define LIB$_INPSTRTRU 0x15821c
|
|
#endif
|
|
|
|
#ifdef UNIX
|
|
#ifdef SMALL_FILE_NAMES
|
|
#define SCRATCH "Fb_q"
|
|
#else
|
|
#define SCRATCH "Fb_query"
|
|
#endif
|
|
#define UNIX_LINE 1
|
|
#endif
|
|
|
|
#if (defined WIN_NT)
|
|
#include <io.h>
|
|
#define SCRATCH "Fb"
|
|
#define UNIX_LINE 1
|
|
#define PC_FILE_SEEK
|
|
#endif
|
|
|
|
#ifndef FOPEN_INPUT_TYPE
|
|
#define FOPEN_INPUT_TYPE "r"
|
|
#endif
|
|
|
|
extern USHORT sw_verify, sw_trace;
|
|
|
|
static BOOLEAN get_line(IB_FILE *, TEXT *, USHORT);
|
|
static int nextchar(BOOLEAN);
|
|
static void next_line(BOOLEAN);
|
|
static void retchar(SSHORT);
|
|
static BOOLEAN scan_number(SSHORT, TEXT **);
|
|
static int skip_white(void);
|
|
|
|
static LLS QLI_statements;
|
|
static int QLI_position;
|
|
static IB_FILE *input_file = NULL, *trace_file = NULL;
|
|
static UCHAR trace_file_name[128];
|
|
static SLONG trans_limit;
|
|
|
|
#define TRANS_LIMIT 10
|
|
|
|
#define CHR_ident 1
|
|
#define CHR_letter 2
|
|
#define CHR_digit 4
|
|
#define CHR_quote 8
|
|
#define CHR_white 16
|
|
#define CHR_eol 32
|
|
|
|
#define CHR_IDENT CHR_ident
|
|
#define CHR_LETTER CHR_letter + CHR_ident
|
|
#define CHR_DIGIT CHR_digit + CHR_ident
|
|
#define CHR_QUOTE CHR_quote
|
|
#define CHR_WHITE CHR_white
|
|
#define CHR_EOL CHR_eol
|
|
|
|
static SCHAR *eol_string = "*end_of_line*", *eof_string = "*end_of_file*";
|
|
|
|
static SCHAR classes[256] = {
|
|
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
|
|
};
|
|
|
|
|
|
|
|
int LEX_active_procedure(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ a c t i v e _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return TRUE if we're running out of a
|
|
* procedure and FALSE otherwise. Somebody
|
|
* somewhere may care.
|
|
*
|
|
**************************************/
|
|
|
|
return (QLI_line->line_type == line_blob) ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
void LEX_edit( SLONG start, SLONG stop)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
IB_FILE *scratch;
|
|
TEXT filename[128];
|
|
SSHORT c;
|
|
|
|
scratch = (IB_FILE *) gds__temp_file(TRUE, SCRATCH, filename);
|
|
if (scratch == (IB_FILE *) - 1)
|
|
IBERROR(61); /* Msg 61 couldn't open scratch file */
|
|
#ifdef WIN_NT
|
|
stop--;
|
|
#endif
|
|
|
|
if (ib_fseek(trace_file, start, 0)) {
|
|
ib_fseek(trace_file, (SLONG) 0, 2);
|
|
IBERROR(59); /* Msg 59 ib_fseek failed */
|
|
}
|
|
|
|
while (++start <= stop) {
|
|
c = ib_getc(trace_file);
|
|
if (c == EOF)
|
|
break;
|
|
ib_putc(c, scratch);
|
|
}
|
|
|
|
ib_fclose(scratch);
|
|
|
|
if (gds__edit(filename, TRUE))
|
|
LEX_push_file(filename, TRUE);
|
|
|
|
unlink(filename);
|
|
|
|
ib_fseek(trace_file, (SLONG) 0, 2);
|
|
}
|
|
|
|
|
|
TOK LEX_edit_string(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ e d i t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the next token as an edit_string.
|
|
*
|
|
**************************************/
|
|
TOK token;
|
|
SSHORT c, d;
|
|
TEXT *p;
|
|
|
|
token = QLI_token;
|
|
|
|
do
|
|
c = skip_white();
|
|
while (c == '\n');
|
|
p = token->tok_string;
|
|
*p = c;
|
|
|
|
if (!QLI_line) {
|
|
token->tok_symbol = NULL;
|
|
token->tok_keyword = KW_none;
|
|
return NULL;
|
|
}
|
|
|
|
while (!(classes[c] & (CHR_white | CHR_eol))) {
|
|
*p++ = c;
|
|
if (classes[c] & CHR_quote)
|
|
for (;;) {
|
|
if ((d = nextchar(FALSE)) == '\n') {
|
|
retchar(d);
|
|
break;
|
|
}
|
|
*p++ = d;
|
|
if (d == c)
|
|
break;
|
|
}
|
|
c = nextchar(TRUE);
|
|
}
|
|
|
|
retchar(c);
|
|
|
|
if (p[-1] == ',')
|
|
retchar(*--p);
|
|
|
|
token->tok_length = p - token->tok_string;
|
|
*p = 0;
|
|
token->tok_symbol = NULL;
|
|
token->tok_keyword = KW_none;
|
|
|
|
if (sw_trace)
|
|
ib_puts(token->tok_string);
|
|
|
|
return token;
|
|
}
|
|
|
|
|
|
TOK LEX_filename(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ f i l e n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the next token as a filename.
|
|
*
|
|
**************************************/
|
|
TOK token;
|
|
SSHORT c, save;
|
|
USHORT class_;
|
|
TEXT *p;
|
|
|
|
token = QLI_token;
|
|
|
|
do
|
|
c = skip_white();
|
|
while (c == '\n');
|
|
p = token->tok_string;
|
|
*p++ = c;
|
|
|
|
/* If there isn't a line, we're all done */
|
|
|
|
if (!QLI_line) {
|
|
token->tok_symbol = NULL;
|
|
token->tok_keyword = KW_none;
|
|
return NULL;
|
|
}
|
|
|
|
/* notice if this looks like a quoted file name */
|
|
|
|
if (classes[c] & CHR_quote) {
|
|
token->tok_type = tok_quoted;
|
|
save = c;
|
|
}
|
|
else
|
|
save = 0;
|
|
|
|
/* Look for white space or end of line, allowing embedded quoted strings. */
|
|
|
|
for (;;) {
|
|
c = nextchar(TRUE);
|
|
class_ = classes[c];
|
|
if (c == '"' && c != save) {
|
|
*p++ = c;
|
|
for (;;) {
|
|
c = nextchar(TRUE);
|
|
class_ = classes[c];
|
|
if ((class_ & CHR_eol) || c == '"')
|
|
break;
|
|
*p++ = c;
|
|
}
|
|
}
|
|
|
|
if (class_ & (CHR_white | CHR_eol))
|
|
break;
|
|
*p++ = c;
|
|
}
|
|
|
|
retchar(c);
|
|
|
|
/* Drop trailing semi-colon to avoid confusion */
|
|
|
|
if (p[-1] == ';') {
|
|
retchar(c);
|
|
--p;
|
|
}
|
|
|
|
/* complain on unterminated quoted string */
|
|
|
|
if ((token->tok_type == tok_quoted) && (p[-1] != save))
|
|
IBERROR(60); /* Msg 60 unterminated quoted string */
|
|
|
|
token->tok_length = p - token->tok_string;
|
|
*p = 0;
|
|
token->tok_symbol = NULL;
|
|
token->tok_keyword = KW_none;
|
|
|
|
if (sw_trace)
|
|
ib_puts(token->tok_string);
|
|
|
|
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 && (trace_file != (IB_FILE *) - 1)) {
|
|
ib_fclose(trace_file);
|
|
unlink((char*) trace_file_name);
|
|
}
|
|
}
|
|
|
|
|
|
void LEX_flush(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ f l u s h
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Flush the input stream after an error.
|
|
*
|
|
**************************************/
|
|
|
|
trans_limit = 0;
|
|
|
|
if (!QLI_line)
|
|
return;
|
|
|
|
/* Pop off line sources until we're down to the last one. */
|
|
|
|
while (QLI_line->line_next)
|
|
LEX_pop_line();
|
|
|
|
/* Look for a semi-colon */
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
#ifdef UNIX_LINE
|
|
int LEX_get_line( TEXT * prompt, TEXT * buffer, int size)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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
|
|
* an EOL, return TRUE. If the buffer is exhausted and non-blanks
|
|
* would be discarded, return an error. If EOF is detected,
|
|
* return FALSE. Regardless, a null terminated string is returned.
|
|
*
|
|
**************************************/
|
|
TEXT *p;
|
|
USHORT overflow_flag;
|
|
SSHORT c;
|
|
|
|
/* UNIX flavor */
|
|
|
|
if (prompt)
|
|
ib_printf(prompt);
|
|
|
|
errno = 0;
|
|
p = buffer;
|
|
overflow_flag = FALSE;
|
|
|
|
while (TRUE) {
|
|
c = ib_getc(input_file);
|
|
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
|
|
ib_stdin (e.g., a script file). */
|
|
|
|
if (prompt && isatty(ib_fileno(ib_stdin))) {
|
|
ib_rewind(ib_stdin);
|
|
ib_putchar('\n');
|
|
}
|
|
if (QLI_abort)
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
if (--size > 0)
|
|
*p++ = c;
|
|
else if (c != ' ' && c != '\n')
|
|
overflow_flag = TRUE;
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
|
|
*p = 0;
|
|
if (c == EOF)
|
|
return FALSE;
|
|
|
|
if (overflow_flag) {
|
|
buffer[0] = 0;
|
|
IBERROR(476); /* Msg 476 input line too long */
|
|
}
|
|
|
|
if (sw_verify)
|
|
ib_fputs(buffer, ib_stdout);
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef VMS
|
|
int LEX_get_line( TEXT * prompt, TEXT * buffer, int size)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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
|
|
* an EOL, return TRUE. If the buffer is exhausted and non-blanks
|
|
* would be discarded, return an error. If EOF is detected,
|
|
* return FALSE. Regardless, a null terminated string is returned.
|
|
*
|
|
**************************************/
|
|
struct dsc$descriptor_s line_desc, prompt_desc;
|
|
SLONG status;
|
|
SSHORT length;
|
|
SCHAR *p;
|
|
|
|
/* We're going to add a null to the end, so don't read too much */
|
|
|
|
--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 = prompt;
|
|
status = lib$get_input(&line_desc, &prompt_desc, &length);
|
|
}
|
|
else
|
|
status = lib$get_input(&line_desc, NULL, &length);
|
|
|
|
p = buffer + length;
|
|
|
|
if (!(status & 1)) {
|
|
if (status != LIB$_INPSTRTRU)
|
|
return FALSE;
|
|
buffer[0] = 0;
|
|
IBERROR(476); /* Msg 476 input line too long */
|
|
}
|
|
else if (length < size)
|
|
*p++ = '\n';
|
|
|
|
*p = 0;
|
|
|
|
if (sw_verify) {
|
|
line_desc.dsc$w_length = length;
|
|
lib$put_output(&line_desc);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#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.
|
|
*
|
|
**************************************/
|
|
|
|
trace_file = (IB_FILE*) gds__temp_file(TRUE, SCRATCH, (TEXT*) trace_file_name);
|
|
if (trace_file == (IB_FILE *) - 1)
|
|
IBERROR(61); /* Msg 61 couldn't open scratch file */
|
|
|
|
QLI_token = (TOK) ALLOCPV(type_tok, MAXSYMLEN);
|
|
|
|
QLI_line = (LINE) ALLOCPV(type_line, 0);
|
|
QLI_line->line_size = sizeof(QLI_line->line_data);
|
|
QLI_line->line_ptr = QLI_line->line_data;
|
|
QLI_line->line_type = line_stdin;
|
|
QLI_line->line_source = (FRBRD *) ib_stdin;
|
|
|
|
QLI_semi = FALSE;
|
|
input_file = ib_stdin;
|
|
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.
|
|
*
|
|
**************************************/
|
|
LLS statement;
|
|
LINE temp;
|
|
|
|
for (temp = QLI_line;
|
|
temp->line_next && QLI_statements;
|
|
temp = temp->line_next)
|
|
if (temp->line_next->line_position == (SLONG) QLI_statements->lls_object) return;
|
|
|
|
statement = (LLS) ALLOCP(type_lls);
|
|
statement->lls_object = (BLK) temp->line_position;
|
|
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.
|
|
*
|
|
**************************************/
|
|
LINE temp;
|
|
|
|
temp = QLI_line;
|
|
QLI_line = temp->line_next;
|
|
|
|
if (temp->line_type == line_blob)
|
|
PRO_close(temp->line_database, temp->line_source);
|
|
else if (temp->line_type == line_file)
|
|
ib_fclose((IB_FILE *) temp->line_source);
|
|
|
|
ALL_release((FRB) temp);
|
|
}
|
|
|
|
|
|
void LEX_procedure( DBB database, FRBRD *blob)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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;
|
|
*
|
|
**************************************/
|
|
LINE temp;
|
|
|
|
temp = (LINE) ALLOCPV(type_line, QLI_token->tok_length);
|
|
temp->line_source = blob;
|
|
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;
|
|
}
|
|
|
|
|
|
int LEX_push_file( TEXT * filename, int error_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ p u s h _ f i l e
|
|
*
|
|
**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
TEXT buffer[64];
|
|
IB_FILE *file;
|
|
LINE line;
|
|
|
|
if (!(file = ib_fopen(filename, FOPEN_INPUT_TYPE))) {
|
|
sprintf(buffer, "%s.com", filename);
|
|
if (!(file = ib_fopen(buffer, FOPEN_INPUT_TYPE))) {
|
|
if (error_flag)
|
|
ERRQ_msg_put(67, filename, NULL, NULL, NULL, NULL); /* Msg 67 can't open command file \"%s\"\n */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
line = (LINE) ALLOCPV(type_line, strlen(filename));
|
|
line->line_type = line_file;
|
|
line->line_source = (FRBRD *) file;
|
|
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;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
int LEX_push_string( TEXT * string)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ p u s h _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Push a simple string on as an input source.
|
|
*
|
|
**************************************/
|
|
LINE line;
|
|
|
|
line = (LINE) ALLOCPV(type_line, 0);
|
|
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;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void LEX_put_procedure( FRBRD *blob, SLONG start, SLONG stop)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
STATUS status_vector[ISC_STATUS_LENGTH];
|
|
int length;
|
|
SSHORT l, c;
|
|
TEXT buffer[1024], *p;
|
|
|
|
if (ib_fseek(trace_file, start, 0)) {
|
|
ib_fseek(trace_file, (SLONG) 0, 2);
|
|
IBERROR(62); /* Msg 62 ib_fseek failed */
|
|
}
|
|
|
|
length = stop - start;
|
|
|
|
while (length) {
|
|
p = buffer;
|
|
while (length) {
|
|
--length;
|
|
*p++ = c = ib_getc(trace_file);
|
|
if (c == '\n') {
|
|
#ifdef PC_FILE_SEEK
|
|
/* account for the extra line-feed on OS/2 and Windows NT */
|
|
|
|
if (length)
|
|
--length;
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
if (l = p - buffer)
|
|
if (gds__put_segment(status_vector, GDS_REF(blob), l, buffer))
|
|
BUGCHECK(58); /* Msg 58 gds__put_segment failed */
|
|
}
|
|
|
|
ib_fseek(trace_file, (SLONG) 0, 2);
|
|
}
|
|
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
LLS LEX_statement_list(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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;
|
|
}
|
|
|
|
|
|
TOK LEX_token(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* L E X _ t o k e n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse and return the next token.
|
|
*
|
|
**************************************/
|
|
TOK token;
|
|
SSHORT c, next, peek;
|
|
TEXT class_, *p, *q;
|
|
SYM symbol;
|
|
LINE prior;
|
|
|
|
token = QLI_token;
|
|
p = token->tok_string;
|
|
|
|
/* Get next significant byte. If it's the last EOL of a blob, throw it
|
|
away */
|
|
|
|
for (;;) {
|
|
c = skip_white();
|
|
if (c != '\n' || QLI_line->line_type != line_blob)
|
|
break;
|
|
prior = QLI_line;
|
|
next_line(TRUE);
|
|
if (prior == QLI_line)
|
|
break;
|
|
}
|
|
|
|
/* If we hit end of file, make up a phoney token */
|
|
|
|
if (!QLI_line) {
|
|
q = eof_string;
|
|
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 */
|
|
|
|
class_ = classes[c];
|
|
|
|
if (class_ & CHR_letter) {
|
|
while (classes[c = nextchar(TRUE)] & CHR_ident)
|
|
*p++ = c;
|
|
retchar(c);
|
|
token->tok_type = tok_ident;
|
|
}
|
|
else if (((class_ & CHR_digit) || c == '.') && scan_number(c, &p))
|
|
token->tok_type = tok_number;
|
|
else if (class_ & CHR_quote) {
|
|
token->tok_type = tok_quoted;
|
|
while (TRUE) {
|
|
if (!(next = nextchar(FALSE)) || next == '\n') {
|
|
retchar(next);
|
|
IBERROR(63); /* Msg 63 unterminated quoted string */
|
|
break;
|
|
}
|
|
*p++ = next;
|
|
if ((p - token->tok_string) >= MAXSYMLEN)
|
|
ERRQ_msg_put(470, (TEXT *) MAXSYMLEN, NULL, NULL, NULL, NULL); /* Msg 470 literal too long */
|
|
|
|
/* If there are 2 quotes in a row, interpret 2nd as a literal */
|
|
|
|
if (next == c) {
|
|
peek = nextchar(FALSE);
|
|
retchar(peek);
|
|
if (peek != c)
|
|
break;
|
|
nextchar(FALSE);
|
|
}
|
|
}
|
|
}
|
|
else if (c == '\n') {
|
|
token->tok_type = tok_eol;
|
|
--p;
|
|
q = "end of line";
|
|
while (*q)
|
|
*p++ = *q++;
|
|
}
|
|
else {
|
|
token->tok_type = tok_punct;
|
|
*p++ = nextchar(TRUE);
|
|
if (!HSH_lookup(token->tok_string, 2))
|
|
retchar(*--p);
|
|
}
|
|
|
|
token->tok_length = p - token->tok_string;
|
|
*p = '\0';
|
|
|
|
if (token->tok_string[0] == '$' &&
|
|
trans_limit < TRANS_LIMIT && (p = getenv(token->tok_string + 1))) {
|
|
LEX_push_string(p);
|
|
++trans_limit;
|
|
token = LEX_token();
|
|
--trans_limit;
|
|
return token;
|
|
}
|
|
|
|
token->tok_symbol = symbol =
|
|
HSH_lookup(token->tok_string, token->tok_length);
|
|
if (symbol && symbol->sym_type == SYM_keyword)
|
|
token->tok_keyword = (KWWORDS) symbol->sym_keyword;
|
|
else
|
|
token->tok_keyword = KW_none;
|
|
|
|
if (sw_trace)
|
|
ib_puts(token->tok_string);
|
|
|
|
return token;
|
|
}
|
|
|
|
|
|
static BOOLEAN get_line( IB_FILE * file, TEXT * buffer, USHORT size)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ l i n e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Read a line. If the line is terminated by
|
|
* an EOL, return TRUE. If the buffer is exhausted and non-blanks
|
|
* would be discarded, return an error. If EOF is detected,
|
|
* return FALSE. Regardless, a null terminated string is returned.
|
|
*
|
|
**************************************/
|
|
TEXT *p;
|
|
SLONG length;
|
|
USHORT overflow_flag;
|
|
SSHORT c;
|
|
|
|
errno = 0;
|
|
p = buffer;
|
|
overflow_flag = FALSE;
|
|
length = size;
|
|
|
|
while (TRUE) {
|
|
c = ib_getc(file);
|
|
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')
|
|
overflow_flag = TRUE;
|
|
if (c == '\n')
|
|
break;
|
|
}
|
|
|
|
*p = 0;
|
|
if (c == EOF)
|
|
return FALSE;
|
|
|
|
if (overflow_flag)
|
|
IBERROR(477); /* Msg 477 input line too long */
|
|
|
|
if (sw_verify)
|
|
ib_fputs(buffer, ib_stdout);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static int nextchar( BOOLEAN eof_ok)
|
|
{
|
|
/**************************************
|
|
*
|
|
* n e x t c h a r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the next character from the input stream.
|
|
*
|
|
**************************************/
|
|
SSHORT c;
|
|
|
|
/* 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. */
|
|
|
|
while (QLI_line) {
|
|
if (c = *QLI_line->line_ptr++)
|
|
return c;
|
|
next_line(eof_ok);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void next_line( BOOLEAN eof_ok)
|
|
{
|
|
/**************************************
|
|
*
|
|
* n e x t _ l i n e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the next line from the input stream.
|
|
*
|
|
**************************************/
|
|
TEXT *p, *q, filename[256];
|
|
SSHORT flag;
|
|
|
|
while (QLI_line) {
|
|
flag = FALSE;
|
|
|
|
/* Get next line from where ever. If it comes from either the terminal
|
|
or command file, check for another command file. */
|
|
|
|
if (QLI_line->line_type == line_blob) {
|
|
/* If the current blob segment contains another line, use it */
|
|
|
|
if ((p = QLI_line->line_ptr) != QLI_line->line_data
|
|
&& p[-1] == '\n' && *p)
|
|
flag = TRUE;
|
|
else {
|
|
/* Initialize line block for retrieval */
|
|
|
|
p = QLI_line->line_data;
|
|
QLI_line->line_ptr = QLI_line->line_data;
|
|
|
|
flag =
|
|
PRO_get_line(QLI_line->line_source, p,
|
|
QLI_line->line_size);
|
|
if (flag && QLI_echo)
|
|
ib_printf("%s", QLI_line->line_data);
|
|
}
|
|
}
|
|
else {
|
|
/* Initialize line block for retrieval */
|
|
|
|
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) {
|
|
flag =
|
|
get_line((FILE*) QLI_line->line_source, p, QLI_line->line_size);
|
|
if (QLI_echo)
|
|
ib_printf("%s", QLI_line->line_data);
|
|
}
|
|
if (flag) {
|
|
for (q = p; classes[*q] & CHR_white; q++);
|
|
if (*q == '@') {
|
|
for (p = q + 1, q = filename; *p && *p != '\n';)
|
|
*q++ = *p++;
|
|
*q = 0;
|
|
QLI_line->line_ptr = (TEXT *) p;
|
|
LEX_push_file(filename, TRUE);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If get got a line, we're almost done */
|
|
|
|
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. */
|
|
|
|
if (eof_ok && (QLI_line->line_next || QLI_prompt != QLI_cont_string)) {
|
|
LEX_pop_line();
|
|
return;
|
|
}
|
|
|
|
/* this is an unexpected end of file */
|
|
|
|
if (QLI_line->line_type == line_blob)
|
|
ERRQ_print_error(64, QLI_line->line_source_name, NULL, NULL, NULL,
|
|
NULL);
|
|
/* Msg 64 unexpected end of procedure in procedure %s */
|
|
else if (QLI_line->line_type == line_file)
|
|
ERRQ_print_error(65, QLI_line->line_source_name, NULL, NULL, NULL,
|
|
NULL);
|
|
/* Msg 65 unexpected end of file in file %s */
|
|
else {
|
|
if (QLI_line->line_type == line_string)
|
|
LEX_pop_line();
|
|
IBERROR(66); /* Msg 66 unexpected eof */
|
|
}
|
|
}
|
|
|
|
if (!QLI_line)
|
|
return;
|
|
|
|
QLI_line->line_position = QLI_position;
|
|
|
|
/* Dump output to the trace file */
|
|
|
|
if (QLI_line->line_type == line_blob)
|
|
while (*p)
|
|
p++;
|
|
else {
|
|
while (*p)
|
|
ib_putc(*p++, trace_file);
|
|
QLI_position += (TEXT *) p - QLI_line->line_data;
|
|
#ifdef PC_FILE_SEEK
|
|
/* account for the extra line-feed on OS/2 and Windows NT
|
|
to determine file position */
|
|
|
|
QLI_position++;
|
|
#endif
|
|
}
|
|
|
|
QLI_line->line_length = (TEXT *) p - QLI_line->line_data;
|
|
}
|
|
|
|
|
|
static void retchar( SSHORT c)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e t c h a r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return a character to the input stream.
|
|
*
|
|
**************************************/
|
|
|
|
--QLI_line->line_ptr;
|
|
}
|
|
|
|
|
|
static BOOLEAN scan_number( SSHORT c, TEXT ** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s c a n _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pass on a possible numeric literal.
|
|
*
|
|
**************************************/
|
|
TEXT *p;
|
|
SSHORT dot;
|
|
|
|
p = *ptr;
|
|
dot = FALSE;
|
|
|
|
/* If this is a leading decimal point, check that the next
|
|
character is really a digit, otherwise backout */
|
|
|
|
if (c == '.') {
|
|
retchar(c = nextchar(TRUE));
|
|
if (!(classes[c] & CHR_digit))
|
|
return FALSE;
|
|
dot = TRUE;
|
|
}
|
|
|
|
/* Gobble up digits up to a single decimal point */
|
|
|
|
for (;;) {
|
|
c = nextchar(TRUE);
|
|
if (classes[c] & CHR_digit)
|
|
*p++ = c;
|
|
else if (!dot && c == '.') {
|
|
*p++ = c;
|
|
dot = TRUE;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
/* If this is an exponential, eat the exponent sign and digits */
|
|
|
|
if (UPPER(c) == 'E') {
|
|
*p++ = c;
|
|
c = nextchar(TRUE);
|
|
if (c == '+' || c == '-') {
|
|
*p++ = c;
|
|
c = nextchar(TRUE);
|
|
}
|
|
while (classes[c] & CHR_digit) {
|
|
*p++ = c;
|
|
c = nextchar(TRUE);
|
|
}
|
|
}
|
|
|
|
retchar(c);
|
|
*ptr = p;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
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, next, class_;
|
|
|
|
while (TRUE) {
|
|
c = nextchar(TRUE);
|
|
class_ = classes[c];
|
|
if (class_ & CHR_white)
|
|
continue;
|
|
if (c == '/') {
|
|
if ((next = nextchar(TRUE)) != '*') {
|
|
retchar(next);
|
|
return c;
|
|
}
|
|
c = nextchar(FALSE);
|
|
while ((next = nextchar(FALSE)) && !(c == '*' && next == '/'))
|
|
c = next;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return c;
|
|
}
|