commit version 0.1
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
set(pwd ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(SOURCE
|
||||
${SOURCE}
|
||||
${pwd}/main.c
|
||||
${pwd}/nhtml_string.c
|
||||
${pwd}/attribute.c
|
||||
${pwd}/html.c
|
||||
${pwd}/includes.c
|
||||
PARENT_SCOPE)
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* attribute.c
|
||||
*
|
||||
* Created on: 08.08.2017
|
||||
* Author: julian
|
||||
*/
|
||||
|
||||
#include "attribute.h"
|
||||
#include <string.h> // needs: strcmp
|
||||
#include <stdlib.h> // needs: realloc
|
||||
|
||||
// linear search in set
|
||||
// returns NULL on failure to find entry
|
||||
attr_t * attr_set_find(attr_set_t *set, const char * name) {
|
||||
size_t current = 0;
|
||||
|
||||
for (; current != set->len; ++current) {
|
||||
if (strcmp(set->arr[current].name.c_str, name) == 0) {
|
||||
return set->arr + current;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Append new Attribute to set
|
||||
int attr_set_append(attr_set_t * set, attr_t *new_entry) {
|
||||
if (new_entry->name.c_str== NULL) return -1; // reject empty entries
|
||||
|
||||
// search first
|
||||
attr_t * new_ptr = attr_set_find(set, new_entry->name.c_str);
|
||||
if (new_ptr != NULL) {
|
||||
// already contained in set
|
||||
// concatenate values
|
||||
//new_ptr->value = string_copy(new_entry->value);
|
||||
string_append(&new_ptr->value, ' ');
|
||||
string_concat(&new_ptr->value, &new_entry->value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
new_ptr = realloc(set->arr, (set->len+1)*sizeof(attr_t));
|
||||
if (new_ptr == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// append and quit
|
||||
set->arr = new_ptr;
|
||||
set->arr[set->len] = attr_copy(new_entry);
|
||||
set->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
attr_t attr_copy(attr_t * attr) {
|
||||
attr_t temp;
|
||||
temp.name = string_copy(attr->name);
|
||||
temp.value = string_copy(attr->value);
|
||||
return temp;
|
||||
}
|
||||
|
||||
void attr_destroy(attr_t * attr) {
|
||||
// clear memory
|
||||
string_destroy(&attr->name);
|
||||
string_destroy(&attr->value);
|
||||
}
|
||||
|
||||
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* html.c
|
||||
*
|
||||
* Created on: 07.08.2017
|
||||
* Author: julian
|
||||
*/
|
||||
|
||||
#include "html.h"
|
||||
|
||||
int html_escape(int c, FILE * output) {
|
||||
switch(c) {
|
||||
case '<':
|
||||
fprintf(output, "<");
|
||||
goto escaped;
|
||||
case '>':
|
||||
fprintf(output, ">");
|
||||
goto escaped;
|
||||
case '&':
|
||||
fprintf(output, "&");
|
||||
goto escaped;
|
||||
case '"':
|
||||
fprintf(output, """);
|
||||
goto escaped;
|
||||
/*case ' ':
|
||||
fprintf(output, " ");
|
||||
goto escaped;*/
|
||||
case '\n':
|
||||
fprintf(output, "<br/>");
|
||||
goto escaped;
|
||||
}
|
||||
|
||||
return 0;
|
||||
escaped:
|
||||
return 1;
|
||||
}
|
||||
|
||||
// write out node
|
||||
void open_node(node_t * node, FILE * output) {
|
||||
// check for empty node (text mostly)
|
||||
if (!node->name.c_str) return;
|
||||
|
||||
// start by writing tag
|
||||
fputc('<', output);
|
||||
|
||||
// follow with tag name
|
||||
fputs(node->name.c_str,output);
|
||||
|
||||
// add attributes
|
||||
size_t i = 0;
|
||||
attr_t * current = node->attributes.arr;
|
||||
for (; i < node->attributes.len; i++,current++) {
|
||||
if (current->value.c_str)
|
||||
fprintf(output, " %s=\"%s\"", current->name.c_str, current->value.c_str);
|
||||
else {
|
||||
fputc(' ', output);
|
||||
fputs(current->name.c_str, output);
|
||||
}
|
||||
}
|
||||
|
||||
// close tag
|
||||
fputc('>', output);
|
||||
}
|
||||
|
||||
void close_node(node_t * node, FILE * output) {
|
||||
if (node->name.c_str) {
|
||||
fprintf(output, "</%s>", node->name.c_str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* includes.c
|
||||
*
|
||||
* Created on: 08.08.2017
|
||||
* Author: julian
|
||||
*/
|
||||
|
||||
#include <includes.h>
|
||||
#include <parser.h>
|
||||
#include <nhtml_string.h>
|
||||
#include <stdlib.h> // needs: malloc, realloc
|
||||
#include <memory.h> // needs: memcpy
|
||||
|
||||
string_t* include_paths;
|
||||
size_t num_includes = 0;
|
||||
|
||||
void include_paths_init() {
|
||||
include_paths = malloc(sizeof(string_t));
|
||||
num_includes = 1;
|
||||
|
||||
include_paths[0] = string_from_cstr("."); // search in current dir by default
|
||||
}
|
||||
|
||||
const char * extension(const string_t * path) {
|
||||
int i;
|
||||
for (i = path->len; i > 0; i--) {
|
||||
if (path->c_str[i] == '/')
|
||||
return NULL;
|
||||
if (path->c_str[i] == '.')
|
||||
break;
|
||||
}
|
||||
|
||||
return path->c_str + i;
|
||||
}
|
||||
|
||||
void include_add_path(const char * path) {
|
||||
if (num_includes == 0) {
|
||||
include_paths_init();
|
||||
}
|
||||
|
||||
include_paths = realloc(include_paths, sizeof(const char*)*(num_includes+1));
|
||||
include_paths[num_includes] = string_from_cstr(path);
|
||||
num_includes++;
|
||||
}
|
||||
|
||||
int include_file(string_t *filename, FILE * output) {
|
||||
FILE * input = NULL;
|
||||
char * tempPath = NULL;
|
||||
|
||||
if(num_includes == 0) {
|
||||
include_paths_init();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_includes; ++i) {
|
||||
// actual length is length + 2 (2* end char)
|
||||
tempPath = malloc(filename->len + include_paths[i].len);
|
||||
if (tempPath == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Concatenate the two paths
|
||||
// adding a safety slash to separate the two
|
||||
memcpy(tempPath, include_paths[i].c_str, include_paths[i].len-1);
|
||||
tempPath[include_paths->len-1] = '/';
|
||||
memcpy(tempPath + include_paths->len, filename->c_str, filename->len);
|
||||
|
||||
input = fopen(tempPath, "r");
|
||||
if (input != NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (input == NULL) {
|
||||
return 0;
|
||||
}
|
||||
const char * ext = extension(filename);
|
||||
#ifdef DEBUG
|
||||
printf("include %s, ext: %s\n", filename->c_str, ext);
|
||||
#endif
|
||||
|
||||
if (ext && !strcmp(ext, ".nhtml")) {
|
||||
// (re)parse whole file
|
||||
int current = strip(input);
|
||||
while((current = parse_node(current, input, output)) != EOF);
|
||||
} else {
|
||||
// copy file 1:1
|
||||
int current = 0;
|
||||
while((current = fgetc(input)) != EOF) {
|
||||
fputc(current, output);
|
||||
}
|
||||
}
|
||||
fclose(input);
|
||||
return 1;
|
||||
}
|
||||
+339
@@ -0,0 +1,339 @@
|
||||
#include <stdio.h> // needs: fgetc, fputs, fopen, fprintf
|
||||
#include <stdlib.h> // needs: abort
|
||||
#include <errno.h>
|
||||
#include <string.h> // needs: sterror
|
||||
#include <ctype.h> // needs: isspace
|
||||
|
||||
#include <getopt.h> // needs: getopt_long
|
||||
|
||||
// Project specific includes
|
||||
#include <html.h>
|
||||
#include <attribute.h>
|
||||
#include <nhtml_string.h>
|
||||
#include <includes.h>
|
||||
|
||||
// globals
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
int strip(FILE * stream) {
|
||||
int current = 0;
|
||||
while((current = fgetc(stream)) != EOF) {
|
||||
if (!isspace(current))
|
||||
break;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
int parse_attr(FILE * stream, attr_set_t * output) {
|
||||
attr_t current_attr = {};
|
||||
#ifdef DEBUG
|
||||
printf("parse_attr\n");
|
||||
#endif
|
||||
int buffer = 0;
|
||||
unsigned char isKey = 1;
|
||||
|
||||
while((buffer = fgetc(stream)) != EOF) {
|
||||
// parse key=value pairs
|
||||
// check for delim
|
||||
//if (buffer == ' ' || buffer == '\t' || buffer == '\n'){
|
||||
if (isspace(buffer)) {
|
||||
attr_set_append(output, ¤t_attr);
|
||||
#ifdef DEBUG
|
||||
printf("parsed attr: %s=%s\n", current_attr.name.c_str, current_attr.value);
|
||||
#endif
|
||||
// reset attribute
|
||||
attr_destroy(¤t_attr);
|
||||
isKey = 1;
|
||||
continue;
|
||||
}
|
||||
if (isKey && buffer == '.') {
|
||||
isKey = 0;
|
||||
current_attr.name = string_from_cstr("class");
|
||||
continue;
|
||||
}
|
||||
if (buffer == '=') {
|
||||
isKey = 0;
|
||||
continue;
|
||||
} else
|
||||
if (buffer == ']') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (isKey) {
|
||||
string_append(¤t_attr.name, buffer);
|
||||
} else {
|
||||
string_append(¤t_attr.value, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// append last attribute
|
||||
attr_set_append(output, ¤t_attr);
|
||||
#ifdef DEBUG
|
||||
printf("parsed attr: %s=%s\n", current_attr.name.c_str, current_attr.value);
|
||||
#endif
|
||||
|
||||
attr_destroy(¤t_attr);
|
||||
return strip(stream);
|
||||
}
|
||||
|
||||
int parse_text(int end_char, FILE * stream, FILE * output) {
|
||||
#ifdef DEBUG
|
||||
printf("parse text\n");
|
||||
#endif
|
||||
int buffer = 0;
|
||||
char escaped = 0;
|
||||
|
||||
while((buffer = fgetc(stream)) != EOF) {
|
||||
if (!escaped && buffer == '\\') {
|
||||
escaped = 1;
|
||||
continue;
|
||||
}
|
||||
if (!escaped && buffer == end_char) {
|
||||
break;
|
||||
}
|
||||
escaped = 0;
|
||||
if (end_char == '"' && html_escape(buffer, output)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
fputc(buffer, output);
|
||||
}
|
||||
|
||||
return strip(stream);
|
||||
}
|
||||
|
||||
int readName(FILE *stream, node_t *node) {
|
||||
int current = strip(stream);
|
||||
if (current == EOF) {
|
||||
return current;
|
||||
}
|
||||
|
||||
do {
|
||||
if (current == '{' || current == '[') {
|
||||
break;
|
||||
}
|
||||
|
||||
if (current == ' ' || current == '\t' || current == '\n') {
|
||||
current = strip(stream);
|
||||
break;
|
||||
}
|
||||
|
||||
string_append(&node->name, current);
|
||||
} while((current = fgetc(stream)) != EOF);
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
// Parse comments
|
||||
int parse_comment(int current, FILE * input) {
|
||||
current = fgetc(input);
|
||||
if (current == '/') {
|
||||
// line comment
|
||||
while((current = fgetc(input)) != EOF) {
|
||||
if (current == '\n') break;
|
||||
}
|
||||
} else
|
||||
if (current == '*') {
|
||||
// block comment
|
||||
unsigned char comment_done = 0;
|
||||
while((current = fgetc(input)) != EOF) {
|
||||
if (current == '*') {
|
||||
comment_done = 1;
|
||||
} else
|
||||
if (comment_done && current == '/') {
|
||||
break;
|
||||
} else
|
||||
comment_done = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return strip(input);
|
||||
}
|
||||
|
||||
int parse_include(FILE * input, FILE * output) {
|
||||
// handle include
|
||||
int current = fgetc(input);
|
||||
string_t filename = {};
|
||||
|
||||
// two possibilities
|
||||
// 1. include with "" <= supports arbitrary paths
|
||||
// 2. include without ""
|
||||
// reads till a nonpath, char (!{")
|
||||
// then strips empty chars from start and end of path
|
||||
// both methods then try to find the file in the include paths
|
||||
if (current == EOF) return current;
|
||||
|
||||
if (current == '"') { // Method 1
|
||||
char escaped = 0;
|
||||
while((current = fgetc(input)) != EOF) {
|
||||
if (!escaped && current == '\\') {
|
||||
escaped = 1;
|
||||
continue;
|
||||
}
|
||||
if (!escaped && current == '"') {
|
||||
break;
|
||||
}
|
||||
|
||||
escaped = 0;
|
||||
string_append(&filename, current);
|
||||
}
|
||||
} else {
|
||||
string_append(&filename, current);
|
||||
while((current = fgetc(input)) != EOF) {
|
||||
switch(current) {
|
||||
case '{':
|
||||
case '!':
|
||||
case '"':
|
||||
case '}':
|
||||
break;
|
||||
}
|
||||
if (isspace(current)) {
|
||||
break;
|
||||
}
|
||||
|
||||
string_append(&filename, current);
|
||||
}
|
||||
}
|
||||
|
||||
if (filename.c_str == NULL) {
|
||||
fprintf(stderr, "include command without filename!\n");
|
||||
return strip(input);
|
||||
}
|
||||
|
||||
// handle filename
|
||||
if (verbose) printf("include file %s\n", filename.c_str);
|
||||
|
||||
if (!include_file(&filename, output)) {
|
||||
fprintf(stderr, "could not include file %s\n", filename.c_str);
|
||||
}
|
||||
|
||||
return strip(input);
|
||||
}
|
||||
|
||||
int parse_node(int current, FILE * stream, FILE * output) {
|
||||
if (current == '"' || current == '(') {
|
||||
if (current == '(') current = ')';
|
||||
return parse_text(current, stream, output);
|
||||
} else
|
||||
if (current == '/') {
|
||||
return parse_comment(current, stream);
|
||||
} else
|
||||
if (current == '@') {
|
||||
return parse_include(stream, output);
|
||||
}
|
||||
|
||||
// normal node
|
||||
node_t current_node = {};
|
||||
string_append(¤t_node.name, current);
|
||||
current = readName(stream, ¤t_node);
|
||||
if (current == EOF) {
|
||||
goto done;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("parse_node: %s\n", current_node.name.c_str);
|
||||
#endif
|
||||
if (current == '[') {
|
||||
current = parse_attr(stream, ¤t_node.attributes);
|
||||
}
|
||||
|
||||
if (current != '{') {
|
||||
// tag is selfclosing
|
||||
open_node(¤t_node, output);
|
||||
return current;
|
||||
}
|
||||
current = strip(stream);
|
||||
|
||||
open_node(¤t_node, output);
|
||||
while(current != '}' && current != EOF) {
|
||||
current = parse_node(current, stream, output);
|
||||
}
|
||||
|
||||
close_node(¤t_node, output);
|
||||
done:
|
||||
return strip(stream);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// long options
|
||||
static struct option long_options[] = {
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"help", no_argument, 0, '?'},
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
void usage(int argc, char ** args) {
|
||||
printf("usage: %s [-o <filename>] file [file...]\n", args[0]);
|
||||
printf("-I\tAdd Path to include paths\n");
|
||||
printf("--output\n");
|
||||
printf("-o\tThe output file to write the html to\n");
|
||||
printf("\tWhen missing this option, stdout is used instead\n");
|
||||
printf("-v\tEnable verbose output\n");
|
||||
printf("--help Print this usage\n");
|
||||
}
|
||||
|
||||
int main(int argc, char ** args) {
|
||||
int i = 0;
|
||||
FILE* output = NULL;
|
||||
|
||||
// parse arguments
|
||||
while((i = getopt_long(argc, args, "o:I:v", long_options, NULL)) != -1) {
|
||||
switch(i) {
|
||||
case 'o':
|
||||
output = fopen(optarg, "w");
|
||||
if (output == NULL) {
|
||||
fprintf(stderr, "could not create output file: %s (%s)\n", optarg, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
// add include dir
|
||||
include_add_path(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
case '?':
|
||||
usage(argc, args);
|
||||
return -1;
|
||||
default:
|
||||
// should not be reachable
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
if (output == NULL) {
|
||||
output = stdout;
|
||||
// disable output when printing to stdout
|
||||
verbose = 0;
|
||||
}
|
||||
|
||||
char * filename= NULL;
|
||||
|
||||
// parse all the files
|
||||
for(i = optind;i < argc; i++) {
|
||||
filename = args[i];
|
||||
if (verbose) printf("starting conversion of %s\n", filename);
|
||||
|
||||
FILE * handle = fopen(filename, "r");
|
||||
|
||||
if (handle == NULL) {
|
||||
fprintf(stderr, "could not open \"%s\": %s\n", filename, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
|
||||
int current = strip(handle);
|
||||
|
||||
// parse the complete file
|
||||
while((current = parse_node(current, handle, output)) != EOF);
|
||||
|
||||
fclose(handle);
|
||||
}
|
||||
|
||||
if (output != stdout) fclose(output);
|
||||
if (verbose) printf("done compiling\n");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* string.c
|
||||
*
|
||||
* Created on: 08.08.2017
|
||||
* Author: julian
|
||||
*/
|
||||
|
||||
#include "nhtml_string.h"
|
||||
#include <stdlib.h> // needs: malloc, free
|
||||
#include <memory.h> // needs: memcpy, memset
|
||||
#include <string.h> // needs: strlen, strdup
|
||||
|
||||
int string_append(string_t *str, char c) {
|
||||
if (str->c_str == NULL) {
|
||||
// new string, need to emit EOS
|
||||
str->len++;
|
||||
}
|
||||
|
||||
char * new_ptr = realloc(str->c_str, str->len+1);
|
||||
if (new_ptr == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// append char
|
||||
str->c_str = new_ptr;
|
||||
str->c_str[str->len-1] = c;
|
||||
str->c_str[str->len] = 0; // make sure
|
||||
str->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int string_concat(string_t * dest, string_t *src) {
|
||||
if (src->c_str == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dest->c_str == NULL) {
|
||||
dest->len++;
|
||||
}
|
||||
|
||||
char * new_ptr = realloc(dest->c_str, dest->len + src->len-1);
|
||||
if (new_ptr == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(dest->c_str + dest->len-1, src->c_str, src->len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void string_destroy(string_t *s) {
|
||||
free(s->c_str);
|
||||
memset(s, 0, sizeof(string_t));
|
||||
}
|
||||
|
||||
string_t string_copy(string_t old) {
|
||||
string_t tmp = {}; // initialize with 0
|
||||
if (old.len == 0) {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
tmp.c_str = malloc(old.len);
|
||||
if (tmp.c_str == NULL) {
|
||||
// The Application will have to handle a out of mem
|
||||
// situation here
|
||||
return tmp;
|
||||
}
|
||||
|
||||
tmp.len = old.len;
|
||||
memcpy(tmp.c_str, old.c_str, tmp.len);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
string_t string_from_cstr(const char * str) {
|
||||
string_t temp;
|
||||
temp.len = strlen(str)+1;
|
||||
temp.c_str = strdup(str);
|
||||
return temp;
|
||||
}
|
||||
Reference in New Issue
Block a user