aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremias Stotter <jeremias@stotter.eu>2021-11-07 21:58:43 +0100
committerJeremias Stotter <jeremias@stotter.eu>2021-11-07 21:58:43 +0100
commite56f613732be1e8e70fca81de9584b601566af75 (patch)
treea494c8ff463244096f97dd4437aa8f67ef628c44
parent578ce10d887d1dc858f963dc09bf8f25b5e30eea (diff)
downloadJBlog-e56f613732be1e8e70fca81de9584b601566af75.tar.gz
JBlog-e56f613732be1e8e70fca81de9584b601566af75.tar.bz2
JBlog-e56f613732be1e8e70fca81de9584b601566af75.zip
Started implementing the admin page feature
For now it can only process inputs you feed it and show you a preview
-rw-r--r--admin.c145
-rw-r--r--admin.h5
-rw-r--r--blog/admin.jblog2
-rw-r--r--jblog.c268
-rw-r--r--jblog.h6
-rw-r--r--makefile4
-rw-r--r--md.c11
-rw-r--r--pattern.xhtml17
-rw-r--r--readme.html1
9 files changed, 342 insertions, 117 deletions
diff --git a/admin.c b/admin.c
new file mode 100644
index 0000000..91fc54f
--- /dev/null
+++ b/admin.c
@@ -0,0 +1,145 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#include "jblog.h"
+#include "md.h"
+
+#define ADMIN_HTML " \
+<table class=\"admin-table\"> \
+ <tr> \
+ <td> \
+ <form class=\"jb-adm-form\" method=\"POST\" target=\"result_html\"> \
+ <label for=\"release_date\">Release Date:</label> <input type=\"date\" id=\"release_date\" name=\"release_date\"/> <br/> \
+ <label for=\"release_time\">Release Time:</label> <input type=\"time\" id=\"release_time\" name=\"release_time\"/> <br/> \
+ <label for=\"author\">Author:</label> <input type=\"text\" id=\"author\" name=\"author\"/> <br/> \
+ <label for=\"title\">Title:</label> <input type=\"text\" id=\"title\" name=\"title\"/> <br/> \
+ <label for=\"md-input\">Markdown:</label><br/> \
+ <textarea name=\"md-input\"></textarea><br/> \
+ <input type=\"submit\" value=\"Process\" name=\"sub\"/> \
+ <input type=\"submit\" value=\"Save\" name=\"sub\"/> \
+ <input type=\"submit\" value=\"Publish\" name=\"sub\"/> <br/> \
+ </form> \
+ </td> \
+ <td> \
+ <iframe class=\"jb-adm-result\" id=\"result_html\" name=\"result_html\"></iframe> \
+ </td> \
+ </tr> \
+ <tr> \
+ <td> \
+ <form class=\"jb-adm-form\" method=\"POST\" target=\"folder_list\"> \
+ <label for=\"folder_name\">Folder Name:</label> <input type=\"text\" id=\"folder_name\" name=\"folder_name\"/> <br/> \
+ <input type=\"submit\" value=\"Delete Folder\" name=\"sub\"/> \
+ <input type=\"submit\" value=\"Create Folder\" name=\"sub\"/> \
+ </form> \
+ </td> \
+ <td> \
+ <iframe class=\"jb-adm-result\" id=\"folder_list\" name=\"folder_list\"></iframe> \
+ </td> \
+ </tr> \
+</table>"
+
+#define ADM_PROCESS 0
+#define ADM_SAVE 1
+#define ADM_PUBLISH 2
+
+#define ADM_FOLDER_DEL
+#define ADM_FOLDER_CREA
+
+// Can be done inplace
+void urldecode(char* src, char* dest) {
+ char *d, *c;
+ for(c = src, d = dest; *c != '\0'; c++, d++) {
+ if(*c == '%' && *(c+1) != '\0') {
+ char decode_str[3] = {0};
+ memcpy(decode_str, c + 1, 2);
+ *d = (unsigned char)strtol(decode_str, NULL, 16) % UCHAR_MAX;
+ c+=2;
+ } else if(*c == '+') {
+ *d = ' ';
+ } else if(*c == '\r' && *c+1 == '\n') {
+ *d = '\n';
+ c++;
+ }
+ else {
+ *d = *c;
+ }
+ }
+ *d = '\0';
+ return;
+}
+
+int create_admin(char* buffer, size_t buffer_size, char* adm_save_dir) {
+ strncpy(buffer, ADMIN_HTML, buffer_size-1);
+ return 0;
+}
+
+// This handles post request received
+// Return 0 if the result should be beautified
+int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_save_dir) {
+ char* md_input = NULL;
+ char* sub_str = NULL;
+ char* date_str = NULL;
+ char* time_str = NULL;
+ char* author = NULL;
+ char* title = NULL;
+
+ char* start = NULL;
+ char* end = NULL;
+
+ for(start = req_content, end = strchr(start, '&');
+ ;
+ start = end + 1, end = strchr(start, '&'))
+ {
+ // Seperate by =
+ char* value = strchr(start, '=');
+ if(value == NULL) {
+ continue;
+ }
+ *value = '\0';
+ value++;
+ // Check if we habe sub or md_input
+ if(strcmp(start, "md-input") == 0) {
+ md_input = value;
+ } else if(strcmp(start, "sub") == 0) {
+ sub_str = value;
+ } else if(strcmp(start, "release_date") == 0) {
+ date_str = value;
+ } else if(strcmp(start, "release_time") == 0) {
+ time_str = value;
+ } else if(strcmp(start, "author") == 0) {
+ author = value;
+ } else if(strcmp(start, "title") == 0) {
+ title = value;
+ }
+
+ if(end == NULL) {
+ break;
+ } else {
+ *end = '\0';
+ }
+ }
+
+ if(!(md_input && sub_str && date_str && time_str && author && title)) {
+ perror("Couldn't read all the values from the form!");
+ return -1;
+ }
+
+ // urldecode the relevant values
+ urldecode(md_input, md_input);
+ urldecode(author, author);
+ urldecode(title, title);
+ urldecode(time_str, time_str);
+
+ if(strcmp(sub_str, "Process") == 0) {
+ process_blog_text(buffer, buffer_size, md_input, title, author, date_str, time_str);
+ } else if(strcmp(sub_str, "Save")) {
+
+ } else if(strcmp(sub_str, "Publish")) {
+ } else if(strcmp(sub_str, "Delete Folder")) {
+ } else if(strcmp(sub_str, "Create Folder")) {
+ }
+
+ return 0;
+}
diff --git a/admin.h b/admin.h
new file mode 100644
index 0000000..706c372
--- /dev/null
+++ b/admin.h
@@ -0,0 +1,5 @@
+#ifndef JADMIN
+#define JADMIN
+int create_admin(char* buffer, size_t buffer_size, char* adm_save_dir);
+int post_admin(char* buffer, size_t buffer_size, char* req_content, char* adm_save_dir);
+#endif
diff --git a/blog/admin.jblog b/blog/admin.jblog
new file mode 100644
index 0000000..2732c3e
--- /dev/null
+++ b/blog/admin.jblog
@@ -0,0 +1,2 @@
+Type: Admin
+///
diff --git a/jblog.c b/jblog.c
index c0f2cee..2c532bb 100644
--- a/jblog.c
+++ b/jblog.c
@@ -33,6 +33,7 @@
#include "md.h"
#include "index.h"
+#include "admin.h"
// Call program like this
// jblog <UNIX socket> <html pattern> <directory with blog files>
@@ -53,7 +54,13 @@
#define XHTML_ID 0
#define HTML_ID 1
-int sockfd;
+int sockfd;
+char* blog_head_pretitle;
+char* blog_head_postitle;
+char* blog_foot;
+char* base_path;
+char* adm_save_dir;
+int pattern_type = 0;
// Root of the search tree used for caching
void* tree_root = NULL;
@@ -103,23 +110,33 @@ void retrieve_headvals(char* head, char retrieve_values[][32], char** returned_s
} while(head != NULL);
}
-char* blog_head_pretitle;
-char* blog_head_postitle;
-char* blog_foot;
-char* base_path;
-int pattern_type = 0;
+int seperate_string(char* input, char* seperator, char** out_pre, char** out_post) {
+ char* seperator_start= strstr(input, seperator);
+ if(seperator_start == NULL) {
+ return -1;
+ }
+ int seperator_len = strlen(seperator);
+
+ *out_post = seperator_start + seperator_len;
+ *seperator_start = '\0';
+ *out_pre = input;
+ return 0;
+}
int handle_request(int socket);
-int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache);
-int seperate_string(char* input, char* seperator, char** out_pre, char** out_post);
+int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content);
int main(int argc, char* argv[]) {
// Signal handling
signal(SIGINT, sigint_catch);
// Arguments
- if(argc != 4) {
- fprintf(stderr, "ERROR: Call like this:\njblog <UNIX socket> <html pattern> <directory with blog files>\n");
+ if(argc == 5) {
+ adm_save_dir = argv[4];
+ } else if(argc == 4) {
+ adm_save_dir = NULL;
+ } else {
+ fprintf(stderr, "ERROR: Call like this:\njblog <UNIX socket> <html pattern> <directory with blog files> [optional: admin save directory]\n");
return -1;
}
@@ -307,12 +324,15 @@ int handle_request(int socket) {
}
free(msg);
printf("Recieved a %s request at %s with content length %ld\n", request_method, request_uri, content_length);
- // Read in the content if there is any
+ // There is a ',' as the next character in the socket, just read it and ignore it
+ char last_msg;
+ (void)!read(socket, &last_msg, 1);
+ // Read in the content if there is any
char* content = NULL;
if(content_length > 0) {
lseek(socket, 1, SEEK_CUR);
printf("There is content of size %ld\n", content_length);
- content = malloc(content_length);
+ content = calloc(content_length + 1, 1);
if(content == NULL) {
perror("malloc error");
return -1;
@@ -333,7 +353,7 @@ int handle_request(int socket) {
char* html;
char http_response[MAX_REPLY] = "";
size_t reply_size = 0;
- int status_code = create_html(content, content_length, request_uri, request_method, &html, &reply_size, ignore_cache);
+ int status_code = create_html(content, content_length, request_uri, request_method, &html, &reply_size, ignore_cache, content);
char pattern_media_type[128] = "";
@@ -357,41 +377,52 @@ int handle_request(int socket) {
free(content);
}
// Read the socket to the end, then close it off
- char last_msg[1024];
- (void)!read(socket, last_msg, 1024);
shutdown(socket, SHUT_RDWR);
close(socket);
return 0;
}
-// Maybe put the patterns in some sort of in memory cache
-char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size_t title_buffer_size, char* request_uri) {
+int process_blog_text(char* buffer, size_t buffer_size, char* blog_text, char* title, char* author, char* date, char* time) {
+ char parsed_text[MAX_REPLY - 150];
+ if(parse_markdown(blog_text, parsed_text, MAX_REPLY - 150) != 0) {
+ return -1;
+ }
+ snprintf(buffer, MAX_REPLY, "<div class=\"jblog\">"
+ "<span class=\"jb-date\">Published on %s %s</span>"
+ "<h1 class=\"jb-title\">%s</h1>"
+ "%s"
+ "<span class=\"jb-author\">Written by %s</span>"
+ "</div>", date, time ? time : "", title, parsed_text, author );
+ return 0;
+}
+
+int read_jblog_file(char* buffer, int buffer_size, char* file_name, int* error_code, char* request_uri, int request_method, char* req_content) {
int blog_fd = open(file_name, O_RDONLY);
if(blog_fd < 0) {
*error_code = 404;
- return NULL;
+ return -1;
}
struct stat jblog_stat;
if(fstat(blog_fd, &jblog_stat) != 0) {
perror("stat error");
close(blog_fd);
*error_code = 500;
- return NULL;
+ return -1;
}
if(!S_ISREG(jblog_stat.st_mode)) {
*error_code = 403;
- return NULL;
+ return -1;
}
char* blog_content = malloc(jblog_stat.st_size);
if(blog_content == NULL) {
perror("malloc error");
*error_code = 500;
- return NULL;
+ return -1;
}
if(read(blog_fd, blog_content, jblog_stat.st_size) != jblog_stat.st_size) {
*error_code = 500;
fprintf(stderr, "Could not read file\n");
- return NULL;
+ return -1;
}
close(blog_fd);
@@ -399,7 +430,7 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
if(blog_text == NULL) {
fprintf(stderr, "File is empty\n");
*error_code = 500;
- return NULL;
+ return -1;
}
memcpy(blog_text, "\0\0\0\n", 4);
blog_text = blog_text+5;
@@ -409,57 +440,55 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
char* type_str = NULL;
char* depth_str = NULL;
char* index_types = NULL;
+ char* time_str = NULL;
// So we can still free() later;
char* original_blog_content = blog_content;
// blog_content now contains the head of the file, blog_text contains all the text
- char retrieve_values[6][32] = {
+ char retrieve_values[7][32] = {
"Date",
"Title",
"Type",
"Author",
"Depth",
- "DTypes"
+ "DTypes",
+ "Time"
};
- char** returned_strings[6] = {
+ char** returned_strings[7] = {
&date_str,
&title,
&type_str,
&author,
&depth_str,
- &index_types
+ &index_types,
+ &time_str
};
- retrieve_headvals(blog_content, retrieve_values, returned_strings, 6);
+ retrieve_headvals(blog_content, retrieve_values, returned_strings, 7);
- char* html = malloc(MAX_REPLY + 1);
- if(html == NULL) {
- perror("malloc error");
+ if(type_str == NULL) {
*error_code = 500;
- return NULL;
+ return -1;
}
- if(type_str == NULL) {
+ bool wrap_html = true;
+ char* html = malloc(buffer_size);
+ if(html == NULL) {
+ perror("malloc error");
*error_code = 500;
- return NULL;
+ return -1;
}
+
if(strcmp(type_str, "Entry") == 0) {
- char parsed_text[MAX_REPLY] = "\0";
- if(parse_markdown(blog_text, parsed_text, MAX_REPLY) != 0) {
+ if(process_blog_text(html, buffer_size, blog_text, title, author, date_str, time_str) != 0) {
*error_code = 500;
- return NULL;
+ return -1;
}
- snprintf(html, MAX_REPLY, "<div class=\"jblog\">"
- "<h1>%s</h1>"
- "%s"
- "<h4>Written by %s</h4>"
- "<h4>Published on %s</h4>"
- "</div>", title, parsed_text, author, date_str);
} else if(strcmp(type_str, "Index") == 0) {
char index_text[MAX_REPLY] = "\0";
char path_name[PATH_MAX] = "\0";
if(get_basepath(path_name, PATH_MAX, file_name) != 0) {
*error_code = 500;
- return NULL;
+ return -1;
}
int depth = 0;
if(depth_str != NULL) {
@@ -478,7 +507,7 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
allow_types = realloc(allow_types, allow_type_count * sizeof(char*));
if(allow_types == NULL) {
*error_code = 500;
- return NULL;
+ return -1;
}
allow_types[i] = index_types;
index_types = end_space+1;
@@ -486,26 +515,57 @@ char* read_jblog_file(char* file_name, int* error_code, char* title_buffer, size
if(create_index(path_name, depth, index_text, MAX_REPLY, NULL, allow_types, allow_type_count, request_uri, NULL, 0) != 0) {
*error_code = 500;
- return NULL;
+ return -1;
}
- snprintf(html, MAX_REPLY, "<div class=\"jblog index\">"
- "<h1>%s</h1>"
+ snprintf(html, buffer_size - 128, "<div class=\"jblog index\">"
+ "<h1 class=\"jb-title\">%s</h1>"
"%s"
- "<h4>Written by %s</h4>"
+ "<span class=\"jb-author\">Written by %s</span>"
"</div>", title, index_text, author);
+ } else if(strcmp(type_str, "Admin") == 0) {
+ if(request_method == REQ_GET) {
+ if(create_admin(html, MAX_REPLY, adm_save_dir) != 0) {
+ *error_code = 500;
+ return -1;
+ }
+ } else if(request_method == REQ_POST) {
+ int post_result = post_admin(html, MAX_REPLY, req_content, adm_save_dir);
+ if(post_result > 0) {
+ wrap_html = false;
+ } else if(post_result < 0) {
+ *error_code = 500;
+ return -1;
+ }
+ }
}
-
- strncpy(title_buffer, title, title_buffer_size-1);
-
+
free(original_blog_content);
- return html;
+
+ if(wrap_html) {
+ int length = snprintf(buffer, buffer_size - 20, "%s\n<title>%s</title>\n%s\n%s\n%s", blog_head_pretitle, title ? title : "", blog_head_postitle, html, blog_foot);
+ free(html);
+ return length;
+ } else {
+ int length = snprintf(buffer, buffer_size - 20, "<html><body>%s</body></html>", html);
+ free(html);
+ return length;
+ }
}
// This will create the html in buffer and return a http status code
// It will also alocate the buffer itself
// The buffer should only be freed by the caller in case the return isn't in the 200-299 success range
-int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache) {
-
+int create_html(char* content, size_t content_length, char request_uri[1024], char request_method[64], char** buffer, size_t* reply_size, bool ignore_cache, char* req_content) {
+ // See if the request method is supported
+ int req_method;
+ if(strcmp(request_method, "GET") == 0) {
+ req_method = REQ_GET;
+ } else if(strcmp(request_method, "POST") == 0) {
+ req_method = REQ_POST;
+ } else {
+ strcpy(*buffer, HTML_ERROR(405));
+ return 405;
+ }
*buffer = malloc(MAX_REPLY);
if(buffer == NULL) {
perror("malloc error");
@@ -525,81 +585,69 @@ int create_html(char* content, size_t content_length, char request_uri[1024], ch
return 400;
}
- struct tree_entry* in_entry = malloc(sizeof(struct tree_entry));
- in_entry->hash = compute_hash(canonicalized_path);
-
- struct tree_entry** found_entry = tfind(in_entry, &tree_root, compare);
- if(found_entry != NULL) {
- if(ignore_cache) {
- // If we ignore the cache we just free the old entry
- printf("Cache drop\n");
- free((*found_entry)->content);
- struct tree_entry* free_entry = *found_entry;
- if(tdelete(in_entry, &tree_root, compare) == NULL) {
- printf("BULLSHIT!!!\n");
+ struct tree_entry* in_entry = NULL;
+ struct tree_entry** found_entry;
+ // Only cache on GET
+ if(req_method == REQ_GET) {
+ in_entry = malloc(sizeof(struct tree_entry));
+ in_entry->hash = compute_hash(canonicalized_path);
+
+ found_entry = tfind(in_entry, &tree_root, compare);
+ if(found_entry != NULL) {
+ if(ignore_cache) {
+ // If we ignore the cache we just free the old entry
+ printf("Cache drop\n");
+ free((*found_entry)->content);
+ struct tree_entry* free_entry = *found_entry;
+ if(tdelete(in_entry, &tree_root, compare) == NULL) {
+ fprintf(stderr, "error deleting entry from cache");
+ }
+ free(free_entry);
+ } else {
+ free(*buffer);
+ *buffer = (*found_entry)->content;
+ return 200;
}
- free(free_entry);
- } else {
- free(*buffer);
- *buffer = (*found_entry)->content;
- return 200;
}
}
int error_code = 0;
- char title[128] = {'\0'};
- char* dyn_content = read_jblog_file(canonicalized_path, &error_code, title, 128, request_uri);
-
- if(dyn_content == NULL) {
- sprintf(*buffer, HTML_ERROR(%d), error_code);
- return error_code;
- }
-
- *reply_size = snprintf(*buffer, MAX_REPLY, "%s\n<title>%s</title>\n%s\n%s\n%s", blog_head_pretitle, title, blog_head_postitle, dyn_content, blog_foot);
-
+ *reply_size = read_jblog_file(*buffer, MAX_REPLY, canonicalized_path, &error_code, request_uri, req_method, req_content);
+
// The response was too big
if(*reply_size >= MAX_REPLY) {
strcpy(*buffer, HTML_ERROR(500));
- free(dyn_content);
return 500;
}
+
+ if(reply_size < 0) {
+ strcpy(*buffer, HTML_ERROR(error_code));
+ return error_code;
+ }
+
*buffer = realloc(*buffer, *reply_size + 1);
if(buffer == NULL) {
perror("realloc error");
strcpy(*buffer, HTML_ERROR(500));
- free(dyn_content);
return 500;
}
+
+ if(req_method == REQ_GET){
+ in_entry->content = *buffer;
- in_entry->content = *buffer;
-
- // Add the entry in
- found_entry = tsearch(in_entry, &tree_root, compare);
+ // Add the entry in
+ found_entry = tsearch(in_entry, &tree_root, compare);
- if(found_entry == NULL) {
- perror("tsearch error");
- strcpy(*buffer, HTML_ERROR(500));
- free(dyn_content);
- return 500;
+ if(found_entry == NULL) {
+ perror("tsearch error");
+ strcpy(*buffer, HTML_ERROR(500));
+ return 500;
+ }
+
+ memcpy(*found_entry, in_entry, sizeof(struct tree_entry));
}
-
- memcpy(*found_entry, in_entry, sizeof(struct tree_entry));
-
+
// @todo handle all the different inputs
- free(dyn_content);
return 200;
}
-
-int seperate_string(char* input, char* seperator, char** out_pre, char** out_post) {
- char* seperator_start= strstr(input, seperator);
- if(seperator_start == NULL) {
- return -1;
- }
- int seperator_len = strlen(seperator);
-
- *out_post = seperator_start + seperator_len;
- *seperator_start = '\0';
- *out_pre = input;
- return 0;
-}
diff --git a/jblog.h b/jblog.h
index b57ca7b..55bd0fb 100644
--- a/jblog.h
+++ b/jblog.h
@@ -21,6 +21,12 @@
// The maximum reply size
// Actually we are not gonna have quite as much usable
#define MAX_REPLY 8096
+
+// different supported request methods
+#define REQ_GET 0
+#define REQ_POST 1
+
void retrieve_headvals(char* head, char retrieve_values[][32], char** returned_strings[], int retrieve_ammount);
char* trim_whitespace(char** str);
+int process_blog_text(char* buffer, size_t buffer_size, char* blog_text, char* title, char* author, char* date, char* time);
#endif
diff --git a/makefile b/makefile
index ab7628c..a2c06fe 100644
--- a/makefile
+++ b/makefile
@@ -1,5 +1,5 @@
CC=gcc
CFLAGS=-O2 -std=c99 -Wall -g
-jblog: jblog.c md.o index.o
- $(CC) $(CFLAGS) jblog.c md.o index.o -o $@
+jblog: jblog.c md.o index.o admin.o
+ $(CC) $(CFLAGS) jblog.c md.o index.o admin.o -o $@
diff --git a/md.c b/md.c
index 2eeb67d..4ebb75a 100644
--- a/md.c
+++ b/md.c
@@ -150,9 +150,10 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
empty_line_carry = false;
empty_line = true;
}
+
+ spaces_skipped = trim_space(line, &trimmed_line);
if(!on_lastline) {
- spaces_skipped = trim_space(line, &trimmed_line);
if(*trimmed_line == '\0' || *trimmed_line == '\r') {
empty_line_carry = true;
continue;
@@ -161,7 +162,6 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
if(on_lastline) {
empty_line = true;
- trimmed_line = "\0";
line = "\0";
}
@@ -504,8 +504,6 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
// Prepend the pre_line_buffer to the line_buffer
line_length = prepend(line_buffer, pre_line_buffer);
- //printf("%ld: %s\n",line_length , line_buffer);
-
if(offset + line_length > buffer_size) {
fprintf(stderr, "Too long!\n");
return -1;
@@ -514,7 +512,10 @@ int parse_markdown(char* input, char* buffer, size_t buffer_size) {
memcpy(buffer + offset, line_buffer, line_length);
offset += line_length;
if(on_lastline) {
- strncat(buffer, "</p>", buffer_size);
+ *(buffer+offset) = '\0';
+ if(in_paragraph) {
+ strncat(buffer, "</p>", buffer_size);
+ }
break;
}
}
diff --git a/pattern.xhtml b/pattern.xhtml
index 03a61dd..d0cb02b 100644
--- a/pattern.xhtml
+++ b/pattern.xhtml
@@ -33,6 +33,23 @@ $TITLE$
width: 100%;
justify-content: right;
}
+ .admin-table td {
+ width: 50%;
+ height: 100%
+ }
+ .admin-table, .admin-table tr {
+ width: 100%;
+ height: 60vh;
+ }
+ .jb-adm-form textarea {
+ width: 95%;
+ height: 60vh;
+ resize: none;
+ }
+ .jb-adm-result {
+ width: 95%;
+ height: 100%;
+ }
</style>
<link rel="icon" href="/favicon.ico"/>
</head>
diff --git a/readme.html b/readme.html
index 6ae7241..5906423 100644
--- a/readme.html
+++ b/readme.html
@@ -12,6 +12,7 @@
<li>Socket file</li>
<li>html/xhtml pattern file (See below)</li>
<li>directory with blog files</li>
+ <li>optional: admin save directory</li>
</ol>
<p>After you did that, start the webserver, make sure the config file of the webserver has a valid user and points to the right socket!</p>
<h3>The pattern file</h3>
Jeremias Stotters git repositories generated by CGIT