aboutsummaryrefslogblamecommitdiffstats
path: root/src/mds-kbdc/process-includes.c
blob: a46533cfc0381aa61d37bf827fc8e2b5f543317f (plain) (tree)
1
2
3

                                 
                                                                         















                                                                        

                          
 



                   

                      





                               
                                         

   
                                 





                                                                                            


                                                                      





                                                      
                                     
 


                                      
                                          
 


                                                
                                      










                                               


   




                                                          

                                                                                              
 



























                                                                                                                  
         













                                                                                                 
         








                                                              



   
                               
   

                                              
   

                                                       
 









































































                                                                                    









                                              

                                                        
 






















                                                                             









                                                                                              

                                                     
 






























                                                                                                 




                
        
/**
 * mds — A micro-display server
 * Copyright © 2014, 2015, 2016, 2017  Mattias Andrée (maandree@kth.se)
 * 
 * This program is free software: 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 3 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 for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "process-includes.h"

#include "make-tree.h"
#include "simplify-tree.h"

#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>



/**
 * Tree type constant shortener
 */
#define C(TYPE) MDS_KBDC_TREE_TYPE_##TYPE

/**
 * Add an error to the error list
 * 
 * @param  NODE:const mds_kbdc_tree_t*    The node the triggered the error
 * @param  SEVERITY:identifier            * in `MDS_KBDC_PARSE_ERROR_*` to indicate severity
 * @param  ...:const char*, ...           Error description format string and arguments
 * @scope  error:mds_kbdc_parse_error_t*  Variable where the new error will be stored
 */
#define NEW_ERROR(NODE, SEVERITY, ...)\
	NEW_ERROR_(result, SEVERITY, 1, (NODE)->loc_line,\
	           (NODE)->loc_start, (NODE)->loc_end, 1, __VA_ARGS__)



/**
 * Variable whether the latest created error is stored
 */
static mds_kbdc_parse_error_t *error;

/**
 * The parameter of `process_includes`
 */
static mds_kbdc_parsed_t *restrict result;

/**
 * Stack of attributes of already included files
 */
static struct stat *restrict included;

/**
 * The number elements allocated for `included`
 */
static size_t included_size = 0;

/**
 * The number elements stored in `included`
 */
static size_t included_ptr = 0;



/**
 * Transfer errors from an included tree
 * 
 * @param  subresult  The results of the processed include
 * @param  tree       The include statement
 */
static int
transfer_errors(mds_kbdc_parsed_t *restrict subresult, mds_kbdc_tree_include_t *restrict tree)
{
	mds_kbdc_parse_error_t **errors = NULL, *suberror, **new_errors;
	size_t errors_ptr = 0, new_errors_size;
	int saved_errno, annotated = 0;

	/* Allocate temporary list for errors. */
	if (!subresult->errors_ptr)
		return 0;
	fail_if (xmalloc(errors, subresult->errors_ptr * 2, mds_kbdc_parse_error_t*));

	/* List errors backwards, so that we can easily handle errors and add “included from here”-note. */
	while (subresult->errors_ptr--) {
		suberror = subresult->errors[subresult->errors_ptr];

		/* If it is more severe than a note, we want to say there it was included. */
		if (!annotated) {
			NEW_ERROR(tree, NOTE, "included from here");
			errors[errors_ptr++] = error;
			result->errors[--(result->errors_ptr)] = NULL;
			annotated = 1;
		}

		/* Include error. */
		errors[errors_ptr++] = suberror;
		subresult->errors[subresult->errors_ptr] = NULL;

		/* Make sure when there are nested inclusions that the outermost inclusion * is annotated last. */
		if (suberror->severity > MDS_KBDC_PARSE_ERROR_NOTE)
			annotated = 0;
	}

	/* Append errors. */
	for (; errors_ptr--; errors[errors_ptr] = NULL) {
		if (result->errors_ptr + 1 >= result->errors_size) {
			new_errors_size = result->errors_size ? (result->errors_size << 1) : 2;
			new_errors = result->errors;

			fail_if (xrealloc(new_errors, new_errors_size, mds_kbdc_parse_error_t*));
			result->errors = new_errors;
			result->errors_size = new_errors_size;
		}

		result->errors[result->errors_ptr++] = errors[errors_ptr];
		result->errors[result->errors_ptr] = NULL;
	}

	free(errors);
	return 0;
fail:
	saved_errno = errno;
	while (errors_ptr--)
		mds_kbdc_parse_error_free(errors[errors_ptr]);
	free(errors);
	return errno = saved_errno, -1;
}


/**
 * Process an include-statement
 * 
 * @param   tree  The include-statement
 * @return        Zero on success, -1 on error
 */
