diff options
Diffstat (limited to 'src/mcpp-include-path.c')
| -rw-r--r-- | src/mcpp-include-path.c | 467 |
1 files changed, 467 insertions, 0 deletions
diff --git a/src/mcpp-include-path.c b/src/mcpp-include-path.c new file mode 100644 index 0000000..1b26137 --- /dev/null +++ b/src/mcpp-include-path.c @@ -0,0 +1,467 @@ +#include <defs.h> + +static char * +join_path( const char *dir, const char *name ) +{ + size_t dl; + size_t nl; + char *p; + int slash; + + if( dir == NULL || name == NULL ) + { + errno = EINVAL; + return( NULL ); + } + + dl = strlen( dir ); + nl = strlen( name ); + slash = dl != 0 && dir[dl - 1] != '/'; + + p = (char *)malloc( dl + (size_t)slash + nl + 1 ); + if( p == NULL ) + return( NULL ); + + memcpy( p, dir, dl ); + if( slash ) + p[dl++] = '/'; + memcpy( p + dl, name, nl + 1 ); + + return( p ); +} + +static int +file_exists( const char *path ) +{ + FILE *fp = fopen( path, "rb" ); + + if( fp == NULL ) + return( 0 ); + + fclose( fp ); + return( 1 ); +} + +static char * +source_directory( const char *filename ) +{ + const char *slash; + size_t n; + char *dir; + + if( filename == NULL ) + return( NULL ); + + slash = strrchr( filename, '/' ); + if( slash == NULL ) + return( strdup(".") ); + + if( slash == filename ) + return( strdup("/") ); + + n = (size_t)(slash - filename); + dir = (char *)malloc( n + 1 ); + if( dir == NULL ) + return( NULL ); + + memcpy( dir, filename, n ); + dir[n] = 0; + + return( dir ); +} + +void +mcpu_include_paths_init( mcpu_include_paths *paths ) +{ + if( paths == NULL ) + return; + + paths->first = NULL; + paths->last = NULL; + paths->no_standard_includes = 0; +} + +void +mcpu_include_paths_free( mcpu_include_paths *paths ) +{ + mcpu_include_dir *p; + + if( paths == NULL ) + return; + + p = paths->first; + while( p ) + { + mcpu_include_dir *next = p->next; + free( p->path ); + free( p ); + p = next; + } + + paths->first = NULL; + paths->last = NULL; +} + +static int +add_dir( mcpu_include_paths *paths, const char *path, + enum mcpu_include_class class_type, + int language_specific, enum mcpu_language language ) +{ + mcpu_include_dir *dir; + + if( paths == NULL || path == NULL ) + { + errno = EINVAL; + return( -1 ); + } + + if( *path == 0 ) + return( 0 ); + + dir = (mcpu_include_dir *)calloc( 1, sizeof(*dir) ); + if( dir == NULL ) + return( -1 ); + + dir->path = strdup( path ); + if( dir->path == NULL ) + { + free( dir ); + return( -1 ); + } + + dir->class_type = class_type; + dir->language = language; + dir->language_specific = language_specific; + + if( paths->last ) + paths->last->next = dir; + else + paths->first = dir; + paths->last = dir; + + return( 0 ); +} + +int +mcpu_include_paths_add( mcpu_include_paths *paths, const char *path, + enum mcpu_include_class class_type ) +{ + return( add_dir( paths, path, class_type, 0, MCPU_LANG_0 ) ); +} + +int +mcpu_include_paths_add_language( mcpu_include_paths *paths, + const char *path, + enum mcpu_include_class class_type, + enum mcpu_language language ) +{ + return( add_dir( paths, path, class_type, 1, language ) ); +} + +int +mcpu_include_paths_add_list( mcpu_include_paths *paths, + const char *list, + enum mcpu_include_class class_type ) +{ + const char *p; + const char *start; + + if( paths == NULL || list == NULL ) + return( 0 ); + + p = start = list; + for( ;; ) + { + if( *p == ':' || *p == 0 ) + { + size_t n = (size_t)(p - start); + if( n != 0 ) + { + char *dir = (char *)malloc( n + 1 ); + int rc; + + if( dir == NULL ) + return( -1 ); + memcpy( dir, start, n ); + dir[n] = 0; + rc = mcpu_include_paths_add( paths, dir, class_type ); + free( dir ); + if( rc != 0 ) + return( -1 ); + } + + if( *p == 0 ) + break; + start = p + 1; + } + ++p; + } + + return( 0 ); +} + +static int +add_language_list( mcpu_include_paths *paths, const char *list, + enum mcpu_include_class class_type, + enum mcpu_language language ) +{ + const char *p; + const char *start; + + if( list == NULL ) + return( 0 ); + + p = start = list; + for( ;; ) + { + if( *p == ':' || *p == 0 ) + { + size_t n = (size_t)(p - start); + if( n != 0 ) + { + char *dir = (char *)malloc( n + 1 ); + int rc; + + if( dir == NULL ) + return( -1 ); + memcpy( dir, start, n ); + dir[n] = 0; + rc = mcpu_include_paths_add_language( paths, dir, + class_type, language ); + free( dir ); + if( rc != 0 ) + return( -1 ); + } + if( *p == 0 ) + break; + start = p + 1; + } + ++p; + } + + return( 0 ); +} + +static int +add_system_root( mcpu_include_paths *paths, const char *root ) +{ + enum mcpu_language language; + + if( root == NULL || *root == 0 ) + return( 0 ); + + for( language = MCPU_LANG_DIFF; + language < MCPU_LANG__COUNT; + language = (enum mcpu_language)(language + 1) ) + { + const char *dirname = mcpu_language_directory_name( language ); + char *dir; + + if( dirname == NULL ) + continue; + + dir = join_path( root, dirname ); + if( dir == NULL ) + return( -1 ); + + if( mcpu_include_paths_add_language(paths, dir, + MCPU_INCLUDE_SYSTEM_CONFIG_LANGUAGE, + language) != 0 ) + { + free( dir ); + return( -1 ); + } + free( dir ); + } + + return( mcpu_include_paths_add(paths, root, + MCPU_INCLUDE_SYSTEM_CONFIG) ); +} + + + +int +mcpu_include_paths_dump( const mcpu_include_paths *paths, FILE *stream ) +{ + enum mcpu_include_class class_type; + const mcpu_include_dir *dir; + + if( paths == NULL || stream == NULL ) + { + errno = EINVAL; + return( -1 ); + } + + for( class_type = MCPU_INCLUDE_USER_EXPLICIT; + class_type <= MCPU_INCLUDE_AFTER_CONFIG; + class_type = (enum mcpu_include_class)(class_type + 1) ) + { + if( paths->no_standard_includes && + (class_type == MCPU_INCLUDE_SYSTEM_CONFIG_LANGUAGE || + class_type == MCPU_INCLUDE_SYSTEM_CONFIG) ) + continue; + + for( dir = paths->first; dir; dir = dir->next ) + { + if( dir->class_type != class_type ) + continue; + + if( fprintf(stream, "search: %s\n", dir->path) < 0 ) + return( -1 ); + } + } + + return( ferror(stream) ? -1 : 0 ); +} + + +int +mcpu_include_paths_from_config( mcpu_include_paths *paths, + const mcpu_config *config ) +{ + enum mcpu_language language; + const char *value; + + if( paths == NULL || config == NULL ) + return( 0 ); + + /* + * Language-specific directories precede the common include root. The + * common MCPU_CPP_INCLUDE_PATH itself is language-independent and remains + * visible in every language state. + */ + for( language = MCPU_LANG_DIFF; + language < MCPU_LANG__COUNT; + language = (enum mcpu_language)(language + 1) ) + { + const char *name = mcpu_language_config_variable( language ); + value = mcpu_config_get( config, name ); + if( add_language_list(paths, value, + MCPU_INCLUDE_USER_CONFIG_LANGUAGE, + language) != 0 ) + return( -1 ); + } + + value = mcpu_config_get( config, "MCPU_CPP_INCLUDE_PATH" ); + if( mcpu_include_paths_add_list( paths, value, + MCPU_INCLUDE_USER_CONFIG ) != 0 ) + return( -1 ); + + value = mcpu_config_get( config, "MCPU_CPP_SYSTEM_INCLUDE_PATH" ); + if( add_system_root(paths, value) != 0 ) + return( -1 ); + + value = mcpu_config_get( config, "MCPU_CPP_AFTER_INCLUDE_PATH" ); + if( mcpu_include_paths_add_list( paths, value, + MCPU_INCLUDE_AFTER_CONFIG ) != 0 ) + return( -1 ); + + return( 0 ); +} + +char * +mcpu_include_find( const mcpu_include_paths *paths, + const char *source_filename, + const char *include_filename, + int quoted, + enum mcpu_language language, + int include_next, + const mcpu_include_dir *after_dir, + const mcpu_include_dir **found_dir ) +{ + mcpu_include_dir *dir; + int past_after = after_dir == NULL; + + if( found_dir != NULL ) + *found_dir = NULL; + + if( include_filename == NULL ) + return( NULL ); + + if( include_filename[0] == '/' ) + { + if( file_exists(include_filename) ) + return( strdup(include_filename) ); + return( NULL ); + } + + /* + * #include_next never searches the directory of the current source file. + * It continues in the configured include chain after the directory entry + * that supplied the containing header. If the containing file came from + * its source directory (or has no search-chain origin), the configured + * chain is searched from its beginning. + */ + if( !include_next && quoted && source_filename ) + { + char *base = source_directory( source_filename ); + char *path; + + if( base == NULL ) + return( NULL ); + path = join_path( base, include_filename ); + free( base ); + if( path && file_exists(path) ) + return( path ); + free( path ); + } + + if( paths == NULL ) + return( NULL ); + + { + static const enum mcpu_include_class order[] = + { + MCPU_INCLUDE_USER_EXPLICIT, + MCPU_INCLUDE_SYSTEM_EXPLICIT, + MCPU_INCLUDE_USER_CONFIG_LANGUAGE, + MCPU_INCLUDE_USER_CONFIG, + MCPU_INCLUDE_SYSTEM_CONFIG_LANGUAGE, + MCPU_INCLUDE_SYSTEM_CONFIG, + MCPU_INCLUDE_AFTER_EXPLICIT, + MCPU_INCLUDE_AFTER_CONFIG + }; + size_t pass; + + for( pass = 0; pass < sizeof(order) / sizeof(order[0]); ++pass ) + { + for( dir = paths->first; dir; dir = dir->next ) + { + char *path; + + if( dir->class_type != order[pass] ) + continue; + + if( !past_after ) + { + if( dir == after_dir ) + past_after = 1; + continue; + } + + if( dir->language_specific && dir->language != language ) + continue; + + if( paths->no_standard_includes && + (dir->class_type == MCPU_INCLUDE_SYSTEM_CONFIG_LANGUAGE || + dir->class_type == MCPU_INCLUDE_SYSTEM_CONFIG) ) + continue; + + path = join_path( dir->path, include_filename ); + if( path == NULL ) + return( NULL ); + + if( file_exists(path) ) + { + if( found_dir != NULL ) + *found_dir = dir; + return( path ); + } + + free( path ); + } + } + } + + return( NULL ); +} |
