version 1.8, 2001/05/14 12:55:48 |
version 1.9, 2001/05/23 12:34:45 |
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
*/ |
|
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
#include <assert.h> |
#include <assert.h> |
#include "make.h" |
#include <stddef.h> |
|
#include <stdio.h> |
|
#include <unistd.h> |
|
#include "config.h" |
|
#include "defines.h" |
#include "buf.h" |
#include "buf.h" |
#include "lowparse.h" |
#include "lowparse.h" |
|
#include "error.h" |
|
#include "lst.h" |
|
#include "memory.h" |
|
|
#ifdef CLEANUP |
/* XXX check whether we can free filenames at the end, for a proper |
|
* definition of `end'. */ |
|
|
|
#if 0 |
static LIST fileNames; /* file names to free at end */ |
static LIST fileNames; /* file names to free at end */ |
#endif |
#endif |
|
|
/* Definitions for handling #include specifications */ |
/* Input stream structure, file or string. */ |
typedef struct IFile_ { |
typedef struct { |
char *fname; /* name of file */ |
const char *fname; /* Name of file */ |
unsigned long lineno; /* line number */ |
unsigned long lineno; /* Line number */ |
FILE *F; /* open stream */ |
FILE *F; /* Open stream, or NULL if pure string. */ |
char *str; /* read from char area */ |
char *str; /* Input string, if F == NULL. */ |
char *ptr; /* where we are */ |
|
char *end; /* don't overdo it */ |
/* Line buffer. */ |
|
char *ptr; /* Where we are. */ |
|
char *end; /* Don't overdo it. */ |
} IFile; |
} IFile; |
|
|
static IFile *current; |
static IFile *current; /* IFile being parsed. */ |
|
|
static LIST includes; /* stack of IFiles generated by |
static LIST input_stack; /* Stack of IFiles waiting to be parsed |
* #includes */ |
* (includes and loop reparses) */ |
|
|
static IFile *new_ifile(char *, FILE *); |
/* IFile ctors. |
static IFile *new_istring(char *, char *, unsigned long); |
* |
|
* obj = new_ifile(filename, filehandle); |
|
* Create input object from filename, filehandle. */ |
|
static IFile *new_ifile(const char *, FILE *); |
|
/* obj = new_istring(str, filename, lineno); |
|
* Create input object from str, filename, lineno. */ |
|
static IFile *new_istring(char *, const char *, unsigned long); |
|
/* free_ifile(obj); |
|
* Discard consumed input object, closing streams, freeing memory. */ |
static void free_ifile(IFile *); |
static void free_ifile(IFile *); |
static void ParseVErrorInternal(char *, unsigned long, int, char *, va_list); |
|
|
|
|
/* Handling basic character reading. |
|
* c = ParseReadc(); |
|
* New character c from current input stream, or EOF at end of stream. */ |
|
#define ParseReadc() current->ptr < current->end ? *current->ptr++ : newline() |
|
/* len = newline(); |
|
* Guts for ParseReadc. Grabs a new line off fgetln when we have |
|
* consumed the current line and returns its length. Or EOF at end of |
|
* stream. */ |
static int newline(void); |
static int newline(void); |
|
/* c = skiptoendofline(); |
|
* Skips to the end of the current line, returns either '\n' or EOF. */ |
static int skiptoendofline(void); |
static int skiptoendofline(void); |
static void ParseFoldLF(Buffer, int); |
|
static int ParseSkipEmptyLines(Buffer); |
|
static int fatals = 0; |
|
|
|
/*- |
|
* ParseVErrorInternal -- |
|
* Error message abort function for parsing. Prints out the context |
|
* of the error (line number and file) as well as the message with |
|
* two optional arguments. |
|
* |
|
* Side Effects: |
|
* "fatals" is incremented if the level is PARSE_FATAL. |
|
*/ |
|
/* VARARGS */ |
|
static void |
|
#ifdef __STDC__ |
|
ParseVErrorInternal(char *cfname, unsigned long clineno, int type, char *fmt, |
|
va_list ap) |
|
#else |
|
ParseVErrorInternal(va_alist) |
|
va_dcl |
|
#endif |
|
{ |
|
(void)fprintf(stderr, "\"%s\", line %lu: ", cfname, clineno); |
|
if (type == PARSE_WARNING) |
|
(void)fprintf(stderr, "warning: "); |
|
(void)vfprintf(stderr, fmt, ap); |
|
va_end(ap); |
|
(void)fprintf(stderr, "\n"); |
|
if (type == PARSE_FATAL) |
|
fatals ++; |
|
} |
|
|
|
/*- |
/* Helper functions to handle basic parsing. */ |
* Parse_Error -- |
/* ParseFoldLF(buffer, firstchar); |
* External interface to ParseVErrorInternal; uses the default filename |
* Grabs logical line into buffer, the first character has already been |
* Line number. |
* read into firstchar. */ |
*/ |
static void ParseFoldLF(Buffer, int); |
/* VARARGS */ |
|
void |
|
#ifdef __STDC__ |
|
Parse_Error(int type, char *fmt, ...) |
|
#else |
|
Parse_Error(va_alist) |
|
va_dcl |
|
#endif |
|
{ |
|
va_list ap; |
|
#ifdef __STDC__ |
|
va_start(ap, fmt); |
|
#else |
|
int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */ |
|
char *fmt; |
|
|
|
va_start(ap); |
/* firstchar = ParseSkipEmptyLines(buffer); |
type = va_arg(ap, int); |
* Scans lines, skipping empty lines. May put some characters into |
fmt = va_arg(ap, char *); |
* buffer, returns the first character useful to continue parsing |
#endif |
* (e.g., not a backslash or a space. */ |
|
static int ParseSkipEmptyLines(Buffer); |
|
|
ParseVErrorInternal(current->fname, current->lineno, type, fmt, ap); |
|
} |
|
|
|
static IFile * |
static IFile * |
new_ifile(name, stream) |
new_ifile(name, stream) |
char *name; |
const char *name; |
FILE *stream; |
FILE *stream; |
{ |
{ |
IFile *ifile; |
IFile *ifile; |
#ifdef CLEANUP |
#if 0 |
Lst_AtEnd(&fileNames, name); |
Lst_AtEnd(&fileNames, name); |
#endif |
#endif |
|
|
ifile = emalloc(sizeof(*ifile)); |
ifile = emalloc(sizeof(*ifile)); |
ifile->fname = name; |
ifile->fname = name; |
ifile->str = NULL; |
ifile->str = NULL; |
/* Naturally enough, we start reading at line 0 */ |
/* Naturally enough, we start reading at line 0. */ |
ifile->lineno = 0; |
ifile->lineno = 0; |
ifile->F = stream; |
ifile->F = stream; |
ifile->ptr = ifile->end = NULL; |
ifile->ptr = ifile->end = NULL; |
|
|
static IFile * |
static IFile * |
new_istring(str, name, lineno) |
new_istring(str, name, lineno) |
char *str; |
char *str; |
char *name; |
const char *name; |
unsigned long lineno; |
unsigned long lineno; |
{ |
{ |
IFile *ifile; |
IFile *ifile; |
|
|
/* No malloc, name is always taken from an already existing ifile */ |
/* No malloc, name is always taken from an already existing ifile */ |
ifile->fname = name; |
ifile->fname = name; |
ifile->F = NULL; |
ifile->F = NULL; |
/* Strings are used from for loops... */ |
/* Strings are used in for loops, so we need to reset the line counter |
|
* to an appropriate value. */ |
ifile->lineno = lineno; |
ifile->lineno = lineno; |
ifile->ptr = ifile->str = str; |
ifile->ptr = ifile->str = str; |
ifile->end = str + strlen(str); |
ifile->end = str + strlen(str); |
|
|
} |
} |
|
|
|
|
/*- |
|
*--------------------------------------------------------------------- |
|
* Parse_FromString -- |
|
* Start Parsing from the given string |
|
* |
|
* Side Effects: |
|
* A structure is added to the includes Lst and readProc, lineno, |
|
* fname and curFILE are altered for the new file |
|
*--------------------------------------------------------------------- |
|
*/ |
|
void |
void |
Parse_FromString(str, lineno) |
Parse_FromString(str, lineno) |
char *str; |
char *str; |
|
|
(void)fprintf(stderr, "%s\n----\n", str); |
(void)fprintf(stderr, "%s\n----\n", str); |
|
|
if (current != NULL) |
if (current != NULL) |
Lst_AtFront(&includes, current); |
Lst_Push(&input_stack, current); |
current = new_istring(str, current->fname, lineno); |
current = new_istring(str, current->fname, lineno); |
} |
} |
|
|
|
|
void |
void |
Parse_FromFile(name, stream) |
Parse_FromFile(name, stream) |
char *name; |
const char *name; |
FILE *stream; |
FILE *stream; |
{ |
{ |
if (current != NULL) |
if (current != NULL) |
Lst_AtFront(&includes, current); |
Lst_Push(&input_stack, current); |
current = new_ifile(name, stream); |
current = new_ifile(name, stream); |
} |
} |
|
|
/*- |
bool |
*--------------------------------------------------------------------- |
|
* Parse_NextFile -- |
|
* Called when EOF is reached in the current file. If we were reading |
|
* an include file, the includes stack is popped and things set up |
|
* to go back to reading the previous file at the previous location. |
|
* |
|
* Results: |
|
* CONTINUE if there's more to do. DONE if not. |
|
* |
|
* Side Effects: |
|
* The old curFILE, is closed. The includes list is shortened. |
|
* lineno, curFILE, and fname are changed if CONTINUE is returned. |
|
*--------------------------------------------------------------------- |
|
*/ |
|
Boolean |
|
Parse_NextFile() |
Parse_NextFile() |
{ |
{ |
IFile *next; |
if (current != NULL) |
|
free_ifile(current); |
next = (IFile *)Lst_DeQueue(&includes); |
current = (IFile *)Lst_Pop(&input_stack); |
if (next != NULL) { |
return current != NULL; |
if (current != NULL) |
|
free_ifile(current); |
|
current = next; |
|
return TRUE; |
|
} else |
|
return FALSE; |
|
} |
} |
|
|
/* guts for ParseReadc. Grab a new line off fgetln when we hit "\n" */ |
|
static int |
static int |
newline() |
newline() |
{ |
{ |
|
|
return EOF; |
return EOF; |
} |
} |
|
|
#define ParseReadc() current->ptr < current->end ? *current->ptr++ : newline() |
|
|
|
/* Take advantage of fgetln: we don't have to scan a whole line to skip it. |
|
*/ |
|
static int |
static int |
skiptoendofline() |
skiptoendofline() |
{ |
{ |
|
|
} |
} |
|
|
|
|
/* ParseSkipGetLine(): |
|
* Return the first logical line that starts with '.' |
|
*/ |
|
char * |
char * |
ParseSkipGetLine(linebuf) |
Parse_ReadNextConditionalLine(linebuf) |
Buffer linebuf; |
Buffer linebuf; |
{ |
{ |
int c; |
int c; |
|
|
} |
} |
|
|
/* This is the line we need to copy */ |
/* This is the line we need to copy */ |
return ParseGetLine(linebuf, "conditional"); |
return Parse_ReadUnparsedLine(linebuf, "conditional"); |
} |
} |
|
|
/* Grab logical line into linebuf. |
|
* The first character is already in c. */ |
|
static void |
static void |
ParseFoldLF(linebuf, c) |
ParseFoldLF(linebuf, c) |
Buffer linebuf; |
Buffer linebuf; |
|
|
} |
} |
} |
} |
|
|
/* ParseGetLine: |
|
* Simply get line, no parsing beyond \ |
|
*/ |
|
char * |
char * |
ParseGetLine(linebuf, type) |
Parse_ReadUnparsedLine(linebuf, type) |
Buffer linebuf; |
Buffer linebuf; |
const char *type; |
const char *type; |
{ |
{ |
|
|
return Buf_Retrieve(linebuf); |
return Buf_Retrieve(linebuf); |
} |
} |
|
|
/* Skip all empty lines, and return the first `useful' character. |
/* This is a fairly complex function, but without it, we could not skip |
* (This does skip blocks of comments at high speed, which justifies |
* blocks of comments without reading them. */ |
* the complexity of the function.) |
|
*/ |
|
static int |
static int |
ParseSkipEmptyLines(linebuf) |
ParseSkipEmptyLines(linebuf) |
Buffer linebuf; |
Buffer linebuf; |
|
|
} |
} |
} |
} |
|
|
/*- |
/* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps |
*--------------------------------------------------------------------- |
* the first tab), handles escaped newlinews, and skip over uninteresting |
* ParseReadLine -- |
* lines. |
* Read an entire line from the input file. |
|
* |
* |
* Results: |
* The line number is advanced, which implies that continuation |
* A line without a new line, or NULL at EOF. |
* lines are numbered with the last line no (we could do better, at a |
|
* price). |
* |
* |
* Notes: |
* Trivial comments are also removed, but we can't do more, as |
* Removes beginning and trailing blanks (but keeps first tab). |
* we don't know which lines are shell commands or not. */ |
* Updates line numbers, handles escaped newlines, and skip over |
|
* uninteresting lines. |
|
* All but trivial comments can't be handled at this point, because we |
|
* don't know yet which lines are shell commands or not. |
|
*--------------------------------------------------------------------- |
|
*/ |
|
char * |
char * |
ParseReadLine(linebuf) |
Parse_ReadNormalLine(linebuf) |
Buffer linebuf; |
Buffer linebuf; |
{ |
{ |
int c; /* the current character */ |
int c; /* the current character */ |
|
|
void |
void |
LowParse_Init() |
LowParse_Init() |
{ |
{ |
Lst_Init(&includes); |
Lst_Init(&input_stack); |
current = NULL; |
current = NULL; |
} |
} |
|
|
void |
void |
LowParse_End() |
LowParse_End() |
{ |
{ |
Lst_Destroy(&includes, NOFREE); /* Should be empty now */ |
Lst_Destroy(&input_stack, NOFREE); /* Should be empty now */ |
|
#ifdef 0 |
|
Lst_Destroy(&fileNames, (SimpleProc)free); |
|
#endif |
} |
} |
#endif |
#endif |
|
|
|
|
void |
void |
Finish_Errors() |
Parse_ReportErrors() |
{ |
{ |
if (current != NULL) { |
if (fatal_errors) { |
free_ifile(current); |
#ifdef CLEANUP |
current = NULL; |
while (Parse_NextFile()) |
} |
; |
if (fatals) { |
#endif |
fprintf(stderr, "Fatal errors encountered -- cannot continue\n"); |
fprintf(stderr, "Fatal errors encountered -- cannot continue\n"); |
exit(1); |
exit(1); |
} |
} else |
|
assert(current == NULL); |
} |
} |