~breadbox/cppp

e92668a11188e0c0cc297c89acb3ecca3c86f25e — Brian Raiter 2 years ago d011ae4
Updated version and copyright, and untabified the source (rip my diffs).
20 files changed, 1033 insertions(+), 1062 deletions(-)

M Makefile
M README
M clexer.c
M clexer.h
M cppp.1
M cppp.c
M error.c
M error.h
M exptree.c
M exptree.h
M gen.c
M gen.h
M mstr.c
M mstr.h
M ppproc.c
M ppproc.h
M symset.c
M symset.h
M unixisms.c
M unixisms.h
M Makefile => Makefile +0 -1
@@ 3,7 3,6 @@
# Where to install
#
prefix = /usr/local
version = 2.7

.PHONY: all check install clean dist


M README => README +5 -4
@@ 32,7 32,8 @@ file, it will output a message and do its best to skip over the
invalid construct and continue parsing. Note that it is possible
(though rare) for compiler extensions to be reported as errors -- e.g.
some compilers reportedly allow #elif directives in a sequence
beginning with #ifdef.
beginning with #ifdef. (Although please note that cppp does include
support for #elifdef/#elifndef.)

The general purpose of this program is to deal with legacy code bases
where #ifdefs have crept in and infiltrated the source. This sort of


@@ 57,9 58,9 @@ That said, cppp is free software, so you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version. This program is
distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License,
distributed in the hope that it will be useful, but without any
warranty; without even the implied warranty of merchantability or
fitness for a particular purpose. See the GNU General Public License,
included in this distribution in the file COPYING, for more details.

cppp website: <http://www.muppetlabs.com/~breadbox/software/cppp.html>.

M clexer.c => clexer.c +128 -130
@@ 1,7 1,5 @@
/* clexer.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* clexer.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <string.h>
#include <ctype.h>


@@ 12,28 10,28 @@

/* Flag values indicating the lexer's current state.
 */
#define	F_InCharQuote		0x0001
#define	F_LeavingCharQuote	0x0002
#define	F_InString		0x0004
#define	F_LeavingString		0x0008
#define	F_InComment		0x0010
#define	F_In99Comment		0x0020
#define	F_InAnyComment		0x0030
#define	F_LeavingComment	0x0040
#define F_LongChar		0x0080
#define	F_Whitespace		0x0100
#define	F_EndOfLine		0x0200
#define	F_Seen1st		0x0400
#define	F_Preprocess		0x0800
#define F_InCharQuote           0x0001
#define F_LeavingCharQuote      0x0002
#define F_InString              0x0004
#define F_LeavingString         0x0008
#define F_InComment             0x0010
#define F_In99Comment           0x0020
#define F_InAnyComment          0x0030
#define F_LeavingComment        0x0040
#define F_LongChar              0x0080
#define F_Whitespace            0x0100
#define F_EndOfLine             0x0200
#define F_Seen1st               0x0400
#define F_Preprocess            0x0800

/* The values that comprise the state of the lexer as it reads the
 * input.
 */
struct clexer {
    int		state;		/* the current set of state flags */
    int		charquote;	/* count of characters inside single quotes */
    int		parenlevel;	/* nesting level of parentheses */
    int		charcount;	/* actual size of current character token */
    int         state;          /* the current set of state flags */
    int         charquote;      /* count of characters inside single quotes */
    int         parenlevel;     /* nesting level of parentheses */
    int         charcount;      /* actual size of current character token */
};

/* The list of preprocess statements that the program knows about,


@@ 41,7 39,7 @@ struct clexer {
 */
struct ppstatement {
    char const *statement;
    enum ppcmd	id;
    enum ppcmd  id;
};

static struct ppstatement const ppstatements[] = {


@@ 109,38 107,38 @@ static char const *readwhack(clexer *cl, char const *input)
    int n;

    if (*input != '\\')
	return input;
        return input;

    ++input;
    ++cl->charcount;
    if (*input >= '0' && *input <= '7') {
	for (n = 1 ; n < 3 ; ++n)
	    if (input[n] < '0' || input[n] > '7')
		break;
	input += n - 1;
	cl->charcount += n - 1;
        for (n = 1 ; n < 3 ; ++n)
            if (input[n] < '0' || input[n] > '7')
                break;
        input += n - 1;
        cl->charcount += n - 1;
    } else if (*input == 'x') {
	for (n = 1 ; n < 3 ; ++n)
	    if (!isxdigit(input[n]))
		break;
	if (n != 3)
	    error(errBadCharLiteral);
	input += n - 1;
	cl->charcount += n - 1;
        for (n = 1 ; n < 3 ; ++n)
            if (!isxdigit(input[n]))
                break;
        if (n != 3)
            error(errBadCharLiteral);
        input += n - 1;
        cl->charcount += n - 1;
    } else {
	switch (*input) {
	  case 'a':	case '"':
	  case 'b':	case '\'':
	  case 'f':	case '\?':
	  case 'n':	case '\\':
	  case 'r':
	  case 't':
	  case 'v':
	    break;
	  default:
	    error(errBadCharLiteral);
	    break;
	}
        switch (*input) {
          case 'a':     case '"':
          case 'b':     case '\'':
          case 'f':     case '\?':
          case 'n':     case '\\':
          case 'r':
          case 't':
          case 'v':
            break;
          default:
            error(errBadCharLiteral);
            break;
        }
    }

    return input;


@@ 159,96 157,96 @@ static char const *examinechar(clexer *cl, char const *input)

    in = input;
    if (cl->state & F_LeavingComment) {
	cl->state &= ~(F_InComment | F_LeavingComment);
        cl->state &= ~(F_InComment | F_LeavingComment);
    } else if (cl->state & F_LeavingString) {
	cl->state &= ~(F_InString | F_LeavingString);
        cl->state &= ~(F_InString | F_LeavingString);
    } else if (cl->state & F_LeavingCharQuote) {
	cl->state &= ~(F_InCharQuote | F_LeavingCharQuote);
	cl->charquote = 0;
        cl->state &= ~(F_InCharQuote | F_LeavingCharQuote);
        cl->charquote = 0;
    }
    if (cl->state & F_EndOfLine) {
	cl->charcount = 0;
	return input;
        cl->charcount = 0;
        return input;
    }
    cl->charcount = 1;
    if (!*in || *in == '\n') {
	cl->state |= F_EndOfLine;
	cl->state &= ~(F_Whitespace | F_In99Comment | F_Preprocess);
	return input;
        cl->state |= F_EndOfLine;
        cl->state &= ~(F_Whitespace | F_In99Comment | F_Preprocess);
        return input;
    }

    if (cl->state & (F_InCharQuote | F_InString))
	cl->state &= ~F_Whitespace;
        cl->state &= ~F_Whitespace;
    else if ((cl->state & F_InAnyComment) || isspace(*in))
	cl->state |= F_Whitespace;
        cl->state |= F_Whitespace;
    else
	cl->state &= ~F_Whitespace;
        cl->state &= ~F_Whitespace;
    if (cl->state & F_In99Comment) {
	/* do nothing */
        /* do nothing */
    } else if (cl->state & F_InComment) {
	if (in[0] == '*' && in[1] == '/') {
	    ++cl->charcount;
	    cl->state |= F_LeavingComment;
	}
        if (in[0] == '*' && in[1] == '/') {
            ++cl->charcount;
            cl->state |= F_LeavingComment;
        }
    } else if (cl->state & F_InCharQuote) {
	if (*in == '\\') {
	    readwhack(cl, in);
	    ++cl->charquote;
	} else if (*in == '\'') {
	    if (!cl->charquote)
		error(errBadCharLiteral);
	    else if (cl->charquote > (cl->state & F_LongChar ? 4 : 1))
        if (*in == '\\') {
            readwhack(cl, in);
            ++cl->charquote;
        } else if (*in == '\'') {
            if (!cl->charquote)
                error(errBadCharLiteral);
	    cl->state |= F_LeavingCharQuote;
            else if (cl->charquote > (cl->state & F_LongChar ? 4 : 1))
                error(errBadCharLiteral);
            cl->state |= F_LeavingCharQuote;
            cl->state &= ~F_LongChar;
	} else {
	    ++cl->charquote;
	}
        } else {
            ++cl->charquote;
        }
    } else if (cl->state & F_InString) {
	if (*in == '\\')
	    readwhack(cl, in);
	else if (*in == '"')
	    cl->state |= F_LeavingString;
        if (*in == '\\')
            readwhack(cl, in);
        else if (*in == '"')
            cl->state |= F_LeavingString;
    } else {
	if (*in == '/') {
	    if (in[1] == '/') {
		++cl->charcount;
		cl->state |= F_In99Comment | F_Whitespace;
	    } else if (in[1] == '*') {
		++cl->charcount;
		cl->state |= F_InComment | F_Whitespace;
	    }
	}
	if (!(cl->state & (F_Whitespace | F_Seen1st))) {
	    cl->state |= F_Seen1st;
	    if (*in == '#') {
		cl->state |= F_Preprocess;
        if (*in == '/') {
            if (in[1] == '/') {
                ++cl->charcount;
                cl->state |= F_In99Comment | F_Whitespace;
            } else if (in[1] == '*') {
                ++cl->charcount;
                cl->state |= F_InComment | F_Whitespace;
            }
        }
        if (!(cl->state & (F_Whitespace | F_Seen1st))) {
            cl->state |= F_Seen1st;
            if (*in == '#') {
                cl->state |= F_Preprocess;
            } else if (in[0] == '%' && in[1] == ':') {
		cl->state |= F_Preprocess;
                cl->state |= F_Preprocess;
                ++cl->charcount;
            }
	}
	if (!(cl->state & F_Whitespace)) {
	    if (*in == '\'') {
		cl->state |= F_InCharQuote;
		cl->charquote = 0;
	    } else if (*in == '"') {
		cl->state |= F_InString;
	    } else if (*in == 'L') {
		if (in[1] == '\'') {
		    ++cl->charcount;
		    cl->state |= F_InCharQuote | F_LongChar;
		    cl->charquote = 0;
		} else if (in[1] == '"') {
		    ++cl->charcount;
		    cl->state |= F_InString;
		}
	    } else if (*in == '(') {
		++cl->parenlevel;
	    } else if (*in == ')') {
		--cl->parenlevel;
	    }
	}
        }
        if (!(cl->state & F_Whitespace)) {
            if (*in == '\'') {
                cl->state |= F_InCharQuote;
                cl->charquote = 0;
            } else if (*in == '"') {
                cl->state |= F_InString;
            } else if (*in == 'L') {
                if (in[1] == '\'') {
                    ++cl->charcount;
                    cl->state |= F_InCharQuote | F_LongChar;
                    cl->charquote = 0;
                } else if (in[1] == '"') {
                    ++cl->charcount;
                    cl->state |= F_InString;
                }
            } else if (*in == '(') {
                ++cl->parenlevel;
            } else if (*in == ')') {
                --cl->parenlevel;
            }
        }
    }
    return input;
}


@@ 274,7 272,7 @@ char const *nextchar(clexer *cl, char const *input)
char const *nextchars(clexer *cl, char const *input, int n)
{
    for ( ; n && !endoflinep(cl) ; --n)
	input = examinechar(cl, input + cl->charcount);
        input = examinechar(cl, input + cl->charcount);
    return input;
}



@@ 291,7 289,7 @@ char const *skipwhite(clexer *cl, char const *input)
char const *restofline(clexer *cl, char const *input)
{
    while (!endoflinep(cl))
	input = nextchar(cl, input);
        input = nextchar(cl, input);
    return input;
}



@@ 299,31 297,31 @@ char const *restofline(clexer *cl, char const *input)
 * and returns the statement type in cmdid.
 */
char const *getpreprocessorcmd(clexer *cl, char const *line,
			       enum ppcmd *cmdid)
                               enum ppcmd *cmdid)
{
    char const *begin, *end;
    size_t size;
    int n;