static int
process_include(mds_kbdc_tree_include_t *restrict tree)
{
#define process(expr)\
	fail_if ((expr) < 0);\
	if (mds_kbdc_parsed_is_fatal(&subresult))\
		goto stop;

	mds_kbdc_parsed_t subresult, *our_result;
	char *dirname = NULL, *cwd = NULL, *old = NULL;
	size_t cwd_size = 4096 >> 1;
	int saved_errno; 

	/* Initialise result structure for the included file. */
	mds_kbdc_parsed_initialise(&subresult);

	/* Get dirname of current file. */
	fail_if (xstrdup(dirname, result->pathname));
	*(strrchr(dirname, '/')) = '\0';

	/* Get the current working directory. */
	/* glibc offers ways to do this in just one function call,
	 * but we will not assume that glibc is used here. */
	for (;;) {
		fail_if (xxrealloc(old, cwd, cwd_size <<= 1, char));
		if (getcwd(cwd, cwd_size))
			break;
		else
			fail_if (errno != ERANGE);
	}

	/* Switch working directory. */
	fail_if (chdir(dirname));
	free(dirname), dirname = NULL;

	/* Store `result` as it will be switched by the inner `process_includes`. */
	our_result = result;

	/* Process include. */
	old = tree->filename, tree->filename = NULL;
	tree->filename = parse_raw_string(old);
	fail_if (tree->filename == NULL);
	free(old), old = NULL;
	process (parse_to_tree(tree->filename, &subresult));
	process (simplify_tree(&subresult));
	process (process_includes(&subresult));
stop:

	/* Switch back `result`. */
	result = our_result;

	/* Switch back to the old working directory. */
	fail_if (chdir(cwd));
	free(cwd), cwd = NULL;

	/* Move over data to our result. */
	free(tree->filename);
	tree->filename = subresult.pathname, subresult.pathname = NULL;
	tree->source_code = subresult.source_code, subresult.source_code = NULL;
	tree->inner = subresult.tree, subresult.tree = NULL;
	if (result->severest_error_level < subresult.severest_error_level)
		result->severest_error_level = subresult.severest_error_level;

	/* Move over errors. */
	fail_if (transfer_errors(&subresult, tree));

	/* Release resources. */
	mds_kbdc_parsed_destroy(&subresult);

	return 0;
fail:
	saved_errno = errno;
	free(dirname);
	free(cwd);
	free(old);
	mds_kbdc_parsed_destroy(&subresult);
	return errno = saved_errno, -1;
#undef process
}


/**
 * Process a subtree
 * 
 * @param   tree  The tree
 * @return        Zero on success, -1 on error
 */
static int
process_includes_in_tree(mds_kbdc_tree_t *restrict tree)
{
#define p(expr) fail_if (process_includes_in_tree(tree->expr))
again:
	if (!tree)
		return 0;

	switch (tree->type) {
	case C(INFORMATION): p (information.inner); break;
	case C(FUNCTION):    p (function.inner);    break;
	case C(MACRO):       p (macro.inner);       break;
	case C(ASSUMPTION):  p (assumption.inner);  break;
	case C(FOR):         p (for_.inner);        break;
	case C(IF):          p (if_.inner);         p (if_.otherwise); break;
	case C(INCLUDE):
		fail_if (process_include(&(tree->include)));
		break;
	default:
		break;
	}

	tree = tree->next;
	goto again;
fail:
	return -1;
#undef p
}


/**
 * Include included files and process them upto this level
 * 
 * @param   result_  `result` from `simplify_tree`, will be updated
 * @return           -1 if an error occursed that cannot be stored in `result`, zero otherwise
 */
int
process_includes(mds_kbdc_parsed_t *restrict result_)
{
	int r, saved_errno;
	struct stat attr, *old;
	size_t i;

	result = result_;

	fail_if (stat(result->pathname, &attr));

	if (included_ptr == included_size) {
		if (xxrealloc(old, included, included_size += 4, struct stat))
			fail_if (included = old, 1);
	}

	for (i = 0; i < included_ptr; i++) {
		if ((included[i].st_dev == attr.st_dev) && (included[i].st_ino == attr.st_ino)) {
			NEW_ERROR_(result, ERROR, 0, 0, 0, 0, 1, "resursive inclusion");
			return 0;
		}
	}

	included[included_ptr++] = attr;

	r = process_includes_in_tree(result_->tree);
	saved_errno = errno;

	if (!--included_ptr)
		free(included), included_size = 0;

	return errno = saved_errno, r;
fail:
	return -1;
}



#undef NEW_ERROR
#undef C