Overview
38-Moths
- Macros
-
Type Definitions
- Enums
- Structures
-
Functions
- m38_set_404_handler
- m38_set_404_cleanup
- m38_set_500_handler
- m38_parse_range_header
- m38_get_header_value_request
- m38_get_header_value_raw
- m38_parse_request
- m38_parse_body
- m38_mmap_file
- m38_render_file
- m38_return_raw_buffer
- m38_heap_cleanup
- m38_heap_cleanup_no_check
- m38_mmap_cleanup
- m38_generate_response
- m38_send_response
- gshkl_init_context
- gshkl_free_context
- gshkl_add_string
- gshkl_add_int
- gshkl_add_array
- gshkl_add_string_to_loop
- gshkl_add_int_to_loop
- gshkl_add_sub_context_to_loop
- gshkl_add_sub_context
- gshkl_add_filter
- gshkl_filter_cleanup
- endswith
- strnstr
- get_file_creation_date
- get_file_size
- hash_string_fnv1a
- m38_get_cookie_value
- vector_new
- vector_append
- vector_append_ptr
- vector_get
- vector_reverse
- vector_free
Greshunkel ¶
Introduction ¶
GRESHUNKEL is the super weird templating language built for 38-Moths. It was originally built as a stand-alone static site generator, but now it's morphed into several disparate implementations.
GRESHUNKEL uses the X-Box Live syntax, which is mostly just xXx
sprinkled
everywhere, for funsies. A good place to view different syntax and implementation
details is on the GREHSUNKEL page for 38-Moths.
Specific C-Level API details can be found on the Documentation page.
Filters ¶
GRESHUNKEL has the ability to blend in custom filters (Think Django-style Python functions that can be called while evaluating a template). Here's a trivial example:
<!DOCTYPE html>
<!-- index.html -->
<html>
<body>
<p>XxX return_z "Test Argument" XxX</p>"
<p>XxX return_z xXx @i xXx XxX</p>"
</body>
<html>
And then for the C portion:
/* The filter function itself. */
char *return_z(const char *argument) {
UNUSED(argument);
return "z";
}/* The template handling code. */
int template_example(const m38_http_request *request, m38_http_response *response) {
greshunkel_ctext *ctext = gshkl_init_context();
gshkl_add_filter(ctext, "return_z", &return_z, NULL);
gshkl_add_int(ctext, "i", 10);
return m38_render_file(ctext, "./index.html", response);
}
Theres a couple things going on here, but it's pretty simple. GRESHUNKEL filters
are things that just take string arguments and return strings. You have the
option to pass in a clean-up handler when adding the filter, which is an
opportune time to add something to free()
memory.
Python Bindings ¶
GREHSUNKEL has Python bindings, which are AWESOME. I don't think I've used them for anything but I wrote them anyway. It's pretty simple to use, and very Flask/Django-esque. Heres an example, it's pretty much as you'd expect:
from greshunkel import Template, Context, GshklFilterFunctest_template =\
"""
<html>
<body>
xXx SCREAM _include.html xXx
xXx LOOP i LOOP_TEST xXx
<li>xXx @TEST xXx xXx @i xXx</li>
xXx BBL xXx
<span>This is the real xXx @TRICKY xXx xXx @ONE xXx</span>
<p>This is a regular string: xXx @TEST xXx</p>
<p>This is an integer: xXx @FAKEINT xXx</p>
<ul>
xXx LOOP i LOOP_TEST xXx
<li>XxX return_z xXx @i xXx XxX</li>
xXx BBL xXx
</ul>
<p>Context Interpolation:</p>
<p>xXx @sub.name xXx - xXx @sub.other xXx</p>
<p>XxX return_hello doesnt_matter_at_all XxX</p>
xXx LOOP subs SUB_LOOP_TEST xXx
<p>FILTERS IN FILTERS IN LOOPS: XxX return_z F XxX</p>
<p>XxX return_hello f XxX</p>
<p>xXx @subs.name xXx - xXx @subs.other xXx</p>
xXx BBL xXx
</body>
</html>
"""
def return_hello(arg):
return b"test"
def return_z(arg):
return b"z"
def main():
return_helloc = GshklFilterFunc(return_hello)
return_zc = GshklFilterFunc(return_z)
context = Context({
"TEST": "This is a test.",
"FAKEINT": 666,
"TRICKY": "TrIcKy",
"ONE": 1,
"LOOP_TEST": ["a", "b", "c", 1, 2, 3],
"SUB_LOOP_TEST": [
{"name": "One", "other": 1 },
{"name": "Two", "other": 2 },
{"name": "Three", "other": 3 },
],
"return_hello": return_helloc,
"return_z": return_zc,
"sub": { "name": "test", "other": 777 },
})
template = Template(test_template)
print(template.render(context))
The main idea is that you define a Context()
, a Template()
and add whatever
filters you want to the context as well (here we use return_zc
and
return_hello
). As far as I know it didn't crash the last time I ran it.
Conditionals ¶
GRESHUNKEL uses the UNLESS
and UNLESS NOT
operators from Ruby, which are
objectively easier to think about than if
and if not
. If you're having
trouble thinking about which is which because you grew up writing C, then just
think of them as if
statements with an extra inversion, so unless
becomes
if not
and unless not
becomes if not not
becomes if
. Get it? Easy!
xXx UNLESS @user xXx
No user.
xXx ENDLESS xXx
This will display No user.
if the @user
variable is not set in the
greshunkel_context
. Heres a rough guide to truthy/falsey statements in
GRESHUNKEL. There does not exist a boolean type, so good luck.
subcontexts = True
integers = False
strings = True as long as they aren't the string FALSE
arrays = False
38-Moths
Macros ¶
VERSION ¶
#define VERSION "0.4"
The current version of the 38-Moths.
VERB_SIZE ¶
#define VERB_SIZE 16
The maximum size of an HTTP verb.
MAX_MATCHES ¶
#define MAX_MATCHES 4
The maximum number of matches one can have on a given url.
MAX_READ_LEN ¶
#define MAX_READ_LEN 1024
The maximum amount of bytes to be read when receiving a request.
MAX_REQUEST_SIZE ¶
#define MAX_REQUEST_SIZE 2097152
The maximum amount of bytes to be read from any single request. Default set to 2Mb.
RESPONSE_OK ¶
#define RESPONSE_OK(status_code) (status_code >= 200 && status_code < 400)
Macro used to check whether a status code is 'good'.
WISDOM_OF_WORDS ¶
#define WISDOM_OF_WORDS 32
The maximum size of a greshunkel variable name.
MAX_GSHKL_STR_SIZE ¶
#define MAX_GSHKL_STR_SIZE 512
The maximum size of a greshunkel string.
INT_LEN(x) ¶
#define INT_LEN(x) x == 0 ? 1 : floor(log10(abs(x))) + 1
Returns the length of an integer if it were rendered as a string.
UINT_LEN(x) ¶
#define UINT_LEN(x) x == 0 ? 1 : floor(log10(x)) + 1
Returns the length of an unsigned integer if it were rendered as a string.
UNUSED(x) ¶
#define UNUSED(x) (void)x
If an argument is unused, use this to avoid compiler warnings. Use with care.
HASH_STR_SIZE ¶
#define HASH_STR_SIZE 65
The length of a 64-character hash string + NULL terminator. Used for the fnv1a function.
Type Definitions ¶
Enums ¶
greshunkel_type ¶
typedef enum {
GSHKL_ARR,
GSHKL_STR,
GSHKL_SUBCTEXT
} greshunkel_type;
Used to tell greshunkel_var
s apart.
GSHKL_ARR: A greshunkel array.
GSHKL_STR: A greshunkel string.
Structures ¶
m38_range_header ¶
typedef struct {
const size_t limit;
const size_t offset;
} m38_range_header;
Used to represent the range header in HTTP requests.
limit: The limit of the range, eg. the length.
offset: The offset, in the file, that the user has requested.
m38_http_request ¶
typedef struct {
char verb[VERB_SIZE];
char resource[512];
regmatch_t matches[MAX_MATCHES];
char *full_header;
size_t header_len;
unsigned char *full_body;
size_t body_len;
struct sparse_dict *form_elements;
} m38_http_request;
A representation of an HTTP request object. This will be passed to views.
verb: The HTTP verb for the given request.
resource[128]: The path for this request. (eg. '/articles/182')
matches: Any REGEX matches from your path are stored here.
header_len: The length of the header, or 0.
*full_header: The full header text of the request.
body_len: The length of the POST body, or 0.
*full_body: The full body of the request.
*form_elements: If the requests was submitted as Form-Encoded, and parsed correctly, this is the populated dictionary of pairs.
m38_http_response ¶
typedef struct {
unsigned char *out;
size_t outsize;
char mimetype[32];
void *extra_data;
struct vector *extra_headers;
} m38_http_response;
Fill this out and return it, signed by your parents. Only *out
and outsize
are really necessary.
*out: A buffer of characters that will be written back to the requester.
outsize: The size of out
.
mimetype[32]: Optional, will be inferred from the m38_http_request's file extension if left blank.
*extra_data: Optional, use this to pass things to the clean up function. For instance, mmap_file() uses extra_data
to store the size of the file allocated.
*extra_headers: A vector containing extra headers. Use `insert_custom_header` to manage this parameter.
m38_header_pair ¶
typedef struct {
const char *header;
const size_t header_len;
const char *value;
const size_t value_len;
} m38_header_pair;
Object used to hold extra header information in an m38_http_request object.
*header: The actual header, eg. "Content-Length"
header_len: Length of the header, in bytes.
*value: The value of the header, eg. "1762"
value_len: Length of the value, in bytes.
m38_route ¶
typedef struct {
char verb[VERB_SIZE];
char name[64];
char route_match[256];
size_t expected_matches;
int (*handler)(const m38_http_request *request, m38_http_response *response);
void (*cleanup)(const int status_code, m38_http_response *response);
} m38_route;
An array of these is how 38-Moths knows how to route requests.
verb[VERB_SIZE]: The verb that this route will handle.
name[64]: The name of the route. Used only logging.
route_match[256]: The POSIX regular expression used to match the route to your handler.
(*handler): A function pointer to your route handler.
(*cleanup): If your route needs to do any cleanup (eg. de-allocating memory), this function will becalled when 38-Moths is done with it.
m38_app ¶
typedef struct {
int *main_sock_fd;
int port;
int num_threads;
const m38_route *routes;
const int num_routes;
State and data information for a 38-Moths instance.
*main_sock_fd: A pointer to the main socket fd. This is a pointer so you can handle SIG* cleanly and shut down the socket.
port: The main port to run the server on. Like 8080. Or something.
num_threads: The number of threads to use to handle requests.
*routes: The array of all routes for your application.
num_routes: The number of routes in *routes
.
*r_404_route: A route representing a custom 404 handler.
*r_error_route: A route representing a custom 500/error handler.
greshunkel_ctext ¶
typedef struct greshunkel_ctext {
vector *values;
vector *filter_functions;
const struct greshunkel_ctext *parent;
} greshunkel_ctext;
The context is a collection of variables, filters and other stuff that will be used to render a file.
*values: The values stored in this context. Strings, arrays, etc.
*filter_functions: The filter functions stored in this context.
*parent: Used internally, this is used to recurse back up to the parent context if a variable is unavailable in the current one.
greshunkel_named_item ¶
typedef struct greshunkel_named_item {
char name[WISDOM_OF_WORDS];
} greshunkel_named_item;
Dirty fucking hack to have a generic baseclass-like thing for both greshunkel_tuple
and greshunkel_filter
objects.
name[WISDOM_OF_WORDS]: The name of this thing. Somehow works. C is fucking scary.
greshunkel_tuple ¶
typedef struct greshunkel_tuple {
char name[WISDOM_OF_WORDS];
const greshunkel_type type;
greshunkel_var value;
} greshunkel_tuple;
Basically the representation of a GRESHUNKEL variable inside of a context.
name[WISDOM_OF_WORDS]: The name of this thing.
type: The type of this thing.
value: The actual value of this thing.
greshunkel_filter ¶
typedef struct greshunkel_filter {
char name[WISDOM_OF_WORDS];
char *(*filter_func)(const char *argument);
void (*clean_up)(char *result);
} greshunkel_filter;
A function that can be applied during template rendering.
name[WISDOM_OF_WORDS]: The name of this thing.
*(*filter_func): Function pointer to the C function you want to have access to in the template.
(*clean_up): If your filter needs any kind of cleanup, set this to a function other than null and you can do whatever.
vector ¶
typedef struct vector {
const size_t item_size;
size_t max_size;
size_t count;
void *items;
} vector;
A simple vector object. Auto-expands and whatever.
item_size: The maximum size of each item.
max_size: Used internally to track the current vector's maximum number of elements.
count: Used internally to track the current vector's current count of items.
*items: The actual memory used for the items stored.
Functions ¶
m38_set_404_handler ¶
int m38_set_404_handler(m38_app *app,
int (*handler)(const m38_http_request *request, m38_http_response *response));
Sets the internal 404 handler. Useful for templating your 404 page.
*app: The app to serve.
*handler: The function pointer to your handler function..
Returns: 0 on success, -1 on failure.
m38_set_404_cleanup ¶
int m38_set_404_cleanup(m38_app *app,
void (*cleanup)(const int status_code, m38_http_response *response));
Sets the cleanup for your custom 404 handler.
*app: The app to serve.
*cleanup: The function pointer to your cleanup function.
Returns: 0 on success, -1 on failure.
m38_set_500_handler ¶
//int m38_set_500_handler(m38_app *app,
// int (*handler)(const m38_http_request *request, m38_http_response *response));
Sets the internal error handler. Useful for templating your internal error page.
*app: The app to serve.
Returns: 0 on success, -1 on failure.
m38_parse_range_header ¶
m38_range_header m38_parse_range_header(const char *range_query);
Figures out the range header for a request, if present.
*range_query: The text from the header.
Returns: Returns a 0/0 range header on failure/absence, or the correct limit/offset if present.
m38_get_header_value_request ¶
char *m38_get_header_value_request(const m38_http_request *req, const char header[static 1]);
Gets the value of `header` (eg. Content-Length) from an http_request object. Wraps get_header_value_raw.
Returns: The char string representing the header value, or NULL. Must be free'd.
m38_get_header_value_raw ¶
char *m38_get_header_value_raw(const char *request, const size_t request_siz, const char header[static 1]);
Gets the value of `header` (eg. Content-Length) from a raw http request string.
Returns: The char string representing the header value, or NULL. Must be free'd.
m38_parse_request ¶
int m38_parse_request(const unsigned char *, const size_t, m38_http_request *);
Turns a raw string buffer into an http_request object.
Returns: 0 on sucess, -1 on failure.
m38_parse_body ¶
int m38_parse_body(const size_t received_body_len, const size_t content_length_num,
const unsigned char *raw_request, m38_http_request *request);
Figures out the actual body on the HTTP request and sticks it into the request object.
Returns: 0 on sucess, -1 on failure.
m38_mmap_file ¶
int m38_mmap_file(const char *file_path, m38_http_response *response);
The primary way of serving static assets in 38-Moths. mmap()'s a file into memory and writes it to the requester.
*file_path: The file to mmap().
*response: The m38_http_response
object your handler was passed.
Returns: An HTTP status code. 200 on success, 404 on not found, etc.
m38_render_file ¶
int m38_render_file(const struct greshunkel_ctext *ctext, const char *file_path, m38_http_response *response);
The easiest way to render a file with GRESHUNKEL.
*ctext: The context you want your file to have. This should contain all variables, loops, etc.
*file_path: The template to render.
*response: The m38_http_response
object your handler was passed.
Returns: An HTTP status code. 200 on success, 404 on not found, etc.
m38_return_raw_buffer ¶
int m38_return_raw_buffer(const char *buf, const size_t buf_size, m38_http_response *response);
If you just want to return a raw string, use this.
*buf: The string to send back to the client.
buf: The length of the buffer, in bytes.
*response: The m38_http_response
object your handler was passed.
Returns: An HTTP status code. 200 on success, 404 on not found, etc.
m38_heap_cleanup ¶
void m38_heap_cleanup(const int status_code, m38_http_response *response);
Simple function that free()
's memory in out
, but only if the response code was OK.
status_code: The status code returned from the handler.
*response: The m38_http_response
object returned from the handler.
Returns: Nothing.
m38_heap_cleanup_no_check ¶
void m38_heap_cleanup_no_check(const int status_code, m38_http_response *response);
Simple function that free()
's memory in out
, always. Good for a custom 404 handler. See m38_heap_cleanup
.
status_code: The status code returned from the handler.
*response: The m38_http_response
object returned from the handler.
Returns: Nothing.
m38_mmap_cleanup ¶
void m38_mmap_cleanup(const int status_code, m38_http_response *response);
The cleanup handler for mmap_file
. Expects *extradata
to be a struct stat
object.
status_code: The status code returned from the handler.
*response: The m38_http_response
object returned from the handler.
Returns: Nothing.
m38_generate_response ¶
m38_handled_request *m38_generate_response(const int accept_fd, const m38_app *app);
Generates and HTTP response from an accepted connection
accept_fd: The successfully accept(2)
'd file descriptor for the requester's socket.
*app: The app to serve routes from.
Returns: A fully formatted HTTP response, NULL otherwise.
m38_send_response ¶
m38_handled_request *m38_send_response(m38_handled_request *hreq);
Takes a handled_request object rom generate_response and sends chunks of it down the wire.
*hreq: A handled_request object either from send_response or generate_response.
Returns: If there is anything left to send, an updated handled_request will be returned. NULL will be reutnred when the object has either been fully sent, or errored out.
gshkl_init_context ¶
greshunkel_ctext *gshkl_init_context();
Used to create a new, empty GRESHUNKEL context.
Returns: A new greshunkel context.
gshkl_free_context ¶
void gshkl_free_context(greshunkel_ctext *ctext);
Frees an cleans up a previously created context.
Returns: Nothing.
gshkl_add_string ¶
int gshkl_add_string(greshunkel_ctext *ctext, const char name[WISDOM_OF_WORDS], const char *value);
Adds a string with the given name to a context.
*ctext: The context to add the string to.
name[WISDOM_OF_WORDS]: The name used to reference this variable later.
*value: The NULL terminated string that will be returned later.
Returns: 0 on success, 1 otherwise.
gshkl_add_int ¶
int gshkl_add_int(greshunkel_ctext *ctext, const char name[WISDOM_OF_WORDS], const int value);
Adds an integer with the given name to a context.
*ctext: The context to add the integer to.
name[WISDOM_OF_WORDS]: The name used to reference this variable later.
value: The integer that will be added to this context.
Returns: 0 on success, 1 otherwise.
gshkl_add_array ¶
greshunkel_var gshkl_add_array(greshunkel_ctext *ctext, const char name[WISDOM_OF_WORDS]);
Creates a new array object inside of the given context.
*ctext: The context to add the array to.
name[WISDOM_OF_WORDS]: The name used to reference this variable later.
Returns: The newly created loop object.
gshkl_add_string_to_loop ¶
int gshkl_add_string_to_loop(greshunkel_var *loop, const char *value);
Adds a string to a greshunkel array.
*loop: A pointer to a loop created with gshkl_add_array
.
*value: The NULL terminated string to be added.
Returns: 0 on success, 1 otherwise.
gshkl_add_int_to_loop ¶
int gshkl_add_int_to_loop(greshunkel_var *loop, const int value);
Adds an integer to a greshunkel array.
*loop: A pointer to a loop created with gshkl_add_array
.
value: The integer to be added.
Returns: 0 on success, 1 otherwise.
gshkl_add_sub_context_to_loop ¶
int gshkl_add_sub_context_to_loop(greshunkel_var *loop,
const greshunkel_ctext *child);
Adds a name sub-context to a loop.
*loop: The loop to add the context to.
*child: The pre-built child context.
Returns: 0 on success, 1 otherwise.
gshkl_add_sub_context ¶
int gshkl_add_sub_context(greshunkel_ctext *parent,
const char name[WISDOM_OF_WORDS],
const greshunkel_ctext *child);
Adds a name sub-context to a parent context. Sub-context values can be references via the 'x Xx @
*parent: The parent context to add the child to.
name: The name of the child context.
*child: The pre-built child context.
Returns: 0 on success, 1 otherwise.
gshkl_add_filter ¶
int gshkl_add_filter(greshunkel_ctext *ctext,
const char name[WISDOM_OF_WORDS],
char *(*filter_func)(const char *argument),
void (*clean_up)(char *filter_result));
Adds a filter function to the given context.
*ctext: The context that the filter will be added to.
name[WISDOM_OF_WORDS]: The name used to reference this filter function.
(*filter_func): The function that will be called from the template.
(*clean_up): The clean up function that will be called after GRESHUNKEL is done calling filter_func
.
Returns: 0 on success, 1 otherwise.
gshkl_filter_cleanup ¶
void gshkl_filter_cleanup(char *result);
Commonly used helper clean-up function that just calls free on the result.
*result: The value obtained from calling your filter function.
Returns: Nothing.
endswith ¶
int endswith(const char *string, const char *suffix);
Helper function determine if a string ends with another string.
*string: The source string to test.
*suffix: The suffix to check for.
Returns: 1 if string
ends with suffix
.
strnstr ¶
char *strnstr(const char *haystack, const char *needle, size_t len);
Like strstr, but only checks up to n chars.
*haystack: Go look at strstr().
*needle: Go look at strstr().
len: Maximum number of characters to look through.
Returns: Whatever the hell strstr() returns.
get_file_creation_date ¶
time_t get_file_creation_date(const char *file_path);
Gets the file creation date.
*file_path: The path of the file to get the creation date of.
Returns: The file creation date.
get_file_size ¶
size_t get_file_size(const char *file_path);
Gets the file size.
*file_path: The path of the file to get the size of.
Returns: The file size.
hash_string_fnv1a ¶
int hash_string_fnv1a(const unsigned char *string, const size_t siz, char outbuf[static HASH_STR_SIZE]);
Hashes a string using the FNV-1a algorithm
*string: The string to hash.
siz: The size of the string.
outbuf[static HASH_STR_SIZE]: This buffer will be filled out with the hashed string.
Returns: 1. Always. I don't care what you think.
m38_get_cookie_value ¶
char *m38_get_cookie_value(const char *cookie_string, const size_t cookie_string_siz, const char *needle);
Gets a specific value out of a cookie, like the session ID.
*cookie_string: The value of the entire cookie string to parse, which comes from m38_get_header_value.
cookie_string_siz: The length of cookie_string.
needle: The value you're looking for inside the cookie, like `sessionid`.
Returns: NULL or the value of the specific cookie value.
vector_new ¶
vector *vector_new(const size_t item_size, const size_t initial_element_count);
Creates a new vector object.
item_size: The maximum size of each item.
initial_element_count: If you know your amount of objects ahead of time, set this accordingly. Otherwise just guess. The closer you get the fewer mallocs will happen.
Returns: A new vector object.
vector_append ¶
int vector_append(vector *vec, const void *item, const size_t item_size);
Adds a new element to a vector.
*vec: The vector to add the new item to.
*item: The item to add to the vector.
item_size: The size of the item to be added.
Returns: 1 on success.
vector_append_ptr ¶
int vector_append_ptr(vector *vec, const void *pointer);
Similar to vector_append but copies just the pointer value, not what it points to.
*vec: The vector to add the pointer to.
*pointer: The item to add to the vector.
Returns: 1 on success.
vector_get ¶
const void *vector_get(const vector *vec, const unsigned int i);
Gets the nth element of the given vector.
*vec: The vector to get the item from.
i: The item you want to retrieve.
Returns: A constant pointer to the nth item in the vector.
vector_reverse ¶
int vector_reverse(vector *vec);
Reverses the vector, from beginning to end.
*vec: The vector to be reversed.
Returns: 1 on success.
vector_free ¶
void vector_free(vector *to_free);
Cleans up and removes a vector's allocated memory.
*to_free: The vector to free.
Returns: Nothing.