#include 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 ); }