Update WL

This commit is contained in:
2025-08-12 22:08:42 +02:00
parent 890a5c48e4
commit 078f9eb319
6 changed files with 3718 additions and 4240 deletions
+3339 -4006
View File
File diff suppressed because it is too large Load Diff
+66 -71
View File
@@ -1,20 +1,7 @@
#ifndef WL_AMALGAMATION #include <stdint.h>
#define WL_AMALGAMATION
// This file was generated automatically. Do not modify directly! typedef struct WL_Runtime WL_Runtime;
typedef struct WL_Compiler WL_Compiler;
////////////////////////////////////////////////////////////////////////////////////////
// src/compile.h
////////////////////////////////////////////////////////////////////////////////////////
#ifndef WL_PUBLIC_INCLUDED
#define WL_PUBLIC_INCLUDED
#ifndef WL_AMALGAMATION
#include "includes.h"
#endif
typedef struct WL_State WL_State;
typedef struct { typedef struct {
char *ptr; char *ptr;
@@ -24,70 +11,78 @@ typedef struct {
typedef struct { typedef struct {
char *ptr; char *ptr;
int len; int len;
} WL_Program; int cur;
} WL_Arena;
typedef enum {
WL_DONE,
WL_ERROR,
WL_VAR,
WL_CALL,
WL_OUTPUT,
} WL_ResultType;
typedef struct {
WL_ResultType type;
WL_String str;
} WL_Result;
typedef struct { typedef struct {
char *ptr; char *ptr;
int len; int len;
int cur; } WL_Program;
} WL_Arena;
typedef struct WL_Compiler WL_Compiler;
typedef enum { typedef enum {
WL_COMPILE_RESULT_DONE, WL_ADD_ERROR,
WL_COMPILE_RESULT_FILE, WL_ADD_AGAIN,
WL_COMPILE_RESULT_ERROR, WL_ADD_LINK,
} WL_CompileResultType; } WL_AddResultType;
typedef struct { typedef struct {
WL_CompileResultType type; WL_AddResultType type;
WL_Program program; WL_String path;
WL_String path; } WL_AddResult;
} WL_CompileResult;
WL_Compiler* WL_Compiler_init (WL_Arena *arena); typedef enum {
void WL_Compiler_free (WL_Compiler *compiler); WL_EVAL_NONE,
WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content); WL_EVAL_DONE,
WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax); WL_EVAL_ERROR,
void WL_State_free (WL_State *state); WL_EVAL_OUTPUT,
void WL_State_trace (WL_State *state, int trace); WL_EVAL_SYSVAR,
WL_Result WL_eval (WL_State *state); WL_EVAL_SYSCALL,
} WL_EvalResultType;
void WL_dump_program(WL_Program program); typedef struct {
WL_EvalResultType type;
WL_String str;
} WL_EvalResult;
int WL_streq (WL_String a, char *b, int blen); WL_Compiler* wl_compiler_init (WL_Arena *arena);
int WL_peeknone (WL_State *state, int off); WL_AddResult wl_compiler_add (WL_Compiler *compiler, WL_String content);
int WL_peekint (WL_State *state, int off, long long *x); int wl_compiler_link (WL_Compiler *compiler, WL_Program *program);
int WL_peekfloat (WL_State *state, int off, float *x); WL_String wl_compiler_error (WL_Compiler *compiler);
int WL_peekstr (WL_State *state, int off, WL_String *str); int wl_dump_ast (WL_Compiler *compiler, char *dst, int cap);
int WL_popnone (WL_State *state); int wl_dump_program (WL_Program program, char *dst, int cap);
int WL_popint (WL_State *state, long long *x);
int WL_popfloat (WL_State *state, float *x);
int WL_popstr (WL_State *state, WL_String *str);
int WL_popany (WL_State *state);
void WL_select (WL_State *state);
void WL_pushnone (WL_State *state);
void WL_pushint (WL_State *state, long long x);
void WL_pushfloat (WL_State *state, float x);
void WL_pushstr (WL_State *state, WL_String str);
void WL_pusharray (WL_State *state, int cap);
void WL_pushmap (WL_State *state, int cap);
void WL_insert (WL_State *state);
void WL_append (WL_State *state);
#endif // WL_PUBLIC_INCLUDED WL_Runtime* wl_runtime_init (WL_Arena *arena, WL_Program program);
#endif // WL_AMALGAMATION WL_EvalResult wl_runtime_eval (WL_Runtime *rt);
WL_String wl_runtime_error (WL_Runtime *rt);
bool wl_streq (WL_String a, char *b, int blen);
int wl_arg_count (WL_Runtime *rt);
bool wl_arg_none (WL_Runtime *rt, int idx);
bool wl_arg_bool (WL_Runtime *rt, int idx, bool *x);
bool wl_arg_s64 (WL_Runtime *rt, int idx, int64_t *x);
bool wl_arg_f64 (WL_Runtime *rt, int idx, double *x);
bool wl_arg_str (WL_Runtime *rt, int idx, WL_String *x);
bool wl_arg_array (WL_Runtime *rt, int idx);
bool wl_arg_map (WL_Runtime *rt, int idx);
bool wl_peek_none (WL_Runtime *rt, int off);
bool wl_peek_bool (WL_Runtime *rt, int off, bool *x);
bool wl_peek_s64 (WL_Runtime *rt, int off, int64_t *x);
bool wl_peek_f64 (WL_Runtime *rt, int off, double *x);
bool wl_peek_str (WL_Runtime *rt, int off, WL_String *x);
bool wl_pop_any (WL_Runtime *rt);
bool wl_pop_none (WL_Runtime *rt);
bool wl_pop_bool (WL_Runtime *rt, bool *x);
bool wl_pop_s64 (WL_Runtime *rt, int64_t *x);
bool wl_pop_f64 (WL_Runtime *rt, double *x);
bool wl_pop_str (WL_Runtime *rt, WL_String *x);
void wl_push_none (WL_Runtime *rt);
void wl_push_true (WL_Runtime *rt);
void wl_push_false (WL_Runtime *rt);
void wl_push_s64 (WL_Runtime *rt, int64_t x);
void wl_push_f64 (WL_Runtime *rt, double x);
void wl_push_str (WL_Runtime *rt, WL_String x);
void wl_push_array (WL_Runtime *rt, int cap);
void wl_push_map (WL_Runtime *rt, int cap);
void wl_push_arg (WL_Runtime *rt, int idx);
void wl_insert (WL_Runtime *rt);
void wl_append (WL_Runtime *rt);
+2 -2
View File
@@ -48,10 +48,10 @@ let main =
<div class="item"> <div class="item">
<div> <div>
<a href=link>\post.title</a> <a href=\'"'\link\'"'>\post.title</a>
</div> </div>
<div> <div>
<span>\post.date</span> | <span><a href=link>\post.num_comments comments</a></span> <span>\post.date</span> | <span><a href=\'"'\link\'"'>\post.num_comments comments</a></span>
</div> </div>
</div> </div>
} }
+1 -1
View File
@@ -1,5 +1,5 @@
fun page(title, login_user_id, style, main) procedure page(title, login_user_id, style, main)
<html> <html>
<head> <head>
<title>CN - \title</title> <title>CN - \title</title>
+35
View File
@@ -1,16 +1,51 @@
include "pages/page.wl" include "pages/page.wl"
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id) let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
let comments = $query("SELECT C.id, U.username, C.content FROM Comments as C, Users as U WHERE C.parent_post=? AND U.id == C.author", $post_id)
let post = posts[0] let post = posts[0]
let style = let style =
<style> <style>
.child {
border-left: 3px solid #ccc;
padding-left: 10px;
}
form textarea {
width: 100%;
}
</style> </style>
let main = let main =
<main> <main>
<h3>\post.title</h3> <h3>\post.title</h3>
<p>\post.content</p> <p>\post.content</p>
<div>
\if $login_user_id != none:
<form action="/api/comment" method="POST">
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
<textarea name="content"></textarea>
<input type="submit" vaue="Publish" />
</form>
</div>
\if len comments == 0:
<span>No comments yet!</span>
else for comment in comments:
<div>
<a href="">\comment.username</a>
<p>
\comment.content
</p>
<div class="child">
\if $login_user_id != none:
<form action="/api/comment" method="POST">
<input type="hidden" name="parent_post" value=\'"'\$post_id\'"' />
<input type="hidden" name="parent_comment" value=\'"'\comment.id\'"' />
<textarea name="content"></textarea>
<input type="submit" vaue="Publish" />
</form>
</div>
</div>
</main> </main>
page(post.title, $login_user_id, style, main) page(post.title, $login_user_id, style, main)
+273 -158
View File
@@ -6,7 +6,7 @@
#include <stdbool.h> #include <stdbool.h>
#include "sqlite3.h" #include "sqlite3.h"
#include "chttp.h" #include "chttp.h"
#include "WL.h" #include "wl.h"
#include "sqlite3utils.h" #include "sqlite3utils.h"
sqlite3 *db; sqlite3 *db;
@@ -29,186 +29,158 @@ int load_file(char *file, char **data, long *size)
return 0; return 0;
} }
static int query_routine(WL_State *state) static int query_routine(WL_Runtime *rt)
{ {
long long num_args; int num_args = wl_arg_count(rt);
if (!WL_popint(state, &num_args)) { if (num_args == 0)
assert(0); // TODO return 0;
}
if (num_args == 0) {
assert(0); // TODO
}
WL_String format; WL_String format;
if (!WL_peekstr(state, -num_args, &format)) { if (!wl_arg_str(rt, 0, &format))
assert(0); // TODO return -1;
}
sqlite3_stmt *res; sqlite3_stmt *stmt;
int ret = sqlite3_prepare_v2(db, format.ptr, format.len, &res, 0); int ret = sqlite3_prepare_v2(db, format.ptr, format.len, &stmt, 0);
if (ret != SQLITE_OK) { if (ret != SQLITE_OK) {
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db)); fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
assert(0); // TODO return -1;
} }
for (int i = 0; i < num_args-1; i++) { for (int i = 1; i < num_args; i++) {
int64_t ival;
double fval;
WL_String str;
if (0) {}
else if (wl_arg_none(rt, i))
ret = sqlite3_bind_null (stmt, i);
else if (wl_arg_s64(rt, i, &ival))
ret = sqlite3_bind_int64 (stmt, i, ival);
else if (wl_arg_f64(rt, i, &fval))
ret = sqlite3_bind_double(stmt, i, fval);
else if (wl_arg_str(rt, i, &str))
ret = sqlite3_bind_text (stmt, i, str.ptr, str.len, NULL);
else assert(0);
if (ret != SQLITE_OK) { if (ret != SQLITE_OK) {
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db)); fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_finalize(stmt);
return -1;
}
}
wl_push_array(rt, 0);
while (sqlite3_step(stmt) == SQLITE_ROW) {
int num_cols = sqlite3_column_count(stmt);
if (num_cols < 0) {
assert(0); // TODO assert(0); // TODO
} }
if (WL_peeknone(state, -num_args + i + 1)) { wl_push_map(rt, num_cols);
ret = sqlite3_bind_null(res, i+1);
continue;
}
long long ival; for (int i = 0; i < num_cols; i++) {
if (WL_peekint(state, -num_args + i + 1, &ival)) { ret = sqlite3_column_type(stmt, i);
ret = sqlite3_bind_int64(res, i+1, ival); switch (ret) {
continue;
}
float fval; case SQLITE_INTEGER:
if (WL_peekfloat(state, -num_args + i + 1, &fval)) { {
ret = sqlite3_bind_double(res, i+1, fval); int64_t x = sqlite3_column_int64(stmt, i);
continue; wl_push_s64(rt, x);
}
WL_String str;
if (WL_peekstr(state, -num_args + i + 1, &str)) {
ret = sqlite3_bind_text(res, i+1, str.ptr, str.len, NULL);
continue;
}
break;
}
ret = sqlite3_step(res);
if (ret == SQLITE_DONE) {
WL_pushnone(state);
} else if (ret != SQLITE_ROW) {
WL_pushnone(state);
} else {
WL_pusharray(state, 0);
do {
int num_cols = sqlite3_column_count(res);
if (num_cols < 0) {
assert(0); // TODO
}
WL_pushmap(state, 0);
for (int i = 0; i < num_cols; i++) {
ret = sqlite3_column_type(res, i);
switch (ret) {
case SQLITE_INTEGER:
{
int64_t x = sqlite3_column_int64(res, i);
WL_pushint(state, x);
}
break;
case SQLITE_FLOAT:
{
double x = sqlite3_column_double(res, i);
WL_pushfloat(state, x);
}
break;
case SQLITE_TEXT:
{
const void *x = sqlite3_column_text(res, i);
int n = sqlite3_column_bytes(res, i);
WL_pushstr(state, (WL_String) { (char*) x, n });
}
break;
case SQLITE_BLOB:
{
const void *x = sqlite3_column_blob(res, i);
int n = sqlite3_column_bytes(res, i);
WL_pushstr(state, (WL_String) { (char*) x, n });
}
break;
case SQLITE_NULL:
{
WL_pushnone(state);
}
break;
} }
break;
const char *name = sqlite3_column_name(res, i); case SQLITE_FLOAT:
{
double x = sqlite3_column_double(stmt, i);
wl_push_f64(rt, x);
}
break;
WL_pushstr(state, (WL_String) { (char*) name, strlen(name) }); case SQLITE_TEXT:
WL_insert(state); {
const void *x = sqlite3_column_text(stmt, i);
int n = sqlite3_column_bytes(stmt, i);
wl_push_str(rt, (WL_String) { (char*) x, n });
}
break;
case SQLITE_BLOB:
{
const void *x = sqlite3_column_blob(stmt, i);
int n = sqlite3_column_bytes(stmt, i);
wl_push_str(rt, (WL_String) { (char*) x, n });
}
break;
case SQLITE_NULL:
{
wl_push_none(rt);
}
break;
} }
WL_append(state); const char *name = sqlite3_column_name(stmt, i);
} while (sqlite3_step(res) == SQLITE_ROW); wl_push_str(rt, (WL_String) { (char*) name, strlen(name) });
wl_insert(rt);
}
wl_append(rt);
} }
WL_pushint(state, 1); sqlite3_finalize(stmt);
sqlite3_finalize(res);
return 0; return 0;
} }
int evaluate_template(HTTP_ResponseBuilder builder, int evaluate_template(HTTP_ResponseBuilder builder,
WL_Program program, WL_Arena arena, WL_Program program, WL_Arena arena, int user_id, int post_id)
char *err, int errmax, int user_id, int post_id)
{ {
//WL_dump_program(program); //wl_dump_program(program);
WL_State *state = WL_State_init(&arena, program, err, errmax); WL_Runtime *rt = wl_runtime_init(&arena, program);
if (state == NULL) if (rt == NULL)
return -1; return -1;
WL_State_trace(state, 0);
for (;;) { for (;;) {
WL_Result result = WL_eval(state); WL_EvalResult result = wl_runtime_eval(rt);
switch (result.type) { switch (result.type) {
case WL_DONE: case WL_EVAL_DONE:
WL_State_free(state);
return 0; return 0;
case WL_ERROR: case WL_EVAL_ERROR:
WL_State_free(state); printf("Error: %s\n", wl_runtime_error(rt).ptr); // TODO
return -1; return -1;
case WL_VAR: case WL_EVAL_SYSVAR:
if (WL_streq(result.str, "login_user_id", -1)) { if (wl_streq(result.str, "login_user_id", -1)) {
if (user_id < 0) if (user_id < 0)
WL_pushnone(state); wl_push_none(rt);
else else
WL_pushint(state, user_id); wl_push_s64(rt, user_id);
} else if (WL_streq(result.str, "post_id", -1)) { } else if (wl_streq(result.str, "post_id", -1)) {
if (post_id < 0) if (post_id < 0)
WL_pushnone(state); wl_push_none(rt);
else else
WL_pushint(state, post_id); wl_push_s64(rt, post_id);
} }
break; break;
case WL_CALL: case WL_EVAL_SYSCALL:
if (WL_streq(result.str, "query", -1)) { if (wl_streq(result.str, "query", -1)) {
query_routine(state); query_routine(rt);
break; break;
} }
break; break;
case WL_OUTPUT: case WL_EVAL_OUTPUT:
http_response_builder_body(builder, (HTTP_String) { result.str.ptr, result.str.len }); http_response_builder_body(builder, (HTTP_String) { result.str.ptr, result.str.len });
break; break;
} }
@@ -222,7 +194,7 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
http_response_builder_status(builder, 200); http_response_builder_status(builder, 200);
http_response_builder_header(builder, HTTP_STR("Content-Type: text/html")); http_response_builder_header(builder, HTTP_STR("Content-Type: text/html"));
WL_Compiler *compiler = WL_Compiler_init(&arena); WL_Compiler *compiler = wl_compiler_init(&arena);
if (compiler == NULL) { if (compiler == NULL) {
assert(0); // TODO assert(0); // TODO
} }
@@ -230,7 +202,7 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
char *loaded_files[128]; char *loaded_files[128];
int num_loaded_files = 0; int num_loaded_files = 0;
WL_CompileResult result; WL_AddResult result;
WL_String path = { file, strlen(file) }; WL_String path = { file, strlen(file) };
for (int i = 0;; i++) { for (int i = 0;; i++) {
@@ -261,41 +233,40 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
fread(file_data, 1, file_size, f); fread(file_data, 1, file_size, f);
fclose(f); fclose(f);
result = WL_compile(compiler, path, (WL_String) { file_data, file_size }); result = wl_compiler_add(compiler, (WL_String) { file_data, file_size });
loaded_files[num_loaded_files++] = file_data; loaded_files[num_loaded_files++] = file_data;
if (result.type == WL_COMPILE_RESULT_ERROR) { if (result.type == WL_ADD_ERROR) {
printf("Compilation of '%.*s' failed\n", path.len, path.ptr); printf("Compilation of '%.*s' failed\n", path.len, path.ptr);
break; break;
} }
if (result.type == WL_COMPILE_RESULT_DONE) if (result.type == WL_ADD_LINK)
break; break;
assert(result.type == WL_COMPILE_RESULT_FILE); assert(result.type == WL_ADD_AGAIN);
path = result.path; path = result.path;
} }
WL_Program program;
int ret = wl_compiler_link(compiler, &program);
for (int i = 0; i < num_loaded_files; i++) for (int i = 0; i < num_loaded_files; i++)
free(loaded_files[i]); free(loaded_files[i]);
WL_Compiler_free(compiler); if (ret < 0) {
WL_String err = wl_compiler_error(compiler);
if (result.type == WL_COMPILE_RESULT_ERROR) {
http_response_builder_undo(builder); http_response_builder_undo(builder);
http_response_builder_status(builder, 500); http_response_builder_status(builder, 500);
http_response_builder_body(builder, HTTP_STR("Couldn't compile the template")); http_response_builder_body(builder, (HTTP_String) { err.ptr, err.len });
http_response_builder_done(builder); http_response_builder_done(builder);
return; return;
} }
WL_Program program = result.program;
char err[1<<9]; if (evaluate_template(builder, program, arena, user_id, post_id) < 0) {
if (evaluate_template(builder, program, arena, err, (int) sizeof(err), user_id, post_id) < 0) {
http_response_builder_undo(builder); http_response_builder_undo(builder);
http_response_builder_status(builder, 500); http_response_builder_status(builder, 500);
http_response_builder_body(builder, (HTTP_String) { err, (int) strlen(err) });
http_response_builder_done(builder); http_response_builder_done(builder);
return; return;
} }
@@ -415,7 +386,33 @@ HTTP_String http_getcookie(HTTP_Request *req, HTTP_String name)
return HTTP_STR(""); return HTTP_STR("");
} }
HTTP_String http_getparam(HTTP_String body, HTTP_String str) void *alloc(WL_Arena *arena, int num, int align)
{
int pad = -(uintptr_t) (arena->ptr + arena->cur) & (align-1);
if (arena->len - arena->cur < num + pad)
return NULL;
void *ptr = arena->ptr + arena->cur + pad;
arena->cur += num + pad;
return ptr;
}
static bool is_hex_digit(char c)
{
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
static int hex_digit_to_int(char c)
{
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return c - '0';
}
HTTP_String http_getparam(HTTP_String body, HTTP_String str, WL_Arena *arena)
{ {
// This is just a best-effort implementation // This is just a best-effort implementation
@@ -450,8 +447,47 @@ HTTP_String http_getparam(HTTP_String body, HTTP_String str)
} }
} }
if (http_streq(str, name)) if (http_streq(str, name)) {
return body;
bool percent_encoded = false;
for (int i = 0; i < body.len; i++)
if (body.ptr[i] == '+' || body.ptr[i] == '%') {
percent_encoded = true;
break;
}
if (!percent_encoded)
return body;
HTTP_String decoded = { alloc(arena, body.len, 1), 0 };
if (decoded.ptr == NULL)
return (HTTP_String) { NULL, 0 };
for (int i = 0; i < body.len; i++) {
char c = body.ptr[i];
if (c == '+')
c = ' ';
else {
if (body.ptr[i] == '%') {
if (body.len - i < 3
|| !is_hex_digit(body.ptr[i+1])
|| !is_hex_digit(body.ptr[i+2]))
return (HTTP_String) { NULL, 0 };
int h = hex_digit_to_int(body.ptr[i+1]);
int l = hex_digit_to_int(body.ptr[i+2]);
c = (h << 4) | l;
i += 2;
}
}
decoded.ptr[decoded.len++] = c;
}
return decoded;
}
} }
return HTTP_STR(""); return HTTP_STR("");
@@ -545,6 +581,12 @@ bool valid_link(HTTP_String str)
return true; return true;
} }
bool valid_comment_content(HTTP_String str)
{
(void) str;
return true;
}
int main(void) int main(void)
{ {
http_global_init(); http_global_init();
@@ -586,8 +628,6 @@ int main(void)
int pool_cap = 1<<20; int pool_cap = 1<<20;
char *pool = malloc(pool_cap); char *pool = malloc(pool_cap);
WL_Arena arena = { pool, pool_cap, 0 };
SessionSet sessions; SessionSet sessions;
session_set_init(&sessions); session_set_init(&sessions);
@@ -599,6 +639,8 @@ int main(void)
int ret = http_server_wait(server, &req, &builder); int ret = http_server_wait(server, &req, &builder);
if (ret < 0) return -1; if (ret < 0) return -1;
WL_Arena arena = { pool, pool_cap, 0 };
//printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO //printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO
// If logged in, these are set to non-negative values // If logged in, these are set to non-negative values
@@ -638,8 +680,8 @@ int main(void)
continue; continue;
} }
HTTP_String name = http_getparam(req->body, HTTP_STR("username")); HTTP_String name = http_getparam(req->body, HTTP_STR("username"), &arena);
HTTP_String pass = http_getparam(req->body, HTTP_STR("password")); HTTP_String pass = http_getparam(req->body, HTTP_STR("password"), &arena);
name = http_trim(name); name = http_trim(name);
pass = http_trim(pass); pass = http_trim(pass);
@@ -694,10 +736,10 @@ int main(void)
continue; continue;
} }
HTTP_String name = http_getparam(req->body, HTTP_STR("username")); HTTP_String name = http_getparam(req->body, HTTP_STR("username"), &arena);
HTTP_String email = http_getparam(req->body, HTTP_STR("email")); HTTP_String email = http_getparam(req->body, HTTP_STR("email"), &arena);
HTTP_String pass1 = http_getparam(req->body, HTTP_STR("password1")); HTTP_String pass1 = http_getparam(req->body, HTTP_STR("password1"), &arena);
HTTP_String pass2 = http_getparam(req->body, HTTP_STR("password2")); HTTP_String pass2 = http_getparam(req->body, HTTP_STR("password2"), &arena);
if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) { if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) {
http_response_builder_status(builder, 400); http_response_builder_status(builder, 400);
@@ -764,9 +806,9 @@ int main(void)
continue; continue;
} }
HTTP_String title = http_getparam(req->body, HTTP_STR("title")); HTTP_String title = http_getparam(req->body, HTTP_STR("title"), &arena);
HTTP_String link = http_getparam(req->body, HTTP_STR("link")); HTTP_String link = http_getparam(req->body, HTTP_STR("link"), &arena);
HTTP_String content = http_getparam(req->body, HTTP_STR("content")); HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
title = http_trim(title); title = http_trim(title);
link = http_trim(link); link = http_trim(link);
@@ -818,6 +860,74 @@ int main(void)
http_response_builder_header(builder, (HTTP_String) { location, ret }); http_response_builder_header(builder, (HTTP_String) { location, ret });
http_response_builder_done(builder); http_response_builder_done(builder);
} else if (http_streq(path, HTTP_STR("/api/comment"))) {
if (req->method != HTTP_METHOD_POST) {
http_response_builder_status(builder, 405);
http_response_builder_done(builder);
continue;
}
if (user_id == -1) {
http_response_builder_status(builder, 303);
http_response_builder_header(builder, HTTP_STR("Location: /index"));
http_response_builder_done(builder);
continue;
}
HTTP_String parent_post_str = http_getparam(req->body, HTTP_STR("parent_post"), &arena);
HTTP_String content = http_getparam(req->body, HTTP_STR("content"), &arena);
int parent_post;
{
char buf[32];
if (parent_post_str.len >= (int) sizeof(buf)) {
assert(0); // TODO
}
memcpy(buf, parent_post_str.ptr, parent_post_str.len);
buf[parent_post_str.len] = '\0';
parent_post = atoi(buf);
if (parent_post == 0) {
assert(0); // TODO
}
}
content = http_trim(content);
if (!valid_comment_content(content)) {
assert(0); // TODO
}
if (content.len == 0) {
assert(0); // TODO
}
sqlite3_stmt *stmt;
int ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post);
if (ret != SQLITE_OK) {
assert(0); // TODO
}
ret = sqlite3_step(stmt);
if (ret != SQLITE_DONE) {
assert(0); // TODO
}
ret = sqlite3_finalize(stmt);
if (ret != SQLITE_OK) {
assert(0); // TODO
}
char location[1<<9];
ret = snprintf(location, sizeof(location), "Location: /post?id=%d", parent_post);
if (ret < 0 || ret >= (int) sizeof(location)) {
assert(0); // TODO
}
http_response_builder_status(builder, 303);
http_response_builder_header(builder, (HTTP_String) { location, ret });
http_response_builder_done(builder);
} else if (http_streq(path, HTTP_STR("/index"))) { } else if (http_streq(path, HTTP_STR("/index"))) {
evaluate_template_2(builder, arena, "pages/index.wl", user_id, -1); evaluate_template_2(builder, arena, "pages/index.wl", user_id, -1);
@@ -860,10 +970,11 @@ int main(void)
} else if (http_streq(path, HTTP_STR("/post"))) { } else if (http_streq(path, HTTP_STR("/post"))) {
HTTP_String idstr = http_getparam(req->url.query, HTTP_STR("id")); HTTP_String idstr = http_getparam(req->url.query, HTTP_STR("id"), &arena);
char buf[32]; char buf[32];
if (idstr.len == 0 || idstr.len >= (int) sizeof(buf)) { if (idstr.len == 0 || idstr.len >= (int) sizeof(buf)) {
printf("post id [%.*s] is not defined\n", idstr.len, idstr.ptr); // TODO
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1); evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
continue; continue;
} }
@@ -872,6 +983,7 @@ int main(void)
int post_id = atoi(buf); int post_id = atoi(buf);
if (post_id == 0) { if (post_id == 0) {
printf("Invalid post id [%s]\n", buf);
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1); evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
continue; continue;
} }
@@ -897,10 +1009,13 @@ int main(void)
assert(0); // TODO assert(0); // TODO
} }
if (num == 0) if (num == 0) {
printf("post with id %d does not exist\n", post_id); // TODO
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1); evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
else } else {
printf("Evaluating template\n");
evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id); evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id);
}
} else { } else {