Edit

IABSD.fr/src/libexec/tradcpp/files.c

Branch :

  • Show log

    Commit

  • Author : jsg
    Date : 2019-08-23 04:38:55
    Hash : 88157d21
    Message : update tradcpp to 0.5.3

  • libexec/tradcpp/files.c
  • /*-
     * Copyright (c) 2010, 2013 The NetBSD Foundation, Inc.
     * All rights reserved.
     *
     * This code is derived from software contributed to The NetBSD Foundation
     * by David A. Holland.
     *
     * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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 <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    
    #include "bool.h"
    #include "array.h"
    #include "mode.h"
    #include "place.h"
    #include "files.h"
    #include "directive.h"
    
    struct incdir {
    	const char *name;
    	bool issystem;
    };
    
    DECLARRAY(incdir, static UNUSED);
    DEFARRAY(incdir, static);
    
    static struct incdirarray quotepath, bracketpath;
    
    ////////////////////////////////////////////////////////////
    // management
    
    static
    struct incdir *
    incdir_create(const char *name, bool issystem)
    {
    	struct incdir *id;
    
    	id = domalloc(sizeof(*id));
    	id->name = name;
    	id->issystem = issystem;
    	return id;
    }
    
    static
    void
    incdir_destroy(struct incdir *id)
    {
    	dofree(id, sizeof(*id));
    }
    
    void
    files_init(void)
    {
    	incdirarray_init(&quotepath);
    	incdirarray_init(&bracketpath);
    }
    
    DESTROYALL_ARRAY(incdir, );
    
    void
    files_cleanup(void)
    {
    	incdirarray_destroyall(&quotepath);
    	incdirarray_cleanup(&quotepath);
    	incdirarray_destroyall(&bracketpath);
    	incdirarray_cleanup(&bracketpath);
    }
    
    ////////////////////////////////////////////////////////////
    // path setup
    
    void
    files_addquotepath(const char *dir, bool issystem)
    {
    	struct incdir *id;
    
    	id = incdir_create(dir, issystem);
    	incdirarray_add(&quotepath, id, NULL);
    }
    
    void
    files_addbracketpath(const char *dir, bool issystem)
    {
    	struct incdir *id;
    
    	id = incdir_create(dir, issystem);
    	incdirarray_add(&bracketpath, id, NULL);
    }
    
    ////////////////////////////////////////////////////////////
    // parsing
    
    /*
     * Find the end of the logical line. End of line characters that are
     * commented out do not count.
     */
    static
    size_t
    findeol(const char *buf, size_t start, size_t limit)
    {
    	size_t i;
    	int incomment = 0;
    	bool inquote = false;
    	char quote = '\0';
    
    	for (i=start; i<limit; i++) {
    		if (incomment) {
    			if (i+1 < limit && buf[i] == '*' && buf[i+1] == '/') {
    				i++;
    				incomment = 0;
    			}
    		} else if (!inquote && i+1 < limit &&
    			   buf[i] == '/' && buf[i+1] == '*') {
    			i++;
    			incomment = 1;
    		} else if (i+1 < limit &&
    			   buf[i] == '\\' && buf[i+1] != '\n') {
    			i++;
    		} else if (!inquote && (buf[i] == '"' || buf[i] == '\'')) {
    			inquote = true;
    			quote = buf[i];
    		} else if (inquote && buf[i] == quote) {
    			inquote = false;
    		} else if (buf[i] == '\n') {
    			return i;
    		}
    	}
    	return limit;
    }
    
    static
    unsigned
    countnls(const char *buf, size_t start, size_t limit)
    {
    	size_t i;
    	unsigned count = 0;
    
    	for (i=start; i<limit; i++) {
    		if (buf[i] == '\n') {
    			count++;
    			if (count == 0) {
    				/* just return the max and error downstream */
    				return count - 1;
    			}
    		}
    	}
    	return count;
    }
    
    static
    void
    file_read(const struct placefile *pf, int fd, const char *name, bool toplevel)
    {
    	struct lineplace places;
    	struct place ptmp;
    	size_t bufend, bufmax, linestart, lineend, nextlinestart, tmp;
    	ssize_t result;
    	bool ateof = false;
    	char *buf;
    
    	place_setfilestart(&places.current, pf);
    	places.nextline = places.current;
    
    	if (name) {
    		debuglog(&places.current, "Reading file %s", name);
    	} else {
    		debuglog(&places.current, "Reading standard input");
    	}
    
    	bufmax = 128;
    	bufend = 0;
    	linestart = 0;
    	lineend = 0;
    	buf = domalloc(bufmax);
    
    	while (1) {
    		if (lineend >= bufend) {
    			/* do not have a whole line in the buffer; read more */
    			assert(bufend >= linestart);
    			if (linestart > 0 && bufend > linestart) {
    				/* slide to beginning of buffer */
    				memmove(buf, buf+linestart, bufend-linestart);
    				bufend -= linestart;
    				lineend -= linestart;
    				linestart = 0;
    			}
    			if (bufend >= bufmax) {
    				/* need bigger buffer */
    				buf = dorealloc(buf, bufmax, bufmax*2);
    				bufmax = bufmax*2;
    				/* just in case someone's screwing around */
    				if (bufmax > 0xffffffff) {
    					complain(&places.current,
    						 "Input line too long");
    					die();
    				}
    			}
    
    			if (ateof) {
    				/* don't read again, in case it's a socket */
    				result = 0;
    			} else {
    				result = read(fd, buf+bufend, bufmax - bufend);
    			}
    
    			if (result == -1) {
    				/* read error */
    				complain(NULL, "%s: %s",
    					 name, strerror(errno));
    				complain_fail();
    			} else if (result == 0 && bufend == linestart) {
    				/* eof */
    				ateof = true;
    				break;
    			} else if (result == 0) {
    				/* eof in middle of line */
    				ateof = true;
    				ptmp = places.current;
    				place_addcolumns(&ptmp, bufend - linestart);
    				if (buf[bufend - 1] == '\n') {
    					complain(&ptmp, "Unclosed comment");
    					complain_fail();
    				} else {
    					complain(&ptmp,
    						 "No newline at end of file");
    				}
    				if (mode.werror) {
    					complain_fail();
    				}
    				assert(bufend < bufmax);
    				lineend = bufend++;
    				buf[lineend] = '\n';
    			} else {
    				bufend += (size_t)result;
    				lineend = findeol(buf, linestart, bufend);
    			}
    			/* loop in case we still don't have a whole line */
    			continue;
    		}
    
    		/* have a line */
    		assert(buf[lineend] == '\n');
    		buf[lineend] = '\0';
    		nextlinestart = lineend+1;
    		place_addlines(&places.nextline, 1);
    
    		/* check for CR/NL */
    		if (lineend > 0 && buf[lineend-1] == '\r') {
    			buf[lineend-1] = '\0';
    			lineend--;
    		}
    
    		/* check for continuation line */
    		if (lineend > 0 && buf[lineend-1]=='\\') {
    			lineend--;
    			tmp = nextlinestart - lineend;
    			if (bufend > nextlinestart) {
    				memmove(buf+lineend, buf+nextlinestart,
    					bufend - nextlinestart);
    			}
    			bufend -= tmp;
    			nextlinestart -= tmp;
    			lineend = findeol(buf, linestart, bufend);
    			/* might not have a whole line, so loop */
    			continue;
    		}
    
    		/* line now goes from linestart to lineend */
    		assert(buf[lineend] == '\0');
    
    		/* count how many commented-out newlines we swallowed */
    		place_addlines(&places.nextline,
    			       countnls(buf, linestart, lineend));
    
    		/* process the line (even if it's empty) */
    		directive_gotline(&places, buf+linestart, lineend-linestart);
    
    		linestart = nextlinestart;
    		lineend = findeol(buf, linestart, bufend);
    		places.current = places.nextline;
    	}
    
    	if (toplevel) {
    		directive_goteof(&places.current);
    	}
    	dofree(buf, bufmax);
    }
    
    ////////////////////////////////////////////////////////////
    // path search
    
    static
    char *
    mkfilename(struct place *place, const char *dir, const char *file)
    {
    	size_t dlen, flen, rlen;
    	char *ret;
    	bool needslash = false;
    
    	if (dir == NULL) {
    		dir = place_getparsedir(place);
    	}
    
    	dlen = strlen(dir);
    	flen = strlen(file);
    	if (dlen > 0 && dir[dlen-1] != '/') {
    		needslash = true;
    	}
    
    	rlen = dlen + (needslash ? 1 : 0) + flen;
    	ret = domalloc(rlen + 1);
    	snprintf(ret, rlen+1, "%s%s%s", dir, needslash ? "/" : "", file);
    	return ret;
    }
    
    static
    int
    file_tryopen(const char *file)
    {
    	int fd;
    
    	/* XXX check for non-regular files */
    
    	fd = open(file, O_RDONLY);
    	if (fd == -1) {
    		if (errno != ENOENT && errno != ENOTDIR) {
    			complain(NULL, "%s: %s", file, strerror(errno));
    		}
    		return -1;
    	}
    
    	return fd;
    }
    
    static
    void
    file_search(struct place *place, struct incdirarray *path, const char *name)
    {
    	unsigned i, num;
    	struct incdir *id;
    	const struct placefile *pf;
    	char *file;
    	int fd;
    
    	assert(place != NULL);
    
    	if (name[0] == '/') {
    		fd = file_tryopen(name);
    		if (fd >= 0) {
    			pf = place_addfile(place, name, true);
    			file_read(pf, fd, name, false);
    			close(fd);
    			return;
    		}
    	} else {
    		num = incdirarray_num(path);
    		for (i=0; i<num; i++) {
    			id = incdirarray_get(path, i);
    			file = mkfilename(place, id->name, name);
    			fd = file_tryopen(file);
    			if (fd >= 0) {
    				pf = place_addfile(place, file, id->issystem);
    				file_read(pf, fd, file, false);
    				dostrfree(file);
    				close(fd);
    				return;
    			}
    			dostrfree(file);
    		}
    	}
    	complain(place, "Include file %s not found", name);
    	complain_fail();
    }
    
    void
    file_readquote(struct place *place, const char *name)
    {
    	file_search(place, &quotepath, name);
    }
    
    void
    file_readbracket(struct place *place, const char *name)
    {
    	file_search(place, &bracketpath, name);
    }
    
    void
    file_readabsolute(struct place *place, const char *name)
    {
    	const struct placefile *pf;
    	int fd;
    
    	assert(place != NULL);
    
    	if (name == NULL) {
    		fd = STDIN_FILENO;
    		pf = place_addfile(place, "<standard-input>", false);
    	} else {
    		fd = file_tryopen(name);
    		if (fd < 0) {
    			complain(NULL, "%s: %s", name, strerror(errno));
    			die();
    		}
    		pf = place_addfile(place, name, false);
    	}
    
    	file_read(pf, fd, name, true);
    
    	if (name != NULL) {
    		close(fd);
    	}
    }