diff options
-rw-r--r-- | LICENSE | 24 | ||||
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | README.md | 80 | ||||
-rw-r--r-- | include/container.h | 35 | ||||
-rw-r--r-- | include/dynstring.h | 1 | ||||
-rw-r--r-- | include/stringbuilder.h | 114 | ||||
-rw-r--r-- | ste/.gitignore | 3 | ||||
-rw-r--r-- | ste/README.md | 48 | ||||
-rw-r--r-- | ste/example/Makefile | 14 | ||||
-rw-r--r-- | ste/example/main.c | 15 | ||||
-rw-r--r-- | ste/example/tmpl.c.ste | 11 | ||||
-rw-r--r-- | ste/ste.c | 57 | ||||
-rw-r--r-- | tests/sb.c | 37 |
13 files changed, 413 insertions, 31 deletions
@@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org> @@ -45,3 +45,8 @@ json: tests/json.c include/jacson.h $(CC) -o $@ tests/json.c $(CFLAGS) ./$@ rm $@ + +stringbuilder: tests/sb.c include/stringbuilder.h + $(CC) -o $@ tests/sb.c $(CFLAGS) + ./$@ + rm $@ @@ -1,37 +1,69 @@ -# What is this? -This is just a collection of (usually single-purpos) header files that I use refularly in my code +# What is this? +This is just a collection of (usually single-purpose) header files that I use refularly in my code # Common conventions -* As these are header-only libraries, there is a macro in form `HEADER_NAME_IMPLEMENTATION` that has to be defined before including the header. A header can only be included once while this macro is defined, otherwise you get multiple definition errors. -* Names starting with underscores are kind of private-use for this libraries, but they may be declared not `static` because they are used in macros that expose API. +* As these are header-only libraries, there is a macro in form `HEADER_NAME_IMPLEMENTATION` that has to be defined + before including the header. A header can only be included once while this macro is defined, otherwise you get + multiple definition errors. +* Names starting with underscores are kind of private-use for this libraries, but they may be declared not `static` + because they are used in macros that expose API. * `snake_case` * Function-like macros are not all-caps. -* For now i only support GCC-based compilers. I will consider removing this dependency in future (e.g. getting rid of all `typeof` uses) -# Descriptions for heders: +* For now i only support GCC-based compilers. I will consider removing this dependency in future (e.g. getting rid of + all `typeof` uses) +# Descriptions for heders: ## [`container.h`](include/container.h) -* Summary: originally was several headers, but since `hash_map.h` used `dynarray.h`, which itself used `utility.h`, it was too complex to work with. So i combined everything in a single header. -* How to use: Define `CONTAINER_IMPLEMENTATION` macro before including `container.h`. It's probably better to `#undef` it after inclusion as well. To use some functions (`__default_int_cmp` and alikes), define `CONTAINER_EXPOSE_HELPERS`. +* Summary: originally was several headers, but since `hash_map.h` used `dynarray.h`, which itself used `utility.h`, it + was too complex to work with. So i combined everything in a single header. +* How to use: Define `CONTAINER_IMPLEMENTATION` macro before including `container.h`. It's probably better to `#undef` + it after inclusion as well. To use some functions (`__default_int_cmp` and alikes), define `CONTAINER_EXPOSE_HELPERS`. * Examples: See [tests](tests) * Notes: if compiled in shared object, must be compiled with -fPIC ### Additional macros -* SHRINK\_RESIGING\_ARRAY: Reallocate the array to smaller capacity when it's size becomes too small. Not impelemented at the moment. +* SHRINK\_RESIGING\_ARRAY: Reallocate the array to smaller capacity when it's size becomes too small. Not impelemented + at the moment. +* Disable parts of the library: `CONTAINER_DISABLE_HMAP` and `CONTAINER_DISABLE_HSET` will disable hash map and hash set + implementation correspondingly. `CONTAINER_DISABLE_LINKED_LIST` will disable linked list, and since hmap and hset + depend on it, will disable them as well. `CONTAINER_DISABLE_HASH` will disable both hash set and hash map. + `CONTAINER_DISABLE_ARRAY` will disable array implementation (and hmap and hset as a consequence). + `CONTAINER_DISABLE_UTILITY` will disable utilities, which will in turn disable everything else. + `CONTAINER_DISABLE_ALL` will not disable anything else but will leave you with unuseable header. Not useful, huh? ## [`rstypes.h`](include/rstypes.h) * Summary: rust type aliases (like `u32`, `f128` e.t.c.) -* How to use: if you don't need `i128` or `u128`, then just include the header. If you do need them, define macro `RS_TYPES_USE_128`. +* How to use: if you don't need `i128` or `u128`, then just include the header. If you do need them, define macro + `RS_TYPES_USE_128`. * Examples: See [tests](tests/types.c). -* Notes: Not many compilers support 128-bit wide integers, and ones which DO support them tend to warn you that these are non ISO-C. That's why I've put them behind a macro. If they are not supported, their usage will fail with compilation error. -## [`embed.h`](include/embed.h) +* Notes: Not many compilers support 128-bit wide integers, and ones which DO support them tend to warn you that these + are non ISO-C. That's why I've put them behind a macro. If they are not supported, their usage will fail with + compilation error. +## [`embed.h`](include/embed.h) \[\[NOT IN DEVELOPMENT\]\] * Summary: Code generator for embedding resources directly into an executable. -* How to use: It exposes C interface, so in order to use it, you will need to have a C program that builds resources for you. It has it's own repo, but i will be adding CLI to there soon. -* Examples: see [this repo](https://github.com/justanothercatgirl/embed_test.c/) +* How to use: It exposes C interface, so in order to use it, you will need to have a C program that builds resources for + you. It has it's own repo, but i will be adding CLI to there soon. +* Examples: see [this repo](https://github.com/justanothercatgirl/embed_test.c/) ## [`jacson.h`](include/jacson.h) * Summary: Spec-compliant json serializer and parser -* How to use: define `JACSON_IMPLEMENTATION` macro. `CONTAINER_IMPLEMENTATION` MUST be defined as well; either somewhere earlier in the code before including `container.h` or just before `jacson.h`. this JSON implementation uses `array` and `hash_set`. -* Macros: `JACSON_EXPORT_RSNPRINTF` will make symbols `__jacson_rsnprintf` and `__jacson_rsnputc` visible. These 2 functions allow for appending a string to a buffer obtained from malloc and resizing it (realloc) if needed. They're not visible by default because they do not have obvious friendly signature. -## `build.h` -nearest TODO for now. -* Summary: a build system based on C. To compile something under it, you do something like `cc -o builder builder.c && ./builder`. The idea is stolen from [Tsoding](https://example.com) -# Warning! -Everything here is written by a relatively inexperienced student (me), so I guarantee basically nothing. Memory leaks? I am sorry for them, but also not responsible. Security? Haven't heard of that either. That's just how it is. But I try to make everything as good as possible, so you can use the code after a careful review. -# License -I did not decide on it yet, but I am inclined towards MIT or even public domain. Don't want to restrict the usage with LGPL, because it's not like this code is a big deal, anyone could write it. Also, I don't want to deal with license issues that I can create for my future self, so yeah, that's how it is. For now, public domain. - +* How to use: define `JACSON_IMPLEMENTATION` macro. `CONTAINER_IMPLEMENTATION` MUST be defined as well; either somewhere + earlier in the code before including `container.h` or just before `jacson.h`. this JSON implementation uses `array` + and `hash_set`. +* Macros: `JACSON_EXPORT_RSNPRINTF` will make symbols `__jacson_rsnprintf` and `__jacson_rsnputc` visible. These 2 + functions allow for appending a string to a buffer obtained from malloc and resizing it (realloc) if needed. They're + not visible by default because they do not have obvious friendly signature. +## [`stringbuilder.h`](include/stringbuilder.h) +* Summary: string builder. Quite simple. +* How to use: include the header, define `STRINGBUILDER_IMPLEMENTATION`. Done. +* Notes: Pretty small at the moment, but I plan on expanding it on-demand. The interfaces are called `jac_sb` despite + the header being called `stringbuilder.h`. You wouldn't want to type `struct stringbuilder` on your keyboard each + time, would you? but `sb.h` it too vague, it could be `sucking_balls.h`, `sizeable_breasts.h`, `system_breaker.h` + e.t.c. +## `build.h` + TODO for now. +* Summary: a build system based on C. To compile something under it, you do something like `cc -o builder builder.c && + ./builder`. The idea is stolen from [Tsoding](https://example.com) # Warning! Everything here is written by a + relatively inexperienced student (me), so I guarantee basically nothing. Memory leaks? I am sorry for them, but also + not responsible. Security? Haven't heard of that either. That's just how it is. But I try to make everything as good + as possible, so you can use the code after a careful review. # License The [Unlicense license](https://unlicense.org). + Also known as [fuck google employees](https://opensource.google/documentation/reference/patching#forbidden) license + (nothing personal), equivalent to BSD0, CC0 or Public Domain. +## `ste/` +Please refer to [README](ste/READNE.md) in ste subdirectory. + <!--vim:tw=120--> diff --git a/include/container.h b/include/container.h index 5f40e4a..0234abe 100644 --- a/include/container.h +++ b/include/container.h @@ -1,6 +1,12 @@ +#ifndef CONTAINER_DISABLE_ALL #ifndef JUSTANOTHERCATGIRL_HEADERS_CONTAINER #define JUSTANOTHERCATGIRL_HEADERS_CONTAINER +#if defined(CONTAINER_DISABLE_ARRAY) || defined(CONTAINER_DISABLE_ARRAY) || defined(CONTAINER_DISABLE_HASH) +# define CONTAINER_DISABLE_HMAP +# define CONTAINER_DISABLE_HSET +#endif + #include <stdint.h> #include <stdarg.h> #include <stdio.h> @@ -10,6 +16,7 @@ /* ----------------------------------------------------------------- */ /* -----------------UTILITY HEADER---------------------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_UTILITY #define stringify(val) _stringify_helper(val) #define _stringify_helper(val) #val #define string_concat_separator(first, ...) stringify(first) ";" string_concat_separator(__VA_ARGS__) @@ -68,7 +75,7 @@ typedef void*(*memcpy_t)(void* restrict, const void*, size_t); #else # define _CONTAINER_STATIC static #endif /* CONTAINER_EXPOSE_HELPERS */ - +#endif /* CONTAINER_DISABLE_UTILITY */ /* ----------------------------------------------------------------- */ /* -------------------------ARRAY HEADER---------------------------- */ @@ -87,7 +94,7 @@ typedef void*(*memcpy_t)(void* restrict, const void*, size_t); /* TODO: implement in operations that change the array size */ /* #define SHINK_RESIZING_ARRAY */ - +#ifndef CONTAINER_DISABLE_ARRAY /* size of the array header. Should not be used directly, unless you know what you are doing */ #define DYNARRAY_HEADER_SIZE sizeof(struct _dynarray_header) @@ -154,11 +161,13 @@ void* array_copy(void* old); /* Returns bool. Assumes that the array is sorted. */ char array_binary_search(void* array, void* element, qsort_cmp_t cmp); struct linked_list array_to_ll(void* array); +#endif /* CONTAINER_DISABLE_ARRAY */ /* ----------------------------------------------------------------- */ /* ----------------------LINKED LIST HEADER------------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_LINKED_LIST typedef void(*free_t)(void*); struct __linked_list_meta { size_t element_size; @@ -292,11 +301,13 @@ void ll_set_free(struct linked_list* list, free_t new_free); /* Creates a deep copy of the list, meaning that it must be free'd as any other list */ /* Uses `cpy` function to copy data to new location. If NULL, memcpy is used. */ struct linked_list ll_deep_copy(const struct linked_list* list, memcpy_t cpy); +#endif /* CONTAINER_DISABLE_LINKED_LIST */ /* ----------------------------------------------------------------- */ /* ------------------------HASH MAP HEADER-------------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_HMAP #ifndef HMAP_MAX_BUCKET_SIZE # define HMAP_MAX_BUCKET_SIZE 8 #endif @@ -378,11 +389,13 @@ char hmapi_le(const struct hash_map_iter *a, const struct hash_map_iter* b); char hmapi_ne(const struct hash_map_iter *a, const struct hash_map_iter* b); /* 1 if `iter` is an end iterator, 0 otherwise */ char hmapi_end(const struct hash_map_iter* iter); +#endif /* CONTAINER_DISABLE_HMAP */ /* ----------------------------------------------------------------- */ /* ------------------------HASH SET HEADER-------------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_HSET #ifndef HSET_MAX_BUCKET_SIZE # define HSET_MAX_BUCKET_SIZE 8 #endif @@ -454,12 +467,14 @@ char hseti_le(const struct hash_set_iter *a, const struct hash_set_iter* b); char hseti_ne(const struct hash_set_iter *a, const struct hash_set_iter* b); /* 1 if `iter` is an end iterator, 0 otherwise */ char hseti_end(const struct hash_set_iter* iter); +#endif /* CONTAINER_DISABLE_HSET */ /* ------------------------------------------------------------------------- */ /* ------From now on, the rest of the header is implementation details------ */ /* -------------------the API and documentation end here-------------------- */ /* ------------------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_ARRAY enum _dynarray_header_idx { _dah_idx_member_size = 0, _dah_idx_capacity = 1, @@ -481,6 +496,7 @@ void *_memshrink_array(void *dynarray); void *_insert_to_index_dynarray(void *const dynarray, const void *const element, size_t el_size, size_t index); void* _array_extend(void* array, void* buffer, size_t len); void* _array_pop_at(void* array, size_t idx); +#endif /* CONTAINER_DISABLE_ARRAY */ /* uncomment in dev mode so that LSP highlights the code */ /* #define CONTAINER_IMPLEMENTATION */ @@ -490,6 +506,7 @@ void* _array_pop_at(void* array, size_t idx); /* ---------------------UTILITY IMPLEMENTATION---------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_UTILITY #ifndef __GNUC__ unsigned long __bit_scan_32(int32_t number) { # ifdef _MSC_VER @@ -572,12 +589,13 @@ const qsort_cmp_t __qsort_cmps[64] = { 0, 0, 0, __default_long_long_cmp, }; #endif /* __GNUC__ */ - +#endif /* CONTAINER_DISABLE_UTILITY */ /* ----------------------------------------------------------------- */ /* ----------------------ARRAY IMPLEMENTATION----------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_ARRAY void *_alloc_dynarray(size_t el_size, size_t len) { byte *data = (byte *)malloc(el_size * len + DYNARRAY_HEADER_SIZE); @@ -699,10 +717,12 @@ struct linked_list array_to_ll(void* array) { struct linked_list ret = ll_create_from_buffer(array_element_size(array), array, array_size(array)); return ret; } +#endif /* CONTAINER_DISABLE_ARRAY */ /* ----------------------------------------------------------------- */ /* ------------------LINKED LIST IMPLEMENTATION--------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_LINKED_LIST struct linked_list ll_create(size_t memb_size) { struct __linked_list_meta meta = { .assumed_size = 0, @@ -1057,12 +1077,13 @@ struct linked_list ll_deep_copy(const struct linked_list* list, memcpy_t cpy) { } return ret; } - +#endif /* CONTAINER_DISABLE_LINKED_LIST */ /* ----------------------------------------------------------------- */ /* ---------------------HASH MAP IMPLEMENTATION--------------------- */ /* ----------------------------------------------------------------- */ +#ifndef CONTAINER_DISABLE_HMAP void __hmap_ll_custom_free(void* data) { struct hmap_pair *pair = data; free(pair->key); @@ -1252,12 +1273,13 @@ char hmapi_ne(const struct hash_map_iter *a, const struct hash_map_iter* b) { char hmapi_end(const struct hash_map_iter* iter) { return iter->current_node == NULL || iter->bucket_pos == SIZE_MAX; } +#endif /* CONTAINER_DISABLE_HMAP */ /* ----------------------------------------------------------------- */ /* ---------------------HASH SET IMPLEMENTATION--------------------- */ /* ----------------------------------------------------------------- */ - +#ifndef CONTAINER_DISABLE_HSET struct hash_set hset_new(const size_t el_size, hset_equal_fn eq, hset_hash_fn hash) { struct hash_set ret = { .buckets = array_new(struct linked_list, HMAP_INIT_SIZE), @@ -1416,8 +1438,9 @@ char hseti_ne(const struct hash_set_iter *a, const struct hash_set_iter* b) { char hseti_end(const struct hash_set_iter* iter) { return iter->current_node == NULL || iter->bucket_pos == SIZE_MAX; } +#endif /* CONTAINER_DISABLE_HSET */ #endif /* CONTAINER_IMPLEMENTATION */ #endif /* JUSTANOTHERCATGIRL_HEADERS_CONTAINER */ - +#endif /* CONTAINER_DISABLE_ALL */ /* vim: set ts=8 noet: */ diff --git a/include/dynstring.h b/include/dynstring.h deleted file mode 100644 index b3b23a0..0000000 --- a/include/dynstring.h +++ /dev/null @@ -1 +0,0 @@ -/* vim: set ts=8 noet: */ diff --git a/include/stringbuilder.h b/include/stringbuilder.h new file mode 100644 index 0000000..e695082 --- /dev/null +++ b/include/stringbuilder.h @@ -0,0 +1,114 @@ +#ifndef JAC_STRINGBUILDER +#define JAC_STRINGBUILDER + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef struct jac_sb { + char *data; + size_t size; + size_t cap; +} jac_sb; + +jac_sb jac_sb_empty(void); +jac_sb jac_sb_new(size_t size); +jac_sb jac_sb_from_buf(const char* buf); +jac_sb jac_sb_from_buf_n(const char* buf, size_t size); + +void jac_sb_append_buf(jac_sb *sb, const char *c); +void jac_sb_append_buf_n(jac_sb *sb, const char *c, size_t n); +void jac_sb_putc(jac_sb *sb, char c); +void jac_sb_snprintf(jac_sb *sb, const char* fmt, ...); +void jac_sb_vsnprintf(jac_sb *sb, const char* fmt, va_list list); + +void jac_sb_free(jac_sb sb); + +#define STRINGBUILDER_IMPLEMENTATION +#ifdef STRINGBUILDER_IMPLEMENTATION + +#define __SB_REALLOC(sb, n) \ +do { \ + if (sb->size + n >= sb->cap) { \ + size_t rsz = 1UL << \ + (64 - __builtin_clzl((unsigned long)(sb->size+n))); \ + sb->data = realloc(sb->data, rsz + 1); \ + sb->cap = rsz; \ + } \ + sb->size += n; \ +} while(0) \ + +jac_sb jac_sb_empty(void) { + return (jac_sb){.data = calloc(1, 1), .size = 0, .cap = 0}; +} +jac_sb jac_sb_new(size_t size) { + return (jac_sb){.data = calloc(size + 1, 1), .size = 0, .cap = size}; +} +jac_sb jac_sb_from_buf(const char* buf) { + size_t ln = strlen(buf); + jac_sb ret = {.data = malloc(ln + 1), .size = ln, .cap = ln}; + memcpy(ret.data, buf, ln); + ret.data[ret.size] = '\0'; + return ret; +} +jac_sb jac_sb_from_buf_n(const char* buf, size_t size) { + jac_sb ret = {.data = malloc(size + 1), .size = size, .cap = size}; + memcpy(ret.data, buf, size); + ret.data[size] = '\0'; + return ret; +} + +void jac_sb_append_buf(jac_sb *sb, const char *c) { + jac_sb_append_buf_n(sb, c, strlen(c)); +} +void jac_sb_append_buf_n(jac_sb *sb, const char *c, size_t n) { + __SB_REALLOC(sb, n); + memcpy(sb->data + sb->size - n, c, n); + sb->data[sb->size] = '\0'; +} +void jac_sb_putc(jac_sb *sb, char c) { + if (sb->size + 1 >= sb->cap) { + sb->cap <<= 1; + sb->data = realloc(sb->data, sb->cap + 1); + } + sb->data[sb->size++] = c; + sb->data[sb->size] = '\0'; +} + +void jac_sb_snprintf(jac_sb *sb, const char* fmt, ...) { + va_list l; + va_start(l, fmt); + jac_sb_vsnprintf(sb, fmt, l); +} +void jac_sb_vsnprintf(jac_sb *sb, const char* fmt, va_list list) { + va_list ls1; + size_t ln; +try_write: + /*cap: 4, size: 2 + * | a | b | \0 | \0 | \0 | + */ + va_copy(ls1, list); + ln = vsnprintf(sb->data + sb->size, sb->cap - sb->size + 1, fmt, ls1); + va_end(ls1); + if (ln >= sb->cap - sb->size + 1) { + sb->cap <<= 1; + sb->data = realloc(sb->data, sb->cap + 1); + goto try_write; + } + va_end(list); + sb->size += ln; +} + +void jac_sb_free(jac_sb sb) { + free(sb.data); +} + + +#undef __SB_REALLOC +#endif /* STRINGBUILDER_IMPLEMENTATION */ + +#endif /* JAC_STRINGBUILDER */ + +/* vim: set ts=8 noet: */ diff --git a/ste/.gitignore b/ste/.gitignore new file mode 100644 index 0000000..953abb8 --- /dev/null +++ b/ste/.gitignore @@ -0,0 +1,3 @@ +tmpl.c +main +ste diff --git a/ste/README.md b/ste/README.md new file mode 100644 index 0000000..680f9a4 --- /dev/null +++ b/ste/README.md @@ -0,0 +1,48 @@ +# Simple Template Engine for C +The idea is stolen from [Tsoding](https://github.com/tsoding). + +This is a template engine for the C programming language. +A really simple one, to be honset. To see how it works, it's best to just check +out examples, but in a nutshell: initially, everything is a string. After `%` +sign, it becomes C source code. basically, `%` sign acts as a flip-flop switch +between C and text. All of the text is wrapped inside the `OUT` macro, which +must be defined in the source code (name may be customized via command-line +arguments). To use the code, `#include generated.c` in the middle of function +where you need this template. + +# Example +`main.c`: +```c +#define STRINGBUILDER_IMPLEMENTATION +#include "stringbuilder.h" /* refer to [c_headers](https://git.twistea.su/c_headers) */ +#include <stdio.h> + +int main(void) { + jac_sb sb = jac_sb_empty(); + #define OUT(str) jac_sb_append_buf(&sb, str) + #define INT(i) jac_sb_snprintf(&sb, "%i", i); /* Note the semicolon */ + #include "tmpl.c" + #undef OUT + printf("%s\n", sb.data); + jac_sb_free(sb); + return 727; +} +``` + +template file (`tmpl.c.ste`): +```html +<!DOCTYPE html> +<html> + <body> + % + for (int i = 0; i < 10; ++i) { + %<p>This is paragraph number % INT(i) %</p>% + } + % + </body> +</html> +``` + +Build: `cc ste.c -o ste && ste tmpl.c.ste && cc main.c -o main` + +<!--vim:tw=80--> diff --git a/ste/example/Makefile b/ste/example/Makefile new file mode 100644 index 0000000..f8a6ef4 --- /dev/null +++ b/ste/example/Makefile @@ -0,0 +1,14 @@ + +.SUFFIXES: + +all: main + +ste: ../ste.c + $(CC) -o $@ $< + +%.c: %.c.ste ste + ./ste -m OUTPUT $< + +main: main.c tmpl.c + $(CC) -o $@ $< + diff --git a/ste/example/main.c b/ste/example/main.c new file mode 100644 index 0000000..18850d9 --- /dev/null +++ b/ste/example/main.c @@ -0,0 +1,15 @@ +#define STRINGBUILDER_IMPLEMENTATION +#include "../../include/stringbuilder.h" /* refer to [c_headers](https://git.twistea.su/c_headers) */ +#include <stdio.h> + +int main(void) { + jac_sb sb = jac_sb_empty(); + #define OUTPUT(str) jac_sb_append_buf(&sb, str) + #define INTEGER(i) jac_sb_snprintf(&sb, "%i", i); /* Note the semicolon */ + # include "tmpl.c" + #undef OUTPUT + #undef INTEGER + printf("%s\n", sb.data); + jac_sb_free(sb); + return 727; +} diff --git a/ste/example/tmpl.c.ste b/ste/example/tmpl.c.ste new file mode 100644 index 0000000..7f486cd --- /dev/null +++ b/ste/example/tmpl.c.ste @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<html> + <body> + % + for (int i = 0; i < 10; ++i) { + %<p>This is paragraph number % INTEGER(i) %</p>% + } + % + </body> +</html> + diff --git a/ste/ste.c b/ste/ste.c new file mode 100644 index 0000000..43252b9 --- /dev/null +++ b/ste/ste.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <string.h> + +void process_files(FILE* in, FILE* out, const char *macro) { + enum {MODESTR = 0, MODESRC = 1} a = MODESTR; + fprintf(out, "%s(\"", macro); + for (;;) { + int c = fgetc(in); + if (feof(in)) break; + if (c == '%') { + a = !a; + if (a == MODESTR) fprintf(out, " %s(\"", macro); + else fputs("\");", out); + continue; + } + if (a == MODESTR) fprintf(out, "\\x%02X", c); + else (fputc(c, out)); + } + if (a == MODESTR) fputs("\");", out); +} + +int main(int argc, char **argv) { + const char *macro = "OUT"; + char *filename = NULL; + FILE *in, *out; + + if (argc < 2) { + usage: + fprintf(stderr, "Usage: %s [-m name] <file.ste>\n", *argv); + return 1; + } + + for (int i = 0; i < argc; ++i) { + if (!strcmp("-m", argv[i])) macro = argv[++i]; + else filename = argv[i]; + } + if (!filename || strlen(filename) <= 4) goto usage; + + in = fopen(filename, "r"); + for (char *fn = filename; fn[3]; ++fn) { + if (!strncmp(".ste", fn, 4)) { + *fn = '\0'; + break; + } + } + out = fopen(filename, "w"); + if (!in || !out) { + perror("fopen"); + goto usage; + } + + process_files(in, out, macro); + + fclose(in); + fclose(out); + return 0; +} diff --git a/tests/sb.c b/tests/sb.c new file mode 100644 index 0000000..8542a88 --- /dev/null +++ b/tests/sb.c @@ -0,0 +1,37 @@ +#define STRINGBUILDER_IMPLEMENTATION +#include "../include/stringbuilder.h" + +#include <stdio.h> + +#define PRINTF(sb) printf("jac_sb { .data = \"%s\", .size = %zu, .cap = %zu }\n", sb.data, sb.size, sb.cap) + +int main() { + jac_sb sb = jac_sb_from_buf("Hello, world!"); + PRINTF(sb); + for (size_t i = 0; i < 10; ++i) { + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + jac_sb_append_buf(&sb, " Actually this is a very long sentence that will probably cause several reallocations "); + } + // PRINTF(sb); + jac_sb_free(sb); + + sb = jac_sb_empty(); + jac_sb_append_buf(&sb, "<!DOCTYPE html><html><body><ul>"); + for (size_t i = 0; i < 50; ++i) + jac_sb_snprintf(&sb, "<li>element %i</li>\n", i); + jac_sb_append_buf(&sb, "</ul></body></html>"); + for (size_t i = 0; i < 2048; ++i) + jac_sb_putc(&sb, 69); + PRINTF(sb); + if (sb.size != 3088 || sb.cap != 4096) return 1; + jac_sb_free(sb); + return 0; +} |