    if (!preproclinep(cl)) {
	*cmdid = cmdNone;
	return line;
        *cmdid = cmdNone;
        return line;
    }
    begin = skipwhite(cl, line);
    if (endoflinep(cl)) {
	*cmdid = cmdNone;
	return begin;
        *cmdid = cmdNone;
        return begin;
    }

    for (end = begin ; _issym(*end) ; end = nextchar(cl, end)) ;
    size = (size_t)(end - begin);
    *cmdid = cmdOther;
    for (n = 0 ; n < sizearray(ppstatements) ; ++n) {
	if (size == strlen(ppstatements[n].statement) &&
			!memcmp(ppstatements[n].statement, begin, size)) {
	    *cmdid = ppstatements[n].id;
	    break;
	}
        if (size == strlen(ppstatements[n].statement) &&
                        !memcmp(ppstatements[n].statement, begin, size)) {
            *cmdid = ppstatements[n].id;
            break;
        }
    }

    return skipwhite(cl, end);


@@ 343,11 341,11 @@ void endline(clexer *cl)
void endstream(clexer *cl)
{
    if (cl->state & F_InCharQuote)
	error(errOpenCharLiteral);
        error(errOpenCharLiteral);
    else if (cl->state & F_InString)
	error(errOpenStringLiteral);
        error(errOpenStringLiteral);
    else if (cl->state & F_InComment)
	error(errOpenComment);
        error(errOpenComment);
    cl->state = 0;
    cl->charquote = 0;
    cl->parenlevel = 0;

M clexer.h => clexer.h +4 -6
@@ 1,10 1,8 @@
/* clexer.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* clexer.h: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef	_clexer_h_
#define	_clexer_h_
#ifndef _clexer_h_
#define _clexer_h_

/*
 * The C lexer object does basic lexical analysis on the input stream.


@@ 96,7 94,7 @@ extern void endstream(clexer *cl);
 * is returned via cmdid.
 */
extern char const *getpreprocessorcmd(clexer *cl, char const *input,
				      enum ppcmd *cmdid);
                                      enum ppcmd *cmdid);

/* Mark the end of the current line of input. This function should be
 * called before the next call to beginline().

M cppp.1 => cppp.1 +2 -2
@@ 65,9 65,9 @@ empty section in the output.
.B cppp
does not collapse these.
.SH COPYRIGHT
Copyright \(co 1995, 1999, 2011 Brian Raiter
Copyright \(co 1995-2022 Brian Raiter
<breadbox@muppetlabs.com>.
.P
License GPLv2+: GNU GPL version 2 or later. This is free software: you
are free to change and redistribute it. There is NO WARRANTY, to the
are free to change and redistribute it. There is no warranty, to the
extent permitted by law.

M cppp.c => cppp.c +117 -119
@@ 1,7 1,5 @@
/* cppp.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* cppp.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdio.h>
#include <stdlib.h>


@@ 35,11 33,11 @@ static char const *const yowzitch3 =
/* Version identifier.
 */
static char const *const vourzhon =
    "cppp: version 2.7\n"
    "Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>\n"
    "cppp: version 2.8\n"
    "Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>\n"
    "License GPLv2+: GNU GPL version 2 or later.\n"
    "This is free software; you are free to change and redistribute it.\n"
    "There is NO WARRANTY, to the extent permitted by law.\n";
    "There is no warranty, to the extent permitted by law.\n";

/* Display a warning message regarding command-line syntax.
 */


@@ 78,64 76,64 @@ static int readcmdline(int argc, char *argv[], symset *defs, symset *undefs)
    int i, j;

    for (i = j = 1 ; i < argc ; ++i) {
	if (argv[i][0] != '-') {
	    argv[j++] = argv[i];
	    continue;
	}
	if (!strcmp(argv[i], "--help")) {
	    fputs(yowzitch1, stdout);
	    fputs(yowzitch2, stdout);
	    fputs(yowzitch3, stdout);
	    exit(EXIT_SUCCESS);
	} else if (!strcmp(argv[i], "--version")) {
	    fputs(vourzhon, stdout);
	    exit(EXIT_SUCCESS);
	}
	if (!strcmp(argv[i], "--")) {
	    for (++i ; i < argc ; ++i)
		argv[j++] = argv[i];
	    break;
	}
	if (argv[i][1] == 'D') {
	    arg = argv[i] + 2;
	    if (!*arg) {
		if (i + 1 < argc)
		    arg = argv[++i];
		else
		    fail("missing argument to -D");
	    }
	    p = strchr(arg, '=');
	    if (p) {
		*p++ = '\0';
		if (!*p)
		    fail("missing value for symbol %s", arg);
		value = strtol(p, &p, 0);
		if (*p || value == LONG_MIN || value == LONG_MAX)
		    fail("invalid numeric value for symbol %s", arg);
	    } else {
		value = 1;
	    }
	    if (removesymbolfromset(undefs, arg))
		warn("defining undefined symbol %s", arg);
	    else if (removesymbolfromset(defs, arg))
		warn("defining already-defined symbol %s", arg);
	    addsymboltoset(defs, arg, value);
	} else if (argv[i][1] == 'U') {
	    arg = argv[i] + 2;
	    if (!*arg) {
		if (i + 1 < argc)
		    arg = argv[++i];
		else
		    fail("missing argument to -U");
	    }
	    if (removesymbolfromset(defs, arg))
		warn("undefining defined symbol %s", arg);
	    else if (removesymbolfromset(undefs, arg))
		warn("undefining already-undefined symbol %s", arg);
	    addsymboltoset(undefs, arg, 0L);
	} else {
	    fail("invalid option: %s", argv[i]);
	}
        if (argv[i][0] != '-') {
            argv[j++] = argv[i];
            continue;
        }
        if (!strcmp(argv[i], "--help")) {
            fputs(yowzitch1, stdout);
            fputs(yowzitch2, stdout);
            fputs(yowzitch3, stdout);
            exit(EXIT_SUCCESS);
        } else if (!strcmp(argv[i], "--version")) {
            fputs(vourzhon, stdout);
            exit(EXIT_SUCCESS);
        }
        if (!strcmp(argv[i], "--")) {
            for (++i ; i < argc ; ++i)
                argv[j++] = argv[i];
            break;
        }
        if (argv[i][1] == 'D') {
            arg = argv[i] + 2;
            if (!*arg) {
                if (i + 1 < argc)
                    arg = argv[++i];
                else
                    fail("missing argument to -D");
            }
            p = strchr(arg, '=');
            if (p) {
                *p++ = '\0';
                if (!*p)
                    fail("missing value for symbol %s", arg);
                value = strtol(p, &p, 0);
                if (*p || value == LONG_MIN || value == LONG_MAX)
                    fail("invalid numeric value for symbol %s", arg);
            } else {
                value = 1;
            }
            if (removesymbolfromset(undefs, arg))
                warn("defining undefined symbol %s", arg);
            else if (removesymbolfromset(defs, arg))
                warn("defining already-defined symbol %s", arg);
            addsymboltoset(defs, arg, value);
        } else if (argv[i][1] == 'U') {
            arg = argv[i] + 2;
            if (!*arg) {
                if (i + 1 < argc)
                    arg = argv[++i];
                else
                    fail("missing argument to -U");
            }
            if (removesymbolfromset(defs, arg))
                warn("undefining defined symbol %s", arg);
            else if (removesymbolfromset(undefs, arg))
                warn("undefining already-undefined symbol %s", arg);
            addsymboltoset(undefs, arg, 0L);
        } else {
            fail("invalid option: %s", argv[i]);
        }
    }
    return j;
}


@@ 166,71 164,71 @@ int main(int argc, char *argv[])

    exitcode = EXIT_SUCCESS;
    if (argc <= 1) {
	seterrorfile(NULL);
	partialpreprocess(ppp, stdin, stdout);
        seterrorfile(NULL);
        partialpreprocess(ppp, stdin, stdout);
    } else if (argc == 2) {
	filename = argv[1];
	seterrorfile(filename);
	if (!(infile = fopen(filename, "r"))) {
	    perror(filename);
	    return EXIT_FAILURE;
	}
	partialpreprocess(ppp, infile, stdout);
	fclose(infile);
        filename = argv[1];
        seterrorfile(filename);
        if (!(infile = fopen(filename, "r"))) {
            perror(filename);
            return EXIT_FAILURE;
        }
        partialpreprocess(ppp, infile, stdout);
        fclose(infile);
    } else if (fileisdir(argv[argc - 1])) {
        if (!savedir()) {
            perror(".");
            exit(EXIT_FAILURE);
        }
	dirname = argv[argc - 1];
	for (i = 1 ; i < argc - 1 ; ++i) {
	    filename = argv[i];
	    seterrorfile(filename);
	    if (!(infile = fopen(filename, "r"))) {
		perror(filename);
		exitcode = EXIT_FAILURE;
		continue;
	    }
        dirname = argv[argc - 1];
        for (i = 1 ; i < argc - 1 ; ++i) {
            filename = argv[i];
            seterrorfile(filename);
            if (!(infile = fopen(filename, "r"))) {
                perror(filename);
                exitcode = EXIT_FAILURE;
                continue;
            }
            if (!changedir(dirname)) {
		perror(dirname);
		exit(EXIT_FAILURE);
	    }
	    filename = getbasefilename(filename);
	    outfile = fopen(filename, "w");
	    if (outfile) {
		partialpreprocess(ppp, infile, outfile);
		if (fclose(outfile)) {
		    perror(filename);
		    exitcode = EXIT_FAILURE;
		}
	    } else {
		perror(filename);
		exitcode = EXIT_FAILURE;
	    }
	    fclose(infile);
	    restoredir();
	}
                perror(dirname);
                exit(EXIT_FAILURE);
            }
            filename = getbasefilename(filename);
            outfile = fopen(filename, "w");
            if (outfile) {
                partialpreprocess(ppp, infile, outfile);
                if (fclose(outfile)) {
                    perror(filename);
                    exitcode = EXIT_FAILURE;
                }
            } else {
                perror(filename);
                exitcode = EXIT_FAILURE;
            }
            fclose(infile);
            restoredir();
        }
        unsavedir();
    } else if (argc == 3) {
	filename = argv[1];
	seterrorfile(filename);
	if (!(infile = fopen(filename, "r"))) {
	    perror(filename);
	    return EXIT_FAILURE;
	}
	filename = argv[2];
	if (!(outfile = fopen(filename, "w"))) {
	    perror(filename);
	    return EXIT_FAILURE;
	}
	partialpreprocess(ppp, infile, outfile);
	fclose(infile);
	if (fclose(outfile)) {
	    perror(filename);
	    exitcode = EXIT_FAILURE;
	}
        filename = argv[1];
        seterrorfile(filename);
        if (!(infile = fopen(filename, "r"))) {
            perror(filename);
            return EXIT_FAILURE;
        }
        filename = argv[2];
        if (!(outfile = fopen(filename, "w"))) {
            perror(filename);
            return EXIT_FAILURE;
        }
        partialpreprocess(ppp, infile, outfile);
        fclose(infile);
        if (fclose(outfile)) {
            perror(filename);
            exitcode = EXIT_FAILURE;
        }
    } else {
	fail("\"%s\" is not a directory.", argv[argc - 1]);
        fail("\"%s\" is not a directory.", argv[argc - 1]);
    }

    if (geterrormark() > 0)

