[BACK]Return to lowparse.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

File: [local] / src / usr.bin / make / lowparse.c (download)

Revision 1.16, Sun Nov 11 12:35:02 2001 UTC (22 years, 6 months ago) by espie
Branch: MAIN
CVS Tags: OPENBSD_3_2_BASE, OPENBSD_3_2, OPENBSD_3_1_BASE, OPENBSD_3_1
Changes since 1.15: +2 -2 lines

Fixed version... don't see how this could work on i386, since it didn't
initialize create in main.c.

/*	$OpenPackages$ */
/*	$OpenBSD: lowparse.c,v 1.16 2001/11/11 12:35:02 espie Exp $ */

/* low-level parsing functions. */

/*
 * Copyright (c) 1999,2000 Marc Espie.
 *
 * Extensive code changes for the OpenBSD project.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
 * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "defines.h"
#include "buf.h"
#include "lowparse.h"
#include "error.h"
#include "lst.h"
#include "memory.h"

/* 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 */
#endif

/* Input stream structure, file or string. */
typedef struct {
    const char		*fname; /* Name of file */
    unsigned long	lineno; /* Line number */
    FILE		*F;	/* Open stream, or NULL if pure string. */
    char		*str;	/* Input string, if F == NULL. */

    /* Line buffer. */
    char		*ptr;	/* Where we are. */
    char		*end;	/* Don't overdo it. */
} IFile;

static IFile	*current;	/* IFile being parsed. */

static LIST	input_stack;	/* Stack of IFiles waiting to be parsed 
				 * (includes and loop reparses) */

/* IFile ctors. 
 *
 * 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 *);


/* 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);
/* c = skiptoendofline();
 *	Skips to the end of the current line, returns either '\n' or EOF.  */
static int skiptoendofline(void);


/* Helper functions to handle basic parsing. */
/* ParseFoldLF(buffer, firstchar);
 *	Grabs logical line into buffer, the first character has already been
 *	read into firstchar.  */
static void ParseFoldLF(Buffer, int);

/* firstchar = ParseSkipEmptyLines(buffer);
 *	Scans lines, skipping empty lines. May put some characters into
 *	buffer, returns the first character useful to continue parsing
 *	(e.g., not a backslash or a space. */
static int ParseSkipEmptyLines(Buffer);

static IFile *
new_ifile(name, stream)
    const char *name;
    FILE *stream;
{
    IFile *ifile;
#if 0
    Lst_AtEnd(&fileNames, name);
#endif

    ifile = emalloc(sizeof(*ifile));
    ifile->fname = name;
    ifile->str = NULL;
    /* Naturally enough, we start reading at line 0. */
    ifile->lineno = 0;
    ifile->F = stream;
    ifile->ptr = ifile->end = NULL;
    return ifile;
}

static void
free_ifile(ifile)
    IFile *ifile;
{
    if (ifile->F && fileno(ifile->F) != STDIN_FILENO)
	(void)fclose(ifile->F);
    free(ifile->str);
    /* Note we can't free the file names yet, as they are embedded in GN for
     * error reports. */
    free(ifile);
}

static IFile *
new_istring(str, name, lineno)
    char *str;
    const char *name;
    unsigned long lineno;
{
    IFile *ifile;

    ifile = emalloc(sizeof(*ifile));
    /* No malloc, name is always taken from an already existing ifile */
    ifile->fname = name;
    ifile->F = NULL;
    /* Strings are used in for loops, so we need to reset the line counter
     * to an appropriate value. */
    ifile->lineno = lineno;
    ifile->ptr = ifile->str = str;
    ifile->end = str + strlen(str);
    return ifile;
}


void
Parse_FromString(str, lineno)
    char		*str;
    unsigned long	lineno;
{
    if (DEBUG(FOR))
	(void)fprintf(stderr, "%s\n----\n", str);

    if (current != NULL)
	Lst_Push(&input_stack, current);
    current = new_istring(str, current->fname, lineno);
}


void
Parse_FromFile(name, stream)
    const char		*name;
    FILE		*stream;
{
    if (current != NULL)
	Lst_Push(&input_stack, current);
    current = new_ifile(name, stream);
}

bool
Parse_NextFile()
{
    if (current != NULL)
	free_ifile(current);
    current = (IFile *)Lst_Pop(&input_stack);
    return current != NULL;
}

static int
newline()
{
    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
skiptoendofline()
{
    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 = ParseReadc();
	} while (c != '\n' && c != EOF);
	return c;
    }
}


char *
Parse_ReadNextConditionalLine(linebuf)
    Buffer linebuf;
{
    int c;

	/* If first char isn't dot, skip to end of line, handling \ */
    while ((c = ParseReadc()) != '.') {
	for (;c != '\n'; c = ParseReadc()) {
	    if (c == '\\') {
		c = ParseReadc();
		if (c == '\n')
		    current->lineno++;
	    }
	    if (c == EOF) {
		Parse_Error(PARSE_FATAL, "Unclosed conditional");
		return NULL;
	    }
	}
	current->lineno++;
    }

	/* This is the line we need to copy */
    return Parse_ReadUnparsedLine(linebuf, "conditional");
}

