From 53786765961af249f31f7cfeb0bb805cabc35b85 Mon Sep 17 00:00:00 2001 From: Julian Daube Date: Tue, 8 Aug 2017 22:24:40 +0200 Subject: [PATCH] commit version 0.1 --- CMakeLists.txt | 13 ++ README.md | 75 +++++++++- example/Makefile | 15 ++ example/entchen.css | 9 ++ example/entchen.nhtml | 35 +++++ example/head.nhtml | 11 ++ inc/attribute.h | 54 +++++++ inc/html.h | 37 +++++ inc/includes.h | 18 +++ inc/nhtml_string.h | 53 +++++++ inc/parser.h | 17 +++ src/CMakeLists.txt | 9 ++ src/attribute.c | 66 ++++++++ src/html.c | 71 +++++++++ src/includes.c | 94 ++++++++++++ src/main.c | 339 ++++++++++++++++++++++++++++++++++++++++++ src/nhtml_string.c | 80 ++++++++++ 17 files changed, 995 insertions(+), 1 deletion(-) create mode 100644 CMakeLists.txt create mode 100644 example/Makefile create mode 100644 example/entchen.css create mode 100644 example/entchen.nhtml create mode 100644 example/head.nhtml create mode 100644 inc/attribute.h create mode 100644 inc/html.h create mode 100644 inc/includes.h create mode 100644 inc/nhtml_string.h create mode 100644 inc/parser.h create mode 100644 src/CMakeLists.txt create mode 100644 src/attribute.c create mode 100644 src/html.c create mode 100644 src/includes.c create mode 100644 src/main.c create mode 100644 src/nhtml_string.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9528306 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.0.0) +project(nhtml C) + +add_subdirectory(src) +add_executable(nhtmlc ${SOURCE}) +target_include_directories(nhtmlc PUBLIC inc) + +if (CMAKE_BUILD_TYPE MATCHES "DEBUG") + message("debug") + target_compile_definitions(nhtmlc PUBLIC DEBUG) +endif (CMAKE_BUILD_TYPE MATCHES "DEBUG") + +target_link_libraries(nhtmlc) diff --git a/README.md b/README.md index b0c99a9..50761a5 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,76 @@ # nhtml -Not html, a very simple template engine \ No newline at end of file +!html, a very simple template language + +Syntax +------------ +As said above, the Template Language is simple. +That also implies a few limitations: +1. No Parametric instantiation (for now) since this would bloat the compiler too much + (I was in no need for that feature when writing this compiler). +2. No language integration of any kind. + This makes this template compiler useful for static webcontent only! + +Sooo, how does it work? +* text enclosed with `""` will be placed in the html output, but will be html escaped and `\n` will be replaced with `
`. + +* text enclosed with () will be placed *without any changes*. + +* One can include other files by writing either +`@`, e.g. `@hi.nhtml` or +`@""`, e.g. `@"filename with spaces.nhtml"` +These files will pe pasted in place unless they end with `.nhtml`, in which case +they get compiled before getting pasted. + +* There are comment blocks (`/*text*/`) and linecomments (`//text`) like there are in C. + +* A left curly brace (`{`) opens a tag, a coresponding right curly brace (`}`) closes it. + +* Finally, tag names are simply written as text and can be accompied by an attribute set +enclosed in `[]`. So in order to e.g. produce +`
Hi
`, the corresponding nhtml would look something like +`div[style=color:white]{"Hi"}`. + +* If the Attributes Key-Name is `class`, its pair can be written as `.` instead of `class=`. +Note that multiple mentions of the same attribute key will result in the attribute values +beeing concatenated with a space. So e.g. +`p[.A class=hi]{}` will become `