M error.c => error.c +57 -59
@@ 1,7 1,5 @@
/* error.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* error.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdio.h>
#include <string.h>


@@ 12,10 10,10 @@
/* Persistent data needed to report and track errors.
 */
struct errhandler {
    char const	   *file;	/* a filename to prefix error messages with */
    unsigned long   lineno;	/* a line number to accompany the filename */
    int		    count;	/* total number of errors seen */
    enum errortype  type;	/* the most recent error */
    char const     *file;       /* a filename to prefix error messages with */
    unsigned long   lineno;     /* a line number to accompany the filename */
    int             count;      /* total number of errors seen */
    enum errortype  type;       /* the most recent error */
};

/* There is only one error handler for the program.


@@ 67,85 65,85 @@ void error(enum errortype type)
{
    err.type = type;
    if (type == errNone)
	return;
        return;
    ++err.count;

    if (err.file) {
	if (err.lineno)
	    fprintf(stderr, "%s:%lu: ", err.file, err.lineno);
	else
	    fprintf(stderr, "%s: ", err.file);
        if (err.lineno)
            fprintf(stderr, "%s:%lu: ", err.file, err.lineno);
        else
            fprintf(stderr, "%s: ", err.file);
    } else {
	if (err.lineno)
	    fprintf(stderr, "line %lu: ", err.lineno);
	else
	    fprintf(stderr, "error: ");
        if (err.lineno)
            fprintf(stderr, "line %lu: ", err.lineno);
        else
            fprintf(stderr, "error: ");
    }

    switch (type) {
      case errSyntax:
	fputs("preprocessor syntax error.", stderr);
	break;
        fputs("preprocessor syntax error.", stderr);
        break;
      case errFileIO:
	if (errno)
	    fputs(strerror(errno), stderr);
	else
	    fputs("file I/O error.", stderr);
	break;
        if (errno)
            fputs(strerror(errno), stderr);
        else
            fputs("file I/O error.", stderr);
        break;
      case errIfsTooDeep:
	fputs("too many nested #ifs.", stderr);
	break;
        fputs("too many nested #ifs.", stderr);
        break;
      case errDanglingElse:
	fputs("#else not matched to any #if.", stderr);
	break;
        fputs("#else not matched to any #if.", stderr);
        break;
      case errDanglingEnd:
	fputs("#endif found without any #if.", stderr);
	break;
        fputs("#endif found without any #if.", stderr);
        break;
      case errOpenIf:
	fputs("#if not closed.", stderr);
	break;
        fputs("#if not closed.", stderr);
        break;
      case errElifWithIfdef:
	fputs("#elif matched with #ifdef/#ifndef.", stderr);
	break;
        fputs("#elif matched with #ifdef/#ifndef.", stderr);
        break;
      case errElifdefWithIf:
	fputs("#elifdef/#elifndef matched with #if.", stderr);
	break;
        fputs("#elifdef/#elifndef matched with #if.", stderr);
        break;
      case errBadCharLiteral:
	fputs("bad character literal.", stderr);
	break;
        fputs("bad character literal.", stderr);
        break;
      case errOpenCharLiteral:
	fputs("last character literal not closed.", stderr);
	break;
        fputs("last character literal not closed.", stderr);
        break;
      case errOpenStringLiteral:
	fputs("last string literal not closed.", stderr);
	break;
        fputs("last string literal not closed.", stderr);
        break;
      case errOpenComment:
	fputs("last comment not closed.", stderr);
	break;
        fputs("last comment not closed.", stderr);
        break;
      case errOpenParenthesis:
	fputs("unmatched left parenthesis.", stderr);
	break;
        fputs("unmatched left parenthesis.", stderr);
        break;
      case errEmptyIf:
	fputs("#if with no argument.", stderr);
	break;
        fputs("#if with no argument.", stderr);
        break;
      case errMissingOperand:
	fputs("operator with missing expression.", stderr);
	break;
        fputs("operator with missing expression.", stderr);
        break;
      case errZeroDiv:
	fputs("division by zero in expression.", stderr);
	break;
        fputs("division by zero in expression.", stderr);
        break;
      case errIfSyntax:
	fputs("bad syntax in #if expression.", stderr);
	break;
        fputs("bad syntax in #if expression.", stderr);
        break;
      case errDefinedSyntax:
	fputs("bad syntax in defined operator.", stderr);
	break;
        fputs("bad syntax in defined operator.", stderr);
        break;
      case errBrokenComment:
	fputs("comment spans deleted line.", stderr);
	break;
        fputs("comment spans deleted line.", stderr);
        break;
      default:
	fprintf(stderr, "unspecified error (%d).", type);
	break;
        fprintf(stderr, "unspecified error (%d).", type);
        break;
    }
    fputc('\n', stderr);
}

M error.h => error.h +20 -22
@@ 1,7 1,5 @@
/* error.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* error.h: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef _error_h_
#define _error_h_


@@ 16,25 14,25 @@
enum errortype
{
    errNone = 0,
    errSyntax,			/* general syntax error */
    errFileIO,			/* file I/O failure (see errno) */
    errBadCharLiteral,		/* invalid character sequence in quotes */
    errOpenCharLiteral,		/* unclosed single quote */
    errOpenStringLiteral,	/* unclosed double quote */
    errOpenComment,		/* unclosed multi-line comment */
    errBrokenComment,		/* comment spanning removed line */
    errDanglingElse,		/* unmatched #else found */
    errDanglingEnd,		/* unmatched #end found */
    errOpenIf,			/* unclosed #if */
    errElifWithIfdef,		/* #elif following #ifdef/#ifndef */
    errElifdefWithIf,		/* #elifdef/#elifndef following #if */
    errIfsTooDeep,		/* way too many nested #ifs */
    errOpenParenthesis,		/* unclosed left parenthesis */
    errMissingOperand,		/* operand expected to follow expression */
    errZeroDiv,			/* division by zero in an expression */
    errEmptyIf,			/* missing #if parameter */
    errIfSyntax,		/* general syntax error inside #if parameter */
    errDefinedSyntax,		/* general syntax error in defined operand */
    errSyntax,                  /* general syntax error */
    errFileIO,                  /* file I/O failure (see errno) */
    errBadCharLiteral,          /* invalid character sequence in quotes */
    errOpenCharLiteral,         /* unclosed single quote */
    errOpenStringLiteral,       /* unclosed double quote */
    errOpenComment,             /* unclosed multi-line comment */
    errBrokenComment,           /* comment spanning removed line */
    errDanglingElse,            /* unmatched #else found */
    errDanglingEnd,             /* unmatched #end found */
    errOpenIf,                  /* unclosed #if */
    errElifWithIfdef,           /* #elif following #ifdef/#ifndef */
    errElifdefWithIf,           /* #elifdef/#elifndef following #if */
    errIfsTooDeep,              /* way too many nested #ifs */
    errOpenParenthesis,         /* unclosed left parenthesis */
    errMissingOperand,          /* operand expected to follow expression */
    errZeroDiv,                 /* division by zero in an expression */
    errEmptyIf,                 /* missing #if parameter */
    errIfSyntax,                /* general syntax error inside #if parameter */
    errDefinedSyntax,           /* general syntax error in defined operand */
    errCount
};


M exptree.c => exptree.c +322 -322
@@ 19,11 19,11 @@
enum exp
{
    expNone = 0,
    expConstant,		/* a literal constant */
    expMacro,			/* a simple macro identifier */
    expParamMacro,		/* a function-like macro */
    expDefined,			/* a defined operator with an identifier */
    expOperator			/* a C operator with 1-3 subexpressions */
    expConstant,                /* a literal constant */
    expMacro,                   /* a simple macro identifier */
    expParamMacro,              /* a function-like macro */
    expDefined,                 /* a defined operator with an identifier */
    expOperator                 /* a C operator with 1-3 subexpressions */
};

/* The different operators available in the C preprocessor. Unary


@@ 46,15 46,15 @@ enum op
/* The representation of a parsed expresssion tree.
 */
struct exptree {
    char const *begin;		/* start of the expression in the source */
    char const *end;		/* end of the expression */
    int		valued;		/* true if it has a definite value */
    long	value;		/* the expression's value, if it has one */
    enum exp	exp;    	/* the type of expression */
    enum op	op;		/* the operator for this expression, if any */
    char const *identifier;	/* the identifer, for the defined operator */
    int		childcount;	/* how many subexpressions are present */
    exptree    *child[4];	/* pointers to the subexpressions */
    char const *begin;          /* start of the expression in the source */
    char const *end;            /* end of the expression */
    int         valued;         /* true if it has a definite value */
    long        value;          /* the expression's value, if it has one */
    enum exp    exp;            /* the type of expression */
    enum op     op;             /* the operator for this expression, if any */
    char const *identifier;     /* the identifer, for the defined operator */
    int         childcount;     /* how many subexpressions are present */
    exptree    *child[4];       /* pointers to the subexpressions */
};

/*


@@ 62,11 62,11 @@ struct exptree {
 */

struct opinfo {
    enum op	    op;		/* the operator */
    char const     *symbol;	/* the operator's representation */
    int		    size;	/* length of the representation string */
    int		    prec;	/* the precedence, from 1 to 14 inclusive */
    int		    l2r;	/* true if associativity is left-to-right */
    enum op         op;         /* the operator */
    char const     *symbol;     /* the operator's representation */
    int             size;       /* length of the representation string */
    int             prec;       /* the precedence, from 1 to 14 inclusive */
    int             l2r;        /* true if associativity is left-to-right */
};

