#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "defines.h"
#include "buf.h"
#include "lowparse.h"
#include "error.h"
#include "lst.h"
#include "memory.h"
#include "pathnames.h"
#ifndef LOCATION_TYPE
#include "location.h"
#endif
#include "var.h"
#define READ_MAKEFILES "MAKEFILE_LIST"
struct input_stream {
Location origin;
FILE *F;
char *str;
char *ptr;
char *end;
};
static struct input_stream *current;
static LIST input_stack;
static Location *post_parse = NULL;
static struct input_stream *new_input_file(const char *, FILE *);
static struct input_stream *new_input_string(char *, const Location *);
static void free_input_stream(struct input_stream *);
#define read_char() \
current->ptr < current->end ? *current->ptr++ : grab_new_line_and_readchar()
static int grab_new_line_and_readchar(void);
static int skip_to_end_of_line(void);
static void read_logical_line(Buffer, int);
static int skip_empty_lines_and_read_char(Buffer);
const char *curdir;
size_t curdir_len;
void
Parse_setcurdir(const char *dir)
{
curdir = dir;
curdir_len = strlen(dir);
}
static bool
startswith(const char *f, const char *s, size_t len)
{
return strncmp(f, s, len) == 0 && f[len] == '/';
}
static const char *
simplify(const char *filename)
{
if (startswith(filename, curdir, curdir_len))
return filename + curdir_len + 1;
else if (startswith(filename, _PATH_DEFSYSPATH,
sizeof(_PATH_DEFSYSPATH)-1)) {
size_t sz;
char *buf;
sz = strlen(filename) - sizeof(_PATH_DEFSYSPATH)+3;
buf = emalloc(sz);
snprintf(buf, sz, "<%s>", filename+sizeof(_PATH_DEFSYSPATH));
return buf;
} else
return filename;
}
static struct input_stream *
new_input_file(const char *name, FILE *stream)
{
struct input_stream *istream;
istream = emalloc(sizeof(*istream));
istream->origin.fname = simplify(name);
Var_Append(READ_MAKEFILES, name);
istream->str = NULL;
istream->origin.lineno = 0;
istream->F = stream;
istream->ptr = istream->end = NULL;
return istream;
}
static void
free_input_stream(struct input_stream *istream)
{
if (istream->F) {
if (ferror(istream->F))
Parse_Error(PARSE_FATAL, "Read error");
if (fileno(istream->F) != STDIN_FILENO)
(void)fclose(istream->F);
}
free(istream->str);
free(istream);
}
static struct input_stream *
new_input_string(char *str, const Location *origin)
{
struct input_stream *istream;
istream = emalloc(sizeof(*istream));
istream->origin = *origin;
istream->F = NULL;
istream->ptr = istream->str = str;
istream->end = str + strlen(str);
return istream;
}
void
Parse_FromString(char *str, unsigned long lineno)
{
Location origin;
origin.fname = current->origin.fname;
origin.lineno = lineno;
if (DEBUG(FOR))
(void)fprintf(stderr, "%s\n----\n", str);
Lst_Push(&input_stack, current);
assert(current != NULL);
current = new_input_string(str, &origin);
}
void
Parse_FromFile(const char *name, FILE *stream)
{
if (current != NULL)
Lst_Push(&input_stack, current);
current = new_input_file(name, stream);
}
bool
Parse_NextFile(void)
{
if (current != NULL)
free_input_stream(current);
current = Lst_Pop(&input_stack);
return current != NULL;
}
static int
grab_new_line_and_readchar(void)
{
size_t len;
if (current->F) {
current->ptr = fgetln(current->F, &len);
if (current->ptr) {
current->end = current->ptr + len;
return *current->ptr++;
} else {
current->end = NULL;
}
}
return EOF;
}
static int
skip_to_end_of_line(void)
{
if (current->F) {
if (current->end - current->ptr > 1)
current->ptr = current->end - 1;
if (*current->ptr == '\n')
return *current->ptr++;
return EOF;
} else {
int c;
do {
c = read_char();
} while (c != '\n' && c != EOF);
return c;
}
}
char *
Parse_ReadNextConditionalLine(Buffer linebuf)
{
int c;
while ((c = read_char()) != '.') {
for (;c != '\n'; c = read_char()) {
if (c == '\\') {
c = read_char();
if (c == '\n')
current->origin.lineno++;
}
if (c == EOF)
return NULL;
}
current->origin.lineno++;
}
return Parse_ReadUnparsedLine(linebuf, "conditional");
}
static void
read_logical_line(Buffer linebuf, int c)
{
for (;;) {
if (c == '\n') {
current->origin.lineno++;
break;
}
if (c == EOF)
break;
Buf_AddChar(linebuf, c);
c = read_char();
while (c == '\\') {
c = read_char();
if (c == '\n') {
Buf_AddSpace(linebuf);
current->origin.lineno++;
do {
c = read_char();
} while (c == ' ' || c == '\t');
} else {
Buf_AddChar(linebuf, '\\');
if (c == '\\') {
Buf_AddChar(linebuf, '\\');
c = read_char();
}
break;
}
}
}
}
char *
Parse_ReadUnparsedLine(Buffer linebuf, const char *type)
{
int c;
Buf_Reset(linebuf);
c = read_char();
if (c == EOF) {
Parse_Error(PARSE_FATAL, "Unclosed %s", type);
return NULL;
}
while (c == '\\') {
c = read_char();
if (c == '\n') {
current->origin.lineno++;
do {
c = read_char();
} while (c == ' ' || c == '\t');
} else {
Buf_AddChar(linebuf, '\\');
if (c == '\\') {
Buf_AddChar(linebuf, '\\');
c = read_char();
}
break;
}
}
read_logical_line(linebuf, c);
return Buf_Retrieve(linebuf);
}
static int
skip_empty_lines_and_read_char(Buffer linebuf)
{
int c;
for (;;) {
Buf_Reset(linebuf);
c = read_char();
if (c == ' ') {
do {
c = read_char();
} while (c == ' ' || c == '\t');
while (c == '\\') {
c = read_char();
if (c == '\n') {
current->origin.lineno++;
do {
c = read_char();
} while (c == ' ' || c == '\t');
} else {
Buf_AddChar(linebuf, '\\');
if (c == '\\') {
Buf_AddChar(linebuf, '\\');
c = read_char();
}
if (c == EOF)
return '\n';
else
return c;
}
}
assert(c != '\t');
}
if (c == '#')
c = skip_to_end_of_line();
if (c == '\t') {
Buf_AddChar(linebuf, '\t');
do {
c = read_char();
} while (c == ' ' || c == '\t');
while (c == '\\') {
c = read_char();
if (c == '\n') {
current->origin.lineno++;
do {
c = read_char();
} while (c == ' ' || c == '\t');
} else {
Buf_AddChar(linebuf, '\\');
if (c == '\\') {
Buf_AddChar(linebuf, '\\');
c = read_char();
}
if (c == EOF)
return '\n';
else
return c;
}
}
}
if (c == '\n')
current->origin.lineno++;
else
return c;
}
}
char *
Parse_ReadNormalLine(Buffer linebuf)
{
int c;
c = skip_empty_lines_and_read_char(linebuf);
if (c == EOF)
return NULL;
else {
read_logical_line(linebuf, c);
return Buf_Retrieve(linebuf);
}
}
unsigned long
Parse_Getlineno(void)
{
return current ? current->origin.lineno : 0;
}
const char *
Parse_Getfilename(void)
{
return current ? current->origin.fname : NULL;
}
void
Parse_SetLocation(Location *origin)
{
post_parse = origin;
}
void
Parse_FillLocation(Location *origin)
{
if (post_parse) {
*origin = *post_parse;
} else {
origin->lineno = Parse_Getlineno();
origin->fname = Parse_Getfilename();
}
}
void
Parse_ReportErrors(void)
{
if (fatal_errors)
exit(1);
else
assert(current == NULL);
}