refactored code and ported project to cmake
This commit is contained in:
parent
17da357871
commit
640a5edee2
17
Makefile
17
Makefile
@ -1,17 +0,0 @@
|
|||||||
OBJ:= html.o main.o
|
|
||||||
OUTPUT:=nhtmlc
|
|
||||||
|
|
||||||
all: debug
|
|
||||||
|
|
||||||
debug: CXXFLAGS = -g -DDEBUG
|
|
||||||
debug: CFLAGS = -g -DDEBUG
|
|
||||||
debug: $(OBJ)
|
|
||||||
gcc $(OBJ) -g -o $(OUTPUT)
|
|
||||||
|
|
||||||
release: $(OBJ)
|
|
||||||
gcc $(OBJ) -o $(OUTPUT)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf $(OBJ) $(OUTPUT)
|
|
||||||
|
|
||||||
|
|
54
inc/attribute.h
Normal file
54
inc/attribute.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* attribute.h
|
||||||
|
*
|
||||||
|
* Created on: 08.08.2017
|
||||||
|
* Author: julian
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ATTRIBUTE_H_
|
||||||
|
#define ATTRIBUTE_H_
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "nhtml_string.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
string_t name, value;
|
||||||
|
} attr_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief copy the give attribute
|
||||||
|
* \return a copy of attr
|
||||||
|
*/
|
||||||
|
attr_t attr_copy(attr_t * attr);
|
||||||
|
/**
|
||||||
|
* \brief reset the attribute
|
||||||
|
* Deletes all strings
|
||||||
|
*/
|
||||||
|
void attr_destroy(attr_t * attr);
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
attr_t * arr;
|
||||||
|
size_t len;
|
||||||
|
} attr_set_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief search in attribute set for key
|
||||||
|
* \param set The Attribute set
|
||||||
|
* \param key The Key to search
|
||||||
|
* \return the pointer to the attribute in the set mathching the key
|
||||||
|
* \return NULL if there is no matching attribute
|
||||||
|
*/
|
||||||
|
attr_t * attr_set_find(attr_set_t *set, const char * key);
|
||||||
|
// Append new Attribute to set
|
||||||
|
/**
|
||||||
|
* \brief Append new attribute pair to set
|
||||||
|
* \return 0 on success
|
||||||
|
* \return -1 on failure (error can be found to errno)
|
||||||
|
*/
|
||||||
|
int attr_set_append(attr_set_t * set, attr_t *new_entry);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* ATTRIBUTE_H_ */
|
37
inc/html.h
Normal file
37
inc/html.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* html.h
|
||||||
|
*
|
||||||
|
* Created on: 07.08.2017
|
||||||
|
* Author: julian
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HTML_H_
|
||||||
|
#define HTML_H_
|
||||||
|
|
||||||
|
#include <stdio.h> // needs FILE
|
||||||
|
#include "nhtml_string.h"
|
||||||
|
#include "attribute.h"
|
||||||
|
|
||||||
|
extern int html_escape(int c, FILE* output);
|
||||||
|
|
||||||
|
typedef struct node {
|
||||||
|
string_t name;
|
||||||
|
attr_set_t attributes;
|
||||||
|
} node_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief emit html opening tag for \node
|
||||||
|
* \param node the Node to create the opening tag for
|
||||||
|
* \param output the File to write to
|
||||||
|
*/
|
||||||
|
void open_node(node_t * node, FILE * output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief emit html closing tag for \node
|
||||||
|
* \param node the Node to create the closing tag for
|
||||||
|
* \param output the File to write to
|
||||||
|
*/
|
||||||
|
void close_node(node_t * node, FILE * output);
|
||||||
|
|
||||||
|
#endif /* HTML_H_ */
|
40
inc/nhtml_string.h
Normal file
40
inc/nhtml_string.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* nhtml_string.h
|
||||||
|
*
|
||||||
|
* Created on: 08.08.2017
|
||||||
|
* Author: julian
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NHTML_STRING_H_
|
||||||
|
#define NHTML_STRING_H_
|
||||||
|
|
||||||
|
#include <stddef.h> // size_t
|
||||||
|
#include <errno.h> // errno
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char * c_str;
|
||||||
|
size_t len;
|
||||||
|
} string_t;
|
||||||
|
|
||||||
|
/**\brief append the char @c to @str
|
||||||
|
* \param str to the String to append to
|
||||||
|
* \param c the char to append
|
||||||
|
* \return -1 on error, errno will be set to errorcode
|
||||||
|
* \return 0 on success
|
||||||
|
*/
|
||||||
|
int string_append(string_t *str, char c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Erase the String from memory
|
||||||
|
*/
|
||||||
|
void string_destroy(string_t s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief copy the contents of a string
|
||||||
|
* \param old The String to copy
|
||||||
|
* \return returns the new string
|
||||||
|
*/
|
||||||
|
string_t string_copy(string_t old);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* NHTML_STRING_H_ */
|
331
main.c
331
main.c
@ -1,331 +0,0 @@
|
|||||||
#include <stdio.h> // needs: fgetc, fputs, fopen, fprintf
|
|
||||||
#include <stdlib.h> // needs: realloc, malloc, free
|
|
||||||
#include <string.h> // needs: strcmp, memcpy
|
|
||||||
#include <errno.h>
|
|
||||||
#include <ctype.h> // needs: isspace
|
|
||||||
|
|
||||||
#include <getopt.h> // needs: getopt_long
|
|
||||||
|
|
||||||
#include "html.h" // needs: html_escaped
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char * c_str;
|
|
||||||
size_t len;
|
|
||||||
} string_t;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void string_destroy(string_t s) {
|
|
||||||
free(s.c_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
string_t string_copy(string_t old) {
|
|
||||||
string_t tmp;
|
|
||||||
tmp.c_str = malloc(old.len);
|
|
||||||
tmp.len = old.len;
|
|
||||||
memcpy(tmp.c_str, old.c_str, tmp.len);
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
string_t name, value;
|
|
||||||
} attr_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
attr_t * arr;
|
|
||||||
size_t len;
|
|
||||||
} attr_set_t;
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// just change entries value
|
|
||||||
string_destroy(new_ptr->value);
|
|
||||||
new_ptr->value = string_copy(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] = *new_entry;
|
|
||||||
set->len++;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void attr_reset(attr_t * attr) {
|
|
||||||
memset(attr, 0, sizeof(attr_t));
|
|
||||||
}
|
|
||||||
|
|
||||||
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_reset(¤t_attr);
|
|
||||||
isKey = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isKey && buffer == '.') {
|
|
||||||
isKey = 0;
|
|
||||||
current_attr.name.c_str = "class";
|
|
||||||
current_attr.name.len = 6;
|
|
||||||
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
|
|
||||||
|
|
||||||
//memset(output, 0, sizeof(attr_set_t));
|
|
||||||
return strip(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
int parse_text(int mode, 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 == mode) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
escaped = 0;
|
|
||||||
if (mode == '"' && html_escape(buffer, output)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fputc(buffer, output);
|
|
||||||
}
|
|
||||||
|
|
||||||
//fprintf(stderr, "stub: parse_text\n");
|
|
||||||
return strip(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct node {
|
|
||||||
string_t name;
|
|
||||||
attr_set_t attributes;
|
|
||||||
} node_t;
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum node_type {
|
|
||||||
NODE_SELFCLOSING,
|
|
||||||
NODE_TEXT,
|
|
||||||
NODE_TAG
|
|
||||||
};
|
|
||||||
|
|
||||||
int parse_node(int current, FILE * stream, FILE * output) {
|
|
||||||
if (current == '"' || current == '(') {
|
|
||||||
if (current == '(') current = ')';
|
|
||||||
|
|
||||||
return parse_text(current, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char ** args) {
|
|
||||||
//getopt_long(argc, args, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
char * filename= NULL;
|
|
||||||
FILE * output = fopen("a.html", "w");
|
|
||||||
if (output == NULL) {
|
|
||||||
fprintf(stderr, "could not create output file\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(;argc > 1; --argc) {
|
|
||||||
filename = args[argc-1];
|
|
||||||
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);
|
|
||||||
|
|
||||||
while((current = parse_node(current, handle, output)) != EOF) {
|
|
||||||
// if () {
|
|
||||||
// fprintf(stderr, "error during parsing of node\n");
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(output);
|
|
||||||
printf("done compiling\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
8
src/CMakeLists.txt
Normal file
8
src/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
set(pwd ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
set(SOURCE
|
||||||
|
${SOURCE}
|
||||||
|
${pwd}/main.c
|
||||||
|
${pwd}/nhtml_string.c
|
||||||
|
${pwd}/attribute.c
|
||||||
|
${pwd}/html.c
|
||||||
|
PARENT_SCOPE)
|
68
src/attribute.c
Normal file
68
src/attribute.c
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
// just change entries value
|
||||||
|
string_destroy(new_ptr->value);
|
||||||
|
new_ptr->value = string_copy(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);
|
||||||
|
|
||||||
|
// reset memory content
|
||||||
|
memset(attr, 0, sizeof(attr_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
71
src/html.c
Normal file
71
src/html.c
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
235
src/main.c
Normal file
235
src/main.c
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#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"
|
||||||
|
|
||||||
|
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.c_str = "class";
|
||||||
|
current_attr.name.len = 6;
|
||||||
|
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
|
||||||
|
|
||||||
|
//memset(output, 0, sizeof(attr_set_t));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_node(int current, FILE * stream, FILE * output) {
|
||||||
|
if (current == '"' || current == '(') {
|
||||||
|
if (current == '(') current = ')';
|
||||||
|
|
||||||
|
return parse_text(current, 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, '?'},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
int verbose = 1;
|
||||||
|
|
||||||
|
void usage(int argc, char ** args) {
|
||||||
|
printf("usage: %s [-o <filename>] file [file...]\n", args[0]);
|
||||||
|
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("--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:v", long_options, NULL)) != -1) {
|
||||||
|
switch(i) {
|
||||||
|
case 'o':
|
||||||
|
printf("output: %s\n", optarg);
|
||||||
|
output = fopen(optarg, "w");
|
||||||
|
if (output == NULL) {
|
||||||
|
fprintf(stderr, "could not create file: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
usage(argc, args);
|
||||||
|
return -1;
|
||||||
|
default:
|
||||||
|
// should not be reachable
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (output == NULL) {
|
||||||
|
output = stdout;
|
||||||
|
verbose = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char * filename= NULL;
|
||||||
|
|
||||||
|
// parse all the files
|
||||||
|
for(i = optind;i < argc; i++) {
|
||||||
|
filename = args[argc-1];
|
||||||
|
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;
|
||||||
|
}
|
50
src/nhtml_string.c
Normal file
50
src/nhtml_string.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* string.c
|
||||||
|
*
|
||||||
|
* Created on: 08.08.2017
|
||||||
|
* Author: julian
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "nhtml_string.h"
|
||||||
|
#include <stdlib.h> // needs: malloc, free
|
||||||
|
#include <memory.h> // needs: memcpy
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void string_destroy(string_t s) {
|
||||||
|
free(s.c_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
string_t string_copy(string_t old) {
|
||||||
|
string_t tmp = {}; // initialize with 0
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user