static struct opinfo const prefixops[] = {


@@ 90,13 90,13 @@ static struct opinfo const infixops[] = {
    { opModulo,        "%", 1, 13, TRUE  },
    { opLesser,        "<", 1, 10, TRUE  },
    { opGreater,       ">", 1, 10, TRUE  },
    { opAdd,	       "+", 1, 12, TRUE  },
    { opAdd,           "+", 1, 12, TRUE  },
    { opSubtract,      "-", 1, 12, TRUE  },
    { opBitAnd,        "&", 1,  8, TRUE  },
    { opBitOr,	       "|", 1,  6, TRUE  },
    { opBitOr,         "|", 1,  6, TRUE  },
    { opBitXor,        "^", 1,  7, TRUE  },
    { opConditional,   "?", 1,  3, FALSE },
    { opComma,	       ",", 1,  1, TRUE  }
    { opComma,         ",", 1,  1, TRUE  }
};

/* Allocates an expression tree.


@@ 121,9 121,9 @@ void freeexptree(exptree *t)
    int n;

    if (t) {
	for (n = 0 ; n < t->childcount ; ++n)
	    freeexptree(t->child[n]);
	deallocate(t);
        for (n = 0 ; n < t->childcount ; ++n)
            freeexptree(t->child[n]);
        deallocate(t);
    }
}



@@ 153,16 153,16 @@ static int addchild(exptree *t, exptree *sub, int pos)
    int n;

    if (t->childcount == sizearray(t->child) || !sub)
	return FALSE;
        return FALSE;
    if (pos < -1 || pos > t->childcount)
	return FALSE;
        return FALSE;

    if (pos == -1 || pos == t->childcount) {
	t->child[t->childcount] = sub;
        t->child[t->childcount] = sub;
    } else {
	for (n = t->childcount ; n > pos ; --n)
	    t->child[n] = t->child[n - 1];
	t->child[pos] = sub;
        for (n = t->childcount ; n > pos ; --n)
            t->child[n] = t->child[n - 1];
        t->child[pos] = sub;
    }
    ++t->childcount;
    return TRUE;


@@ 177,8 177,8 @@ static exptree *addnewchild(exptree *t, int pos)

    child = initexptree();
    if (!addchild(t, child, pos)) {
	deallocate(child);
	return NULL;
        deallocate(child);
        return NULL;
    }
    return child;
}


@@ 192,28 192,28 @@ static int getcharconstant(char const *input)
    int value, n;

    if (*input != '\\')
	return *input;
        return *input;

    ++input;
    if (*input >= '0' && *input <= '7') {
	value = 0;
	for (n = 0 ; n < 3 ; ++n) {
	    if (input[n] < '0' || input[n] > '7')
		break;
	    value = value * 8 + input[n] - '0';
	}
	return value;
        value = 0;
        for (n = 0 ; n < 3 ; ++n) {
            if (input[n] < '0' || input[n] > '7')
                break;
            value = value * 8 + input[n] - '0';
        }
        return value;
    }

    if (*input == 'x') {
	value = 0;
	for (n = 1 ; n < 3 ; ++n) {
	    if (!isxdigit(input[n]))
		break;
	    value = value * 16 + (isdigit(input[n]) ? input[n] - '0' :
						tolower(input[n]) - 'a' + 10);
	}
	return value;
        value = 0;
        for (n = 1 ; n < 3 ; ++n) {
            if (!isxdigit(input[n]))
                break;
            value = value * 16 + (isdigit(input[n]) ? input[n] - '0' :
                                                tolower(input[n]) - 'a' + 10);
        }
        return value;
    }

    switch (*input) {


@@ 243,101 243,101 @@ static char const *parseconstant(exptree *t, clexer *cl, char const *input)
    mark = geterrormark();
    t->begin = input;
    if (charquotep(cl)) {
	t->exp = expConstant;
	t->valued = TRUE;
	t->value = getcharconstant(input + 1);
	while (!endoflinep(cl) && charquotep(cl))
	    input = nextchar(cl, input);
	t->end = input;
	input = skipwhite(cl, input);
        t->exp = expConstant;
        t->valued = TRUE;
        t->value = getcharconstant(input + 1);
        while (!endoflinep(cl) && charquotep(cl))
            input = nextchar(cl, input);
        t->end = input;
        input = skipwhite(cl, input);
    } else if (!memcmp(input, "defined", 7)) {
	t->exp = expDefined;
	input = skipwhite(cl, nextchars(cl, input, 7));
	paren = *input == '(';
	if (paren)
	    input = skipwhite(cl, nextchar(cl, input));
	size = getidentifierlength(input);
	if (!size) {
	    error(errDefinedSyntax);
	    goto failure;
	}
	t->identifier = input;
	input = nextchars(cl, input, size);
	if (paren) {
	    input = skipwhite(cl, input);
	    if (*input != ')') {
		error(errDefinedSyntax);
		goto failure;
	    }
	    input = nextchar(cl, input);
	}
	t->valued = FALSE;
	t->end = input;
	input = skipwhite(cl, input);
        t->exp = expDefined;
        input = skipwhite(cl, nextchars(cl, input, 7));
        paren = *input == '(';
        if (paren)
            input = skipwhite(cl, nextchar(cl, input));
        size = getidentifierlength(input);
        if (!size) {
            error(errDefinedSyntax);
            goto failure;
        }
        t->identifier = input;
        input = nextchars(cl, input, size);
        if (paren) {
            input = skipwhite(cl, input);
            if (*input != ')') {
                error(errDefinedSyntax);
                goto failure;
            }
            input = nextchar(cl, input);
        }
        t->valued = FALSE;
        t->end = input;
        input = skipwhite(cl, input);
    } else if (isdigit(*input)) {
	t->exp = expConstant;
	if (*input == '0') {
	    input = nextchar(cl, input);
	    if (tolower(*input) == 'x') {
		do
		    input = nextchar(cl, input);
		while (isxdigit(*input));
	    } else {
		while (*input >= '0' && *input <= '7')
		    input = nextchar(cl, input);
	    }
	} else {
	    do
		input = nextchar(cl, input);
	    while (isdigit(*input));
	}
	t->value = strtol(t->begin, &p, 0);
	t->valued = p == input;
	if (toupper(*input) == 'L') {
	    input = nextchar(cl, input);
	    if (toupper(*input) == 'L')
		input = nextchar(cl, input);
	    if (toupper(*input) == 'U')
		input = nextchar(cl, input);
	} else if (toupper(*input) == 'U') {
	    input = nextchar(cl, input);
	    if (toupper(*input) == 'L') {
		input = nextchar(cl, input);
		if (toupper(*input) == 'L')
		    input = nextchar(cl, input);
	    }
	}
	t->end = input;
	input = skipwhite(cl, input);
        t->exp = expConstant;
        if (*input == '0') {
            input = nextchar(cl, input);
            if (tolower(*input) == 'x') {
                do
                    input = nextchar(cl, input);
                while (isxdigit(*input));
            } else {
                while (*input >= '0' && *input <= '7')
                    input = nextchar(cl, input);
            }
        } else {
            do
                input = nextchar(cl, input);
            while (isdigit(*input));
        }
        t->value = strtol(t->begin, &p, 0);
        t->valued = p == input;
        if (toupper(*input) == 'L') {
            input = nextchar(cl, input);
            if (toupper(*input) == 'L')
                input = nextchar(cl, input);
            if (toupper(*input) == 'U')
                input = nextchar(cl, input);
        } else if (toupper(*input) == 'U') {
            input = nextchar(cl, input);
            if (toupper(*input) == 'L') {
                input = nextchar(cl, input);
                if (toupper(*input) == 'L')
                    input = nextchar(cl, input);
            }
        }
        t->end = input;
        input = skipwhite(cl, input);
    } else if (_issym(*input)) {
	do
	    input = nextchar(cl, input);
	while (_issym(*input));
	t->end = input;
	input = skipwhite(cl, input);
	if (*input == '(') {
	    t->exp = expParamMacro;
	    paren = getparenlevel(cl);
	    do {
		input = nextchar(cl, input);
		if (endoflinep(cl)) {
		    error(errOpenParenthesis);
		    goto failure;
		}
	    } while (getparenlevel(cl) >= paren);
	    t->valued = FALSE;
	    t->end = input;
	    input = skipwhite(cl, input);
	} else {
	    t->exp = expMacro;
	}
        do
            input = nextchar(cl, input);
        while (_issym(*input));
        t->end = input;
        input = skipwhite(cl, input);
        if (*input == '(') {
            t->exp = expParamMacro;
            paren = getparenlevel(cl);
            do {
                input = nextchar(cl, input);
                if (endoflinep(cl)) {
                    error(errOpenParenthesis);
                    goto failure;
                }
            } while (getparenlevel(cl) >= paren);
            t->valued = FALSE;
            t->end = input;
            input = skipwhite(cl, input);
        } else {
            t->exp = expMacro;
        }
    } else {
	error(errSyntax);
	goto failure;
        error(errSyntax);
        goto failure;
    }

    if (!errorsincemark(mark))
	return input;
        return input;

  failure:
    t->exp = expNone;


@@ 360,98 360,98 @@ static char const *parseexp(exptree *t, clexer *cl, char const *input,
    int found, n;

    if (t->exp != expNone)
	return input;
        return input;

    if (*input == '(') {
	tmp = input;
	input = skipwhite(cl, nextchar(cl, input));
	input = parseexp(t, cl, input, 0);
	if (t->exp == expNone) {
	    error(errSyntax);
	    goto failure;
	} else if (*input != ')') {
	    error(errOpenParenthesis);
	    goto failure;
	}
	t->begin = tmp;
	input = nextchar(cl, input);
	t->end = input;
	input = skipwhite(cl, input);
        tmp = input;
        input = skipwhite(cl, nextchar(cl, input));
        input = parseexp(t, cl, input, 0);
        if (t->exp == expNone) {
            error(errSyntax);
            goto failure;
        } else if (*input != ')') {
            error(errOpenParenthesis);
            goto failure;
        }
        t->begin = tmp;
        input = nextchar(cl, input);
        t->end = input;
        input = skipwhite(cl, input);
    } else {
	found = FALSE;
	for (n = 0 ; n < sizearray(prefixops) ; ++n) {
	    if (!memcmp(input, prefixops[n].symbol, prefixops[n].size)) {
		found = TRUE;
		break;
	    }
	}
	if (found) {
	    if (prefixops[n].prec < prec) {
		error(errMissingOperand);
		goto failure;
	    }
	    t->exp = expOperator;
	    t->op = prefixops[n].op;
	    t->begin = input;
	    input = nextchars(cl, input, prefixops[n].size);
	    input = skipwhite(cl, input);
	    x = addnewchild(t, -1);
	    input = parseexp(x, cl, input, prefixops[n].prec);
	    if (x->exp == expNone) {
		error(errSyntax);
		goto failure;
	    }
	    t->end = x->end;
	} else {
	    input = parseconstant(t, cl, input);
	    if (t->exp == expNone) {
		error(errSyntax);
		goto failure;
	    }
	}
        found = FALSE;
        for (n = 0 ; n < sizearray(prefixops) ; ++n) {
            if (!memcmp(input, prefixops[n].symbol, prefixops[n].size)) {
                found = TRUE;
                break;
            }
        }
        if (found) {
            if (prefixops[n].prec < prec) {
                error(errMissingOperand);
                goto failure;
            }
            t->exp = expOperator;
            t->op = prefixops[n].op;
            t->begin = input;
            input = nextchars(cl, input, prefixops[n].size);
            input = skipwhite(cl, input);
            x = addnewchild(t, -1);
            input = parseexp(x, cl, input, prefixops[n].prec);
            if (x->exp == expNone) {
                error(errSyntax);
                goto failure;
            }
            t->end = x->end;
        } else {
            input = parseconstant(t, cl, input);
            if (t->exp == expNone) {
                error(errSyntax);
                goto failure;
            }
        }
    }

    for (;;) {
	found = FALSE;
	for (n = 0 ; n < sizearray(infixops) ; ++n) {
	    if (!memcmp(input, infixops[n].symbol, infixops[n].size)) {
		found = TRUE;
		break;
	    }
	}
	if (!found || infixops[n].prec < prec
		   || (infixops[n].prec == prec && infixops[n].l2r))
	    break;
	input = nextchars(cl, input, infixops[n].size);
	input = skipwhite(cl, input);
	x = initexptree();
	tmp = t->begin;
	*x = *t;
	clearexptree(t);
	t->exp = expOperator;
	t->op = infixops[n].op;
	t->begin = tmp;
	addchild(t, x, -1);
	x = addnewchild(t, -1);
	if (t->op == opConditional) {
	    input = parseexp(x, cl, input, infixops[n].prec);
	    if (x->exp == expNone) {
		error(errSyntax);
		goto failure;
	    }
	    if (*input != ':') {
		error(errSyntax);
		goto failure;
	    }
	    input = skipwhite(cl, nextchar(cl, input));
	    x = addnewchild(t, -1);
	}
	input = parseexp(x, cl, input, infixops[n].prec);
	if (x->exp == expNone) {
	    error(errSyntax);
	    goto failure;
	}
	t->end = x->end;
        found = FALSE;
        for (n = 0 ; n < sizearray(infixops) ; ++n) {
            if (!memcmp(input, infixops[n].symbol, infixops[n].size)) {
                found = TRUE;
                break;
            }
        }
        if (!found || infixops[n].prec < prec
                   || (infixops[n].prec == prec && infixops[n].l2r))
            break;
        input = nextchars(cl, input, infixops[n].size);
        input = skipwhite(cl, input);
        x = initexptree();
        tmp = t->begin;
        *x = *t;
        clearexptree(t);
        t->exp = expOperator;
        t->op = infixops[n].op;
        t->begin = tmp;
        addchild(t, x, -1);
        x = addnewchild(t, -1);
        if (t->op == opConditional) {
            input = parseexp(x, cl, input, infixops[n].prec);
            if (x->exp == expNone) {
                error(errSyntax);
                goto failure;
            }
            if (*input != ':') {
                error(errSyntax);
                goto failure;
            }
            input = skipwhite(cl, nextchar(cl, input));
            x = addnewchild(t, -1);
        }
        input = parseexp(x, cl, input, infixops[n].prec);
        if (x->exp == expNone) {
            error(errSyntax);
            goto failure;
        }
        t->end = x->end;
    }
    return input;



@@ 479,21 479,21 @@ int markdefined(exptree *t, symset const *set, int defined)

    count = 0;
    for (n = 0 ; n < t->childcount ; ++n)
	count += markdefined(t->child[n], set, defined);
        count += markdefined(t->child[n], set, defined);
    if (!t->valued) {
	if (t->exp == expDefined) {
	    if (findsymbolinset(set, t->identifier, NULL)) {
		t->valued = TRUE;
		t->value = defined ? 1 : 0;
		++count;
	    }
	} else if (t->exp == expMacro) {
	    if (findsymbolinset(set, t->begin, &value)) {
		t->valued = TRUE;
		t->value = defined ? value : 0;
		++count;
	    }
	}
        if (t->exp == expDefined) {
            if (findsymbolinset(set, t->identifier, NULL)) {
                t->valued = TRUE;
                t->value = defined ? 1 : 0;
                ++count;
            }
        } else if (t->exp == expMacro) {
            if (findsymbolinset(set, t->begin, &value)) {
                t->valued = TRUE;
                t->value = defined ? value : 0;
                ++count;
            }
        }
    }
    return count;
}


@@ 509,36 509,36 @@ long evaltree(exptree *t, int *defined)
    int valued;

    if (t->exp == expNone) {
	if (defined)
	    *defined = FALSE;
	return 0;
        if (defined)
            *defined = FALSE;
        return 0;
    }
    if (t->exp != expOperator) {
	if (defined)
	    *defined = t->valued;
	return t->valued ? t->value : 0;
        if (defined)
            *defined = t->valued;
        return t->valued ? t->value : 0;
    }

    if (t->op < opPrefixCount) {
	val1 = evaltree(t->child[0], &valued);
	if (valued) {
	    switch (t->op) {
	      case opLogNot:	val1 = !val1;	break;
	      case opBitNot:	val1 = ~val1;	break;
	      case opPositive:	val1 = +val1;	break;
	      case opNegative:	val1 = -val1;	break;
	      default:				break;
	    }
	}
	goto done;
        val1 = evaltree(t->child[0], &valued);
        if (valued) {
            switch (t->op) {
              case opLogNot:    val1 = !val1;   break;
              case opBitNot:    val1 = ~val1;   break;
              case opPositive:  val1 = +val1;   break;
              case opNegative:  val1 = -val1;   break;
              default:                          break;
            }
        }
        goto done;
    }
    val1 = evaltree(t->child[0], &valued);
    if (t->op == opComma) {
	val1 = evaltree(t->child[1], &valued);
	goto done;
        val1 = evaltree(t->child[1], &valued);
        goto done;
    } else if (t->op == opConditional) {
	if (valued) {
	    val1 = evaltree(t->child[val1 ? 1 : 2], &valued);
        if (valued) {
            val1 = evaltree(t->child[val1 ? 1 : 2], &valued);
        } else {
            val1 = evaltree(t->child[1], &valued);
            if (valued) {


@@ 548,65 548,65 @@ long evaltree(exptree *t, int *defined)
                evaltree(t->child[2], NULL);
            }
        }
	goto done;
        goto done;
    } else if (t->op == opLogAnd) {
	if (valued) {
	    if (val1)
		val1 = evaltree(t->child[1], &valued);
	} else {
	    val1 = evaltree(t->child[1], &valued);
	    if (valued && val1)
		valued = FALSE;
	}
	goto done;
        if (valued) {
            if (val1)
                val1 = evaltree(t->child[1], &valued);
        } else {
            val1 = evaltree(t->child[1], &valued);
            if (valued && val1)
                valued = FALSE;
        }
        goto done;
    } else if (t->op == opLogOr) {
	if (valued) {
	    if (!val1)
		val1 = evaltree(t->child[1], &valued);
	} else {
	    val1 = evaltree(t->child[1], &valued);
	    if (valued && !val1)
		valued = FALSE;
	}
	goto done;
        if (valued) {
            if (!val1)
                val1 = evaltree(t->child[1], &valued);
        } else {
            val1 = evaltree(t->child[1], &valued);
            if (valued && !val1)
                valued = FALSE;
        }
        goto done;
    }
    if (!valued)
	goto done;
        goto done;

    val2 = evaltree(t->child[1], &valued);
    if (valued) {
	if (val2 == 0 && (t->op == opDivide || t->op == opModulo)) {
	    error(errZeroDiv);
	    valued = FALSE;
	    goto done;
	}
	switch (t->op) {
	  case opLeftShift:	val1 = val1 << (int)val2;	break;
	  case opRightShift:	val1 = val1 >> (int)val2;	break;
	  case opLesserEqual:	val1 = val1 <= val2;		break;
	  case opGreaterEqual:	val1 = val1 >= val2;		break;
	  case opEqual:		val1 = val1 == val2;		break;
	  case opInequal:	val1 = val1 != val2;		break;
	  case opMultiply:	val1 = val1 * val2;		break;
	  case opDivide:	val1 = val1 / val2;		break;
	  case opModulo:	val1 = val1 % val2;		break;
	  case opLesser:	val1 = val1 < val2;		break;
	  case opGreater:	val1 = val1 > val2;		break;
	  case opAdd:		val1 = val1 + val2;		break;
	  case opSubtract:	val1 = val1 - val2;		break;
	  case opBitAnd:	val1 = val1 & val2;		break;
	  case opBitXor:	val1 = val1 ^ val2;		break;
	  case opBitOr:		val1 = val1 | val2;		break;
	  default:						break;
	}
        if (val2 == 0 && (t->op == opDivide || t->op == opModulo)) {
            error(errZeroDiv);
            valued = FALSE;
            goto done;
        }
        switch (t->op) {
          case opLeftShift:     val1 = val1 << (int)val2;       break;
          case opRightShift:    val1 = val1 >> (int)val2;       break;
          case opLesserEqual:   val1 = val1 <= val2;            break;
          case opGreaterEqual:  val1 = val1 >= val2;            break;
          case opEqual:         val1 = val1 == val2;            break;
          case opInequal:       val1 = val1 != val2;            break;
          case opMultiply:      val1 = val1 * val2;             break;
          case opDivide:        val1 = val1 / val2;             break;
          case opModulo:        val1 = val1 % val2;             break;
          case opLesser:        val1 = val1 < val2;             break;
          case opGreater:       val1 = val1 > val2;             break;
          case opAdd:           val1 = val1 + val2;             break;
          case opSubtract:      val1 = val1 - val2;             break;
          case opBitAnd:        val1 = val1 & val2;             break;
          case opBitXor:        val1 = val1 ^ val2;             break;
          case opBitOr:         val1 = val1 | val2;             break;
          default:                                              break;
        }
    }

  done:
    t->valued = valued;
    if (valued)
	t->value = val1;
        t->value = val1;
    if (defined)
	*defined = valued;
        *defined = valued;
    return valued ? val1 : 0;
}



@@ 622,41 622,41 @@ int unparseevaluated(exptree const *t, char *buffer)
    int n;

    if (t->exp == expNone)
	return 0;
        return 0;
    if (t->valued)
        return sprintf(buffer, "%ld", t->value);

    if (t->exp != expOperator) {
        size = getexplength(t);
        memcpy(buffer, t->begin, size);
	return size;
        return size;
    }
    if (t->op == opConditional) {
	if (t->child[0]->valued)
	    return unparseevaluated(t->child[t->child[0]->value ? 1 : 2],
				    buffer);
        if (t->child[0]->valued)
            return unparseevaluated(t->child[t->child[0]->value ? 1 : 2],
                                    buffer);
    } else if (t->op == opLogAnd || t->op == opLogOr) {
	if (t->child[0]->valued)
	    return unparseevaluated(t->child[1], buffer);
	else if (t->child[1]->valued)
	    return unparseevaluated(t->child[0], buffer);
        if (t->child[0]->valued)
            return unparseevaluated(t->child[1], buffer);
        else if (t->child[1]->valued)
            return unparseevaluated(t->child[0], buffer);
    }

    buf = buffer;
    src = t->begin;
    for (n = 0 ; n < t->childcount ; ++n) {
	size = t->child[n]->begin - src;
	if (size) {
	    memcpy(buf, src, size);
	    buf += size;
	}
	buf += unparseevaluated(t->child[n], buf);
	src = t->child[n]->end;
        size = t->child[n]->begin - src;
        if (size) {
            memcpy(buf, src, size);
            buf += size;
        }
        buf += unparseevaluated(t->child[n], buf);
        src = t->child[n]->end;
    }
    size = t->end - src;
    if (size) {
	memcpy(buf, src, size);
	buf += size;
        memcpy(buf, src, size);
        buf += size;
    }
    return (int)(buf - buffer);
}

M exptree.h => exptree.h +2 -2
@@ 3,8 3,8 @@
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef	_exptree_h_
#define	_exptree_h_
#ifndef _exptree_h_
#define _exptree_h_

/*
 * An expression tree parses and, where possible, evaluates C

M gen.c => gen.c +5 -7
@@ 1,7 1,5 @@
/* gen.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* gen.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdio.h>
#include <stdlib.h>


@@ 15,8 13,8 @@ void *allocate(size_t size)

    p = malloc(size);
    if (!p) {
	fputs("Out of memory.\n", stderr);
	exit(EXIT_FAILURE);
        fputs("Out of memory.\n", stderr);
        exit(EXIT_FAILURE);
    }
    return p;
}


@@ 27,8 25,8 @@ void *reallocate(void *p, size_t size)
{
    p = realloc(p, size);
    if (!p) {
	fputs("Out of memory.\n", stderr);
	exit(EXIT_FAILURE);
        fputs("Out of memory.\n", stderr);
        exit(EXIT_FAILURE);
    }
    return p;
}

M gen.h => gen.h +5 -7
@@ 1,10 1,8 @@
/* gen.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* gen.h: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef	_gen_h_
#define	_gen_h_
#ifndef _gen_h_
#define _gen_h_

/*
 * General definitions and functionality not specific to any module.


@@ 13,8 11,8 @@
#include <stddef.h>

#ifndef TRUE
#define	TRUE  1
#define	FALSE 0
#define TRUE  1
#define FALSE 0
#endif

/* Returns the number of elements in an array.

M mstr.c => mstr.c +6 -9
@@ 1,9 1,6 @@
/* mstr.c: Copyright (C) 2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */

#include <stdlib.h>
#include <string.h>
#include <limits.h>


@@ 14,8 11,8 @@
/* Specification of substring.
 */
typedef struct chspan {
    int		from;           /* index of start of substring */
    int		to;             /* first byte past substring */
    int         from;           /* index of start of substring */
    int         to;             /* first byte past substring */
} chspan;

/* A modifiable string with two representations, an editable


@@ 23,12 20,12 @@ typedef struct chspan {
 */
struct mstr {
    char       *str;            /* the edited, or "presentation" string */
    int		length;         /* the length of the string */
    int		allocated;      /* the size of the string buffer (and spans) */
    int         length;         /* the length of the string */
    int         allocated;      /* the size of the string buffer (and spans) */
    chspan     *spans;          /* what substring each char represents */
    char       *basestr;        /* the underlying, or "base" string */
    int		baselength;     /* the length of the base string */
    int		baseallocated;  /* the size of the base buffer */
    int         baselength;     /* the length of the base string */
    int         baseallocated;  /* the size of the base buffer */
};

/* Allocates an empty mstr object.

M mstr.h => mstr.h +0 -2
@@ 1,7 1,5 @@
/* mstr.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef _mstr_h_
#define _mstr_h_

M ppproc.c => ppproc.c +317 -319
@@ 1,7 1,5 @@
/* ppproc.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* ppproc.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdio.h>
#include <stdlib.h>


@@ 21,14 19,14 @@

/* State flags tracking the current state of ppproc.
 */
#define F_If		0x0001		/* inside a #if section */
#define F_Else		0x0002		/* inside a #else section */
#define F_Elif		0x0004		/* inside a #elif section */
#define F_Ifdef		0x0008		/* #if is actually #ifdef/#ifndef */
#define F_Ours		0x0010		/* guarded by user-specified symbol */
#define F_Copy		0x0020		/* section is passed to output */
#define F_IfModify	0x0040		/* modified #if expression */
#define F_ElseModify	0x0080		/* modified #elif expression */
#define F_If            0x0001          /* inside a #if section */
#define F_Else          0x0002          /* inside a #else section */
#define F_Elif          0x0004          /* inside a #elif section */
#define F_Ifdef         0x0008          /* #if is actually #ifdef/#ifndef */
#define F_Ours          0x0010          /* guarded by user-specified symbol */
#define F_Copy          0x0020          /* section is passed to output */
#define F_IfModify      0x0040          /* modified #if expression */
#define F_ElseModify    0x0080          /* modified #elif expression */

/* Return codes for the seqif() function.
 */


@@ 40,14 38,14 @@ enum status
/* The partial preprocessor.
 */
struct ppproc {
    clexer     *cl;			/* the lexer */
    symset const *defs;			/* list of defined symbols */
    symset const *undefs;		/* list of undefined symbols */
    mstr       *line;			/* the current line of input */
    int		copy;			/* true if input is going to output */
    int		absorb;			/* true if input is being suppressed */
    int		level;			/* current nesting level */
    int		stack[STACK_SIZE];	/* state flags for each level */
    clexer     *cl;                     /* the lexer */
    symset const *defs;                 /* list of defined symbols */
    symset const *undefs;               /* list of undefined symbols */
    mstr       *line;                   /* the current line of input */
    int         copy;                   /* true if input is going to output */
    int         absorb;                 /* true if input is being suppressed */
    int         level;                  /* current nesting level */
    int         stack[STACK_SIZE];      /* state flags for each level */
};

/* Allocates a partial preprocessor object.


@@ 88,7 86,7 @@ static void endfile(ppproc *ppp)
{
    endstream(ppp->cl);
    if (ppp->level != -1)
	error(errOpenIf);
        error(errOpenIf);
    erasemstr(ppp->line);
}



@@ 116,21 114,21 @@ static char const *seqif(ppproc *ppp, char *ifexp, enum status *status)
    n = geterrormark();
    ret = parseexptree(tree, ppp->cl, ifexp);
    if (errorsincemark(n)) {
	*status = statError;
	goto quit;
        *status = statError;
        goto quit;
    }

    n = markdefined(tree, ppp->defs, TRUE) +
	markdefined(tree, ppp->undefs, FALSE);
        markdefined(tree, ppp->undefs, FALSE);
    if (n) {
	*status = evaltree(tree, &defined) ? statDefined : statUndefined;
	if (!defined) {
	    *status = statPartDefined;
	    str = allocate(strlen(ifexp) + 1);
	    n = unparseevaluated(tree, str);
        *status = evaltree(tree, &defined) ? statDefined : statUndefined;
        if (!defined) {
            *status = statPartDefined;
            str = allocate(strlen(ifexp) + 1);
            n = unparseevaluated(tree, str);
            ret = editmstr(ppp->line, ifexp, getexplength(tree), str, n) + n;
	    deallocate(str);
	}
            deallocate(str);
        }
    }

  quit:


@@ 157,9 155,9 @@ static void seq(ppproc *ppp)
    ppp->absorb = FALSE;
    input = beginline(ppp->cl, getmstrbuf(ppp->line));
    while (!preproclinep(ppp->cl)) {
	if (endoflinep(ppp->cl))
	    return;
	input = nextchar(ppp->cl, input);
        if (endoflinep(ppp->cl))
            return;
        input = nextchar(ppp->cl, input);
    }

    cmd = skipwhite(ppp->cl, nextchar(ppp->cl, input));


@@ 168,273 166,273 @@ static void seq(ppproc *ppp)
    switch (id) {
      case cmdIfdef:
      case cmdIfndef:
	if (ppp->level + 1 >= sizearray(ppp->stack)) {
	    error(errIfsTooDeep);
	    break;
	}
	++ppp->level;
	ppp->stack[ppp->level] = F_If | F_Ifdef;
	if (!ppp->copy) {
	    input = restofline(ppp->cl, input);
	    break;
	}
	ppp->stack[ppp->level] |= F_Copy;
	size = getidentifierlength(input);
	if (!size) {
	    error(errEmptyIf);
	    break;
	}
	if (findsymbolinset(ppp->defs, input, NULL))
	    status = statDefined;
	else if (findsymbolinset(ppp->undefs, input, NULL))
	    status = statUndefined;
	else
	    status = statUnaffected;
        if (ppp->level + 1 >= sizearray(ppp->stack)) {
            error(errIfsTooDeep);
            break;
        }
        ++ppp->level;
        ppp->stack[ppp->level] = F_If | F_Ifdef;
        if (!ppp->copy) {
            input = restofline(ppp->cl, input);
            break;
        }
        ppp->stack[ppp->level] |= F_Copy;
        size = getidentifierlength(input);
        if (!size) {
            error(errEmptyIf);
            break;
        }
        if (findsymbolinset(ppp->defs, input, NULL))
            status = statDefined;
        else if (findsymbolinset(ppp->undefs, input, NULL))
            status = statUndefined;
        else
            status = statUnaffected;
        cmdend = nextchars(ppp->cl, input, size);
	input = skipwhite(ppp->cl, cmdend);
	if (!endoflinep(ppp->cl)) {
	    error(errSyntax);
	    break;
	}
	if (status != statUnaffected) {
	    ppp->absorb = TRUE;
	    ppp->stack[ppp->level] |= F_Ours;
	    if (id == cmdIfdef)
		ppp->copy = status == statDefined;
	    else
		ppp->copy = status == statUndefined;
	}
	break;
        input = skipwhite(ppp->cl, cmdend);
        if (!endoflinep(ppp->cl)) {
            error(errSyntax);
            break;
        }
        if (status != statUnaffected) {
            ppp->absorb = TRUE;
            ppp->stack[ppp->level] |= F_Ours;
            if (id == cmdIfdef)
                ppp->copy = status == statDefined;
            else
                ppp->copy = status == statUndefined;
        }
        break;

      case cmdIf:
	if (ppp->level + 1 >= sizearray(ppp->stack)) {
	    error(errIfsTooDeep);
	    break;
	}
	++ppp->level;
	ppp->stack[ppp->level] = F_If | (ppp->copy ? F_Copy : 0);
	if (!ppp->copy) {
	    input = restofline(ppp->cl, input);
	    break;
	}
	cmdend = seqif(ppp, (char*)input, &status);
	if (status == statError)
	    break;
	input = skipwhite(ppp->cl, cmdend);
	if (!endoflinep(ppp->cl)) {
	    error(errIfSyntax);
	    break;
	}
	if (status == statDefined || status == statUndefined) {
	    ppp->absorb = TRUE;
	    ppp->stack[ppp->level] |= F_Ours;
	    ppp->copy = status == statDefined;
	}
	break;
        if (ppp->level + 1 >= sizearray(ppp->stack)) {
            error(errIfsTooDeep);
            break;
        }
        ++ppp->level;
        ppp->stack[ppp->level] = F_If | (ppp->copy ? F_Copy : 0);
        if (!ppp->copy) {
            input = restofline(ppp->cl, input);
            break;
        }
        cmdend = seqif(ppp, (char*)input, &status);
        if (status == statError)
            break;
        input = skipwhite(ppp->cl, cmdend);
        if (!endoflinep(ppp->cl)) {
            error(errIfSyntax);
            break;
        }
        if (status == statDefined || status == statUndefined) {
            ppp->absorb = TRUE;
            ppp->stack[ppp->level] |= F_Ours;
            ppp->copy = status == statDefined;
        }
        break;

      case cmdElse:
	if (ppp->level < 0 || (ppp->stack[ppp->level] & F_Else)) {
	    error(errDanglingElse);
	    break;
	}
	ppp->stack[ppp->level] |= F_Else;
	if (!endoflinep(ppp->cl)) {
	    error(errSyntax);
	    break;
	}
        if (ppp->level < 0 || (ppp->stack[ppp->level] & F_Else)) {
            error(errDanglingElse);
            break;
        }
        ppp->stack[ppp->level] |= F_Else;
        if (!endoflinep(ppp->cl)) {
            error(errSyntax);
            break;
        }
        cmdend = input;
	if (ppp->stack[ppp->level] & F_Ours) {
	    ppp->copy = !ppp->copy;
	    ppp->absorb = TRUE;
	    n = ppp->level;
	    while (ppp->stack[n] & F_Elif) {
		if (ppp->stack[n] & F_ElseModify) {
		    ppp->absorb = TRUE;
		    break;
		}
		--n;
		if (!(ppp->stack[n] & F_Ours))
		    ppp->absorb = FALSE;
	    }
	}
	break;
        if (ppp->stack[ppp->level] & F_Ours) {
            ppp->copy = !ppp->copy;
            ppp->absorb = TRUE;
            n = ppp->level;
            while (ppp->stack[n] & F_Elif) {
                if (ppp->stack[n] & F_ElseModify) {
                    ppp->absorb = TRUE;
                    break;
                }
                --n;
                if (!(ppp->stack[n] & F_Ours))
                    ppp->absorb = FALSE;
            }
        }
        break;

      case cmdElifdef:
      case cmdElifndef:
	if (ppp->level < 0 || (ppp->stack[ppp->level] & F_Else)) {
	    error(errDanglingElse);
	    break;
	} else if (ppp->level + 1 >= sizearray(ppp->stack)) {
	    error(errIfsTooDeep);
	    break;
	}
	if (!(ppp->stack[ppp->level] & F_Ifdef))
	    error(errElifdefWithIf);
	ppp->stack[ppp->level] |= F_Else;
	if (ppp->stack[ppp->level] & F_Ours) {
	    ppp->copy = !ppp->copy;
	    ppp->absorb = TRUE;
	    n = ppp->level;
	    while (ppp->stack[n] & F_Elif) {
		if (ppp->stack[n] & F_ElseModify) {
		    ppp->absorb = TRUE;
		    break;
		}
		--n;
		if (!(ppp->stack[n] & F_Ours))
		    ppp->absorb = FALSE;
	    }
	}
	++ppp->level;
	ppp->stack[ppp->level] = F_If | F_Elif | F_Ifdef;
        if (ppp->level < 0 || (ppp->stack[ppp->level] & F_Else)) {
            error(errDanglingElse);
            break;
        } else if (ppp->level + 1 >= sizearray(ppp->stack)) {
            error(errIfsTooDeep);
            break;
        }
        if (!(ppp->stack[ppp->level] & F_Ifdef))
            error(errElifdefWithIf);
        ppp->stack[ppp->level] |= F_Else;
        if (ppp->stack[ppp->level] & F_Ours) {
            ppp->copy = !ppp->copy;
            ppp->absorb = TRUE;
            n = ppp->level;
            while (ppp->stack[n] & F_Elif) {
                if (ppp->stack[n] & F_ElseModify) {
                    ppp->absorb = TRUE;
                    break;
                }
                --n;
                if (!(ppp->stack[n] & F_Ours))
                    ppp->absorb = FALSE;
            }
        }
        ++ppp->level;
        ppp->stack[ppp->level] = F_If | F_Elif | F_Ifdef;
        cmdend = input;
	if (!ppp->copy) {
	    input = restofline(ppp->cl, input);
	    break;
	}
	ppp->stack[ppp->level] |= F_Copy;
	size = getidentifierlength(input);
	if (!size) {
	    error(errEmptyIf);
	    break;
	}
	if (findsymbolinset(ppp->defs, input, NULL))
	    status = statDefined;
	else if (findsymbolinset(ppp->undefs, input, NULL))
	    status = statUndefined;
	else
	    status = statUnaffected;
        if (!ppp->copy) {
            input = restofline(ppp->cl, input);
            break;
        }
        ppp->stack[ppp->level] |= F_Copy;
        size = getidentifierlength(input);
        if (!size) {
            error(errEmptyIf);
            break;
        }
        if (findsymbolinset(ppp->defs, input, NULL))
            status = statDefined;
        else if (findsymbolinset(ppp->undefs, input, NULL))
            status = statUndefined;
        else
            status = statUnaffected;
        cmdend = nextchars(ppp->cl, input, size);
        input = skipwhite(ppp->cl, cmdend);
	if (!endoflinep(ppp->cl)) {
	    error(errSyntax);
	    break;
	}
	if (status == statUnaffected) {
	    ppp->absorb = FALSE;
	    n = ppp->level;
	    while (ppp->stack[n] & F_Elif) {
		--n;
		if (!(ppp->stack[n] & F_Ours)) {
		    n = -1;
		    break;
		}
	    }
	    if (n >= 0) {
        if (!endoflinep(ppp->cl)) {
            error(errSyntax);
            break;
        }
        if (status == statUnaffected) {
            ppp->absorb = FALSE;
            n = ppp->level;
            while (ppp->stack[n] & F_Elif) {
                --n;
                if (!(ppp->stack[n] & F_Ours)) {
                    n = -1;
                    break;
                }
            }
            if (n >= 0) {
                editmstr(ppp->line, cmd, 2, "", 0);
		ppp->stack[ppp->level] |= F_IfModify;
	    }
	} else {
	    if (id == cmdElifdef)
		ppp->copy = status == statDefined;
	    else
		ppp->copy = status == statUndefined;
	    ppp->absorb = TRUE;
	    if (ppp->copy) {
		n = ppp->level;
		while (ppp->stack[n] & F_Elif) {
		    --n;
		    if (!(ppp->stack[n] & F_Ours)) {
                ppp->stack[ppp->level] |= F_IfModify;
            }
        } else {
            if (id == cmdElifdef)
                ppp->copy = status == statDefined;
            else
                ppp->copy = status == statUndefined;
            ppp->absorb = TRUE;
            if (ppp->copy) {
                n = ppp->level;
                while (ppp->stack[n] & F_Elif) {
                    --n;
                    if (!(ppp->stack[n] & F_Ours)) {
                        editmstr(ppp->line, cmd, cmdend - cmd, "else", 4);
			ppp->stack[ppp->level] |= F_ElseModify;
			ppp->absorb = FALSE;
			break;
		    }
		}
	    }
	    ppp->stack[ppp->level] |= F_Ours;
	}
	break;
                        ppp->stack[ppp->level] |= F_ElseModify;
                        ppp->absorb = FALSE;
                        break;
                    }
                }
            }
            ppp->stack[ppp->level] |= F_Ours;
        }
        break;

      case cmdElif:
	if (ppp->level < 0 || !(ppp->stack[ppp->level] & F_If)
			   || (ppp->stack[ppp->level] & F_Else)) {
	    error(errDanglingElse);
	    break;
	} else if (ppp->level + 1 >= sizearray(ppp->stack)) {
	    error(errIfsTooDeep);
	    break;
	}
	if (ppp->stack[ppp->level] & F_Ifdef)
	    error(errElifWithIfdef);
	ppp->stack[ppp->level] |= F_Else;
	if (ppp->stack[ppp->level] & F_Ours)
	    ppp->copy = !ppp->copy;
	++ppp->level;
	ppp->stack[ppp->level] = F_If | F_Elif | (ppp->copy ? F_Copy : 0);
	if (!ppp->copy) {
	    input = restofline(ppp->cl, input);
	    break;
	}
	cmdend = seqif(ppp, (char*)input, &status);
	if (status == statError)
	    break;
	input = skipwhite(ppp->cl, cmdend);
	if (!endoflinep(ppp->cl)) {
	    error(errIfSyntax);
	    break;
	}
	if (status == statUndefined) {
	    ppp->copy = FALSE;
	    ppp->absorb = TRUE;
	    ppp->stack[ppp->level] |= F_Ours;
	} else if (status == statDefined) {
	    ppp->absorb = TRUE;
	    n = ppp->level;
	    while (ppp->stack[n] & F_Elif) {
		--n;
		if (!(ppp->stack[n] & F_Ours)) {
        if (ppp->level < 0 || !(ppp->stack[ppp->level] & F_If)
                           || (ppp->stack[ppp->level] & F_Else)) {
            error(errDanglingElse);
            break;
        } else if (ppp->level + 1 >= sizearray(ppp->stack)) {
            error(errIfsTooDeep);
            break;
        }
        if (ppp->stack[ppp->level] & F_Ifdef)
            error(errElifWithIfdef);
        ppp->stack[ppp->level] |= F_Else;
        if (ppp->stack[ppp->level] & F_Ours)
            ppp->copy = !ppp->copy;
        ++ppp->level;
        ppp->stack[ppp->level] = F_If | F_Elif | (ppp->copy ? F_Copy : 0);
        if (!ppp->copy) {
            input = restofline(ppp->cl, input);
            break;
        }
        cmdend = seqif(ppp, (char*)input, &status);
        if (status == statError)
            break;
        input = skipwhite(ppp->cl, cmdend);
        if (!endoflinep(ppp->cl)) {
            error(errIfSyntax);
            break;
        }
        if (status == statUndefined) {
            ppp->copy = FALSE;
            ppp->absorb = TRUE;
            ppp->stack[ppp->level] |= F_Ours;
        } else if (status == statDefined) {
            ppp->absorb = TRUE;
            n = ppp->level;
            while (ppp->stack[n] & F_Elif) {
                --n;
                if (!(ppp->stack[n] & F_Ours)) {
                    editmstr(ppp->line, cmd, cmdend - cmd, "else", 4);
		    ppp->stack[ppp->level] |= F_ElseModify;
		    ppp->absorb = FALSE;
		    break;
		}
	    }
	    ppp->stack[ppp->level] |= F_Ours;
	} else {
	    n = ppp->level;
	    while (ppp->stack[n] & F_Elif) {
		--n;
		if (!(ppp->stack[n] & F_Ours)) {
		    n = -1;
		    break;
		}
	    }
	    if (n >= 0) {
                    ppp->stack[ppp->level] |= F_ElseModify;
                    ppp->absorb = FALSE;
                    break;
                }
            }
            ppp->stack[ppp->level] |= F_Ours;
        } else {
            n = ppp->level;
            while (ppp->stack[n] & F_Elif) {
                --n;
                if (!(ppp->stack[n] & F_Ours)) {
                    n = -1;
                    break;
                }
            }
            if (n >= 0) {
                editmstr(ppp->line, cmd, 2, "", 0);
		ppp->stack[ppp->level] |= F_IfModify;
	    }
	}
	break;
                ppp->stack[ppp->level] |= F_IfModify;
            }
        }
        break;

      case cmdEndif:
	if (ppp->level < 0) {
	    error(errDanglingEnd);
	    break;
	}
        if (ppp->level < 0) {
            error(errDanglingEnd);
            break;
        }
        cmdend = input;
	if (!endoflinep(ppp->cl)) {
	    error(errSyntax);
	    input = restofline(ppp->cl, input);
	}
	ppp->absorb = TRUE;
	for ( ; ppp->stack[ppp->level] & F_Elif ; --ppp->level) {
	    if (ppp->stack[ppp->level] & (F_IfModify | F_ElseModify))
		ppp->absorb = FALSE;
	}
	if (ppp->absorb)
	    ppp->absorb = ppp->stack[ppp->level] & F_Ours;
	ppp->copy = ppp->stack[ppp->level] & F_Copy;
	--ppp->level;
	break;
        if (!endoflinep(ppp->cl)) {
            error(errSyntax);
            input = restofline(ppp->cl, input);
        }
        ppp->absorb = TRUE;
        for ( ; ppp->stack[ppp->level] & F_Elif ; --ppp->level) {
            if (ppp->stack[ppp->level] & (F_IfModify | F_ElseModify))
                ppp->absorb = FALSE;
        }
        if (ppp->absorb)
            ppp->absorb = ppp->stack[ppp->level] & F_Ours;
        ppp->copy = ppp->stack[ppp->level] & F_Copy;
        --ppp->level;
        break;

      default:
	input = restofline(ppp->cl, input);
	break;
        input = restofline(ppp->cl, input);
        break;
    }

    if (ppp->absorb && incomment != ccommentp(ppp->cl))
	error(errBrokenComment);
        error(errBrokenComment);
}

/* Reads the next line of source code, and applies the first two


@@ 453,56 451,56 @@ static int readline(ppproc *ppp, FILE *infile)

    ch = fgetc(infile);
    if (ch == EOF)
	return 0;
        return 0;
    back2 = EOF;
    back1 = EOF;
    erasemstr(ppp->line);
    while (ch != EOF) {
        appendmstr(ppp->line, ch);
	if (back2 == '?' && back1 == '?') {
	    switch (ch) {
	      case '=':		replacement = '#';	break;
	      case '(':		replacement = '[';	break;
	      case '/':		replacement = '\\';	break;
	      case ')':		replacement = ']';	break;
	      case '\'':	replacement = '^';	break;
	      case '<':		replacement = '{';	break;
	      case '!':		replacement = '|';	break;
	      case '>':		replacement = '}';	break;
	      case '-':		replacement = '~';	break;
	      default:		replacement = 0;	break;
	    }
	    if (replacement) {
        if (back2 == '?' && back1 == '?') {
            switch (ch) {
              case '=':         replacement = '#';      break;
              case '(':         replacement = '[';      break;
              case '/':         replacement = '\\';     break;
              case ')':         replacement = ']';      break;
              case '\'':        replacement = '^';      break;
              case '<':         replacement = '{';      break;
              case '!':         replacement = '|';      break;
              case '>':         replacement = '}';      break;
              case '-':         replacement = '~';      break;
              default:          replacement = 0;        break;
            }
            if (replacement) {
                p = getmstrbuf(ppp->line) + getmstrlen(ppp->line);
                altermstr(ppp->line, p - 3, 3, replacement);
		ch = replacement;
		back1 = back2 = EOF;
	    }
	}
	if (back1 == '\r' && ch == '\n') {
                ch = replacement;
                back1 = back2 = EOF;
            }
        }
        if (back1 == '\r' && ch == '\n') {
            p = getmstrbuf(ppp->line) + getmstrlen(ppp->line);
            altermstr(ppp->line, p - 2, 2, '\n');
	    back1 = back2;
	    back2 = EOF;
	}
	if (ch == '\n') {
	    if (back1 == '\\') {
            back1 = back2;
            back2 = EOF;
        }
        if (ch == '\n') {
            if (back1 == '\\') {
                p = getmstrbuf(ppp->line) + getmstrlen(ppp->line);
                altermstr(ppp->line, p - 2, 2, 0);
		ch = back2;
		back1 = back2 = EOF;
	    } else {
		break;
	    }
	}
	back2 = back1;
	back1 = ch;
	ch = fgetc(infile);
                ch = back2;
                back1 = back2 = EOF;
            } else {
                break;
            }
        }
        back2 = back1;
        back1 = ch;
        ch = fgetc(infile);
    }

    if (ferror(infile)) {
	error(errFileIO);
	return 0;
        error(errFileIO);
        return 0;
    }
    return 1;
}


@@ 516,17 514,17 @@ static int writeline(ppproc *ppp, FILE *outfile)
    size_t size;

    if (!ppp->line)
	return 1;
        return 1;
    if (!ppp->copy || ppp->absorb)
	return 1;
        return 1;

    size = getmstrbaselen(ppp->line);
    if (size) {
	if (fwrite(getmstrbase(ppp->line), size, 1, outfile) != 1) {
	    seterrorfile(NULL);
	    error(errFileIO);
	    return 0;
	}
        if (fwrite(getmstrbase(ppp->line), size, 1, outfile) != 1) {
            seterrorfile(NULL);
            error(errFileIO);
            return 0;
        }
    }
    return 1;
}


@@ 539,7 537,7 @@ static void advanceline(mstr const *line)
    char const *p;

    for (p = getmstrbuf(line) - 1 ; p ; p = strchr(p + 1, '\n'))
	nexterrorline();
        nexterrorline();
}

/* Partially preprocesses each line of infile and writes the results


@@ 554,7 552,7 @@ void partialpreprocess(ppproc *ppp, FILE *infile, FILE *outfile)
        endline(ppp->cl);
        if (!writeline(ppp, outfile))
            break;
	advanceline(ppp->line);
        advanceline(ppp->line);
    }
    seterrorline(0);
    endfile(ppp);

M ppproc.h => ppproc.h +3 -5
@@ 1,10 1,8 @@
/* ppproc.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* ppproc.h: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef	_ppproc_h_
#define	_ppproc_h_
#ifndef _ppproc_h_
#define _ppproc_h_

/*
 * The ppproc object does the actual work of identifying preprocessor

M symset.c => symset.c +32 -34
@@ 1,7 1,5 @@
/* symset.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* symset.c: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdlib.h>
#include <string.h>


@@ 23,16 21,16 @@ typedef struct sym sym;
/* A preprocessor symbol.
 */
struct sym {
    char const *id;		/* the symbol's name */
    long	value;		/* the symbol's value */
    char const *id;             /* the symbol's name */
    long        value;          /* the symbol's value */
};

/* An unordered collection of symbols.
 */
struct symset {
    sym	       *syms;		/* an array of symbols */
    int		allocated;	/* how many entries are allocated */
    int		size;		/* how many entries are currently stored */
    sym        *syms;           /* an array of symbols */
    int         allocated;      /* how many entries are allocated */
    int         size;           /* how many entries are currently stored */
};

/* Allocate a new symset.


@@ 53,8 51,8 @@ symset *initsymset(void)
void freesymset(symset *set)
{
    if (set) {
	deallocate(set->syms);
	deallocate(set);
        deallocate(set->syms);
        deallocate(set);
    }
}



@@ 63,9 61,9 @@ void freesymset(symset *set)
void addsymboltoset(symset *set, char const *id, long value)
{
    if (set->size == set->allocated) {
	set->allocated *= 2;
	set->syms = reallocate(set->syms,
			       set->allocated * sizeof *set->syms);
        set->allocated *= 2;
        set->syms = reallocate(set->syms,
                               set->allocated * sizeof *set->syms);
    }
    set->syms[set->size].id = id;
    set->syms[set->size].value = value;


@@ 81,13 79,13 @@ static int idcmp(char const *a, char const *b)

    i = 0;
    for (;;) {
	if (!_issym(a[i]))
	    return _issym(b[i]) ? -1 : 0;
	else if (!_issym(b[i]))
	    return +1;
	else if (a[i] != b[i])
	    return a[i] - b[i];
	++i;
        if (!_issym(a[i]))
            return _issym(b[i]) ? -1 : 0;
        else if (!_issym(b[i]))
            return +1;
        else if (a[i] != b[i])
            return a[i] - b[i];
        ++i;
    }
}



@@ 98,13 96,13 @@ int findsymbolinset(symset const *set, char const *id, long *value)
    int i;

    if (set) {
	for (i = 0 ; i < set->size ; ++i) {
	    if (!idcmp(set->syms[i].id, id)) {
		if (value)
		    *value = set->syms[i].value;
		return TRUE;
	    }
	}
        for (i = 0 ; i < set->size ; ++i) {
            if (!idcmp(set->syms[i].id, id)) {
                if (value)
                    *value = set->syms[i].value;
                return TRUE;
            }
        }
    }
    return FALSE;
}


@@ 116,13 114,13 @@ int removesymbolfromset(symset *set, char const *id)
    int i;

    if (set) {
	for (i = 0 ; i < set->size ; ++i) {
	    if (!idcmp(set->syms[i].id, id)) {
		set->syms[i] = set->syms[set->size - 1];
		--set->size;
		return TRUE;
	    }
	}
        for (i = 0 ; i < set->size ; ++i) {
            if (!idcmp(set->syms[i].id, id)) {
                set->syms[i] = set->syms[set->size - 1];
                --set->size;
                return TRUE;
            }
        }
    }
    return FALSE;
}

M symset.h => symset.h +1 -3
@@ 1,7 1,5 @@
/* symset.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* symset.h: Copyright (C) 2011-2022 by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef _symset_h_
#define _symset_h_

M unixisms.c => unixisms.c +3 -4
@@ 1,7 1,6 @@
/* unixisms.c: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* unixisms.c: Copyright (C) 2011-2022
 * by Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#include <stdlib.h>
#include <string.h>


@@ 25,7 24,7 @@ int fileisdir(char const *name)
    struct stat s;

    if (stat(name, &s))
	return 0;
        return 0;
    return S_ISDIR(s.st_mode);
}


M unixisms.h => unixisms.h +4 -5
@@ 1,10 1,9 @@
/* unixisms.h: Copyright (C) 2011 by Brian Raiter <breadbox@muppetlabs.com>
/* unixisms.h: Copyright (C) 2011-2022 by
 * Brian Raiter <breadbox@muppetlabs.com>
 * License GPLv2+: GNU GPL version 2 or later.
 * This is free software; you are free to change and redistribute it.
 * There is NO WARRANTY, to the extent permitted by law.
 */
#ifndef	_unixisms_h_
#define	_unixisms_h_
#ifndef _unixisms_h_
#define _unixisms_h_

/*
 * Basic functionality not provided by the standard C library. The