static void
ParseFoldLF(linebuf, c)
    Buffer linebuf;
    int c;
{
    for (;;) {
	if (c == '\n') {
	    current->lineno++;
	    break;
	}
	if (c == EOF)
	    break;
	Buf_AddChar(linebuf, c);
	c = ParseReadc();
	while (c == '\\') {
	    c = ParseReadc();
	    if (c == '\n') {
		Buf_AddSpace(linebuf);
		current->lineno++;
		do {
		    c = ParseReadc();
		} while (c == ' ' || c == '\t');
	    } else {
		Buf_AddChar(linebuf, '\\');
		if (c == '\\') {
		    Buf_AddChar(linebuf, '\\');
		    c = ParseReadc();
		}
		break;
	    }
	}
    }
}

char *
Parse_ReadUnparsedLine(linebuf, type)
    Buffer	linebuf;
    const char	*type;
{
    int c;

    Buf_Reset(linebuf);
    c = ParseReadc();
    if (c == EOF) {
	Parse_Error(PARSE_FATAL, "Unclosed %s", type);
	return NULL;
    }

    /* Handle '\' at beginning of line, since \\n needs special treatment */
    while (c == '\\') {
	c = ParseReadc();
	if (c == '\n') {
	    current->lineno++;
	    do {
		c = ParseReadc();
	    } while (c == ' ' || c == '\t');
	} else {
	    Buf_AddChar(linebuf, '\\');
	    if (c == '\\') {
		Buf_AddChar(linebuf, '\\');
		c = ParseReadc();
	    }
	    break;
	}
    }
    ParseFoldLF(linebuf, c);

    return Buf_Retrieve(linebuf);
}

/* This is a fairly complex function, but without it, we could not skip
 * blocks of comments without reading them. */
static int
ParseSkipEmptyLines(linebuf)
    Buffer	linebuf;
{
    int 	c;		/* the current character */

    for (;;) {
	Buf_Reset(linebuf);
	c = ParseReadc();
	/* Strip leading spaces, fold on '\n' */
	if (c == ' ') {
	    do {
		c = ParseReadc();
	    } while (c == ' ' || c == '\t');
	    while (c == '\\') {
		c = ParseReadc();
		if (c == '\n') {
		    current->lineno++;
		    do {
			c = ParseReadc();
		    } while (c == ' ' || c == '\t');
		} else {
		    Buf_AddChar(linebuf, '\\');
		    if (c == '\\') {
			Buf_AddChar(linebuf, '\\');
			c = ParseReadc();
		    }
		    if (c == EOF)
			return '\n';
		    else
			return c;
		}
	    }
	    assert(c != '\t');
	}
	if (c == '#')
	    c = skiptoendofline();
	/* Almost identical to spaces, except this occurs after comments
	 * have been taken care of, and we keep the tab itself.  */
	if (c == '\t') {
	    Buf_AddChar(linebuf, '\t');
	    do {
		c = ParseReadc();
	    } while (c == ' ' || c == '\t');
	    while (c == '\\') {
		c = ParseReadc();
		if (c == '\n') {
		    current->lineno++;
		    do {
			c = ParseReadc();
		    } while (c == ' ' || c == '\t');
		} else {
		    Buf_AddChar(linebuf, '\\');
		    if (c == '\\') {
			Buf_AddChar(linebuf, '\\');
			c = ParseReadc();
		    }
		    if (c == EOF)
			return '\n';
		    else
			return c;
		    return c;
		}
	    }
	}
	if (c == '\n')
	    current->lineno++;
	else
	    return c;
    }
}

/* Parse_ReadNormalLine removes beginning and trailing blanks (but keeps
 * the first tab), handles escaped newlines, and skip over uninteresting
 * lines.  
 *
 * The line number is advanced, which implies that continuation
 * lines are numbered with the last line no (we could do better, at a
 * price).
 *
 * Trivial comments are also removed, but we can't do more, as
 * we don't know which lines are shell commands or not.  */
char *
Parse_ReadNormalLine(linebuf)
    Buffer	linebuf;
{
    int 	c;		/* the current character */

    c = ParseSkipEmptyLines(linebuf);

    if (c == EOF)
	return NULL;
    else {
	ParseFoldLF(linebuf, c);
	Buf_KillTrailingSpaces(linebuf);
	return Buf_Retrieve(linebuf);
    }
}

unsigned long
Parse_Getlineno()
{
    return current ? current->lineno : 0;
}

const char *
Parse_Getfilename()
{
    return current ? current->fname : NULL;
}

#ifdef CLEANUP
void
LowParse_Init()
{
    Static_Lst_Init(&input_stack);
    current = NULL;
}

void
LowParse_End()
{
    Lst_Destroy(&input_stack, NOFREE);	/* Should be empty now */
#ifdef 0
    Lst_Destroy(&fileNames, (SimpleProc)free);
#endif
}
#endif


void
Parse_ReportErrors()
{
    if (fatal_errors) {
#ifdef CLEANUP
	while (Parse_NextFile())
		;
#endif
	fprintf(stderr, "Fatal errors encountered -- cannot continue\n");
	exit(1);
    } else
    	assert(current == NULL);
}