#include #include struct mcpp_dependency { char *path; dev_t device; ino_t inode; int has_identity; int unresolved; int system_only; mcpp_dependency *next; }; static int include_dir_is_system( const mcpu_include_dir *dir ) { if( dir == NULL ) return( 0 ); switch( dir->class_type ) { case MCPU_INCLUDE_SYSTEM_EXPLICIT: case MCPU_INCLUDE_SYSTEM_CONFIG_LANGUAGE: case MCPU_INCLUDE_SYSTEM_CONFIG: case MCPU_INCLUDE_AFTER_EXPLICIT: case MCPU_INCLUDE_AFTER_CONFIG: return( 1 ); default: return( 0 ); } } static int dependency_add_internal( mcpp *cpp, const char *path, int system_header, int unresolved ) { mcpp_dependency *p; struct stat st; int has_identity; if( cpp == NULL || path == NULL ) { errno = EINVAL; return( -1 ); } /* * Resolved dependencies belong to the physical identity domain and are * deduplicated by st_dev/st_ino whenever possible. -MG dependencies have * no physical object yet, so they deliberately bypass stat() and occupy a * separate unresolved identity domain keyed by the exact include operand. * This prevents an unrelated file in the current directory from turning an * unresolved into a false physical match. */ has_identity = !unresolved && stat(path, &st) == 0; for( p = cpp->dependencies_first; p != NULL; p = p->next ) { int same = 0; if( p->unresolved != unresolved ) continue; if( unresolved ) same = strcmp(p->path, path) == 0; else if( has_identity && p->has_identity ) same = p->device == st.st_dev && p->inode == st.st_ino; else if( !has_identity && !p->has_identity ) same = strcmp(p->path, path) == 0; if( same ) { /* * A resolved physical file may first be reached through a system path * and later through a user path; preserve the established registry rule * that the user reach makes it a user dependency. An unresolved -MG * entry has no physical provenance, so its first classification remains * authoritative, matching GNU CPP's missing-header behavior. */ if( !unresolved && !system_header ) p->system_only = 0; return( 0 ); } } p = (mcpp_dependency *)calloc( 1, sizeof(*p) ); if( p == NULL ) return( -1 ); p->path = strdup( path ); if( p->path == NULL ) { free( p ); return( -1 ); } p->has_identity = has_identity; p->unresolved = unresolved != 0; if( has_identity ) { p->device = st.st_dev; p->inode = st.st_ino; } p->system_only = system_header != 0; if( cpp->dependencies_last != NULL ) cpp->dependencies_last->next = p; else cpp->dependencies_first = p; cpp->dependencies_last = p; return( 0 ); } static int dependency_add( mcpp *cpp, const char *path, int system_header ) { return( dependency_add_internal(cpp, path, system_header, 0) ); } static int dependency_add_unresolved( mcpp *cpp, const char *path, int system_header ) { return( dependency_add_internal(cpp, path, system_header, 1) ); } static void dependencies_free( mcpp *cpp ) { mcpp_dependency *p; if( cpp == NULL ) return; p = cpp->dependencies_first; while( p != NULL ) { mcpp_dependency *next = p->next; free( p->path ); free( p ); p = next; } cpp->dependencies_first = NULL; cpp->dependencies_last = NULL; } struct mcpp_once_file { dev_t device; ino_t inode; mcpp_once_file *next; }; static int once_file_identity( const char *filename, dev_t *device, ino_t *inode ) { struct stat st; if( filename == NULL || device == NULL || inode == NULL ) { errno = EINVAL; return( -1 ); } if( stat(filename, &st) != 0 ) return( -1 ); *device = st.st_dev; *inode = st.st_ino; return( 0 ); } static int once_file_seen_identity( const mcpp *cpp, dev_t device, ino_t inode ) { const mcpp_once_file *p; if( cpp == NULL ) return( 0 ); for( p = cpp->once_files; p != NULL; p = p->next ) if( p->device == device && p->inode == inode ) return( 1 ); return( 0 ); } static int once_file_seen( const mcpp *cpp, const char *filename ) { dev_t device; ino_t inode; if( once_file_identity(filename, &device, &inode) != 0 ) return( 0 ); return( once_file_seen_identity(cpp, device, inode) ); } static int once_file_mark( mcpp *cpp, const char *filename ) { mcpp_once_file *entry; dev_t device; ino_t inode; if( cpp == NULL || filename == NULL ) { errno = EINVAL; return( -1 ); } /* * A stream such as has no stable filesystem identity. In that * case #pragma once is consumed but there is nothing that can be entered * in the physical-file registry. */ if( once_file_identity(filename, &device, &inode) != 0 ) return( 0 ); if( once_file_seen_identity(cpp, device, inode) ) return( 0 ); entry = (mcpp_once_file *)calloc( 1, sizeof(*entry) ); if( entry == NULL ) return( -1 ); entry->device = device; entry->inode = inode; entry->next = cpp->once_files; cpp->once_files = entry; return( 0 ); } static void once_files_free( mcpp *cpp ) { mcpp_once_file *p; if( cpp == NULL ) return; p = cpp->once_files; while( p != NULL ) { mcpp_once_file *next = p->next; free( p ); p = next; } cpp->once_files = NULL; } static enum mcpu_language current_language( const mcpp *cpp ) { return( cpp->lang_stack[cpp->lang_depth - 1] ); } static int install_builtin( mcpp *cpp, const char *name, enum mcpu_macro_builtin builtin ) { mcpu_text text; int rc; mcpu_text_init( &text ); if( mcpu_text_from_utf8(&text, name) != 0 ) return( -1 ); rc = mcpu_macro_define_builtin( &cpp->macros, text.data, text.length, builtin ); mcpu_text_free( &text ); return( rc ); } static int initialize_builtins( mcpp *cpp ) { if( cpp->builtins_initialized ) return( 0 ); if( install_builtin(cpp, "__FILE__", MCPU_MACRO_BUILTIN_FILE) != 0 || install_builtin(cpp, "__LINE__", MCPU_MACRO_BUILTIN_LINE) != 0 || install_builtin(cpp, "__DATE__", MCPU_MACRO_BUILTIN_DATE) != 0 || install_builtin(cpp, "__TIME__", MCPU_MACRO_BUILTIN_TIME) != 0 || install_builtin(cpp, "__BASE_FILE__", MCPU_MACRO_BUILTIN_BASE_FILE) != 0 || install_builtin(cpp, "__INCLUDE_LEVEL__", MCPU_MACRO_BUILTIN_INCLUDE_LEVEL) != 0 || mcpu_predefined_install(&cpp->macros) != 0 ) return( -1 ); cpp->builtins_initialized = 1; return( 0 ); } static int initialize_timestamp( mcpp *cpp ) { static const char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; time_t now; struct tm *tm; now = time( NULL ); if( now == (time_t)-1 ) return( -1 ); tm = localtime( &now ); if( tm == NULL || tm->tm_mon < 0 || tm->tm_mon > 11 ) { errno = EINVAL; return( -1 ); } snprintf( cpp->preprocess_date, sizeof(cpp->preprocess_date), "%s %2d %4d", months[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900 ); snprintf( cpp->preprocess_time, sizeof(cpp->preprocess_time), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec ); return( 0 ); } static void macro_context( const mcpp *cpp, const char *filename, unsigned line_number, mcpu_macro_expansion_context *context ) { context->filename = filename; context->base_filename = cpp->base_filename; context->line_number = line_number; context->include_level = cpp->include_depth ? cpp->include_depth - 1 : 0; context->date = cpp->preprocess_date; context->time = cpp->preprocess_time; context->options = MCPP_OPTIONS(cpp); } static char * escape_line_filename( const char *filename ) { size_t n; size_t i; size_t o = 0; char *escaped; if( filename == NULL ) { errno = EINVAL; return( NULL ); } n = strlen( filename ); if( n > (SIZE_MAX - 1) / 2 ) { errno = EOVERFLOW; return( NULL ); } escaped = (char *)malloc( n * 2 + 1 ); if( escaped == NULL ) return( NULL ); for( i = 0; i < n; ++i ) { switch( filename[i] ) { case '\\': case '"': escaped[o++] = '\\'; escaped[o++] = filename[i]; break; case '\n': escaped[o++] = '\\'; escaped[o++] = 'n'; break; case '\r': escaped[o++] = '\\'; escaped[o++] = 'r'; break; case '\t': escaped[o++] = '\\'; escaped[o++] = 't'; break; default: escaped[o++] = filename[i]; break; } } escaped[o] = 0; return( escaped ); } static int set_output_position( mcpp *cpp, unsigned line, const char *filename ) { char *copy; if( cpp == NULL || filename == NULL ) { errno = EINVAL; return( -1 ); } copy = strdup( filename ); if( copy == NULL ) return( -1 ); free( cpp->output_filename ); cpp->output_filename = copy; cpp->output_line = line; return( 0 ); } static int append_line_marker( mcpp *cpp, unsigned line, const char *filename, int file_change ) { char number[64]; char *escaped; char *position_filename; escaped = escape_line_filename( filename ); if( escaped == NULL ) return( -1 ); position_filename = strdup( filename ); if( position_filename == NULL ) { free( escaped ); return( -1 ); } if( mcpu_text_append_ascii( &cpp->output, "# " ) != 0 ) { free( position_filename ); free( escaped ); return( -1 ); } snprintf( number, sizeof(number), "%u", line ); if( mcpu_text_append_ascii( &cpp->output, number ) != 0 || mcpu_text_append_ascii( &cpp->output, " \"" ) != 0 || mcpu_text_append_utf8( &cpp->output, escaped ) != 0 || mcpu_text_append_ascii( &cpp->output, "\"" ) != 0 ) { free( position_filename ); free( escaped ); return( -1 ); } if( file_change == 1 || file_change == 2 ) { if( mcpu_text_append_ascii(&cpp->output, file_change == 1 ? " 1" : " 2") != 0 ) { free( position_filename ); free( escaped ); return( -1 ); } } if( mcpu_text_append_char(&cpp->output, '\n') != 0 ) { free( position_filename ); free( escaped ); return( -1 ); } free( cpp->output_filename ); cpp->output_filename = position_filename; cpp->output_line = line; free( escaped ); return( 0 ); } static int append_output_newlines( mcpp *cpp, unsigned count ) { unsigned i; for( i = 0; i < count; ++i ) { if( mcpu_text_append_char(&cpp->output, '\n') != 0 ) return( -1 ); } if( cpp->output_filename != NULL ) cpp->output_line += count; return( 0 ); } /* * GNU CPP keeps short invisible source gaps as ordinary newlines, but once * the next visible source position is eight or more lines away it emits a * fresh line marker instead. The discarded text is therefore forgotten as * output bytes, but never forgotten as source position: "remove, but * remember". */ static int sync_output_position( mcpp *cpp, unsigned line, const char *filename ) { if( cpp->output_filename != NULL && strcmp(cpp->output_filename, filename) == 0 && line >= cpp->output_line && line - cpp->output_line < 8 ) return( append_output_newlines(cpp, line - cpp->output_line) ); return( append_line_marker(cpp, line, filename, 0) ); } static int text_has_visible_tokens( const __mpu_char16_t *text, size_t length ) { size_t i; for( i = 0; i < length; ++i ) { switch( text[i] ) { case ' ': case '\t': case '\f': case '\v': case '\r': case '\n': break; default: return( 1 ); } } return( 0 ); } static void advance_output_position( mcpp *cpp, const __mpu_char16_t *text, size_t length ) { size_t i; if( cpp->output_filename == NULL ) return; for( i = 0; i < length; ++i ) if( text[i] == '\n' ) ++cpp->output_line; } static int append_visible_text( mcpp *cpp, unsigned line, const char *filename, const __mpu_char16_t *text, size_t length ) { if( !text_has_visible_tokens(text, length) ) return( 0 ); if( sync_output_position(cpp, line, filename) != 0 || mcpu_text_append(&cpp->output, text, length) != 0 ) return( -1 ); advance_output_position( cpp, text, length ); return( 0 ); } static int is_space16( __mpu_char16_t c ) { return( c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r' ); } static int is_ident16( __mpu_char16_t c ) { return( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' || c >= 0x80 ); } static int replacement_quote16( __mpu_char16_t c, enum mcpu_language language ) { if( c == '"' || c == '`' ) return( 1 ); if( c == '\'' && language != MCPU_LANG_DIFF ) return( 1 ); return( 0 ); } static int normalize_macro_replacement( const __mpu_char16_t *text, size_t length, enum mcpu_language language, mcpu_text *normalized ) { size_t p = 0; __mpu_char16_t quote = 0; int escaped = 0; int pending_space = 0; while( p < length ) { __mpu_char16_t c = text[p++]; if( quote != 0 ) { if( mcpu_text_append_char(normalized, c) != 0 ) return( -1 ); if( escaped ) escaped = 0; else if( c == '\\' ) escaped = 1; else if( c == quote || c == '\n' ) quote = 0; continue; } if( is_space16(c) || c == '\n' ) { if( normalized->length != 0 ) pending_space = 1; continue; } if( pending_space ) { if( mcpu_text_append_char(normalized, ' ') != 0 ) return( -1 ); pending_space = 0; } if( mcpu_text_append_char(normalized, c) != 0 ) return( -1 ); if( replacement_quote16(c, language) ) quote = c; } return( 0 ); } static void skip_space16( const __mpu_char16_t *line, size_t length, size_t *pos ) { while( *pos < length && is_space16(line[*pos]) ) ++*pos; } static int parse_directive_name( const __mpu_char16_t *line, size_t length, size_t hash, size_t *name_start, size_t *name_length, size_t *after_name ) { size_t p = hash + 1; size_t start; skip_space16( line, length, &p ); start = p; while( p < length && is_ident16(line[p]) ) ++p; *name_start = start; *name_length = p - start; *after_name = p; return( *name_length != 0 ); } static int handle_lang( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, int spliced ) { size_t start; size_t end; size_t q; enum mcpu_language language; char *name; int newline; if( spliced ) { fprintf( stderr, "%s:%u: error: #lang must be contained in one physical source line\n", filename, line_number ); return( -1 ); } skip_space16( line, length, &p ); if( p >= length || line[p] != '"' ) { fprintf( stderr, "%s:%u: error: string constant expected after #lang\n", filename, line_number ); return( -1 ); } start = ++p; while( p < length && line[p] != '"' && line[p] != '\n' ) ++p; if( p >= length || line[p] != '"' ) { fprintf( stderr, "%s:%u: error: unterminated #lang string constant\n", filename, line_number ); return( -1 ); } end = p++; if( start == end ) { fprintf( stderr, "%s:%u: error: empty language name in #lang\n", filename, line_number ); return( -1 ); } for( q = start; q < end; ++q ) { if( line[q] == ' ' || line[q] == '\t' || line[q] == '\f' || line[q] == '\v' || line[q] == '\r' || line[q] == '\n' ) { fprintf( stderr, "%s:%u: error: #lang language name must be one word without whitespace\n", filename, line_number ); return( -1 ); } } skip_space16( line, length, &p ); if( p < length && line[p] != '\n' ) { fprintf( stderr, "%s:%u: error: extra text after #lang string constant\n", filename, line_number ); return( -1 ); } if( mcpu_language_from_text( line + start, end - start, &language ) != 0 ) { name = mcpu_text_to_utf8( line + start, end - start ); fprintf( stderr, "%s:%u: error: unknown language '%s'\n", filename, line_number, name ? name : "?" ); free( name ); return( -1 ); } if( cpp->lang_depth >= MCPU_CPP_LANG_STACK_SIZE ) { fprintf( stderr, "%s:%u: error: #lang stack overflow\n", filename, line_number ); return( -1 ); } cpp->lang_stack[cpp->lang_depth++] = language; if( cpp->verbose ) fprintf( stderr, "%s:%u: #lang %s (depth %lu)\n", filename, line_number, mcpu_language_name(language), (unsigned long)cpp->lang_depth ); newline = length != 0 && line[length - 1] == '\n'; if( mcpu_text_append_ascii(&cpp->output, "#lang \"") != 0 || mcpu_text_append(&cpp->output, line + start, end - start) != 0 || mcpu_text_append_char(&cpp->output, '"') != 0 || (newline && mcpu_text_append_char(&cpp->output, '\n') != 0) ) return( -1 ); return( 0 ); } static int handle_endlang( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length ) { if( cpp->lang_depth <= 1 ) { fprintf( stderr, "%s:%u: error: unbalanced #endlang\n", filename, line_number ); return( -1 ); } --cpp->lang_depth; if( cpp->verbose ) fprintf( stderr, "%s:%u: #endlang -> %s (depth %lu)\n", filename, line_number, mcpu_language_name(current_language(cpp)), (unsigned long)cpp->lang_depth ); return( mcpu_text_append( &cpp->output, line, length ) ); } static int parse_include_filename( const __mpu_char16_t *line, size_t length, size_t p, char **filename, int *quoted ) { __mpu_char16_t open; __mpu_char16_t close; size_t start; size_t end; *filename = NULL; *quoted = 0; skip_space16( line, length, &p ); if( p >= length ) return( -1 ); open = line[p++]; if( open == '"' ) { close = '"'; *quoted = 1; } else if( open == '<' ) close = '>'; else return( -1 ); start = p; while( p < length && line[p] != close && line[p] != '\n' ) ++p; end = p; if( p >= length || line[p] != close || end == start ) return( -1 ); *filename = mcpu_text_to_utf8( line + start, end - start ); return( *filename ? 0 : -1 ); } static size_t line_content_end( const __mpu_char16_t *line, size_t length ) { size_t end = length; if( end != 0 && line[end - 1] == '\n' ) --end; while( end != 0 && is_space16(line[end - 1]) ) --end; return( end ); } static void free_define_args( const __mpu_char16_t **argnames, size_t *argname_lengths ) { free( argnames ); free( argname_lengths ); } static int append_define_arg( const __mpu_char16_t ***argnames, size_t **argname_lengths, size_t *nargs, size_t *capacity, const __mpu_char16_t *name, size_t name_length ) { const __mpu_char16_t **new_names; size_t *new_lengths; size_t new_capacity; if( *nargs == *capacity ) { new_capacity = *capacity ? *capacity * 2 : 4; if( new_capacity < *capacity || new_capacity > SIZE_MAX / sizeof(**argnames) || new_capacity > SIZE_MAX / sizeof(**argname_lengths) ) { errno = EOVERFLOW; return( -1 ); } new_names = (const __mpu_char16_t **)realloc( (void *)*argnames, new_capacity * sizeof(**argnames) ); if( new_names == NULL ) return( -1 ); *argnames = new_names; new_lengths = (size_t *)realloc( *argname_lengths, new_capacity * sizeof(**argname_lengths) ); if( new_lengths == NULL ) return( -1 ); *argname_lengths = new_lengths; *capacity = new_capacity; } (*argnames)[*nargs] = name; (*argname_lengths)[*nargs] = name_length; ++*nargs; return( 0 ); } static int replacement_va_opt_range_valid( const __mpu_char16_t *text, size_t length, enum mcpu_language language, int function_like, int variadic, int inside_va_opt, const char *filename, unsigned line_number ) { size_t p = 0; __mpu_char16_t quote = 0; int escaped = 0; while( p < length ) { __mpu_char16_t c = text[p]; if( quote ) { if( escaped ) escaped = 0; else if( c == '\\' ) escaped = 1; else if( c == quote || c == '\n' ) quote = 0; ++p; continue; } if( c == '"' || c == '`' || (c == '\'' && language != MCPU_LANG_DIFF) ) { quote = c; ++p; continue; } if( mcpu_pp_is_identifier_start(c) ) { size_t q = p + 1; while( q < length && mcpu_pp_is_identifier_char(text[q]) ) ++q; if( mcpu_text_equal_ascii(text + p, q - p, "__VA_OPT__") ) { size_t open; size_t close; size_t first; size_t last; size_t r; int depth; __mpu_char16_t inner_quote = 0; int inner_escaped = 0; if( !function_like || !variadic ) { fprintf( stderr, "%s:%u: error: '__VA_OPT__' may appear only in a variadic macro replacement list\n", filename, line_number ); return( 0 ); } if( inside_va_opt ) { fprintf( stderr, "%s:%u: error: '__VA_OPT__' may not appear inside another '__VA_OPT__'\n", filename, line_number ); return( 0 ); } open = q; while( open < length && is_space16(text[open]) ) ++open; if( open >= length || text[open] != '(' ) { fprintf( stderr, "%s:%u: error: '__VA_OPT__' must be followed by '('\n", filename, line_number ); return( 0 ); } depth = 1; close = open + 1; while( close < length && depth != 0 ) { __mpu_char16_t d = text[close]; if( inner_quote ) { if( inner_escaped ) inner_escaped = 0; else if( d == '\\' ) inner_escaped = 1; else if( d == inner_quote || d == '\n' ) inner_quote = 0; ++close; continue; } if( d == '"' || d == '`' || (d == '\'' && language != MCPU_LANG_DIFF) ) { inner_quote = d; ++close; continue; } if( d == '(' ) ++depth; else if( d == ')' ) --depth; ++close; } if( depth != 0 ) { fprintf( stderr, "%s:%u: error: unterminated '__VA_OPT__'\n", filename, line_number ); return( 0 ); } --close; first = open + 1; while( first < close && is_space16(text[first]) ) ++first; last = close; while( last > first && is_space16(text[last - 1]) ) --last; if( first + 1 < last && text[first] == '#' && text[first + 1] == '#' ) { fprintf( stderr, "%s:%u: error: '##' cannot appear at the beginning of '__VA_OPT__'\n", filename, line_number ); return( 0 ); } if( last >= first + 2 && text[last - 2] == '#' && text[last - 1] == '#' ) { fprintf( stderr, "%s:%u: error: '##' cannot appear at the end of '__VA_OPT__'\n", filename, line_number ); return( 0 ); } if( !replacement_va_opt_range_valid(text + open + 1, close - open - 1, language, function_like, variadic, 1, filename, line_number) ) return( 0 ); r = close + 1; p = r; continue; } p = q; continue; } ++p; } return( 1 ); } static int replacement_va_opt_valid( const __mpu_char16_t *text, size_t length, enum mcpu_language language, int function_like, int variadic, const char *filename, unsigned line_number ) { return( replacement_va_opt_range_valid(text, length, language, function_like, variadic, 0, filename, line_number) ); } static int replacement_macro_operators_valid( const __mpu_char16_t *text, size_t length, enum mcpu_language language, int function_like, int variadic, const __mpu_char16_t *const *argnames, const size_t *argname_lengths, size_t nargs, const char *filename, unsigned line_number ) { size_t p = 0; __mpu_char16_t quote = 0; int escaped = 0; int saw_token = 0; int paste_at_end = 0; while( p < length ) { __mpu_char16_t c = text[p]; if( quote ) { if( escaped ) escaped = 0; else if( c == '\\' ) escaped = 1; else if( c == quote || c == '\n' ) quote = 0; ++p; continue; } if( c == '"' || c == '`' || (c == '\'' && language != MCPU_LANG_DIFF) ) { quote = c; saw_token = 1; paste_at_end = 0; ++p; continue; } if( c == '#' && p + 1 < length && text[p + 1] == '#' ) { if( !saw_token ) { fprintf( stderr, "%s:%u: error: '##' cannot appear at the beginning of a macro replacement list\n", filename, line_number ); return( 0 ); } paste_at_end = 1; p += 2; continue; } if( c == '#' && function_like ) { size_t q = p + 1; size_t start; size_t i; int found = 0; while( q < length && is_space16(text[q]) ) ++q; if( q >= length || !mcpu_pp_is_identifier_start(text[q]) ) { fprintf( stderr, "%s:%u: error: '#' operator is not followed by a macro argument name\n", filename, line_number ); return( 0 ); } start = q++; while( q < length && mcpu_pp_is_identifier_char(text[q]) ) ++q; for( i = 0; i < nargs; ++i ) { if( argname_lengths[i] == q - start && memcmp(argnames[i], text + start, (q - start) * sizeof(__mpu_char16_t)) == 0 ) { found = 1; break; } } if( !found && variadic && (mcpu_text_equal_ascii(text + start, q - start, "__VA_ARGS__") || mcpu_text_equal_ascii(text + start, q - start, "__VA_OPT__")) ) found = 1; if( !found ) { fprintf( stderr, "%s:%u: error: '#' operator should be followed by a macro argument name\n", filename, line_number ); return( 0 ); } saw_token = 1; paste_at_end = 0; p = q; continue; } if( is_space16(c) ) { ++p; continue; } saw_token = 1; paste_at_end = 0; ++p; } if( paste_at_end ) { fprintf( stderr, "%s:%u: error: '##' cannot appear at the end of a macro replacement list\n", filename, line_number ); return( 0 ); } return( 1 ); } static int handle_define( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, enum mcpu_macro_origin origin ) { size_t name_start; size_t name_end; size_t replacement_start; size_t replacement_end; const __mpu_char16_t **argnames = NULL; size_t *argname_lengths = NULL; size_t nargs = 0; size_t capacity = 0; int function_like = 0; int variadic = 0; mcpu_macro *old; mcpu_text replacement; int rc = -1; mcpu_text_init( &replacement ); skip_space16( line, length, &p ); if( p >= length || !mcpu_pp_is_identifier_start(line[p]) ) { fprintf( stderr, "%s:%u: error: macro name expected after #define\n", filename, line_number ); return( -1 ); } name_start = p++; while( p < length && mcpu_pp_is_identifier_char(line[p]) ) ++p; name_end = p; /*************************************************************** A function-like definition is recognized only when the opening parenthesis immediately follows the macro name. A space changes the definition into an object-like macro whose replacement happens to begin with '('. ***************************************************************/ if( p < length && line[p] == '(' ) { size_t i; function_like = 1; ++p; skip_space16( line, length, &p ); if( p + 2 < length && line[p] == '.' && line[p + 1] == '.' && line[p + 2] == '.' ) { variadic = 1; p += 3; skip_space16( line, length, &p ); if( p >= length || line[p] != ')' ) { fprintf( stderr, "%s:%u: error: badly punctuated parameter list in #define\n", filename, line_number ); goto done; } } else { while( p < length && line[p] != ')' ) { size_t arg_start; size_t arg_end; if( !mcpu_pp_is_identifier_start(line[p]) ) { fprintf( stderr, "%s:%u: error: invalid macro parameter name in #define\n", filename, line_number ); goto done; } arg_start = p++; while( p < length && mcpu_pp_is_identifier_char(line[p]) ) ++p; arg_end = p; if( mcpu_text_equal_ascii(line + arg_start, arg_end - arg_start, "__VA_ARGS__") ) { fprintf( stderr, "%s:%u: error: '__VA_ARGS__' cannot be used as a macro parameter name\n", filename, line_number ); goto done; } for( i = 0; i < nargs; ++i ) { if( argname_lengths[i] == arg_end - arg_start && memcmp( argnames[i], line + arg_start, (arg_end - arg_start) * sizeof(__mpu_char16_t) ) == 0 ) { char *arg = mcpu_text_to_utf8( line + arg_start, arg_end - arg_start ); fprintf( stderr, "%s:%u: error: duplicate argument name '%s' in #define\n", filename, line_number, arg ? arg : "?" ); free( arg ); goto done; } } if( append_define_arg(&argnames, &argname_lengths, &nargs, &capacity, line + arg_start, arg_end - arg_start) != 0 ) goto done; skip_space16( line, length, &p ); if( p >= length ) { fprintf( stderr, "%s:%u: error: unterminated parameter list in #define\n", filename, line_number ); goto done; } if( line[p] == ',' ) { ++p; skip_space16( line, length, &p ); if( p + 2 < length && line[p] == '.' && line[p + 1] == '.' && line[p + 2] == '.' ) { variadic = 1; p += 3; skip_space16( line, length, &p ); if( p >= length || line[p] != ')' ) { fprintf( stderr, "%s:%u: error: badly punctuated parameter list in #define\n", filename, line_number ); goto done; } break; } if( p >= length || !mcpu_pp_is_identifier_start(line[p]) ) { fprintf( stderr, "%s:%u: error: badly punctuated parameter list in #define\n", filename, line_number ); goto done; } continue; } if( line[p] != ')' ) { fprintf( stderr, "%s:%u: error: badly punctuated parameter list in #define\n", filename, line_number ); goto done; } } } if( p >= length || line[p] != ')' ) { fprintf( stderr, "%s:%u: error: unterminated parameter list in #define\n", filename, line_number ); goto done; } ++p; } skip_space16( line, length, &p ); replacement_start = p; replacement_end = line_content_end( line, length ); if( replacement_end < replacement_start ) replacement_end = replacement_start; if( normalize_macro_replacement(line + replacement_start, replacement_end - replacement_start, current_language(cpp), &replacement) != 0 ) goto done; if( !replacement_va_opt_valid( replacement.data, replacement.length, current_language(cpp), function_like, variadic, filename, line_number) || !replacement_macro_operators_valid( replacement.data, replacement.length, current_language(cpp), function_like, variadic, argnames, argname_lengths, nargs, filename, line_number) ) goto done; old = mcpu_macro_find( &cpp->macros, line + name_start, name_end - name_start ); if( old && !mcpu_macro_definition_equal(old, function_like ? (int)nargs : -1, function_like ? variadic : 0, argnames, argname_lengths, replacement.data, replacement.length) ) { char *name = mcpu_text_to_utf8( line + name_start, name_end - name_start ); if( mcpp_diagnostic_warning(MCPP_OPTIONS(cpp), filename, line_number, "macro '%s' redefined", name ? name : "?") != 0 ) { free( name ); goto done; } free( name ); } if( function_like ) rc = mcpu_macro_define_function(&cpp->macros, line + name_start, name_end - name_start, argnames, argname_lengths, nargs, variadic, replacement.data, replacement.length, origin); else rc = mcpu_macro_define_object(&cpp->macros, line + name_start, name_end - name_start, replacement.data, replacement.length, origin); done: mcpu_text_free( &replacement ); free_define_args( argnames, argname_lengths ); return( rc ); } static int handle_undef( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p ) { size_t name_start; size_t name_end; skip_space16( line, length, &p ); if( p >= length || !mcpu_pp_is_identifier_start(line[p]) ) { fprintf( stderr, "%s:%u: error: macro name expected after #undef\n", filename, line_number ); return( -1 ); } name_start = p++; while( p < length && mcpu_pp_is_identifier_char(line[p]) ) ++p; name_end = p; skip_space16( line, length, &p ); if( p < length && line[p] != '\n' ) { fprintf( stderr, "%s:%u: error: extra tokens after #undef\n", filename, line_number ); return( -1 ); } if( mcpu_macro_undef(&cpp->macros, line + name_start, name_end - name_start) < 0 ) return( -1 ); return( 0 ); } static int command_line_text( const mcpp *cpp, const mcpp_option_action *action, mcpu_text *text ) { if( mcpu_text_from_utf8(text, action->argument) == 0 ) return( 0 ); if( errno == EILSEQ ) fprintf( stderr, "%s: %s: invalid UTF-8 or non-UCS-2 character\n", MCPP_OPTIONS(cpp)->progname, action->option ); else fprintf( stderr, "%s: %s: %s\n", MCPP_OPTIONS(cpp)->progname, action->option, strerror(errno) ); return( -1 ); } static size_t command_line_define_declarator_end( const mcpu_text *text, size_t equal ) { size_t p; if( text == NULL || text->length == 0 || equal == 0 ) return( 0 ); if( !mcpu_pp_is_identifier_start(text->data[0]) ) return( equal ); p = 1; while( p < equal && mcpu_pp_is_identifier_char(text->data[p]) ) ++p; if( p < equal && text->data[p] == '(' ) { ++p; while( p < equal && text->data[p] != ')' ) ++p; if( p < equal && text->data[p] == ')' ) ++p; } return( p ); } static int prepare_command_line_define( mcpp *cpp, const mcpp_option_action *action, const mcpu_text *text, mcpu_text *prepared ) { mcpu_text line; mcpu_pp_lexer_state lexer; size_t equal; size_t declarator_end; int has_equal; int rc = -1; mcpu_text_init( &line ); equal = 0; has_equal = 0; while( equal < text->length ) { if( text->data[equal] == '=' ) { has_equal = 1; break; } ++equal; } declarator_end = command_line_define_declarator_end( text, has_equal ? equal : text->length ); if( mcpu_text_append(&line, text->data, declarator_end) != 0 ) goto done; if( has_equal ) { if( mcpu_text_append_char(&line, ' ') != 0 || mcpu_text_append(&line, text->data + equal + 1, text->length - equal - 1) != 0 ) goto done; } else { if( mcpu_text_append_ascii(&line, " 1") != 0 ) goto done; } mcpu_pp_lexer_init( &lexer ); if( mcpu_pp_prepare_line(&lexer, line.data, line.length, current_language(cpp), prepared) != 0 ) goto done; if( lexer.in_block_comment ) { fprintf( stderr, "%s: %s: unterminated comment\n", MCPP_OPTIONS(cpp)->progname, action->option ); goto done; } rc = 0; done: mcpu_text_free( &line ); return( rc ); } static int prepare_command_line_undef( mcpp *cpp, const mcpp_option_action *action, const mcpu_text *text, mcpu_text *prepared ) { mcpu_text line; mcpu_pp_lexer_state lexer; size_t p; int rc = -1; mcpu_text_init( &line ); if( text->length != 0 && mcpu_pp_is_identifier_start(text->data[0]) ) { p = 1; while( p < text->length && mcpu_pp_is_identifier_char(text->data[p]) ) ++p; } else p = text->length; if( mcpu_text_append(&line, text->data, p) != 0 ) goto done; mcpu_pp_lexer_init( &lexer ); if( mcpu_pp_prepare_line(&lexer, line.data, line.length, current_language(cpp), prepared) != 0 ) goto done; if( lexer.in_block_comment ) { fprintf( stderr, "%s: %s: unterminated comment\n", MCPP_OPTIONS(cpp)->progname, action->option ); goto done; } rc = 0; done: mcpu_text_free( &line ); return( rc ); } static int apply_command_line_define( mcpp *cpp, const mcpp_option_action *action ) { mcpu_text text; mcpu_text prepared; int rc; mcpu_text_init( &text ); mcpu_text_init( &prepared ); if( command_line_text(cpp, action, &text) != 0 || prepare_command_line_define(cpp, action, &text, &prepared) != 0 ) goto fail; rc = handle_define( cpp, "", 0, prepared.data, prepared.length, 0, MCPU_MACRO_ORIGIN_COMMAND_LINE ); mcpu_text_free( &prepared ); mcpu_text_free( &text ); return( rc ); fail: mcpu_text_free( &prepared ); mcpu_text_free( &text ); return( -1 ); } static int apply_command_line_undef( mcpp *cpp, const mcpp_option_action *action ) { mcpu_text text; mcpu_text prepared; int rc; mcpu_text_init( &text ); mcpu_text_init( &prepared ); if( command_line_text(cpp, action, &text) != 0 || prepare_command_line_undef(cpp, action, &text, &prepared) != 0 ) { mcpu_text_free( &prepared ); mcpu_text_free( &text ); return( -1 ); } rc = handle_undef( cpp, "", 0, prepared.data, prepared.length, 0 ); mcpu_text_free( &prepared ); mcpu_text_free( &text ); return( rc ); } static int apply_command_line_macros( mcpp *cpp ) { size_t i; if( cpp->command_line_macros_applied || MCPP_OPTIONS(cpp) == NULL ) return( 0 ); for( i = 0; i < MCPP_OPTIONS(cpp)->action_count; ++i ) { const mcpp_option_action *action = &MCPP_OPTIONS(cpp)->actions[i]; switch( action->kind ) { case MCPP_ACTION_DEFINE: if( apply_command_line_define(cpp, action) != 0 ) return( -1 ); break; case MCPP_ACTION_UNDEF: if( apply_command_line_undef(cpp, action) != 0 ) return( -1 ); break; default: break; } } cpp->command_line_macros_applied = 1; return( 0 ); } enum mcpp_conditional_directive { MCPP_COND_NONE = 0, MCPP_COND_IF, MCPP_COND_IFDEF, MCPP_COND_IFNDEF, MCPP_COND_ELIF, MCPP_COND_ELSE, MCPP_COND_ENDIF }; static int diagnostic_directive( const __mpu_char16_t *name, size_t length, int *is_error, const char **directive_name ) { if( mcpu_text_equal_ascii(name, length, "error") ) { *is_error = 1; *directive_name = "error"; return( 1 ); } if( mcpu_text_equal_ascii(name, length, "warning") ) { *is_error = 0; *directive_name = "warning"; return( 1 ); } return( 0 ); } static int append_diagnostic_text( mcpu_text *message, const __mpu_char16_t *line, size_t length, size_t p ) { int pending_space = 0; __mpu_char16_t quote = 0; int escaped = 0; skip_space16( line, length, &p ); while( p < length && line[p] != '\n' ) { __mpu_char16_t c = line[p++]; if( quote ) { if( mcpu_text_append_char(message, c) != 0 ) return( -1 ); if( escaped ) escaped = 0; else if( c == '\\' ) escaped = 1; else if( c == quote ) quote = 0; continue; } if( c == ' ' || c == '\t' || c == '\f' || c == '\v' || c == '\r' ) { if( message->length != 0 ) pending_space = 1; continue; } if( pending_space ) { if( mcpu_text_append_char(message, ' ') != 0 ) return( -1 ); pending_space = 0; } if( c == '"' || c == '\'' || c == '`' ) quote = c; if( mcpu_text_append_char(message, c) != 0 ) return( -1 ); } return( 0 ); } static int handle_diagnostic( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, int is_error, const char *directive_name ) { mcpu_text message; char *utf8; mcpu_text_init( &message ); if( append_diagnostic_text(&message, line, length, p) != 0 ) { mcpu_text_free( &message ); return( -1 ); } utf8 = mcpu_text_to_utf8( message.data, message.length ); mcpu_text_free( &message ); if( utf8 == NULL ) return( -1 ); if( is_error ) { fprintf( stderr, "%s:%u: error: #%s%s%s\n", filename, line_number, directive_name, *utf8 ? " " : "", utf8 ); free( utf8 ); return( -1 ); } { int rc = mcpp_diagnostic_warning(MCPP_OPTIONS(cpp), filename, line_number, "#%s%s%s", directive_name, *utf8 ? " " : "", utf8); free( utf8 ); return( rc ); } } static enum mcpp_conditional_directive conditional_directive( const __mpu_char16_t *name, size_t length ) { if( mcpu_text_equal_ascii(name, length, "if") ) return( MCPP_COND_IF ); if( mcpu_text_equal_ascii(name, length, "ifdef") ) return( MCPP_COND_IFDEF ); if( mcpu_text_equal_ascii(name, length, "ifndef") ) return( MCPP_COND_IFNDEF ); if( mcpu_text_equal_ascii(name, length, "elif") ) return( MCPP_COND_ELIF ); if( mcpu_text_equal_ascii(name, length, "else") ) return( MCPP_COND_ELSE ); if( mcpu_text_equal_ascii(name, length, "endif") ) return( MCPP_COND_ENDIF ); return( MCPP_COND_NONE ); } static int conditional_active( const mcpp *cpp ) { if( cpp->conditional_depth == 0 ) return( 1 ); return( cpp->conditional_stack[cpp->conditional_depth - 1].active ); } static int conditional_push( mcpp *cpp, const char *filename, unsigned line_number, int parent_active, int condition ) { mcpp_conditional *frame; if( cpp->conditional_depth >= MCPU_CPP_CONDITIONAL_STACK_SIZE ) { fprintf( stderr, "%s:%u: error: conditional nesting exceeds %d levels\n", filename, line_number, MCPU_CPP_CONDITIONAL_STACK_SIZE ); return( -1 ); } frame = &cpp->conditional_stack[cpp->conditional_depth++]; frame->filename = filename; frame->line_number = line_number; frame->parent_active = parent_active; frame->active = parent_active && condition; frame->branch_succeeded = frame->active; frame->seen_else = 0; return( 0 ); } static int conditional_identifier( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, int negate, int parent_active ) { size_t start; size_t end; int defined; if( !parent_active ) return( conditional_push(cpp, filename, line_number, 0, 0) ); skip_space16( line, length, &p ); if( p >= length || !mcpu_pp_is_identifier_start(line[p]) ) { fprintf( stderr, "%s:%u: error: identifier expected after #%s\n", filename, line_number, negate ? "ifndef" : "ifdef" ); return( -1 ); } start = p++; while( p < length && mcpu_pp_is_identifier_char(line[p]) ) ++p; end = p; skip_space16( line, length, &p ); if( p < length && line[p] != '\n' ) { fprintf( stderr, "%s:%u: error: extra tokens after #%s\n", filename, line_number, negate ? "ifndef" : "ifdef" ); return( -1 ); } defined = mcpu_macro_find( &cpp->macros, line + start, end - start ) != NULL; if( negate ) defined = !defined; return( conditional_push(cpp, filename, line_number, 1, defined) ); } static int conditional_if( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, int parent_active ) { mcpu_macro_expansion_context context; int value = 0; if( parent_active ) { macro_context( cpp, filename, line_number, &context ); if( mcpp_eval_if_expression(&cpp->macros, line + p, length - p, current_language(cpp), &context, filename, line_number, &value) != 0 ) return( -1 ); } return( conditional_push(cpp, filename, line_number, parent_active, value) ); } static int conditional_elif( mcpp *cpp, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, size_t conditional_base ) { mcpp_conditional *frame; mcpu_macro_expansion_context context; int value = 0; if( cpp->conditional_depth <= conditional_base ) { fprintf( stderr, "%s:%u: error: '#elif' not within a conditional\n", filename, line_number ); return( -1 ); } frame = &cpp->conditional_stack[cpp->conditional_depth - 1]; if( frame->seen_else ) { fprintf( stderr, "%s:%u: error: '#elif' after '#else'\n", filename, line_number ); return( -1 ); } if( !frame->parent_active || frame->branch_succeeded ) { frame->active = 0; return( 0 ); } macro_context( cpp, filename, line_number, &context ); if( mcpp_eval_if_expression(&cpp->macros, line + p, length - p, current_language(cpp), &context, filename, line_number, &value) != 0 ) return( -1 ); frame->active = value != 0; if( frame->active ) frame->branch_succeeded = 1; return( 0 ); } static int conditional_else( mcpp *cpp, const char *filename, unsigned line_number, size_t conditional_base ) { mcpp_conditional *frame; if( cpp->conditional_depth <= conditional_base ) { fprintf( stderr, "%s:%u: error: '#else' not within a conditional\n", filename, line_number ); return( -1 ); } frame = &cpp->conditional_stack[cpp->conditional_depth - 1]; if( frame->seen_else ) { fprintf( stderr, "%s:%u: error: '#else' after '#else'\n", filename, line_number ); return( -1 ); } frame->seen_else = 1; frame->active = frame->parent_active && !frame->branch_succeeded; if( frame->active ) frame->branch_succeeded = 1; return( 0 ); } static int conditional_endif( mcpp *cpp, const char *filename, unsigned line_number, size_t conditional_base ) { if( cpp->conditional_depth <= conditional_base ) { fprintf( stderr, "%s:%u: error: unbalanced '#endif'\n", filename, line_number ); return( -1 ); } --cpp->conditional_depth; return( 0 ); } static int handle_conditional( mcpp *cpp, enum mcpp_conditional_directive directive, const char *filename, unsigned line_number, const __mpu_char16_t *line, size_t length, size_t p, size_t conditional_base ) { int parent_active = conditional_active( cpp ); switch( directive ) { case MCPP_COND_IF: return( conditional_if(cpp, filename, line_number, line, length, p, parent_active) ); case MCPP_COND_IFDEF: return( conditional_identifier(cpp, filename, line_number, line, length, p, 0, parent_active) ); case MCPP_COND_IFNDEF: return( conditional_identifier(cpp, filename, line_number, line, length, p, 1, parent_active) ); case MCPP_COND_ELIF: return( conditional_elif(cpp, filename, line_number, line, length, p, conditional_base) ); case MCPP_COND_ELSE: return( conditional_else(cpp, filename, line_number, conditional_base) ); case MCPP_COND_ENDIF: return( conditional_endif(cpp, filename, line_number, conditional_base) ); default: return( 0 ); } } static int parse_line_filename( const __mpu_char16_t *text, size_t length, size_t *pos, char **filename ) { mcpu_text decoded; size_t p = *pos; *filename = NULL; if( p >= length || text[p] != '"' ) return( 0 ); ++p; mcpu_text_init( &decoded ); while( p < length && text[p] != '\n' ) { __mpu_char16_t c = text[p++]; if( c == '"' ) { *filename = mcpu_text_to_utf8( decoded.data, decoded.length ); mcpu_text_free( &decoded ); if( *filename == NULL ) return( -1 ); *pos = p; return( 1 ); } if( c == '\\' ) { unsigned value; int digit; unsigned count; if( p >= length || text[p] == '\n' ) goto bad; c = text[p++]; switch( c ) { case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case '\\': c = '\\'; break; case '\'': c = '\''; break; case '"': c = '"'; break; case '?': c = '?'; break; case 'x': case 'X': value = 0; count = 0; while( p < length ) { __mpu_char16_t h = text[p]; if( h >= '0' && h <= '9' ) digit = h - '0'; else if( h >= 'a' && h <= 'f' ) digit = h - 'a' + 10; else if( h >= 'A' && h <= 'F' ) digit = h - 'A' + 10; else break; if( value > 0xffffU / 16U ) goto bad; value = value * 16U + (unsigned)digit; if( value > 0xffffU ) goto bad; ++p; ++count; } if( count == 0 || value == 0 || (value >= 0xd800U && value <= 0xdfffU) ) goto bad; c = (__mpu_char16_t)value; break; default: if( c >= '0' && c <= '7' ) { value = c - '0'; for( count = 1; count < 3 && p < length; ++count ) { c = text[p]; if( c < '0' || c > '7' ) break; value = value * 8U + (unsigned)(c - '0'); ++p; } if( value == 0 || value > 0xffffU || (value >= 0xd800U && value <= 0xdfffU) ) goto bad; c = (__mpu_char16_t)value; } break; } } if( mcpu_text_append_char(&decoded, c) != 0 ) goto fail; } bad: errno = EINVAL; fail: mcpu_text_free( &decoded ); return( -1 ); } static int handle_line( mcpp *cpp, const char *filename, unsigned line_number, unsigned next_physical_line, const __mpu_char16_t *line, size_t length, size_t p, char **nominal_filename, int64_t *line_delta ) { mcpu_macro_expansion_context context; mcpu_text expanded; const __mpu_char16_t *text; size_t text_length; size_t end; uintmax_t value = 0; char *new_filename = NULL; int parsed_filename; macro_context( cpp, *nominal_filename, line_number, &context ); mcpu_text_init( &expanded ); if( mcpu_macro_expand(&cpp->macros, line + p, length - p, current_language(cpp), &context, &expanded) != 0 ) goto fail; text = expanded.data; text_length = expanded.length; end = line_content_end( text, text_length ); p = 0; skip_space16( text, end, &p ); if( p >= end || text[p] < '0' || text[p] > '9' ) { fprintf( stderr, "%s:%u: error: invalid #line directive\n", filename, line_number ); goto fail; } while( p < end && text[p] >= '0' && text[p] <= '9' ) { unsigned digit = text[p++] - '0'; if( value > (UINT_MAX - digit) / 10U ) { fprintf( stderr, "%s:%u: error: line number out of range in #line directive\n", filename, line_number ); goto fail; } value = value * 10U + digit; } skip_space16( text, end, &p ); parsed_filename = parse_line_filename( text, end, &p, &new_filename ); if( parsed_filename < 0 ) { fprintf( stderr, "%s:%u: error: invalid filename in #line directive\n", filename, line_number ); goto fail; } skip_space16( text, end, &p ); if( p != end ) { fprintf( stderr, "%s:%u: error: garbage at end of #line directive\n", filename, line_number ); goto fail; } if( parsed_filename > 0 ) { free( *nominal_filename ); *nominal_filename = new_filename; new_filename = NULL; } *line_delta = (int64_t)value - (int64_t)next_physical_line; if( append_line_marker(cpp, (unsigned)value, *nominal_filename, 0) != 0 ) goto fail; mcpu_text_free( &expanded ); return( 0 ); fail: free( new_filename ); mcpu_text_free( &expanded ); return( -1 ); } static int process_file( mcpp *cpp, const char *filename, const mcpu_include_dir *include_dir, int system_header ); static int pragma_once_directive( const __mpu_char16_t *line, size_t length, size_t p ) { size_t end = line_content_end( line, length ); skip_space16( line, end, &p ); if( p + 4 > end || !mcpu_text_equal_ascii(line + p, 4, "once") ) return( 0 ); p += 4; skip_space16( line, end, &p ); return( p == end ); } static int handle_include( mcpp *cpp, const char *source_filename, const char *nominal_filename, unsigned line_number, unsigned resume_line_number, const __mpu_char16_t *line, size_t length, size_t p, int include_next, const mcpu_include_dir *current_dir, int source_is_system ) { char *requested = NULL; char *found = NULL; const mcpu_include_dir *found_dir = NULL; int quoted; int rc = -1; mcpu_macro_expansion_context context; macro_context( cpp, nominal_filename, line_number, &context ); if( parse_include_filename( line, length, p, &requested, "ed ) != 0 ) { mcpu_text expanded; mcpu_text_init( &expanded ); if( mcpu_macro_expand(&cpp->macros, line + p, length - p, current_language(cpp), &context, &expanded) != 0 || parse_include_filename(expanded.data, expanded.length, 0, &requested, "ed) != 0 ) { fprintf( stderr, "%s:%u: error: #%s does not expand to \"file\" or \n", nominal_filename, line_number, include_next ? "include_next" : "include" ); mcpu_text_free( &expanded ); goto done; } mcpu_text_free( &expanded ); } found = mcpu_include_find( &cpp->include_paths, source_filename, requested, quoted, current_language(cpp), include_next, include_next ? current_dir : NULL, &found_dir ); if( found == NULL ) { if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->print_deps_missing_files ) { int dependency_is_system = source_is_system || !quoted; if( dependency_add_unresolved(cpp, requested, dependency_is_system) != 0 ) goto done; if( cpp->verbose ) fprintf( stderr, "%s:%u: %s %s -> [%s]\n", nominal_filename, line_number, include_next ? "include_next" : "include", requested, mcpu_language_name(current_language(cpp)) ); rc = 0; goto done; } fprintf( stderr, "%s:%u: error: cannot find %s file '%s'\n", nominal_filename, line_number, include_next ? "include_next" : "include", requested ); goto done; } if( cpp->verbose ) fprintf( stderr, "%s:%u: %s %s -> %s [%s]\n", nominal_filename, line_number, include_next ? "include_next" : "include", requested, found, mcpu_language_name(current_language(cpp)) ); { int dependency_is_system = source_is_system || include_dir_is_system(found_dir); if( dependency_add(cpp, found, dependency_is_system) != 0 ) goto done; if( once_file_seen(cpp, found) ) { rc = 0; goto done; } if( sync_output_position(cpp, line_number, nominal_filename) != 0 || append_line_marker(cpp, 1, found, 1) != 0 ) goto done; if( process_file( cpp, found, found_dir, dependency_is_system ) != 0 ) goto done; } if( append_line_marker( cpp, resume_line_number, nominal_filename, 2 ) != 0 ) goto done; rc = 0; done: free( requested ); free( found ); return( rc ); } static int process_source( mcpp *cpp, mcpu_source *source, const mcpu_include_dir *include_dir, int source_is_system ) { mcpu_pp_lexer_state lexer; mcpu_text prepared; mcpu_text expanded; const __mpu_char16_t *line; size_t length; unsigned line_number; unsigned next_line_number; int spliced; size_t conditional_base; int status; mcpu_macro_expansion_context context; const char *filename = source->filename; char *nominal_filename; int64_t line_delta = 0; if( cpp->include_depth >= MCPU_CPP_INCLUDE_STACK_SIZE ) { fprintf( stderr, "%s: error: include nesting exceeds %d files\n", filename, MCPU_CPP_INCLUDE_STACK_SIZE ); return( -1 ); } nominal_filename = strdup( filename ); if( nominal_filename == NULL ) return( -1 ); ++cpp->include_depth; conditional_base = cpp->conditional_depth; mcpu_pp_lexer_init( &lexer ); mcpu_text_init( &prepared ); mcpu_text_init( &expanded ); while( (status = mcpu_source_next_line( source, &line, &length, &line_number, &next_line_number, &spliced )) > 0 ) { int64_t reported = (int64_t)line_number + line_delta; int64_t reported_next = (int64_t)next_line_number + line_delta; unsigned logical_line; unsigned logical_next_line; size_t hash = 0; int directive; if( reported < 0 || reported > UINT_MAX || reported_next < 0 || reported_next > UINT_MAX ) { fprintf( stderr, "%s:%u: error: logical line number out of range\n", filename, line_number ); goto fail; } logical_line = (unsigned)reported; logical_next_line = (unsigned)reported_next; mcpu_pp_lexer_set_diagnostics(&lexer, MCPP_OPTIONS(cpp), nominal_filename, logical_line, source->splice_offsets, source->splice_count); if( mcpu_pp_prepare_line(&lexer, line, length, current_language(cpp), &prepared) != 0 ) goto fail; line = prepared.data; length = prepared.length; if( !mcpu_source_valid_ucs2(line, length) ) { fprintf( stderr, "%s:%u: error: character outside UCS-2\n", nominal_filename, logical_line ); goto fail; } directive = mcpu_pp_find_directive( line, length, &hash ); if( directive < 0 ) goto fail; if( directive ) { size_t name_start; size_t name_length; size_t after_name; if( parse_directive_name( line, length, hash, &name_start, &name_length, &after_name ) ) { enum mcpp_conditional_directive conditional; conditional = conditional_directive( line + name_start, name_length ); if( conditional != MCPP_COND_NONE ) { if( handle_conditional(cpp, conditional, source->filename, line_number, line, length, after_name, conditional_base) != 0 ) goto fail; continue; } if( !conditional_active(cpp) ) continue; { int diagnostic_error; const char *diagnostic_name; if( diagnostic_directive(line + name_start, name_length, &diagnostic_error, &diagnostic_name) ) { if( handle_diagnostic(cpp, nominal_filename, logical_line, line, length, after_name, diagnostic_error, diagnostic_name) != 0 ) goto fail; continue; } } if( mcpu_text_equal_ascii(line + name_start, name_length, "line") ) { if( handle_line( cpp, nominal_filename, logical_line, next_line_number, line, length, after_name, &nominal_filename, &line_delta ) != 0 ) goto fail; continue; } if( mcpu_text_equal_ascii(line + name_start, name_length, "pragma") ) { if( pragma_once_directive(line, length, after_name) ) { if( once_file_mark(cpp, source->filename) != 0 ) { fprintf( stderr, "%s:%u: error: cannot record #pragma once: %s\n", nominal_filename, logical_line, strerror(errno) ); goto fail; } continue; } } if( mcpu_text_equal_ascii(line + name_start, name_length, "include") || mcpu_text_equal_ascii(line + name_start, name_length, "include_next") ) { int include_next = mcpu_text_equal_ascii( line + name_start, name_length, "include_next" ); if( handle_include( cpp, source->filename, nominal_filename, logical_line, logical_next_line, line, length, after_name, include_next, include_dir, source_is_system ) != 0 ) goto fail; continue; } if( mcpu_text_equal_ascii(line + name_start, name_length, "define") ) { if( handle_define( cpp, nominal_filename, logical_line, line, length, after_name, MCPU_MACRO_ORIGIN_SOURCE ) != 0 ) goto fail; if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->dump_macros == MCPP_DUMP_DEFINITIONS && append_visible_text(cpp, logical_line, nominal_filename, line, length) != 0 ) goto fail; continue; } if( mcpu_text_equal_ascii(line + name_start, name_length, "undef") ) { if( handle_undef( cpp, nominal_filename, logical_line, line, length, after_name ) != 0 ) goto fail; continue; } if( mcpu_text_equal_ascii(line + name_start, name_length, "lang") ) { { size_t output_start; if( sync_output_position(cpp, logical_line, nominal_filename) != 0 ) goto fail; output_start = cpp->output.length; if( handle_lang(cpp, nominal_filename, logical_line, line, length, after_name, spliced) != 0 ) goto fail; advance_output_position( cpp, cpp->output.data + output_start, cpp->output.length - output_start ); } continue; } if( mcpu_text_equal_ascii( line + name_start, name_length, "endlang" ) ) { { size_t output_start; if( sync_output_position(cpp, logical_line, nominal_filename) != 0 ) goto fail; output_start = cpp->output.length; if( handle_endlang(cpp, nominal_filename, logical_line, line, length) != 0 ) goto fail; advance_output_position( cpp, cpp->output.data + output_start, cpp->output.length - output_start ); } continue; } } } if( !conditional_active(cpp) ) continue; macro_context( cpp, nominal_filename, logical_line, &context ); if( mcpu_macro_expand(&cpp->macros, line, length, current_language(cpp), &context, &expanded) != 0 || append_visible_text(cpp, logical_line, nominal_filename, expanded.data, expanded.length) != 0 ) goto fail; } if( status < 0 ) goto fail; if( lexer.in_block_comment ) { fprintf( stderr, "%s: error: unterminated comment at end of file\n", nominal_filename ); goto fail; } if( cpp->conditional_depth != conditional_base ) { mcpp_conditional *frame = &cpp->conditional_stack[cpp->conditional_depth - 1]; fprintf( stderr, "%s:%u: error: unterminated conditional directive\n", frame->filename, frame->line_number ); goto fail; } --cpp->include_depth; mcpu_text_free( &prepared ); mcpu_text_free( &expanded ); free( nominal_filename ); return( 0 ); fail: cpp->conditional_depth = conditional_base; --cpp->include_depth; mcpu_text_free( &prepared ); mcpu_text_free( &expanded ); free( nominal_filename ); return( -1 ); } static int process_file( mcpp *cpp, const char *filename, const mcpu_include_dir *include_dir, int system_header ) { mcpu_source source; int rc; mcpu_source_init( &source ); if( mcpu_source_open( &source, filename ) != 0 ) { fprintf( stderr, "%s: %s\n", filename, strerror(errno) ); mcpu_source_free( &source ); return( -1 ); } rc = process_source( cpp, &source, include_dir, system_header ); mcpu_source_free( &source ); return( rc ); } static int process_stream( mcpp *cpp, FILE *stream, const char *name ) { mcpu_source source; int rc; mcpu_source_init( &source ); if( mcpu_source_open_stream( &source, stream, name ) != 0 ) { fprintf( stderr, "%s: %s\n", name, strerror(errno) ); mcpu_source_free( &source ); return( -1 ); } rc = process_source( cpp, &source, NULL, 0 ); mcpu_source_free( &source ); return( rc ); } void mcpp_init( mcpp *cpp ) { if( cpp == NULL ) return; cpp->options_data = NULL; mcpu_config_init( &cpp->config ); mcpp_runtime_paths_init( &cpp->runtime_paths ); mcpu_include_paths_init( &cpp->include_paths ); cpp->lang_depth = 1; cpp->lang_stack[0] = MCPU_LANG_0; cpp->include_depth = 0; cpp->conditional_depth = 0; cpp->base_filename = NULL; cpp->preprocess_date[0] = 0; cpp->preprocess_time[0] = 0; cpp->builtins_initialized = 0; cpp->command_line_macros_applied = 0; mcpu_macro_table_init( &cpp->macros ); cpp->once_files = NULL; cpp->dependencies_first = NULL; cpp->dependencies_last = NULL; cpp->verbose = 0; cpp->output_filename = NULL; cpp->output_line = 0; mcpu_text_init( &cpp->output ); } void mcpp_free( mcpp *cpp ) { if( cpp == NULL ) return; mcpu_config_free( &cpp->config ); mcpp_runtime_paths_free( &cpp->runtime_paths ); mcpu_include_paths_free( &cpp->include_paths ); free( cpp->base_filename ); cpp->base_filename = NULL; mcpu_macro_table_free( &cpp->macros ); once_files_free( cpp ); dependencies_free( cpp ); free( cpp->output_filename ); cpp->output_filename = NULL; cpp->output_line = 0; mcpu_text_free( &cpp->output ); } int mcpp_load_config( mcpp *cpp, const char *explicit_filename, int no_config ) { const char *home; if( cpp == NULL ) return( -1 ); /* * The physical MCPU runtime root is not a configuration-file value. The * sibling /include directory is the lowest-priority default and * therefore remains active even when --no-config suppresses all * configuration-file reads. A subsequently read configuration file may * replace it, including with an empty value that disables the configured * system include tree. */ if( mcpu_config_set( &cpp->config, "MCPU_CPP_SYSTEM_INCLUDE_PATH", cpp->runtime_paths.system_include_path ) != 0 ) return( -1 ); if( no_config ) return( 0 ); if( explicit_filename ) { if( mcpu_config_read( &cpp->config, explicit_filename, 1 ) != 0 ) { fprintf( stderr, "%s: cannot read configuration: %s\n", explicit_filename, strerror(errno) ); return( -1 ); } } else { char *user = NULL; if( cpp->runtime_paths.config_file == NULL || mcpu_config_read( &cpp->config, cpp->runtime_paths.config_file, 0 ) != 0 ) return( -1 ); if( mcpu_config_read( &cpp->config, MCPU_CPP_SYSTEM_CONFIG_FILE, 0 ) != 0 ) return( -1 ); home = getenv( "HOME" ); if( home && *home ) { size_t n = strlen(home) + sizeof("/.mcpu/etc/mcpu-cpp.conf"); user = (char *)malloc( n ); if( user == NULL ) return( -1 ); snprintf( user, n, "%s/.mcpu/etc/mcpu-cpp.conf", home ); if( mcpu_config_read( &cpp->config, user, 0 ) != 0 ) { free( user ); return( -1 ); } free( user ); } } return( 0 ); } int mcpp_prepare( mcpp *cpp ) { if( cpp == NULL ) { errno = EINVAL; return( -1 ); } if( cpp->preprocess_date[0] == 0 && initialize_timestamp(cpp) != 0 ) return( -1 ); if( initialize_builtins(cpp) != 0 ) return( -1 ); return( apply_command_line_macros(cpp) ); } static void restore_output_length( mcpp *cpp, size_t length ) { if( cpp == NULL ) return; cpp->output.length = length; if( cpp->output.data != NULL ) cpp->output.data[length] = 0; } static int process_forced_file( mcpp *cpp, const mcpp_option_action *action, int discard_output ) { const mcpu_include_dir *found_dir = NULL; char *found; char *saved_output_filename = NULL; size_t output_length; unsigned saved_output_line = 0; int system_header; int rc = -1; if( cpp == NULL || action == NULL ) { errno = EINVAL; return( -1 ); } /* * GNU-style command-line forced files are resolved relative to the current * working directory first. Passing "." as the synthetic containing source * gives mcpu_include_find() exactly that quoted-include first step, followed * by the ordinary configured include chain. The primary input directory is * deliberately not involved. */ found = mcpu_include_find( &cpp->include_paths, ".", action->argument, 1, current_language(cpp), 0, NULL, &found_dir ); if( found == NULL ) { const char *progname = MCPP_OPTIONS(cpp) != NULL ? MCPP_OPTIONS(cpp)->progname : "mcpu-cpp"; if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->print_deps_missing_files ) { /* * GNU -MG also tolerates missing command-line -include/-imacros files. * With no successful search there is no physical search provenance, so * the command-line operand enters the unresolved registry as a user * dependency exactly as written. */ if( dependency_add_unresolved(cpp, action->argument, 0) != 0 ) return( -1 ); if( cpp->verbose ) fprintf( stderr, "%s %s -> [%s]\n", action->option, action->argument, mcpu_language_name(current_language(cpp)) ); return( 0 ); } fprintf( stderr, "%s: error: %s cannot find file '%s'\n", progname, action->option, action->argument ); return( -1 ); } system_header = include_dir_is_system( found_dir ); if( dependency_add(cpp, found, system_header) != 0 ) goto done; if( once_file_seen(cpp, found) ) { rc = 0; goto done; } output_length = cpp->output.length; if( discard_output && cpp->output_filename != NULL ) { saved_output_filename = strdup( cpp->output_filename ); if( saved_output_filename == NULL ) goto done; saved_output_line = cpp->output_line; } if( cpp->verbose ) fprintf( stderr, "%s %s -> %s [%s]\n", action->option, action->argument, found, mcpu_language_name(current_language(cpp)) ); if( append_line_marker(cpp, 1, found, 1) != 0 ) goto forced_fail; /* * A forced file is conceptually included by the primary source before its * first line. Reserve the primary-source frame so __INCLUDE_LEVEL__ is 1 * in the forced file and increases normally in headers included from it. */ if( cpp->include_depth >= MCPU_CPP_INCLUDE_STACK_SIZE ) { fprintf( stderr, "%s: error: include nesting exceeds %d files\n", found, MCPU_CPP_INCLUDE_STACK_SIZE ); goto forced_fail; } ++cpp->include_depth; rc = process_file( cpp, found, found_dir, system_header ); --cpp->include_depth; if( rc != 0 ) goto forced_fail; if( discard_output ) { restore_output_length( cpp, output_length ); if( saved_output_filename != NULL && set_output_position(cpp, saved_output_line, saved_output_filename) != 0 ) goto done; } else if( append_line_marker(cpp, 1, cpp->base_filename, 2) != 0 ) { rc = -1; goto done; } rc = 0; goto done; forced_fail: if( discard_output ) { restore_output_length( cpp, output_length ); if( saved_output_filename != NULL ) (void)set_output_position(cpp, saved_output_line, saved_output_filename); } rc = -1; done: free( saved_output_filename ); free( found ); return( rc ); } static int process_forced_files( mcpp *cpp ) { size_t i; int pass; if( cpp == NULL || MCPP_OPTIONS(cpp) == NULL ) return( 0 ); /* * Command-line -D/-U actions have already been applied by mcpp_prepare(). * Process every -imacros file before every -include file while preserving * command-line order inside each class. */ for( pass = 0; pass < 2; ++pass ) { enum mcpp_option_action_kind kind = pass == 0 ? MCPP_ACTION_IMACROS : MCPP_ACTION_INCLUDE; for( i = 0; i < MCPP_OPTIONS(cpp)->action_count; ++i ) { const mcpp_option_action *action = &MCPP_OPTIONS(cpp)->actions[i]; if( action->kind != kind ) continue; if( process_forced_file(cpp, action, kind == MCPP_ACTION_IMACROS) != 0 ) return( -1 ); } } return( 0 ); } int mcpp_dump_macros( mcpp *cpp, FILE *stream ) { if( cpp == NULL || stream == NULL ) { errno = EINVAL; return( -1 ); } if( mcpp_prepare(cpp) != 0 ) return( -1 ); return( mcpu_macro_dump(&cpp->macros, stream, MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->dump_only_plus_predefined) ); } int mcpp_process( mcpp *cpp, const char *filename ) { if( cpp == NULL || filename == NULL ) { errno = EINVAL; return( -1 ); } free( cpp->base_filename ); cpp->base_filename = strdup( filename ); if( cpp->base_filename == NULL || mcpp_prepare(cpp) != 0 ) return( -1 ); if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->dump_macros == MCPP_DUMP_DEFINITIONS ) { const char *marker = ""; if( append_line_marker(cpp, 0, filename, 0) != 0 || mcpu_macro_dump_text(&cpp->macros, &cpp->output, marker) != 0 ) return( -1 ); } if( dependency_add(cpp, filename, 0) != 0 ) return( -1 ); if( append_line_marker(cpp, 1, filename, 0) != 0 ) return( -1 ); if( process_forced_files(cpp) != 0 ) return( -1 ); if( process_file( cpp, filename, NULL, 0 ) != 0 ) return( -1 ); if( cpp->lang_depth != 1 ) { fprintf( stderr, "%s: error: unbalanced #lang/#endlang at end of translation unit (depth %lu)\n", filename, (unsigned long)cpp->lang_depth ); return( -1 ); } return( 0 ); } int mcpp_process_stream( mcpp *cpp, FILE *stream, const char *name ) { if( cpp == NULL || stream == NULL || name == NULL ) { errno = EINVAL; return( -1 ); } free( cpp->base_filename ); cpp->base_filename = strdup( name ); if( cpp->base_filename == NULL || mcpp_prepare(cpp) != 0 ) return( -1 ); if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->dump_macros == MCPP_DUMP_DEFINITIONS ) { const char *marker = ""; if( append_line_marker(cpp, 0, name, 0) != 0 || mcpu_macro_dump_text(&cpp->macros, &cpp->output, marker) != 0 ) return( -1 ); } if( dependency_add(cpp, strcmp(name, "") == 0 ? "-" : name, 0) != 0 ) return( -1 ); if( append_line_marker(cpp, 1, name, 0) != 0 ) return( -1 ); if( process_forced_files(cpp) != 0 ) return( -1 ); if( process_stream(cpp, stream, name) != 0 ) return( -1 ); if( cpp->lang_depth != 1 ) { fprintf( stderr, "%s: error: unbalanced #lang/#endlang at end of translation unit (depth %lu)\n", name, (unsigned long)cpp->lang_depth ); return( -1 ); } return( 0 ); } static int write_make_escaped( FILE *stream, const char *text ) { const unsigned char *p = (const unsigned char *)text; while( *p ) { if( *p == '$' ) { if( fputs("$$", stream) == EOF ) return( -1 ); } else { if( (*p == ' ' || *p == '\t' || *p == '#' || *p == '\\') && fputc('\\', stream) == EOF ) return( -1 ); if( fputc(*p, stream) == EOF ) return( -1 ); } ++p; } return( 0 ); } static int write_make_target_quoted( FILE *stream, const char *text ) { const unsigned char *p = (const unsigned char *)text; size_t slashes = 0; while( *p ) { unsigned char c = *p++; if( c == '\\' ) { if( fputc(c, stream) == EOF ) return( -1 ); ++slashes; continue; } if( c == ' ' || c == '\t' ) { size_t i; for( i = 0; i < slashes; ++i ) if( fputc('\\', stream) == EOF ) return( -1 ); if( fputc('\\', stream) == EOF ) return( -1 ); } else if( c == '#' ) { if( fputc('\\', stream) == EOF ) return( -1 ); } else if( c == '$' ) { if( fputc('$', stream) == EOF ) return( -1 ); } slashes = 0; if( fputc(c, stream) == EOF ) return( -1 ); } return( 0 ); } static char * default_dependency_target( const mcpp *cpp ) { const char *base; const char *dot; const char *suffix = ".o"; size_t stem_length; size_t suffix_length; char *target; if( cpp == NULL || cpp->base_filename == NULL ) return( NULL ); if( strcmp(cpp->base_filename, "") == 0 || strcmp(cpp->base_filename, "-") == 0 ) return( strdup("-") ); base = strrchr( cpp->base_filename, '/' ); base = base == NULL ? cpp->base_filename : base + 1; dot = strrchr( base, '.' ); stem_length = dot != NULL && dot != base ? (size_t)(dot - base) : strlen(base); if( MCPP_OPTIONS(cpp) != NULL && MCPP_OPTIONS(cpp)->object_suffix != NULL ) suffix = MCPP_OPTIONS(cpp)->object_suffix; suffix_length = strlen( suffix ); target = (char *)malloc( stem_length + suffix_length + 1 ); if( target == NULL ) return( NULL ); memcpy( target, base, stem_length ); memcpy( target + stem_length, suffix, suffix_length + 1 ); return( target ); } static int write_dependency_targets( mcpp *cpp, FILE *stream ) { char *target; size_t i; int pass; int written = 0; if( cpp == NULL || stream == NULL ) { errno = EINVAL; return( -1 ); } if( MCPP_OPTIONS(cpp) != NULL ) for( pass = 0; pass < 2; ++pass ) { enum mcpp_option_action_kind kind = pass == 0 ? MCPP_ACTION_DEP_TARGET : MCPP_ACTION_DEP_TARGET_QUOTED; for( i = 0; i < MCPP_OPTIONS(cpp)->action_count; ++i ) { const mcpp_option_action *action = &MCPP_OPTIONS(cpp)->actions[i]; if( action->kind != kind ) continue; if( written && fputc(' ', stream) == EOF ) return( -1 ); if( kind == MCPP_ACTION_DEP_TARGET ) { if( fputs(action->argument, stream) == EOF ) return( -1 ); } else if( write_make_target_quoted(stream, action->argument) != 0 ) return( -1 ); written = 1; } } if( written ) return( 0 ); target = default_dependency_target( cpp ); if( target == NULL ) return( -1 ); if( write_make_target_quoted(stream, target) != 0 ) { free( target ); return( -1 ); } free( target ); return( 0 ); } int mcpp_write_dependencies( mcpp *cpp, FILE *stream, int include_system ) { mcpp_dependency *p; if( cpp == NULL || stream == NULL ) { errno = EINVAL; return( -1 ); } if( write_dependency_targets(cpp, stream) != 0 || fputs(":", stream) == EOF ) return( -1 ); for( p = cpp->dependencies_first; p != NULL; p = p->next ) { if( !include_system && p->system_only ) continue; if( fputc(' ', stream) == EOF || write_make_escaped(stream, p->path) != 0 ) return( -1 ); } if( fputc('\n', stream) == EOF ) return( -1 ); return( ferror(stream) ? -1 : 0 ); } int mcpp_write_output( mcpp *cpp, const char *filename ) { char *utf8; FILE *fp = stdout; int close_fp = 0; size_t n; if( cpp == NULL ) return( -1 ); utf8 = mcpu_text_to_utf8( cpp->output.data, cpp->output.length ); if( utf8 == NULL ) return( -1 ); if( filename ) { fp = fopen( filename, "wb" ); if( fp == NULL ) { free( utf8 ); return( -1 ); } close_fp = 1; } n = strlen( utf8 ); if( fwrite( utf8, 1, n, fp ) != n ) { if( close_fp ) fclose( fp ); free( utf8 ); return( -1 ); } if( close_fp && fclose(fp) != 0 ) { free( utf8 ); return( -1 ); } free( utf8 ); return( 0 ); }