c-preprocessor/pp.c

577 lines
11 KiB
C

// SPDX-License-Identifier: BSD-2
#include "pp.h"
#include "pp_defines.h"
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#define DEFINE_LINE_START "#define"
#define DEFINE_LINE_START_SZ (sizeof(DEFINE_LINE_START) - 1)
#define DELIMS "\t []{}<>=+-*/%!&|^.,:;()\\"
#define TEMP_PRECOMPILED_FILE "temp.c"
int pp_errno = PP_OK;
int _is_define_line(char *line)
{
return !strncmp(line, DEFINE_LINE_START, DEFINE_LINE_START_SZ);
}
int _get_next_whitespace_index(char *str)
{
int c;
c = 0;
while (!isspace(*str)) {
str++;
c++;
}
return c;
}
void _strip_extra_spaces_in_define(char *str)
{
int i;
int x;
int inside_quotes;
inside_quotes = 0;
for (i = x = 0; str[i]; i++) {
if (i > 0 && str[i] == '\"' && str[i - 1] != '\\' && !inside_quotes)
inside_quotes = 1;
else if (i > 0 && str[i] == '\"' && str[i - 1] != '\\' && inside_quotes)
inside_quotes = 0;
if (!inside_quotes && (!isspace(str[i]) || (i > 0 && !isspace(str[i - 1]))))
str[x++] = str[i];
else if (inside_quotes)
x++;
}
str[x] = 0x00;
}
char *_skip_word(char *str)
{
char *s;
s = str;
while (!isspace(*s++));
return s;
}
char *_get_symbol(char *define_line)
{
char *symbol;
int symbol_end_index;
define_line = _skip_word(define_line);
if (!(*define_line)) {
pp_errno = PP_BAD_DEFINE_SYMBOL;
return NULL;
}
symbol_end_index = _get_next_whitespace_index(define_line);
symbol = calloc(symbol_end_index + 1, sizeof(char));
if (!symbol) {
pp_errno = PP_OMEM;
return NULL;
}
strncpy(symbol, define_line, symbol_end_index);
return symbol;
}
char *_get_mapping(char *define_line)
{
char *mapping;
define_line = _skip_word(define_line);
if (!(*define_line)) {
pp_errno = PP_BAD_DEFINE_SYMBOL;
return NULL;
}
define_line = _skip_word(define_line);
if (!(*define_line)) {
pp_errno = PP_BAD_DEFINE_MAPPING;
return NULL;
}
mapping = calloc(strlen(define_line), sizeof(char));
if (!mapping) {
pp_errno = PP_OMEM;
return NULL;
}
strncpy(mapping, define_line, strlen(define_line) - 1);
return mapping;
}
int _add_symbol_mapping_to_hashmap(string_map_t *map, char *define_line)
{
int ret;
char *symbol;
char *mapping;
symbol = _get_symbol(define_line);
if (!symbol)
return PP_FAILED;
mapping = _get_mapping(define_line);
if (!mapping)
return PP_FAILED;
ret = hashmap_put(map, symbol, mapping);
if (ret == MAP_OMEM) {
pp_errno = PP_OMEM;
return PP_FAILED;
}
return PP_OK;
}
int _concat_str(char **str1, char *str2)
{
char *result;
char *temp;
int needed;
needed = strlen(*str1) + strlen(str2) + 1;
result = calloc(needed, sizeof(char));
if (!result) {
pp_errno = PP_OMEM;
return PP_FAILED;
}
sprintf(result, "%s%s", *str1, str2);
temp = *str1;
*str1 = result;
free(temp);
return PP_OK;
}
int _collect_multiline_define(string_map_t *map, char *define_line, FILE *f)
{
char *define_multiline;
char *line;
size_t len;
ssize_t read;
int ret;
_strip_extra_spaces_in_define(define_line);
if (define_line[strlen(define_line) - 2] != '\\') {
ret = _add_symbol_mapping_to_hashmap(map, define_line);
} else {
define_multiline = strdup(define_line);
if (!define_multiline)
return PP_FAILED;
line = NULL;
len = 0;
while ((read = getline(&line, &len, f)) != -1) {
_strip_extra_spaces_in_define(line);
define_multiline[strlen(define_multiline) - 2] = 0x00;
_concat_str(&define_multiline, line);
if (line[strlen(line) - 2] != '\\')
break;
}
free(line);
ret = _add_symbol_mapping_to_hashmap(map, define_multiline);
free(define_multiline);
}
return ret;
}
int pp_collect_defines(string_map_t map, FILE *f)
{
char *line;
size_t len;
ssize_t read;
int ret;
ret = PP_OK;
line = NULL;
len = 0;
while ((read = getline(&line, &len, f)) != -1) {
if (_is_define_line(line)) {
ret = _collect_multiline_define(map, line, f);
if (ret == PP_FAILED)
goto free_resources_and_exit;
}
}
free_resources_and_exit:
free(line);
return ret;
}
int _is_inside_multiline_define(char *curr_line)
{
static int is_inside;
if (_is_define_line(curr_line))
is_inside = 1;
if (is_inside && curr_line[strlen(curr_line) - 2] != '\\')
is_inside = 0;
return is_inside;
}
int _can_skip_multiline_define(int is_inside, int prev_is_inside)
{
return !(is_inside || (!is_inside && prev_is_inside));
}
int pp_create_file_without_defines(FILE *in, FILE *out)
{
char *line;
size_t len;
ssize_t read;
char *stripped_line;
int is_inside_multiline_define;
int prev_is_inside_multiline_define;
int fwritten_bytes;
int ret;
ret = PP_OK;
line = NULL;
len = 0;
is_inside_multiline_define = 0;
prev_is_inside_multiline_define = 0;
while ((read = getline(&line, &len, in)) != -1) {
stripped_line = strdup(line);
if (!stripped_line) {
pp_errno = PP_OMEM;
ret = PP_FAILED;
goto free_resources_and_exit;
}
prev_is_inside_multiline_define = is_inside_multiline_define;
is_inside_multiline_define = _is_inside_multiline_define(stripped_line);
free(stripped_line);
if (!_is_define_line(line)) {
if (_can_skip_multiline_define(is_inside_multiline_define, prev_is_inside_multiline_define)) {
fwritten_bytes = fprintf(out, "%s", line);
if (fwritten_bytes < 0 || fwritten_bytes != read) {
pp_errno = PP_BAD_FILE_OP;
ret = PP_FAILED;
goto free_resources_and_exit;
}
}
}
}
free_resources_and_exit:
free(line);
return ret;
}
char *_get_expanded_token(string_map_t map, char *token)
{
char *expanded;
char *expanded_copy;
int res;
int size;
res = hashmap_get(map, token, &expanded);
if (res != MAP_OK) {
size = strlen(token);
expanded_copy = calloc(size + 1, sizeof(char));
if (!expanded_copy) {
pp_errno = PP_OMEM;
return NULL;
}
strcpy(expanded_copy, token);
} else {
expanded_copy = strdup(expanded);
if (!expanded_copy) {
pp_errno = PP_OMEM;
return NULL;
}
}
return expanded_copy;
}
int _concat_delim_to_line(char *delim, int delim_size, char **line)
{
char *d_delim;
char res;
d_delim = calloc(delim_size + 1, sizeof(char));
if (!d_delim) {
pp_errno = PP_OMEM;
return PP_FAILED;
}
strncpy(d_delim, delim, delim_size);
res = _concat_str(line, d_delim);
free(d_delim);
return res;
}
int _concat_expanded_token_to_line(string_map_t map, char *token, char **line)
{
char *expanded;
int res;
expanded = _get_expanded_token(map, token);
if (!expanded) {
pp_errno = PP_OMEM;
return PP_FAILED;
}
res = _concat_str(line, expanded);
free(expanded);
return res;
}
int _is_delim_at_str_start(char *token, char *str_start)
{
return token - str_start;
}
int _compute_delim_start(char *token, char *line)
{
return token - line + strlen(token);
}
int _compute_delim_end(char *token, char *line, char *line_copy)
{
return token != NULL ? token - line : (int) strlen(line_copy);
}
char *_compute_expanded_line(string_map_t map, char *line)
{
char *int_line;
char *line_start;
char *line_copy;
char *token;
char *expanded_line;
int delim_start;
int delim_end;
int res;
int_line = strdup(line);
if (!int_line) {
pp_errno = PP_OMEM;
return NULL;
}
line_start = int_line;
line_copy = strdup(int_line);
if (!line_copy) {
pp_errno = PP_OMEM;
return NULL;
}
expanded_line = calloc(1, sizeof(char));
if (!expanded_line) {
pp_errno = PP_OMEM;
return NULL;
}
token = strtok(int_line, DELIMS);
if (_is_delim_at_str_start(token, line_start)) {
res = _concat_delim_to_line(line_start, token - line_start, &expanded_line);
if (res == PP_FAILED)
return NULL;
}
res = _concat_expanded_token_to_line(map, token, &expanded_line);
if (res == PP_FAILED)
return NULL;
while (token) {
delim_start = _compute_delim_start(token, int_line);
token = strtok(NULL, DELIMS);
delim_end = _compute_delim_end(token, int_line, line_copy);
res = _concat_delim_to_line(line_copy + delim_start, delim_end - delim_start, &expanded_line);
if (res == PP_FAILED)
return NULL;
if (token) {
_concat_expanded_token_to_line(map, token, &expanded_line);
if (res == PP_FAILED)
return NULL;
} else {
break;
}
}
free(line_copy);
free(int_line);
return expanded_line;
}
char *_get_expanded_line(string_map_t map, char *line)
{
char *expanded_line;
char *prev_expanded_line;
expanded_line = _compute_expanded_line(map, line);
if (!expanded_line)
return NULL;
prev_expanded_line = strdup(line);
if (!prev_expanded_line) {
pp_errno = PP_OMEM;
return NULL;
}
while (strcmp(prev_expanded_line, expanded_line)) {
free(prev_expanded_line);
prev_expanded_line = expanded_line;
expanded_line = _compute_expanded_line(map, prev_expanded_line);
if (!expanded_line)
return NULL;
}
free(prev_expanded_line);
return expanded_line;
}
int _write_expanded_line(string_map_t map, char *line, FILE *out)
{
char *expanded_line;
int line_size;
int res;
expanded_line = _get_expanded_line(map, line);
if (!expanded_line)
return PP_FAILED;
line_size = strlen(expanded_line);
res = fprintf(out, "%s", expanded_line);
if (res < 0) {
pp_errno = PP_BAD_FILE_OP;
return PP_FAILED;
}
free(expanded_line);
return res == line_size ? PP_OK : PP_FAILED;
}
int pp_write_expanded_defines_to_file(string_map_t map, FILE *in, FILE *out)
{
char *line;
size_t len;
ssize_t read;
int ret;
ret = PP_OK;
line = NULL;
len = 0;
while ((read = getline(&line, &len, in)) != -1) {
ret = _write_expanded_line(map, line, out);
if (ret == PP_FAILED)
goto free_resources_and_exit;
}
free_resources_and_exit:
free(line);
return ret;
}
char *pp_strerr(int pp_errno)
{
switch (pp_errno) {
case PP_BAD_DEFINE_SYMBOL:
return "bad define symbol";
case PP_BAD_DEFINE_MAPPING:
return "bad define mapping";
default:
return strerror(errno);
}
}
int pp_expand_defines(string_map_t define_map, FILE *source, FILE *output)
{
int ret;
FILE *temp_precompiled;
ret = pp_collect_defines(define_map, source);
if (ret != PP_OK) {
puts("Preprocessor failed while collecting defines");
return ret;
}
fseek(source, 0, SEEK_SET);
temp_precompiled = fopen(TEMP_PRECOMPILED_FILE, "w+");
if (!temp_precompiled) {
puts("Preprocessor failed while opening temp precompiled file");
return PP_FAILED;
}
ret = pp_create_file_without_defines(source, temp_precompiled);
if (ret != PP_OK) {
puts("Preprocessor failed while writing temp precompiled file");
goto close_file_and_exit;
}
fseek(temp_precompiled, 0, SEEK_SET);
ret = pp_write_expanded_defines_to_file(define_map, temp_precompiled, output);
if (ret != PP_OK) {
puts("Preprocessor failed while writing output file");
goto close_file_and_exit;
}
fclose(temp_precompiled);
remove(TEMP_PRECOMPILED_FILE);
return PP_OK;
close_file_and_exit:
fclose(temp_precompiled);
return ret;
}