`. + +That about covers everything this little thing can do :) +For a simple demo, look in example. + +How to compile +-------------- + +The Project used cmake to build. It has no hard dependencies but it requires unix-style pathnames. (`/path/to/file`) for includes to work. + +## on linux +Simply install cmake for your distro +### Ubuntu +`apt-get install cmake` +### Arch Linux +`pacman -S cmake` + +The create a subdirectory for the build to reside in +`mkdir build` +`cd build` + +And run cmake +Release: `cmake -G"Unix Makefiles" ..` +Debug: `cmake -G"Unix Makefiles" .. -DCMAKE_BUILD_TYPE=DEBUG` + +after that run `make` and you should be left with a compiled version of the !html compiler `nhtmlc` + +How to use +---------- +``` +./nhtmlc [-o ] file [file...] +-I Add Path to include paths +--output +-o The output file to write the html to + When missing this option, stdout is used instead +-v Enable verbose output +--help Print this usage +``` + +When given multple input files, the resulting html is concatenated into output. diff --git a/example/Makefile b/example/Makefile new file mode 100644 index 0000000..cb17144 --- /dev/null +++ b/example/Makefile @@ -0,0 +1,15 @@ + +all: entchen.html + +NHTMLC:=../build/nhtmlc + +%.html: %.nhtml + $(NHTMLC) $< -o $@ + +index.html: head.nhtml entchen.nhtml + $(NHTMLC) entchen.nhtml -o $@ + +clean: + rm -f entchen.html + +.PHONY: all clean diff --git a/example/entchen.css b/example/entchen.css new file mode 100644 index 0000000..f9efad5 --- /dev/null +++ b/example/entchen.css @@ -0,0 +1,9 @@ +.strophe { + border-width:1px; + border-type:solid; + border-color:black; + background-color:gray; + font-type:italic; + color: white; + padding: 0.5em; +} diff --git a/example/entchen.nhtml b/example/entchen.nhtml new file mode 100644 index 0000000..d1a10f5 --- /dev/null +++ b/example/entchen.nhtml @@ -0,0 +1,35 @@ +/* +* Generates the body of our document +* +* File created by Julian Daube +* date: 08.08.2017 +*/ + +@head.nhtml +body { + style { + @entchen.css + } + + h3{ "Alle meine Entchen" } + p[.strophe] { + "Alle meine Entchen schwimmen auf dem See, + schwimmen auf dem See, + Köpfchen in das Wasser, Schwänzchen in die Höh'." + } + p[.strophe] { + "Alle meine Täubchen gurren auf dem Dach, + gurren auf dem Dach, + eins fliegt in die Lüfte, fliegen alle nach." + } + p[.strophe] { + "Alle meine Hühner scharren in dem Stroh, + scharren in dem Stroh, + finden sie ein Körnchen, sind sie alle froh." + } + p[.strophe] { + "Alle meine Gänschen watscheln durch den Grund, + watscheln durch den Grund, + suchen in dem Tümpel, werden kugelrund." + } +} diff --git a/example/head.nhtml b/example/head.nhtml new file mode 100644 index 0000000..83fff3b --- /dev/null +++ b/example/head.nhtml @@ -0,0 +1,11 @@ +/* +* Generates the document header +* +* file generated by Julian Daube +* date: 08.08.2017 +*/ +!DOCTYPE[html] +head { + title { "Alle meine Entchen" } + meta[charset=utf-8] +} diff --git a/inc/attribute.h b/inc/attribute.h new file mode 100644 index 0000000..452f27c --- /dev/null +++ b/inc/attribute.h @@ -0,0 +1,54 @@ +/* + * attribute.h + * + * Created on: 08.08.2017 + * Author: julian + */ + +#ifndef ATTRIBUTE_H_ +#define ATTRIBUTE_H_ + +#include + +#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_ */ diff --git a/inc/html.h b/inc/html.h new file mode 100644 index 0000000..15d73e6 --- /dev/null +++ b/inc/html.h @@ -0,0 +1,37 @@ +/* + * html.h + * + * Created on: 07.08.2017 + * Author: julian + */ + +#ifndef HTML_H_ +#define HTML_H_ + +#include // 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_ */ diff --git a/inc/includes.h b/inc/includes.h new file mode 100644 index 0000000..0b82d5f --- /dev/null +++ b/inc/includes.h @@ -0,0 +1,18 @@ +/* + * includes.h + * + * Created on: 08.08.2017 + * Author: julian + */ + +#ifndef INC_INCLUDES_H_ +#define INC_INCLUDES_H_ + +#include +#include + +void include_add_path(const char * path); + +int include_file(string_t *filename, FILE * output); + +#endif /* INC_INCLUDES_H_ */ diff --git a/inc/nhtml_string.h b/inc/nhtml_string.h new file mode 100644 index 0000000..a9d51c9 --- /dev/null +++ b/inc/nhtml_string.h @@ -0,0 +1,53 @@ +/* + * nhtml_string.h + * + * Created on: 08.08.2017 + * Author: julian + */ + +#ifndef NHTML_STRING_H_ +#define NHTML_STRING_H_ + +#include // size_t +#include // 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 append src to dest + * will enlarge dest to accommodate src + * \return -1 on failure (check errno) + * \return 0 on success + */ +int string_concat(string_t * dest, string_t * src); + +/** + * \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); + +/** + * \brief creates copy of str + * \return a copy of str + */ +string_t string_from_cstr(const char * str); + +#endif /* NHTML_STRING_H_ */ diff --git a/inc/parser.h b/inc/parser.h new file mode 100644 index 0000000..1a68e2d --- /dev/null +++ b/inc/parser.h @@ -0,0 +1,17 @@ +/* + * parser.h + * + * Created on: 08.08.2017 + * Author: julian + */ + +#ifndef INC_PARSER_H_ +#define INC_PARSER_H_ + +#include + +int strip(FILE * input); +int parse_node(int current, FILE * stream, FILE * output); + + +#endif /* INC_PARSER_H_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..effd3f4 --- /dev/null +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/attribute.c b/src/attribute.c new file mode 100644 index 0000000..86ffad8 --- /dev/null +++ b/src/attribute.c @@ -0,0 +1,66 @@ +/* + * attribute.c + * + * Created on: 08.08.2017 + * Author: julian + */ + +#include "attribute.h" +#include // needs: strcmp +#include // 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); +} + + diff --git a/src/html.c b/src/html.c new file mode 100644 index 0000000..ee595e0 --- /dev/null +++ b/src/html.c @@ -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, "
"); + 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, "", node->name.c_str); + } +} + + + diff --git a/src/includes.c b/src/includes.c new file mode 100644 index 0000000..8b85174 --- /dev/null +++ b/src/includes.c @@ -0,0 +1,94 @@ +/* + * includes.c + * + * Created on: 08.08.2017 + * Author: julian + */ + +#include +#include +#include +#include // needs: malloc, realloc +#include // 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; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d3eb575 --- /dev/null +++ b/src/main.c @@ -0,0 +1,339 @@ +#include // needs: fgetc, fputs, fopen, fprintf +#include // needs: abort +#include +#include // needs: sterror +#include // needs: isspace + +#include // needs: getopt_long + +// Project specific includes +#include +#include +#include +#include + +// 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 ] 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; +} diff --git a/src/nhtml_string.c b/src/nhtml_string.c new file mode 100644 index 0000000..6ead993 --- /dev/null +++ b/src/nhtml_string.c @@ -0,0 +1,80 @@ +/* + * string.c + * + * Created on: 08.08.2017 + * Author: julian + */ + +#include "nhtml_string.h" +#include // needs: malloc, free +#include // needs: memcpy, memset +#include // 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; +}