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

1273 lines
26 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* 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
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#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"
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/gds_proto.h"
#include "../jrd/utl_proto.h"
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-05-23 15:26:42 +02:00
#ifdef VMS
#include <descrip.h>
#define LIB$_INPSTRTRU 0x15821c
#endif
#ifdef UNIX
#define UNIX_LINE 1
#endif
#if (defined WIN_NT)
#include <io.h>
#define UNIX_LINE 1
#define PC_FILE_SEEK
#endif
2003-07-06 09:04:03 +02:00
#ifdef SMALL_FILE_NAMES
#define SCRATCH "fb_q"
#else
#define SCRATCH "fb_query_"
#endif
2001-05-23 15:26:42 +02:00
#ifndef FOPEN_INPUT_TYPE
#define FOPEN_INPUT_TYPE "r"
#endif
2003-09-10 19:52:12 +02:00
extern bool sw_verify;
extern bool sw_trace;
2001-05-23 15:26:42 +02:00
2003-09-10 19:52:12 +02:00
static bool get_line(IB_FILE *, TEXT *, USHORT);
static int nextchar(bool);
static void next_line(bool);
2001-05-23 15:26:42 +02:00
static void retchar(SSHORT);
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);
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
#ifdef NOT_USED_OR_REPLACED
static SCHAR *eol_string = "*end_of_line*";
#endif
static SCHAR *eof_string = "*end_of_file*";
2001-05-23 15:26:42 +02:00
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
};
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
}
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;
2003-02-08 01:58:10 +01:00
TEXT filename[128];
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
}
while (++start <= stop) {
c = ib_getc(trace_file);
if (c == EOF)
break;
ib_putc(c, scratch);
}
ib_fclose(scratch);
if (gds__edit(filename, TRUE))
2003-09-10 19:52:12 +02:00
LEX_push_file(filename, true);
2001-05-23 15:26:42 +02:00
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 (;;) {
2003-09-10 19:52:12 +02:00
if ((d = nextchar(false)) == '\n') {
2001-05-23 15:26:42 +02:00
retchar(d);
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
}
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;
}
2003-09-10 13:48:08 +02:00
// notice if this looks like a quoted file name
2001-05-23 15:26:42 +02:00
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.
2001-05-23 15:26:42 +02:00
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
class_ = classes[c];
if (c == '"' && c != save) {
*p++ = c;
for (;;) {
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
if (p[-1] == ';') {
retchar(c);
--p;
}
2003-09-10 13:48:08 +02:00
// complain on unterminated quoted string
2001-05-23 15:26:42 +02:00
if ((token->tok_type == tok_quoted) && (p[-1] != save))
2003-09-10 13:48:08 +02:00
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)
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);
2001-07-12 07:46:06 +02:00
unlink((char*) 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.
*
**************************************/
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
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();
}
#ifdef UNIX_LINE
2003-09-10 19:52:12 +02:00
bool LEX_get_line(TEXT * prompt,
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
*
**************************************/
TEXT *p;
2003-09-10 19:52:12 +02:00
bool overflow_flag = false;
2001-05-23 15:26:42 +02:00
SSHORT c;
2003-09-10 13:48:08 +02:00
// UNIX flavor
2001-05-23 15:26:42 +02:00
if (prompt)
ib_printf(prompt);
errno = 0;
p = buffer;
2003-09-10 19:52:12 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
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')
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;
2003-09-10 13:48:08 +02:00
IBERROR(476); // Msg 476 input line too long
2001-05-23 15:26:42 +02:00
}
if (sw_verify)
ib_fputs(buffer, ib_stdout);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef VMS
2003-09-10 19:52:12 +02:00
bool LEX_get_line(TEXT * prompt,
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;
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)
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
buffer[0] = 0;
2003-09-10 13:48:08 +02:00
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.
*
**************************************/
2003-02-08 01:58:10 +01:00
2001-07-12 07:46:06 +02:00
trace_file = (IB_FILE*) gds__temp_file(TRUE, SCRATCH, (TEXT*) trace_file_name);
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
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);
2001-07-12 07:46:06 +02:00
ALL_release((FRB) temp);
2001-05-23 15:26:42 +02:00
}
void LEX_procedure( DBB database, FRBRD *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;
*
**************************************/
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;
}
2003-09-10 19:52:12 +02:00
bool LEX_push_file(TEXT * filename,
bool error_flag)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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 */
2003-09-10 19:52:12 +02:00
return false;
2001-05-23 15:26:42 +02:00
}
}
line = (LINE) ALLOCPV(type_line, strlen(filename));
line->line_type = line_file;
line->line_source = (FRBRD *) 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
}
2003-09-10 19:52:12 +02:00
bool LEX_push_string( TEXT * 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.
*
**************************************/
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;
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
void LEX_put_procedure( FRBRD *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
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
2001-05-23 15:26:42 +02:00
}
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
2001-05-23 15:26:42 +02:00
if (length)
--length;
#endif
break;
}
}
if (l = p - buffer)
2003-08-30 04:12:44 +02:00
if (gds__put_segment(status_vector, &blob, l, buffer))
BUGCHECK(58); // Msg 58 gds__put_segment failed
2001-05-23 15:26:42 +02:00
}
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;
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) {
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
2001-05-23 15:26:42 +02:00
class_ = classes[c];
if (class_ & CHR_letter) {
2003-09-10 19:52:12 +02:00
while (classes[c = nextchar(true)] & CHR_ident)
2001-05-23 15:26:42 +02:00
*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;
2003-09-10 19:52:12 +02:00
while (true) {
if (!(next = nextchar(false)) || next == '\n') {
2001-05-23 15:26:42 +02:00
retchar(next);
2003-09-10 13:48:08 +02:00
IBERROR(63); // Msg 63 unterminated quoted string
2001-05-23 15:26:42 +02:00
break;
}
*p++ = next;
if ((p - token->tok_string) >= MAXSYMLEN)
2003-09-10 13:48:08 +02:00
ERRQ_msg_put(470, (TEXT *) MAXSYMLEN, NULL, NULL, NULL, NULL); // 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) {
2003-09-10 19:52:12 +02:00
peek = nextchar(false);
2001-05-23 15:26:42 +02:00
retchar(peek);
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') {
token->tok_type = tok_eol;
--p;
q = "end of line";
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))
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;
}
2003-09-10 19:52:12 +02:00
static bool get_line(IB_FILE * file,
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
*
**************************************/
TEXT *p;
SLONG length;
2003-09-10 19:52:12 +02:00
bool overflow_flag = false;
2001-05-23 15:26:42 +02:00
SSHORT c;
errno = 0;
p = buffer;
length = size;
2003-09-10 19:52:12 +02:00
while (true) {
2001-05-23 15:26:42 +02:00
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')
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)
2003-09-10 13:48:08 +02:00
IBERROR(477); // Msg 477 input line too long
2001-05-23 15:26:42 +02:00
if (sw_verify)
ib_fputs(buffer, ib_stdout);
2003-09-10 19:52:12 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
2003-09-10 19:52:12 +02:00
static int nextchar(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.
*
**************************************/
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;
}
2003-09-10 19:52:12 +02:00
static void next_line(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, *q, filename[256];
2003-09-10 19:52:12 +02:00
bool flag;
2001-05-23 15:26:42 +02:00
while (QLI_line) {
2003-09-10 19:52:12 +02:00
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. */
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 {
2003-09-10 13:48:08 +02:00
// 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;
2003-09-10 19:52:12 +02:00
flag = PRO_get_line(QLI_line->line_source, p,
2001-05-23 15:26:42 +02:00
QLI_line->line_size);
if (flag && QLI_echo)
ib_printf("%s", QLI_line->line_data);
}
}
else {
2003-09-10 13:48:08 +02:00
// 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) {
2003-09-10 19:52:12 +02:00
flag = get_line((FILE*) QLI_line->line_source, p, QLI_line->line_size);
2001-05-23 15:26:42 +02:00
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;
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 */
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;
}
2003-09-10 13:48:08 +02:00
// this is an unexpected end of file
2001-05-23 15:26:42 +02:00
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();
2003-09-10 13:48:08 +02:00
IBERROR(66); // Msg 66 unexpected eof
2001-05-23 15:26:42 +02:00
}
}
if (!QLI_line)
return;
QLI_line->line_position = QLI_position;
2003-09-10 13:48:08 +02:00
// 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)
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;
}
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.
*
**************************************/
TEXT *p;
2003-09-10 19:52:12 +02:00
bool dot = false;
2001-05-23 15:26:42 +02:00
p = *ptr;
/* If this is a leading decimal point, check that the next
character is really a digit, otherwise backout */
if (c == '.') {
2003-09-10 19:52:12 +02:00
retchar(c = nextchar(true));
2001-05-23 15:26:42 +02:00
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
}
2003-09-10 13:48:08 +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);
2001-05-23 15:26:42 +02:00
if (classes[c] & CHR_digit)
*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) {
*p++ = c;
2003-09-10 19:52:12 +02:00
c = nextchar(true);
2001-05-23 15:26:42 +02:00
}
}
retchar(c);
*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, next, class_;
2003-09-10 19:52:12 +02:00
while (true) {
c = nextchar(true);
2001-05-23 15:26:42 +02:00
class_ = classes[c];
if (class_ & CHR_white)
continue;
if (c == '/') {
2003-09-10 19:52:12 +02:00
if ((next = nextchar(true)) != '*') {
2001-05-23 15:26:42 +02:00
retchar(next);
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;
}