summaryrefslogtreecommitdiff
path: root/src/mcpp-include-path.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mcpp-include-path.c')
-rw-r--r--src/mcpp-include-path.c467
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 );
+}