diff --git a/.gitignore b/.gitignore
index f50972e..cba7efc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
a.out
-hashmap.c
\ No newline at end of file
diff --git a/README.md b/README.md
index 918960d..d647f50 100644
--- a/README.md
+++ b/README.md
@@ -35,11 +35,12 @@ Wow! Looks pretty similar to the Express functions. Below lays out the current f
1. [teru() -- new Teru](#New-Teru)
## App functionalities:
-2. [app_use()](#Use-Teru)
-3. [app_set()](#Set-Teru)
+2. [app_use() -- Public directories and library usage](#Use-Teru)
+3. [app_set() -- Views directory](#Set-Teru)
## Send functionalities:
-4. [res_sendFile()](#Send-File-to-User)
-5. [res_end()](#Send-Message-to-User)
+4. [res_sendFile() -- Send a file](#Send-File-to-User)
+5. [res_end() -- Send a string](#Send-Message-to-User)
+6. [res_render() -- Send a file with match keys that replace to allow for dynamic HTML pages](#Render-File-to-User)
## Request parameters:
6. [req_query()](#See-Request-Query-Parameters)
7. [req_body()](#See-Request-Body-Parameters)
@@ -112,6 +113,30 @@ Then connect to an endpoint:
app_get(app, "/hi", hello_there);
```
+# Render File to User
+This allows for a server to take an HTML page and find and replace occurences of _match strings_. There are a few steps for setting this up:
+- Create an HTML file with match strings. The _start match_ and _end match_ can be whatever strings you wish. However, these strings must match what you give the `render()` function in the following steps.
+
+```HTML
+
+
+ Render Example
+
+
+
Hi there {{NAME}}!
+
+
+```
+- Next, set the match parameters using the `res_matches()` function, which for the previous example would look like the following. Note that `res` references the second parameter of the handler function (see [res_sendFile](#Send-File-to-User) for an example).
+```C
+res_matches(res, "NAME", "charlie-map");
+```
+- Finally, use the `res_render()` function to interpret the match strings and send the result to the user. `res_render()` takes in `res`, the name of the file, and the _start match_ and _end match_. Assuming the above HTML file is named "home.html", the `res_render()` call would look like:
+
+```C
+res_render(res, "home", "{{", "}}");
+```
+
# See Request Query Parameters
Request query parameters are added at the end of the URL (for example `localhost:8888/hi?name=Charlie`). `req_query()` allows access to these by inserting the name of the query parameter:
@@ -147,7 +172,5 @@ app_post(app, "/hi", hello_name);
# Teru's Future
Currently there are a few bucket list items that will be check off over time. However, feel free to [leave an issue](https://github.com/charlie-map/wiki_backend/issues) with any suggested enhancements.
-- `app_use()` functionality
- `app_put()`, `app_delete()`, etc.
- more `app_set()` functionality
-- `res_render()` see [Mustache Express](https://www.npmjs.com/package/mustache-express)
diff --git a/compile b/compile
index f97213b..fd6c718 100755
--- a/compile
+++ b/compile
@@ -1 +1 @@
-gcc -g server.c teru.c hashmap.c request.c -pthread -lm
\ No newline at end of file
+gcc -g server.c teru.c hashmap.c request.c typeinfer.c -pthread -lm
\ No newline at end of file
diff --git a/hashmap.c b/hashmap.c
new file mode 100644
index 0000000..c48e18f
--- /dev/null
+++ b/hashmap.c
@@ -0,0 +1,718 @@
+#include
+#include
+#include
+#include
+#include "hashmap.h"
+
+/*
+ vtableKeyStore is used for using unknown type keys in the
+ hashmap. Because the variable type is unknown, the use
+ of this struct plus the function (some function name)__hashmap() can
+ be used to decipher any type of possible key in the hashmap
+
+ SEE bottom of the page for the function declarations and uses
+*/
+typedef struct VTable {
+ void *key;
+
+ // these define how we utilize the key
+ // DEFAULT behavior is using a char * setup
+ void (*printKey)(void *);
+ int (*compareKey)(void *, void *);
+ void (*destroyKey)(void *);
+} vtableKeyStore;
+
+typedef struct ll_def {
+ struct ll_def *next;
+
+ vtableKeyStore key;
+ int max__arrLength, arrIndex; // only for hash type 1
+ int isArray;
+ void *ll_meat; // single value pointer
+ // for hash__type = 0
+ // or array for
+ // hash__type = 1
+} ll_main_t;
+
+// will store a single hashmap
+struct Store {
+ int hashmap__size;
+ // takes a type of hashmap
+ // 0: replace value
+ // 1: append to growing value list
+ int hash__type;
+ ll_main_t **map;
+
+ // used for printing the hashmap values
+ void (*printer)(void *);
+ // destroying hashmap values
+ void (*destroy)(void *);
+};
+
+const int MAX_BUCKET_SIZE = 5;
+const int START_SIZE = 1023; // how many initial buckets in array
+
+unsigned long hash(unsigned char *str) {
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++) != 0)
+ hash = ((hash << 5) + hash) + c;
+
+ return hash;
+}
+
+
+// define some linked list functions (see bottom of file for function write outs):
+ll_main_t *ll_makeNode(vtableKeyStore key, void *value, int hash__type);
+int ll_insert(ll_main_t *node, vtableKeyStore key, void *payload, int hash__type, void (*destroy)(void *));
+
+ll_main_t *ll_next(ll_main_t *curr);
+
+int ll_print(ll_main_t *curr, void (*printer)(void *));
+
+int ll_isolate(ll_main_t *node);
+int ll_destroy(ll_main_t *node, void (destroyObjectPayload)(void *));
+
+
+hashmap *make__hashmap(int hash__type, void (*printer)(void *), void (*destroy)(void *)) {
+ hashmap *newMap = (hashmap *) malloc(sizeof(hashmap));
+
+ newMap->hash__type = hash__type;
+ newMap->hashmap__size = START_SIZE;
+
+ // define needed input functions
+ newMap->printer = printer;
+ newMap->destroy = destroy;
+
+ newMap->map = (ll_main_t **) malloc(sizeof(ll_main_t *) * newMap->hashmap__size);
+
+ for (int i = 0; i < newMap->hashmap__size; i++) {
+ newMap->map[i] = NULL;
+ }
+
+ return newMap;
+}
+
+// take a previous hashmap and double the size of the array
+// this means we have to take all the inputs and calculate
+// a new position in the new array size
+// within hash__m->map we can access each of the linked list pointer
+// and redirect them since we store the keys
+int re__hashmap(hashmap *hash__m) {
+ // define the sizes / updates to size:
+ int old__mapLength = hash__m->hashmap__size;
+ int new__mapLength = hash__m->hashmap__size * 2;
+
+ // create new map (twice the size of old map (AKA hash__m->map))
+ ll_main_t **new__map = (ll_main_t **) malloc(sizeof(ll_main_t *) * new__mapLength);
+
+ for (int set__newMapNulls = 0; set__newMapNulls < new__mapLength; set__newMapNulls++)
+ new__map[set__newMapNulls] = NULL;
+
+ int new__mapPos = 0;
+
+ for (int old__mapPos = 0; old__mapPos < old__mapLength; old__mapPos++) {
+ // look at each bucket
+ // if there is contents
+ while (hash__m->map[old__mapPos]) { // need to look at each linked node
+ // recalculate hash
+ new__mapPos = hash(hash__m->map[old__mapPos]->key.key) % new__mapLength;
+
+ // store the node in temporary storage
+ ll_main_t *currNode = hash__m->map[old__mapPos];
+
+ // extract currNode from old map (hash__m->map)
+ hash__m->map[old__mapPos] = ll_next(currNode); // advance root
+ ll_isolate(currNode); // isolate old root
+
+ // defines the linked list head in the new map
+ ll_main_t *new__mapBucketPtr = new__map[new__mapPos];
+
+ // if there is one, we have to go to the tail
+ if (new__mapBucketPtr) {
+
+ while (new__mapBucketPtr->next) new__mapBucketPtr = ll_next(new__mapBucketPtr);
+
+ new__mapBucketPtr->next = currNode;
+ } else
+ new__map[new__mapPos] = currNode;
+ }
+ }
+
+ free(hash__m->map);
+ hash__m->map = new__map;
+ hash__m->hashmap__size = new__mapLength;
+
+ return 0;
+}
+
+int METAinsert__hashmap(hashmap *hash__m, vtableKeyStore key, void *value) {
+ int mapPos = hash(key.key) % hash__m->hashmap__size;
+ int bucketLength = 0; // counts size of the bucket at mapPos
+
+ // see if there is already a bucket defined at mapPos
+ if (hash__m->map[mapPos])
+ bucketLength = ll_insert(hash__m->map[mapPos], key, value, hash__m->hash__type, hash__m->destroy);
+ else
+ hash__m->map[mapPos] = ll_makeNode(key, value, hash__m->hash__type);
+
+ // if bucketLength is greater than an arbitrary amount,
+ // need to grow the size of the hashmap (doubling)
+ if (bucketLength >= MAX_BUCKET_SIZE)
+ re__hashmap(hash__m);
+
+ return 0;
+}
+
+int ll_get_keys(ll_main_t *ll_node, void **keys, int *max_key, int key_index) {
+ while(ll_node) {
+ keys[key_index++] = ll_node->key.key;
+
+ if (key_index == *max_key) { // resize
+ *max_key *= 2;
+
+ keys = realloc(keys, sizeof(void *) * *max_key);
+ }
+
+ ll_node = ll_node->next;
+ }
+
+ return key_index;
+}
+
+// creates an array of all keys in the hash map
+void **keys__hashmap(hashmap *hash__m, int *max_key) {
+ int key_index = 0;
+ *max_key = 16;
+ void **keys = malloc(sizeof(void *) * *max_key);
+
+ for (int find_keys = 0; find_keys < hash__m->hashmap__size; find_keys++) {
+ if (hash__m->map[find_keys]) {
+ // search LL:
+ key_index = ll_get_keys(hash__m->map[find_keys], keys, max_key, key_index);
+ }
+ }
+
+ *max_key = key_index;
+
+ return keys;
+}
+
+/*
+ get__hashmap search through a bucket for the inputted key
+ the response varies widely based on hash__type
+
+ -- for all of them: NULL is return if the key is not found
+
+ TYPE 0:
+ Returns the single value that is found
+ TYPE 1:
+ Returns a pointer to a struct (hashmap__response) with an array that can be
+ searched through and a length of said array.
+ This array should be used with caution because accidental
+ free()ing of this array will leave the key to that array
+ pointing to unknown memory. However, the freeing of the
+ returned struct will be left to the user
+ -- TERU update: update for "wildcarding" the get request
+ use "i" to implement this and an
+ int is_match(void *, void *);
+ to compare keys
+ leave blank for no is_match option
+
+ use "w" to implement a weight function
+ int is_lower(void *, void *);
+ to compare values
+ leave blank for no is_lower option
+*/
+void *get__hashmap(hashmap *hash__m, void *key, char *ep, ...) {
+ if (!hash__m)
+ return NULL;
+
+ int (*is_match)(void *, void *) = NULL;
+ int (*is_lower)(void *, void *) = NULL;
+
+ va_list new_poly_reader;
+ va_start(new_poly_reader, ep);
+
+ for (int check_ep = 0; ep[check_ep]; check_ep++) {
+ if (ep[check_ep] == 'w') // found match
+ is_lower = va_arg(new_poly_reader, int (*)(void *, void *));
+ if (ep[check_ep] == 'i') // found match
+ is_match = va_arg(new_poly_reader, int (*)(void *, void *));
+ }
+
+ // get hash position
+ int mapPos;
+
+ for (mapPos = is_match ? 0 : hash(key) % hash__m->hashmap__size; mapPos < hash__m->hashmap__size; mapPos++) {
+ ll_main_t *ll_search = hash__m->map[mapPos];
+
+ // search through the bucket to find any keys that match
+ while (ll_search) {
+ if ((is_match && is_match(ll_search->key.key, key)) || ll_search->key.compareKey(ll_search->key.key, key)) { // found a match
+
+ // depending on the type and mode, this will just return
+ // the value:
+ if (hash__m->hash__type == 0)
+ return ll_search->ll_meat;
+ else {
+ hashmap__response *returnMeat = malloc(sizeof(hashmap__response));
+
+ if (ll_search->isArray) {
+ returnMeat->payload = ((void **) ll_search->ll_meat)[0];
+ returnMeat->next = NULL;
+
+ hashmap__response *set_intern_meat = returnMeat;
+
+ for (int set_return_meat = 1; set_return_meat < ll_search->arrIndex + 1; set_return_meat++) {
+ // if is_lower exists, find position in return:
+ if (is_lower) {
+ set_intern_meat = returnMeat;
+
+ // search for the position of the next value based on is_lower
+ while (set_intern_meat->next && is_lower(set_intern_meat->next->payload, ((void **) ll_search->ll_meat)[set_return_meat])) {
+ set_intern_meat = set_intern_meat->next;
+ }
+
+ hashmap__response *new_node_element = malloc(sizeof(hashmap__response));
+ new_node_element->payload = ((void **) ll_search->ll_meat)[set_return_meat];
+
+ // check for setting as head
+ if (is_lower(((void **) ll_search->ll_meat)[set_return_meat], set_intern_meat->payload)) {
+ returnMeat->next = returnMeat;
+ returnMeat = new_node_element;
+ } else { // insert as next of set_intern_meat
+ hashmap__response *curr_next = set_intern_meat->next;
+
+ // splicing ll_meat value into the linked list
+ set_intern_meat->next = new_node_element;
+ new_node_element->next = curr_next;
+ }
+
+ continue;
+ }
+
+ // otherwise add the value at the end of the linked list
+ set_intern_meat->next = malloc(sizeof(hashmap__response));
+ set_intern_meat = set_intern_meat->next;
+
+ set_intern_meat->payload = ((void **) ll_search->ll_meat)[set_return_meat];
+ }
+ } else { // define array
+ void *ll_tempMeatStorage = ll_search->ll_meat;
+
+ ll_search->max__arrLength = 2;
+ ll_search->arrIndex = 0;
+
+ ll_search->ll_meat = malloc(sizeof(void *) * ll_search->max__arrLength * 2);
+ ((void **) ll_search->ll_meat)[0] = ll_tempMeatStorage;
+
+ returnMeat->payload = ll_search->ll_meat;
+ returnMeat->next = NULL;
+
+ ll_search->isArray = 1;
+ }
+
+ return returnMeat;
+ }
+ }
+
+ ll_search = ll_next(ll_search);
+ }
+
+ if (!is_match)
+ break;
+ }
+
+ // no key found
+ return NULL;
+}
+
+// scroll through linked list and delete headers
+int clear__hashmap__response(hashmap__response *hr) {
+ while (hr) {
+ hashmap__response *n = hr->next;
+ free(hr);
+
+ hr = n;
+ }
+
+ return 0;
+}
+
+int print__hashmap(hashmap *hash__m) {
+ for (int i = 0; i < hash__m->hashmap__size; i++) {
+ if (hash__m->map[i]) {
+ printf("Linked list at index %d ", i);
+ ll_print(hash__m->map[i], hash__m->printer);
+ printf("\n");
+ }
+ }
+
+ return 0;
+}
+
+// uses the same process as get__hashmap, but deletes the result
+// instead. Unfortunately, the get__hashmap function cannot be
+// utilized in this context because when the linked list node
+// is being extracted, we need to know what the parent of
+// the node is
+int delete__hashmap(hashmap *hash__m, void *key) {
+ // get hash position
+ int mapPos = hash(key) % hash__m->hashmap__size;
+
+ ll_main_t *ll_parent = hash__m->map[mapPos];
+ ll_main_t *ll_search = ll_next(ll_parent);
+
+ // check parent then move into children nodes in linked list
+ if (ll_parent->key.compareKey(ll_parent->key.key, key)) {
+ // extract parent from the hashmap:
+ hash__m->map[mapPos] = ll_search;
+
+ ll_destroy(ll_parent, hash__m->destroy);
+
+ return 0;
+ }
+
+ // search through the bucket to find any keys that match
+ while (ll_search) {
+ if (ll_search->key.compareKey(ll_search->key.key, key)) { // found a match
+
+ // we can then delete the key using the same approach as above
+ // extract the key from the linked list
+ ll_parent->next = ll_next(ll_search);
+
+ ll_destroy(ll_search, hash__m->destroy);
+
+ return 0;
+ }
+
+ ll_parent = ll_search;
+ ll_search = ll_next(ll_search);
+ }
+
+ return 0;
+}
+
+int deepdestroy__hashmap(hashmap *hash) {
+ // destroy linked list children
+ for (int i = 0; i < hash->hashmap__size; i++) {
+ if (hash->map[i]) {
+ ll_destroy(hash->map[i], hash->destroy);
+ }
+ }
+
+ // destroy map
+ free(hash->map);
+ free(hash);
+
+ return 0;
+}
+
+
+ll_main_t *ll_makeNode(vtableKeyStore key, void *newValue, int hash__type) {
+ ll_main_t *new__node = (ll_main_t *) malloc(sizeof(ll_main_t));
+
+ new__node->isArray = 0;
+ new__node->next = NULL;
+ new__node->key = key;
+ new__node->ll_meat = newValue;
+
+ return new__node;
+}
+
+/*
+ for hash__type = 0
+ takes a linked list node value ptr
+ and replaces the value with
+ updated value
+*/
+void *ll_specialUpdateIgnore(void *ll_oldVal, void *newValue, void (*destroy)(void *)) {
+ // clean up previous info at this pointer
+ destroy(ll_oldVal);
+
+ // update
+ return newValue;
+}
+
+// takes the ll_pointer->ll_meat and doubles
+// size of current array
+int ll_resizeArray(ll_main_t *ll_pointer) {
+ // declare new array
+ void **new__arrayPtr = malloc(sizeof(void *) * ll_pointer->max__arrLength * 2);
+
+ // copy values over
+ for (int copyVal = 0; copyVal < ll_pointer->max__arrLength; copyVal++) {
+ new__arrayPtr[copyVal] = (void *) ((void **) ll_pointer->ll_meat)[copyVal];
+ }
+
+ // free old array pointer
+ free(ll_pointer->ll_meat);
+
+ // update to new meat
+ ll_pointer->ll_meat = new__arrayPtr;
+
+ return 0;
+}
+
+/*
+ for hash__type = 1
+ takes linked list pointer
+ - first makes sure that ll_pointer->ll_meat
+ is an array, if not it creates and array
+ - then appends value to end of array
+*/
+int ll_specialUpdateArray(ll_main_t *ll_pointer, void *newValue) {
+ // The same "leave key be" for update works here as with ignore
+
+ if (!ll_pointer->isArray) { // need to create an array
+ void *ll_tempMeatStorage = ll_pointer->ll_meat;
+
+ // update settings for this pointer
+ ll_pointer->max__arrLength = 8;
+ ll_pointer->arrIndex = 0;
+ ll_pointer->isArray = 1;
+
+ ll_pointer->ll_meat = (void **) malloc(sizeof(void *) * ll_pointer->max__arrLength);
+ ((void **) (ll_pointer->ll_meat))[0] = ll_tempMeatStorage;
+
+ for (int memSet = 1; memSet < ll_pointer->arrIndex; memSet++)
+ ((void **) (ll_pointer->ll_meat))[memSet] = NULL;
+ }
+
+ // add new value
+ ll_pointer->arrIndex++;
+ ((void **) (ll_pointer->ll_meat))[ll_pointer->arrIndex] = newValue;
+
+ if (ll_pointer->arrIndex == ll_pointer->max__arrLength - 1)
+ ll_resizeArray(ll_pointer);
+
+ return 0;
+}
+
+// finds the tail and appends
+int ll_insert(ll_main_t *crawler__node, vtableKeyStore key, void *newValue, int hash__type, void (*destroy)(void *)) {
+
+ int bucket_size = 1, addedPayload = 0;
+
+ // search through the entire bucket
+ // (each node in this linked list)
+ while (crawler__node->next) {
+ // found a duplicate (only matters
+ // for hash__type == 0 or 1)
+ if (crawler__node->key.compareKey(crawler__node->key.key, key.key)) {
+ if (hash__type == 0) {
+ crawler__node->ll_meat = ll_specialUpdateIgnore(crawler__node->ll_meat, newValue, destroy);
+ addedPayload = 1;
+ } else if (hash__type == 1) {
+ ll_specialUpdateArray(crawler__node, newValue);
+ addedPayload = 1;
+ }
+ }
+
+ crawler__node = ll_next(crawler__node);
+ bucket_size++;
+ }
+
+ if (crawler__node->key.compareKey(crawler__node->key.key, key.key)) {
+ if (hash__type == 0) {
+ crawler__node->ll_meat = ll_specialUpdateIgnore(crawler__node->ll_meat, newValue, destroy);
+ addedPayload = 1;
+ } else if (hash__type == 1) {
+ ll_specialUpdateArray(crawler__node, newValue);
+ addedPayload = 1;
+ }
+ }
+
+ if (addedPayload == 0) {
+ crawler__node->next = ll_makeNode(key, newValue, hash__type);
+ }
+
+ // return same head
+ return bucket_size;
+}
+
+ll_main_t *ll_next(ll_main_t *curr) {
+ return curr->next;
+}
+
+int ll_printNodeArray(ll_main_t *curr, void (*printer)(void *)) {
+ for (int printVal = 0; printVal < curr->arrIndex + 1; printVal++) {
+ printer(((void **) curr->ll_meat)[printVal]);
+ }
+
+ return 0;
+}
+
+int ll_print(ll_main_t *curr, void (*printer)(void *)) {
+ printf("\n\tLL node ");
+ //printVoid()
+ curr->key.printKey(curr->key.key);
+ printf(" with payload(s):\n");
+ if (curr->isArray)
+ ll_printNodeArray(curr, printer);
+ else
+ printer(curr->ll_meat);
+
+ while ((curr = ll_next(curr)) != NULL) {
+ printf("\tLL node ");
+ printf(" with payload(s):\n");
+ if (curr->isArray)
+ ll_printNodeArray(curr, printer);
+ else
+ printer(curr->ll_meat);
+ }
+
+ return 0;
+}
+
+int ll_isolate(ll_main_t *node) {
+ node->next = NULL;
+
+ return 0;
+}
+
+int ll_destroy(ll_main_t *node, void (destroyObjectPayload)(void *)) {
+ ll_main_t *node_nextStore;
+
+ do {
+ if (node->key.destroyKey)
+ node->key.destroyKey(node->key.key);
+
+ if (node->isArray) {
+ if (destroyObjectPayload)
+ for (int destroyVal = 0; destroyVal < node->arrIndex + 1; destroyVal++)
+ destroyObjectPayload(((void **)node->ll_meat)[destroyVal]);
+
+ free(node->ll_meat);
+ } else if (destroyObjectPayload)
+ destroyObjectPayload(node->ll_meat);
+
+ node_nextStore = node->next;
+ free(node);
+ } while ((node = node_nextStore) != NULL);
+
+ return 0;
+}
+
+
+// DEFAULT function declarations
+void printCharKey(void *characters) {
+ printf("%s", (char *) characters);
+}
+int compareCharKey(void *characters, void *otherValue) {
+ return strcmp((char *) characters, (char *) otherValue) == 0;
+}
+void destroyCharKey(void *characters) { free(characters); }
+
+void printIntKey(void *integer) {
+ printf("%d", *((int *) integer));
+}
+int compareIntKey(void *integer, void *otherValue) {
+ return *((int *) integer) == *((int *) otherValue);
+}
+void destroyIntKey(void *integer) { /* We can't free that! */ }
+
+
+int insert__hashmap(hashmap *hash__m, void *key, void *value, ...) {
+ va_list ap;
+ vtableKeyStore inserter = { .key = key };
+
+ va_start(ap, value);
+ // could be param definer or the printKey function
+ void *firstArgumentCheck = va_arg(ap, char *);
+
+ // check for DEFAULT ("-d") or INT ("-i"):
+ if (strcmp((char *) firstArgumentCheck, "-d") == 0) {// use default
+ inserter.printKey = printCharKey;
+ inserter.compareKey = compareCharKey;
+ inserter.destroyKey = NULL;
+ } else if (strcmp((char *) firstArgumentCheck, "-i") == 0) {
+ inserter.printKey = printIntKey;
+ inserter.compareKey = compareIntKey;
+ inserter.destroyKey = NULL;
+ } else {
+ inserter.printKey = firstArgumentCheck;
+ // do the same for compareKey
+ inserter.compareKey = va_arg(ap, int (*)(void *, void *));
+
+ inserter.destroyKey = va_arg(ap, void (*)(void *));
+ // if destroy is NULL, we don't want to add since this by DEFAULT
+ // does no exist
+ }
+
+ METAinsert__hashmap(hash__m, inserter, value);
+
+ return 0;
+}
+
+char *read_key(char *buffer, int *key_len) {
+ *key_len = 8;
+ int key_index = 0;
+
+ char *key = malloc(sizeof(char) * *key_len);
+
+ while (buffer[key_index] != '|') {
+ key[key_index] = buffer[key_index];
+ key_index++;
+
+ if (key_index + 1 == *key_len) {
+ *key_len *= 2;
+
+ key = realloc(key, sizeof(char) * *key_len);
+ }
+
+ key[key_index] = '\0';
+ }
+
+ *key_len = key_index;
+ return key;
+}
+// assumes the pattern:
+// name|Charlie\nname|Jack
+// NOTE: hashmap should have a destroyValue function
+int batchInsert__hashmap(hashmap *hash__m, char *filename) {
+ void (*printKey)(void *) = printCharKey;
+ int (*compareKey)(void *, void *) = compareCharKey;
+ void (*destroyKey)(void *) = destroyCharKey;
+
+ FILE *fp = fopen(filename, "r");
+
+ if (!fp) {
+ printf("\033[0;31m");
+ printf("\n** Batch Insert File Error **\n");
+ printf("\033[0;37m");
+
+ return 1;
+ }
+
+ size_t buff_size = sizeof(char) * 8;
+ char *buffer = malloc(buff_size);
+ int buffer_len;
+
+ int *key_len = malloc(sizeof(int));
+ while ((buffer_len = getline(&buffer, &buff_size, fp)) != -1) {
+ buffer_len = strlen(buffer);
+ vtableKeyStore newtable = { .key = read_key(buffer, key_len),
+ .printKey = printKey,
+ .compareKey = compareKey,
+ .destroyKey = destroyKey };
+
+ // read value starting from where key_len stopped
+ char *value = malloc(sizeof(char) * (buffer_len - *key_len - 1));
+ int cp_value;
+ for (cp_value = 0; cp_value < buffer_len - *key_len - 2; cp_value++) {
+ value[cp_value] = *(buffer + sizeof(char) * (*key_len + cp_value + 1));
+ }
+ value[cp_value] = '\0';
+
+ METAinsert__hashmap(hash__m, newtable, value);
+ }
+
+ free(buffer);
+ free(key_len);
+ fclose(fp);
+
+ return 0;
+}
\ No newline at end of file
diff --git a/hashmap.h b/hashmap.h
index 052637f..5dd0891 100644
--- a/hashmap.h
+++ b/hashmap.h
@@ -2,33 +2,35 @@
#define __HASH_T__
typedef struct ReturnHashmap { // used only for type 1
- void **payload;
- int payload__length;
+ void *payload;
+
+ struct ReturnHashmap *next;
} hashmap__response;
+int clear__hashmap__response(hashmap__response *);
typedef struct Store hashmap;
-hashmap *make__hashmap(int hash__type, void (*printer)(void *), void (*destroy)(void *));
+hashmap *make__hashmap(int, void (*)(void *), void (*)(void *));
-void **keys__hashmap(hashmap *hash__m, int *max_key);
-void *get__hashmap(hashmap *hash__m, void *key);
+void **keys__hashmap(hashmap *, int *);
+void *get__hashmap(hashmap *, void *, char *, ...);
-int print__hashmap(hashmap *hash__m);
+int print__hashmap(hashmap *);
-int delete__hashmap(hashmap *hash__m, void *key);
+int delete__hashmap(hashmap *, void *);
-int deepdestroy__hashmap(hashmap *hash);
+int deepdestroy__hashmap(hashmap *);
-int batchInsert__hashmap(hashmap *hash__m, char *filename);
-int insert__hashmap(hashmap *hash__m, void *key, void *value, ...);
+int batchInsert__hashmap(hashmap *, char *);
+int insert__hashmap(hashmap *, void *, void *, ...);
// simple key type functions
-void printCharKey(void *characters);
-int compareCharKey(void *characters, void *otherValue);
-void destroyCharKey(void *characters);
+void printCharKey(void *);
+int compareCharKey(void *, void *);
+void destroyCharKey(void *);
-void printIntKey(void *integer);
-int compareIntKey(void *integer, void *otherValue);
-void destroyIntKey(void *integer);
+void printIntKey(void *);
+int compareIntKey(void *, void *);
+void destroyIntKey(void *);
#endif
\ No newline at end of file
diff --git a/public/style.css b/public/style.css
new file mode 100644
index 0000000..889ea5f
--- /dev/null
+++ b/public/style.css
@@ -0,0 +1,4 @@
+/* TEST CSS */
+* {
+ background: green;
+}
\ No newline at end of file
diff --git a/request.c b/request.c
index 78c79c9..c5e357d 100644
--- a/request.c
+++ b/request.c
@@ -170,13 +170,12 @@ char *build_url(char *request_url, int *req_length, char *query_param, char **at
}
char *create_header(int STATUS, int *header_max, hashmap *status_code, hashmap *headers, int post_data_size, char *post_data) {
- int header_size;
int header_index = 0; *header_max = 32;
char *header = malloc(sizeof(char) * *header_max);
char *status_char = malloc(sizeof(char) * 4);
sprintf(status_char, "%d", STATUS);
- char *status_phrase = (char *) get__hashmap(status_code, status_char);
+ char *status_phrase = (char *) get__hashmap(status_code, status_char, "");
int status_phrase_len = strlen(status_char) + strlen(status_phrase);
header_index = status_phrase_len + 11;
@@ -190,7 +189,7 @@ char *create_header(int STATUS, int *header_max, hashmap *status_code, hashmap *
char **header_key = (char **) keys__hashmap(headers, key_num);
for (int cp_header = 0; cp_header < *key_num; cp_header++) {
- char *header_value = (char *) get__hashmap(headers, header_key[cp_header]);
+ char *header_value = (char *) get__hashmap(headers, header_key[cp_header], "");
int head_add_on = strlen(header_key[cp_header]) + strlen(header_value) + 3;
header = resize_array(header, header_max, header_index + head_add_on + 4, sizeof(char));
@@ -203,15 +202,15 @@ char *create_header(int STATUS, int *header_max, hashmap *status_code, hashmap *
free(key_num);
free(header_key);
- int add_on = 0;
- if (post_data) {
- add_on = strlen(post_data) + 3;
- header = resize_array(header, header_max, header_index + add_on, sizeof(char));
+ int add_on = (post_data ? strlen(post_data) : 0) + 3;
+ header = resize_array(header, header_max, header_index + add_on, sizeof(char));
- sprintf(header + sizeof(char) * header_index, "\n\n%s\0", post_data);
- }
+ strcat(header, "\n\n");
+ if (post_data)
+ strcat(header, post_data);
*header_max = header_index + add_on;
+
return header;
}
@@ -292,7 +291,8 @@ hashmap *read_headers(char *header_str, void (*print_key)(void *), int *header_e
// head head tag
while ((int) header_str[past_lines + head_index] != 58) {
- head_tag[head_index++] = header_str[past_lines + head_index];
+ head_tag[head_index] = header_str[past_lines + head_index];
+ head_index++;
head_tag = resize_array(head_tag, head_max, head_index, sizeof(char));
head_tag[head_index] = '\0';
@@ -302,7 +302,8 @@ hashmap *read_headers(char *header_str, void (*print_key)(void *), int *header_e
// read attr tag
while ((int) header_str[past_lines + attr_index + 1] != 10) {
- attr_tag[attr_index++] = header_str[past_lines + attr_index];
+ attr_tag[attr_index] = header_str[past_lines + attr_index];
+ attr_index++;
attr_tag = resize_array(attr_tag, attr_max, attr_index, sizeof(char));
attr_tag[attr_index] = '\0';
@@ -326,8 +327,6 @@ socket_t *get_socket(char *HOST, char *PORT) {
//int *sock_fd = malloc(sizeof(int)); // listen on sock_fd
int sock;
struct addrinfo hints, *servinfo, *p;
- struct sockaddr_storage their_addr; // connector's address information
- struct sigaction sa;
int yes = 1;
int status;
diff --git a/request.h b/request.h
index bb502e5..4ce1a3f 100644
--- a/request.h
+++ b/request.h
@@ -10,7 +10,7 @@ typedef struct SocketData {
} socket_t;
socket_t *get_socket(char *HOST, char *PORT);
-int destroy_socket(socket_t *socket_data);
+int destroy_socket(socket_t *);
char *create_header(int STATUS, int *header_max, hashmap *status_code, hashmap *headers, int post_data_size, char *post_data);
hashmap *read_headers(char *header_str, void (*print_key)(void *), int *header_end);
diff --git a/server.c b/server.c
index 2194831..b1ad261 100644
--- a/server.c
+++ b/server.c
@@ -12,8 +12,8 @@ void home_page(req_t req, res_t res) {
char *name = req_query(req, "name");
printf("name is %s\n", name);
- printf("%d %s\n", res.socket, res.__dirname);
- res_sendFile(res, "home.html");
+ res_matches(res, "NAME", name);
+ res_render(res, "home", "{{", "}}");
return;
}
diff --git a/teru.c b/teru.c
index d11e47d..e88750a 100644
--- a/teru.c
+++ b/teru.c
@@ -17,22 +17,30 @@
#include
#include "teru.h"
+#include "typeinfer.h"
#define MAXLINE 4096
typedef struct Listener {
char *r_type; // "GET" / "POST" / etc.
+ // "TERU_PUBLIC" for app_use()
+ char *url_wrap; // for app_use() only
+ int add_num; // for calculating which values to look at first (PQ-esque)
void (*handler)(req_t, res_t); // handles processing for the request
- // made by user
+ // made by user
/* more to come! */
} listen_t;
-listen_t *new_listener(char *r_type, void (*handler)(req_t, res_t)) {
+listen_t *new_listener(char *r_type, void (*handler)(req_t, res_t), int add_num, char *url_wrap) {
listen_t *r = malloc(sizeof(listen_t));
r->r_type = r_type;
+ r->add_num = add_num;
+
+ r->url_wrap = url_wrap;
+
r->handler = handler;
return r;
@@ -67,8 +75,10 @@ teru_t teru() {
app->status_code = make__hashmap(0, NULL, destroyCharKey);
batchInsert__hashmap(app->status_code, "request_code.data");
+ app->curr_add_num = 0;
app->routes = make__hashmap(1, print_listen_t, free_listen_t);
- app->app_settings = make__hashmap(0, print_app_settings, destroyCharKey);
+ app->use_settings = make__hashmap(0, print_app_settings, destroyCharKey);
+ app->set_settings = make__hashmap(0, print_app_settings, destroyCharKey);
app->server_active = 1;
@@ -78,17 +88,34 @@ teru_t teru() {
void destroy_teru(teru_t *app) {
deepdestroy__hashmap(app->status_code);
deepdestroy__hashmap(app->routes);
- deepdestroy__hashmap(app->app_settings);
+
+ deepdestroy__hashmap(app->use_settings);
+ deepdestroy__hashmap(app->set_settings);
free(app);
return;
}
+hashmap *inferer_map;
+
void *acceptor_function(void *app_ptr);
+int fsck_directory(char *major_path, char *minor_fp); // check for if a file exists
int app_listen(char *HOST, char *PORT, teru_t app) {
+ printf("\033[0;32m");
+ printf("\nTeru Server starting on ");
+ printf("\033[0;37m");
+ printf("%s", HOST);
+ printf("\033[0;32m");
+ printf(":");
+ printf("\033[0;37m");
+ printf("%s", PORT);
+ printf("\033[0;32m");
+ printf("...\n\n");
+
socket_t *socket = get_socket(HOST, PORT);
+ inferer_map = infer_load();
if (listen(socket->sock, BACKLOG) == -1) {
perror("listen error");
@@ -102,6 +129,7 @@ int app_listen(char *HOST, char *PORT, teru_t app) {
pthread_create(&accept_thread, NULL, &acceptor_function, app.app_ptr);
printf("server go vroom\n");
+ printf("\033[0;37m");
while (getchar() != '0');
@@ -112,18 +140,20 @@ int app_listen(char *HOST, char *PORT, teru_t app) {
destroy_socket(socket);
destroy_teru(app.app_ptr);
+ deepdestroy__hashmap(inferer_map);
+
return 0;
}
/* ROUTE BUILDER -- POINTS TO GENERIC ROUTE BUILDER
|__ the only reason for these functions is to build a slightly
simpler interface on top of the library */
-int build_new_route(teru_t app, char *type, char *endpoint, void (*handler)(req_t, res_t)) {
+int build_new_route(teru_t app, char *type, char *endpoint, void (*handler)(req_t, res_t), char *url_wrap) {
// check that the route doesn't exist (assumes the type matches)
- hashmap__response *routes = (hashmap__response *) get__hashmap(app.routes, endpoint);
+ hashmap__response *routes = (hashmap__response *) get__hashmap(app.routes, endpoint, "");
- for (int check_route = 0; routes && check_route < routes->payload__length; check_route) {
- listen_t *r = (listen_t *) routes->payload[check_route];
+ for (hashmap__response *rt_thru = routes; rt_thru; rt_thru = rt_thru->next) {
+ listen_t *r = (listen_t *) rt_thru->payload;
if (strcmp(type, r->r_type) == 0) { // found match
// replace current handler with new handler
@@ -136,8 +166,12 @@ int build_new_route(teru_t app, char *type, char *endpoint, void (*handler)(req_
}
}
+ if (routes)
+ free(routes);
+
// otherwise insert new listen_t
- listen_t *r = new_listener(type, handler);
+ listen_t *r = new_listener(type, handler, app.app_ptr->curr_add_num, url_wrap);
+ app.app_ptr->curr_add_num += 1;
insert__hashmap(app.routes, endpoint, r, "", compareCharKey, NULL);
@@ -145,14 +179,25 @@ int build_new_route(teru_t app, char *type, char *endpoint, void (*handler)(req_
}
int app_get(teru_t app, char *endpoint, void (*handler)(req_t, res_t)) {
- return build_new_route(app, "GET", endpoint, handler);
+ return build_new_route(app, "GET", endpoint, handler, NULL);
}
int app_post(teru_t app, char *endpoint, void (*handler)(req_t, res_t)) {
- return build_new_route(app, "POST", endpoint, handler);
+ return build_new_route(app, "POST", endpoint, handler, NULL);
}
/* TERU APP SETTINGS BUILDER */
+void get_public_file(req_t req, res_t res) {
+ // req for specific file name
+ // "/public/style.css" for example
+ res_sendFile(res, req.url);
+
+ return;
+}
+
+/* UPDATE:
+ if route starts with a "/", the it assumes setting of a public directory
+*/
void app_use(teru_t app, char *route, ...) {
// update route name to have a "set" at the beginning
char *descript = NULL;
@@ -167,12 +212,12 @@ void app_use(teru_t app, char *route, ...) {
strcpy(descript, file_path);
strcat(descript, sub_path);
- // update route name to have a "use" at the beginning
- char *new_route_name = malloc(sizeof(char) * (strlen(route) + 4));
- new_route_name[0] = 'u'; new_route_name[1] = 's'; new_route_name[2] = 'e'; new_route_name[3] = '\0';
- strcat(new_route_name, route);
+ insert__hashmap(app.use_settings, route, descript, "", compareCharKey, NULL);
- insert__hashmap(app.app_settings, new_route_name, descript, "", compareCharKey, destroyCharKey);
+ if (route[0] == '/') {
+ // add if a new route
+ build_new_route(app, "TERU_PUBLIC", route, get_public_file, route);
+ }
return;
}
@@ -202,7 +247,7 @@ void app_set(teru_t app, char *route, ...) {
strcat(new_route_name, route);
if (descript)
- insert__hashmap(app.app_settings, new_route_name, descript, "", compareCharKey, destroyCharKey);
+ insert__hashmap(app.set_settings, new_route_name, descript, "", compareCharKey, destroyCharKey);
return;
}
@@ -211,11 +256,8 @@ void app_set(teru_t app, char *route, ...) {
options:
-- SENDING DATA
-t for simple text/plain -- expects a single char * (for data) in the overload
- -h for text/html; charset=UTF-8
- -- expects a char * (for data) and an int (length of char *) in the overload
- -hc for text/html; charset=?
- -- expects a char * (for data) and an int (length of char *)
- and a char * (for charset) in the overload
+ -i for infering the text type (simple function currently)
+ -- expects a char * (for FILENAME), a char * (for data) and an int (length of char *)
-- ADDITIONAL HEADER OPTIONS:
-o for adding another parameter
-- expects a char * for the header name and a char *
@@ -241,23 +283,12 @@ void data_send(int sock, hashmap *status_code, int status, char *options, ...) {
data = va_arg(read_opts, char *);
data_length = strlen(data) + 1;
- } else if (options[check_option + 1] == 'h') {
- // start with default values
+ } else if (options[check_option + 1] == 'i') {
+ char *file_data_name = va_arg(read_opts, char *);
data = va_arg(read_opts, char *);
- data_length = va_arg(read_opts, int);
- char *html_option = va_arg(read_opts, char *);
-
- char *char_set = NULL;
- content_type = malloc(sizeof(char) * 25);
- strcpy(content_type, "text/html; charset=");
+ data_length = va_arg(read_opts, int) + 1;
- if (options[check_option + 2] == 'c') {
- char_set = va_arg(read_opts, char *);
-
- content_type = realloc(content_type, sizeof(char) * (20 + strlen(char_set)));
- strcat(content_type, char_set);
- } else
- strcat(content_type, "UTF-8");
+ char *content_type = content_type_infer(inferer_map, file_data_name, data, data_length);
insert__hashmap(headers, "Content-Type", content_type, "", compareCharKey, NULL);
} else if (options[check_option + 1] == 'o') {
@@ -279,7 +310,7 @@ void data_send(int sock, hashmap *status_code, int status, char *options, ...) {
char *main_head_msg = create_header(status, head_msg_len, status_code, headers, data_length, data);
int bytes_sent = 0;
- while ((bytes_sent = send(sock, main_head_msg, *head_msg_len - bytes_sent / sizeof(char), 0)) < sizeof(char) * *head_msg_len);
+ while ((bytes_sent = send(sock, main_head_msg + bytes_sent, *head_msg_len - bytes_sent / sizeof(char), 0)) < sizeof(char) * *head_msg_len);
free(head_msg_len);
if (lengthOf_data_length) free(lengthOf_data_length);
@@ -309,7 +340,9 @@ void destroy_req_t(req_t *r) {
}
void destroy_res_t(res_t *r) {
- /* NEEDS UPDATING */
+ free(r);
+
+ return;
}
/* HEADER PARSER
This handles taking in a header like:
@@ -490,11 +523,28 @@ req_t *read_header_helper(char *header_str, int header_length) {
}
char *req_query(req_t req, char *name) {
- return (char *) get__hashmap(req.query_map, name);
+ return (char *) get__hashmap(req.query_map, name, "");
}
char *req_body(req_t req, char *name) {
- return (char *) get__hashmap(req.body_map, name);
+ return (char *) get__hashmap(req.body_map, name, "");
+}
+
+res_t *create_response_struct(int socket, hashmap *status_code, char *__dirname) {
+ res_t *res = malloc(sizeof(res_t));
+
+ res->res_self = res;
+
+ res->render = 0;
+ res->fileName = NULL; res->matchStart = NULL; res->matchEnd = NULL;
+ res->render_matches = NULL;
+
+ res->socket = socket;
+ res->status_code = status_code;
+
+ res->__dirname = __dirname;
+
+ return res;
}
typedef struct ConnectionHandle {
@@ -580,6 +630,25 @@ int join_all_threads(ch_t *curr) {
return 0;
}
+int match_hashmap_substrings(void *other_key, void *curr_key) {
+ char *io_key = (char *) other_key, *ic_key = (char *) curr_key;
+
+ // compare using length of ic_key
+ int ic_key_len = strlen(ic_key), io_key_len = strlen(io_key);
+ int check_length = io_key_len < ic_key_len ? io_key_len : ic_key_len;
+
+ int check_match;
+ for (check_match = 0; io_key[check_match] && check_match < check_length; check_match++) {
+ if (io_key[check_match] != ic_key[check_match])
+ break;
+ }
+
+ return check_match == check_length;
+}
+
+int is_lower_hashmap_data(void *key1, void *key2) {
+ return ((listen_t *) key1)->add_num < ((listen_t *) key2)->add_num;
+}
/* LISTENER FUNCTIONS FOR SOCKET */
// Based on my trie-suggestor-app (https://github.com/charlie-map/trie-suggestorC/blob/main/server.c) and
// Beej Hall websockets (http://beej.us/guide/bgnet/html/)
@@ -604,9 +673,14 @@ void *connection(void *app_ptr) {
// otherwise parse header data
req_t *new_request = read_header_helper(buffer, recv_res / sizeof(char));
+ int request_url_len = strlen(new_request->url);
// using the new_request, acceess the app to see how to handle it:
- hashmap__response *handler = get__hashmap(app.routes, new_request->url);
+ hashmap__response *handler;
+ if (new_request->url[request_url_len - 1] == '/') // is not a file -- don't look for public directories
+ handler = get__hashmap(app.routes, new_request->url, "w", is_lower_hashmap_data);
+ else
+ handler = get__hashmap(app.routes, new_request->url, "iw", match_hashmap_substrings, is_lower_hashmap_data);
if (!handler) { /* ERROR HANDLE */
char *err_msg = malloc(sizeof(char) * (10 + strlen(new_request->type) + strlen(new_request->url)));
sprintf(err_msg, "Cannot %s %s\n", new_request->type, new_request->url);
@@ -621,13 +695,24 @@ void *connection(void *app_ptr) {
// there might be multiple (aka "/number" is a POST and a GET)
// need to find the one that matches the request:
- int find_handle;
- for (find_handle = 0; find_handle < handler->payload__length; find_handle++) {
- if (strcmp(((listen_t *) handler->payload[find_handle])->r_type, new_request->type) == 0)
+ hashmap__response *reader;
+ int is_public = 0;
+ for (reader = handler; reader; reader = reader->next) { // nice
+ // case: if r_type == "TERU_PUBLIC", check system directory to
+ // see if there is a file in that folder that corresponds with
+ // the name of the request
+ if (!(new_request->url[request_url_len - 1] == '/') && strcmp(((listen_t *) reader->payload)->r_type, "TERU_PUBLIC") == 0) {
+ if (fsck_directory((char *) get__hashmap(app.use_settings, ((listen_t *) reader->payload)->url_wrap, new_request->url), new_request->url)) {
+ is_public = 1;
+ break;
+ }
+ }
+
+ if (strcmp(((listen_t *) reader->payload)->r_type, new_request->type) == 0)
break;
}
- if (find_handle == handler->payload__length) { /* not found -- ERROR HANDLE */
+ if (!reader) { /* not found -- ERROR HANDLE */
char *err_msg = malloc(sizeof(char) * (10 + strlen(new_request->type) + strlen(new_request->url)));
sprintf(err_msg, "Cannot %s %s\n", new_request->type, new_request->url);
data_send(new_fd, app.status_code, 404, "-t", err_msg);
@@ -641,12 +726,13 @@ void *connection(void *app_ptr) {
// find handle will now have the handler within it
// can call the handler with the data
- res_t res = { .socket = new_fd, .status_code = app.status_code,
- .__dirname = (char *) get__hashmap(app.app_settings, "setviews") };
- ((listen_t *) handler->payload[find_handle])->handler(*new_request, res);
-
+ res_t *res = create_response_struct(new_fd, app.status_code,
+ (char *) (is_public ? get__hashmap(app.use_settings, ((listen_t *) reader->payload)->url_wrap, "") : get__hashmap(app.set_settings, "setviews", "")));
+ ((listen_t *) reader->payload)->handler(*new_request, *res);
+
destroy_req_t(new_request);
- free(handler);
+ destroy_res_t(res);
+ clear__hashmap__response(handler);
}
// if the close occurs due to thread_status, send an error page
@@ -701,15 +787,199 @@ void *acceptor_function(void *app_ptr) {
}
+typedef struct RenderSchema {
+ int *max_render_match_check, render_match_check_index,
+ render_start_matcher_index, render_end_matcher_index;
+
+ int render_start_matcher_length, render_end_matcher_length;
+
+ int *max_render_match_buffer, render_match_buffer_index;
+ char *render_match_buffer;
+
+ char *render_match_check;
+ char *render_start_matcher, *render_end_matcher;
+
+ hashmap *res_render_matches;
+} render_scheme_t;
+render_scheme_t *create_render_scheme(res_t *res) {
+ render_scheme_t *new_scheme = malloc(sizeof(render_scheme_t));
+
+ new_scheme->max_render_match_check = malloc(sizeof(int));
+ *new_scheme->max_render_match_check = 8;
+ new_scheme->render_match_check_index = 0;
+ new_scheme->render_start_matcher_index = 0;
+ new_scheme->render_end_matcher_index = 0;
+
+ new_scheme->max_render_match_buffer = malloc(sizeof(int));
+ *new_scheme->max_render_match_buffer = 8;
+ new_scheme->render_match_buffer_index = 0;
+
+ new_scheme->render_start_matcher_length = strlen(res->matchStart);
+ new_scheme->render_end_matcher_length = strlen(res->matchEnd);
+
+ new_scheme->res_render_matches = res->render_matches;
+
+ new_scheme->render_match_buffer = malloc(sizeof(char) * 8);
+ new_scheme->render_match_check = malloc(sizeof(char) * 8);
+ new_scheme->render_start_matcher = res->matchStart;
+ new_scheme->render_end_matcher = res->matchEnd;
+
+ return new_scheme;
+}
+
+int scheme_reset(render_scheme_t *r_scheme) {
+ r_scheme->render_match_check_index = 0;
+ r_scheme->render_start_matcher_index = 0;
+ r_scheme->render_end_matcher_index = 0;
+
+ r_scheme->render_match_check[0] = '\0';
+
+ return 0;
+}
+
+int destroy_render_scheme(render_scheme_t *r_scheme) {
+ free(r_scheme->max_render_match_check);
+ free(r_scheme->max_render_match_buffer);
+
+ free(r_scheme->render_match_check);
+ free(r_scheme->render_match_buffer);
+
+ free(r_scheme);
+
+ return 0;
+}
+
+int check_renders(render_scheme_t *r_scheme, char *full_line, char **full_data,
+ int *full_data_max, int full_data_index) {
+
+ int has_found_start_match; // checking if the matcher indices have changed
+ int has_index_change; // for checking if data in buffer needs
+ // to be written to full_data
+ // read through the line and check against render matches and see if we should start reading a key
+ for (int read_full_line = 0; full_line[read_full_line]; read_full_line++) {
+ has_index_change = 0;
+ has_found_start_match = 0;
+ // casing:
+ // no nothing, look for render_start_matcher
+ if (r_scheme->render_start_matcher_index < r_scheme->render_start_matcher_length) {
+ // check for if line matches renderer:
+ if (full_line[read_full_line] == r_scheme->render_start_matcher[r_scheme->render_start_matcher_index]) {
+ has_found_start_match = 1;
+ r_scheme->render_start_matcher_index++;
+
+ if (r_scheme->render_start_matcher_index == r_scheme->render_start_matcher_length - 1)
+ r_scheme->render_match_buffer_index = 0;
+ } else {
+ has_index_change = r_scheme->render_start_matcher_index > 0;
+ r_scheme->render_start_matcher_index = 0;
+ }
+ }
+ if (r_scheme->render_start_matcher_index == r_scheme->render_end_matcher_length &&
+ r_scheme->render_end_matcher_index < r_scheme->render_end_matcher_length) {
+ if (full_line[read_full_line] == r_scheme->render_end_matcher[r_scheme->render_end_matcher_index]) {
+ has_found_start_match = 1;
+ r_scheme->render_end_matcher_index++;
+ } else {
+ has_index_change = r_scheme->render_end_matcher_index > 0;
+ r_scheme->render_end_matcher_index = 0;
+ }
+ }
+
+ // check if a character should be briefly stored in buffer to see
+ // if the character is part of the start or end matcher
+ if (has_found_start_match && (r_scheme->render_start_matcher_index < r_scheme->render_start_matcher_length ||
+ r_scheme->render_end_matcher_index < r_scheme->render_end_matcher_length)) {
+
+ // copy character into buffer
+ r_scheme->render_match_buffer[r_scheme->render_match_buffer_index] = full_line[read_full_line];
+ r_scheme->render_match_buffer_index++;
+
+ r_scheme->render_match_buffer = resize_array(r_scheme->render_match_buffer, r_scheme->max_render_match_buffer, r_scheme->render_match_buffer_index, sizeof(char));
+ r_scheme->render_match_buffer[r_scheme->render_match_buffer_index] = '\0';
+
+ continue;
+ }
+
+ if (has_index_change) {
+ // read render_match_buffer into full_data
+ *full_data = resize_array(*full_data, full_data_max, full_data_index + r_scheme->render_match_buffer_index, sizeof(char));
+
+ strcat(*full_data, r_scheme->render_match_buffer);
+ r_scheme->render_match_buffer_index = 0;
+ }
+
+ // have render_start_matcher, start reading directly into render_match_check
+ if (r_scheme->render_start_matcher_index == r_scheme->render_start_matcher_length &&
+ r_scheme->render_end_matcher_index != r_scheme->render_end_matcher_length) {
+
+ r_scheme->render_match_check[r_scheme->render_match_check_index] = full_line[read_full_line];
+ r_scheme->render_match_check_index++;
+
+ r_scheme->render_match_check = resize_array(r_scheme->render_match_check, r_scheme->max_render_match_check, r_scheme->render_match_check_index, sizeof(char));
+ r_scheme->render_match_check[r_scheme->render_match_check_index] = '\0';
+
+ continue;
+ }
+ // find render_end_matcher:
+ // close connection
+ if (r_scheme->render_start_matcher_index == r_scheme->render_start_matcher_length &&
+ r_scheme->render_end_matcher_index == r_scheme->render_end_matcher_length) {
+ // see what should be in the place of this found word:
+ char *replacer = get__hashmap(r_scheme->res_render_matches, r_scheme->render_match_check, "");
+
+ if (!replacer) {
+ scheme_reset(r_scheme);
+ continue;
+ }
+
+ int replacer_len = strlen(replacer);
+
+ *full_data = resize_array(*full_data, full_data_max, full_data_index + replacer_len, sizeof(char));
+
+ strcat(*full_data, replacer);
+
+ // reset data
+ scheme_reset(r_scheme);
+
+ full_data_index += replacer_len;
+ } else {
+ // add to full_data
+ (*full_data)[full_data_index] = full_line[read_full_line];
+ full_data_index++;
+
+ (*full_data)[full_data_index] = '\0';
+ }
+ }
+
+ return full_data_index;
+}
/* RESULT SENDERS */
int res_sendFile(res_t res, char *name) {
+ res_t *res_pt = res.res_self;
+
+ if (res_pt->render && (!res_pt->matchStart || !res_pt->matchEnd)) {
+ printf("\033[0;31m");
+ printf("\n** Render match schema failed -- missing: %s%s%s **\n",
+ res_pt->matchStart ? "" : "start match ", !res_pt->matchStart && !res_pt->matchEnd ?
+ "and " : "", res_pt->matchEnd ? "" : "end match");
+ printf("\033[0;37m");
+
+ char *err_msg = malloc(sizeof(char) * (27));
+ sprintf(err_msg, "Render match schema failed");
+ data_send(res.socket, res.status_code, 500, "-t", err_msg);
+ free(err_msg);
+
+ return 0;
+ }
+
// load full file path
- int full_fpath_len = strlen(res.__dirname ? res.__dirname : getenv("PWD")) + strlen(name) + 1 + (!res.__dirname ? 1 : 0);
+ int full_fpath_len = strlen(res.__dirname ? res.__dirname : getenv("PWD")) + strlen(name) +
+ (name[0] == '/' ? -1 : 0) + 1 + (!res.__dirname ? 1 : 0);
char *full_fpath = malloc(sizeof(char) * full_fpath_len);
strcpy(full_fpath, res.__dirname ? res.__dirname : getenv("PWD"));
if (!res.__dirname)
strcat(full_fpath, "/");
- strcat(full_fpath, name);
+ strcat(full_fpath, name + (name[0] == '/' ? 1 : 0));
FILE *f_pt = fopen(full_fpath, "r");
@@ -727,8 +997,6 @@ int res_sendFile(res_t res, char *name) {
return 1;
}
- free(full_fpath);
-
// if file opens, read from file to create a new request
size_t read_line_size = sizeof(char) * 64;
char *read_line = malloc(read_line_size);
@@ -738,20 +1006,32 @@ int res_sendFile(res_t res, char *name) {
char *full_data = malloc(sizeof(char) * *full_data_max);
full_data[0] = '\0';
+ /* RENDER SCHEMES */
+ render_scheme_t *r_scheme = res_pt->render ? create_render_scheme(res_pt) : NULL;
+
int curr_line_len = 0;
while ((curr_line_len = getline(&read_line, &read_line_size, f_pt)) != -1) {
- full_data_index += curr_line_len;
- full_data = resize_array(full_data, full_data_max, full_data_index + 1, sizeof(char));
+ full_data = resize_array(full_data, full_data_max, full_data_index + curr_line_len + 1, sizeof(char));
+
+ // check for if any rendering calculations should occur
+ if (res_pt->render) {
+ full_data_index = check_renders(r_scheme, read_line, &full_data, full_data_max, full_data_index);
+ } else {
+ strcat(full_data, read_line);
+ }
- strcat(full_data, read_line);
full_data[full_data_index] = '\0';
}
+ if (r_scheme)
+ destroy_render_scheme(r_scheme);
+
free(read_line);
fclose(f_pt);
- data_send(res.socket, res.status_code, 200, "-h", full_data, full_data_index);
+ data_send(res.socket, res.status_code, 200, "-i", full_fpath, full_data, full_data_index);
+ free(full_fpath);
free(full_data_max);
free(full_data);
return 0;
@@ -759,4 +1039,58 @@ int res_sendFile(res_t res, char *name) {
int res_end(res_t res, char *data) {
data_send(res.socket, res.status_code, 200, "-t", data);
+
+ return 0;
+}
+
+int res_matches(res_t res, char *match, char *replacer) {
+ res_t *res_pt = res.res_self;
+
+ if (!res_pt->render_matches)
+ res_pt->render_matches = make__hashmap(0, NULL, NULL);
+
+ insert__hashmap(res_pt->render_matches, match, replacer, "", compareCharKey, NULL);
+
+ return 0;
+}
+
+int res_render(res_t res, char *name, char *match_start, char *match_end) {
+ res_t *res_pt = res.res_self;
+ res_pt->render = 1;
+
+ // open file:
+ char *full_file_name = malloc(sizeof(char) * (strlen(name) + ((res_pt->fileName && res_pt->fileName[0] == 'f') ? 0 : 6)));
+ strcpy(full_file_name, name);
+ printf("%d %s\n", strlen(name) + ((res_pt->fileName && res_pt->fileName[0] == 'f') ? 0 : 5), full_file_name);
+ if (!res_pt->fileName)
+ strcat(full_file_name, ".html");
+
+ res_pt->matchStart = match_start;
+ res_pt->matchEnd = match_end;
+
+ int response = res_sendFile(res, full_file_name);
+
+ free(full_file_name);
+ deepdestroy__hashmap(res_pt->render_matches);
+
+ return response;
+}
+
+int fsck_directory(char *major_path, char *minor_fp) {
+ // combine the char *:
+ char *full_path = malloc(sizeof(char) * (strlen(major_path) + strlen(minor_fp)));
+
+ strcpy(full_path, major_path);
+ strcat(full_path, minor_fp + sizeof(char)); // remove first "/"
+
+ FILE *f_ck = fopen(full_path, "r");
+
+ int have_file = f_ck ? 1 : 0;
+
+ if (f_ck)
+ fclose(f_ck);
+
+ free(full_path);
+
+ return have_file;
}
\ No newline at end of file
diff --git a/teru.h b/teru.h
index a83ffe0..2abb4f5 100644
--- a/teru.h
+++ b/teru.h
@@ -16,10 +16,11 @@ typedef struct Teru {
hashmap *status_code; // holds the code -> textual phrase pair
// routes for different request types (currently on GET and POST)
- hashmap *routes;
+ int curr_add_num;
+ hashmap *routes; // also for any public routes...
/* currently only hashes string to string */
- hashmap *app_settings;
+ hashmap *use_settings, *set_settings;
/*
`use`d parameters -- with concatenated string ("use")
`set` parameters -- with concatenated string ("set")
@@ -45,12 +46,26 @@ typedef struct HeaderMap {
hashmap *query_map;
hashmap *body_map;
+
} req_t;
typedef struct ResStruct {
+ struct ResStruct *res_self;
+
int socket;
hashmap *status_code;
char *__dirname;
+
+
+ int render; // to check if rendering the document should occur
+ // (instead of just reading) -- INTERNAL
+ char *fileName; // empty for default render,
+ // "f" for full file name render
+ // more to come!
+ // set each one using res.matchStart = ""; and res.matchEnd = "";
+ // to use the render functionality
+ char *matchStart, *matchEnd;
+ hashmap *render_matches;
} res_t;
teru_t teru();
@@ -64,6 +79,12 @@ int app_post(teru_t app, char *endpoint, void (*handler)(req_t, res_t));
int res_sendFile(res_t res, char *name);
int res_end(res_t res, char *data);
+int res_matches(res_t res, char *match, char *replacer);
+/* assumes a .html file (so inputting "home" will look for "home.html")
+ However, can also use a full path by setting res.fileName = 'f';
+*/
+int res_render(res_t res, char *name, char *match_start, char *match_end);
+
char *req_query(req_t req, char *name);
char *req_body(req_t req, char *name);
diff --git a/typeinfer.c b/typeinfer.c
new file mode 100644
index 0000000..df6a6fa
--- /dev/null
+++ b/typeinfer.c
@@ -0,0 +1,58 @@
+#include
+#include
+#include
+
+#include "typeinfer.h"
+
+hashmap *infer_load() {
+ hashmap *load_map = make__hashmap(0, NULL, destroyCharKey);
+
+ char *text_plain = malloc(sizeof(char) * 11); strcpy(text_plain, "text/plain");
+ char *text_html = malloc(sizeof(char) * 10); strcpy(text_html, "text/html");
+ char *text_css = malloc(sizeof(char) * 9); strcpy(text_css, "text/css");
+
+ insert__hashmap(load_map, "txt", text_plain, "", compareCharKey, NULL);
+ insert__hashmap(load_map, "html", text_html, "", compareCharKey, NULL);
+ insert__hashmap(load_map, "css", text_css, "", compareCharKey, NULL);
+
+ return load_map;
+}
+
+// reads final values from filename (after find_p) and looks in load_map
+char *end_type_script_check(hashmap *load_map, char *filename, int find_p) {
+ char *sub_filename = malloc(sizeof(char) * 8);
+ int max_sub_filename = 8, sub_filename_index = 0;
+
+ for (int read_file_name = find_p + 1; filename[read_file_name]; read_file_name++) {
+ sub_filename[sub_filename_index] = filename[read_file_name];
+ sub_filename_index++;
+
+ if (sub_filename_index + 1 == max_sub_filename) {
+ max_sub_filename *= 2;
+
+ sub_filename = realloc(sub_filename, sizeof(char) * max_sub_filename);
+ }
+ }
+
+ sub_filename[sub_filename_index] = '\0';
+ char *res = (char *) get__hashmap(load_map, sub_filename, "");
+
+ free(sub_filename);
+ return res ? res : "text/html"; // default handle
+}
+
+char *content_type_infer(hashmap *load_map, char *filename, char *data, int data_length) {
+ // first check the file for an ending within the system
+ // find last "." and read data after that
+ // if no ".", try inferring from the data within char *data
+
+ int find_p;
+ for (find_p = strlen(filename) - 1; find_p >= 0; find_p--)
+ if (filename[find_p] == '.')
+ break;
+
+ if (find_p >= 0)
+ return end_type_script_check(load_map, filename, find_p);
+
+ return "text/plain"; // no value
+}
\ No newline at end of file
diff --git a/typeinfer.h b/typeinfer.h
new file mode 100644
index 0000000..939f85c
--- /dev/null
+++ b/typeinfer.h
@@ -0,0 +1,9 @@
+#ifndef __TYPEINFER_L__
+#define __TYPEINFER_L__
+
+#include "hashmap.h"
+
+hashmap *infer_load();
+char *content_type_infer(hashmap *load_map, char *filename, char *data, int data_length);
+
+#endif
\ No newline at end of file
diff --git a/views/home.html b/views/home.html
index 69d900f..f92ab1e 100644
--- a/views/home.html
+++ b/views/home.html
@@ -1,9 +1,11 @@
+
+
Example Page
-