Reorganize code and clean up Makefile
This commit is contained in:
@@ -1,2 +1,4 @@
|
|||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
|
*.out
|
||||||
|
|
||||||
|
|||||||
+72
-11
@@ -351,6 +351,7 @@ typedef enum {
|
|||||||
NODE_INCLUDE,
|
NODE_INCLUDE,
|
||||||
NODE_SELECT,
|
NODE_SELECT,
|
||||||
NODE_NESTED,
|
NODE_NESTED,
|
||||||
|
NODE_OPER_LEN,
|
||||||
NODE_OPER_POS,
|
NODE_OPER_POS,
|
||||||
NODE_OPER_NEG,
|
NODE_OPER_NEG,
|
||||||
NODE_OPER_ASS,
|
NODE_OPER_ASS,
|
||||||
@@ -459,6 +460,7 @@ typedef enum {
|
|||||||
TOKEN_KWORD_TRUE,
|
TOKEN_KWORD_TRUE,
|
||||||
TOKEN_KWORD_FALSE,
|
TOKEN_KWORD_FALSE,
|
||||||
TOKEN_KWORD_INCLUDE,
|
TOKEN_KWORD_INCLUDE,
|
||||||
|
TOKEN_KWORD_LEN,
|
||||||
TOKEN_VALUE_FLOAT,
|
TOKEN_VALUE_FLOAT,
|
||||||
TOKEN_VALUE_INT,
|
TOKEN_VALUE_INT,
|
||||||
TOKEN_VALUE_STR,
|
TOKEN_VALUE_STR,
|
||||||
@@ -550,6 +552,7 @@ String tok2str(Token token, char *buf, int max)
|
|||||||
case TOKEN_KWORD_TRUE: return S("true");
|
case TOKEN_KWORD_TRUE: return S("true");
|
||||||
case TOKEN_KWORD_FALSE: return S("false");
|
case TOKEN_KWORD_FALSE: return S("false");
|
||||||
case TOKEN_KWORD_INCLUDE: return S("include");
|
case TOKEN_KWORD_INCLUDE: return S("include");
|
||||||
|
case TOKEN_KWORD_LEN: return S("len");
|
||||||
|
|
||||||
case TOKEN_VALUE_FLOAT:
|
case TOKEN_VALUE_FLOAT:
|
||||||
{
|
{
|
||||||
@@ -676,18 +679,19 @@ Token next_token(Parser *p)
|
|||||||
p->s.cur - start
|
p->s.cur - start
|
||||||
};
|
};
|
||||||
|
|
||||||
if (streq(kword, S("if"))) return (Token) { .type=TOKEN_KWORD_IF };
|
if (streq(kword, S("if"))) return (Token) { .type=TOKEN_KWORD_IF };
|
||||||
if (streq(kword, S("else"))) return (Token) { .type=TOKEN_KWORD_ELSE };
|
if (streq(kword, S("else"))) return (Token) { .type=TOKEN_KWORD_ELSE };
|
||||||
if (streq(kword, S("while"))) return (Token) { .type=TOKEN_KWORD_WHILE };
|
if (streq(kword, S("while"))) return (Token) { .type=TOKEN_KWORD_WHILE };
|
||||||
if (streq(kword, S("for"))) return (Token) { .type=TOKEN_KWORD_FOR };
|
if (streq(kword, S("for"))) return (Token) { .type=TOKEN_KWORD_FOR };
|
||||||
if (streq(kword, S("in"))) return (Token) { .type=TOKEN_KWORD_IN };
|
if (streq(kword, S("in"))) return (Token) { .type=TOKEN_KWORD_IN };
|
||||||
if (streq(kword, S("fun"))) return (Token) { .type=TOKEN_KWORD_FUN };
|
if (streq(kword, S("fun"))) return (Token) { .type=TOKEN_KWORD_FUN };
|
||||||
if (streq(kword, S("let"))) return (Token) { .type=TOKEN_KWORD_LET };
|
if (streq(kword, S("let"))) return (Token) { .type=TOKEN_KWORD_LET };
|
||||||
if (streq(kword, S("print"))) return (Token) { .type=TOKEN_KWORD_PRINT };
|
if (streq(kword, S("print"))) return (Token) { .type=TOKEN_KWORD_PRINT };
|
||||||
if (streq(kword, S("none"))) return (Token) { .type=TOKEN_KWORD_NONE };
|
if (streq(kword, S("none"))) return (Token) { .type=TOKEN_KWORD_NONE };
|
||||||
if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE };
|
if (streq(kword, S("true"))) return (Token) { .type=TOKEN_KWORD_TRUE };
|
||||||
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
|
if (streq(kword, S("false"))) return (Token) { .type=TOKEN_KWORD_FALSE };
|
||||||
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
|
if (streq(kword, S("include"))) return (Token) { .type=TOKEN_KWORD_INCLUDE };
|
||||||
|
if (streq(kword, S("len"))) return (Token) { .type=TOKEN_KWORD_LEN };
|
||||||
|
|
||||||
return (Token) { .type=TOKEN_IDENT, .sval=kword };
|
return (Token) { .type=TOKEN_IDENT, .sval=kword };
|
||||||
}
|
}
|
||||||
@@ -1249,6 +1253,23 @@ Node *parse_atom(Parser *p)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOKEN_KWORD_LEN:
|
||||||
|
{
|
||||||
|
Node *child = parse_atom(p);
|
||||||
|
if (child == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
Node *parent = alloc_node(p);
|
||||||
|
if (parent == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
parent->type = NODE_OPER_LEN;
|
||||||
|
parent->left = child;
|
||||||
|
|
||||||
|
ret = parent;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case TOKEN_IDENT:
|
case TOKEN_IDENT:
|
||||||
{
|
{
|
||||||
Node *node = alloc_node(p);
|
Node *node = alloc_node(p);
|
||||||
@@ -2046,6 +2067,12 @@ void print_node(Node *node)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NODE_OPER_LEN:
|
||||||
|
printf("len(");
|
||||||
|
print_node(node->left);
|
||||||
|
printf(")");
|
||||||
|
break;
|
||||||
|
|
||||||
case NODE_OPER_POS:
|
case NODE_OPER_POS:
|
||||||
printf("(");
|
printf("(");
|
||||||
printf("+");
|
printf("+");
|
||||||
@@ -2420,6 +2447,7 @@ enum {
|
|||||||
OPCODE_FOR = 0x2E,
|
OPCODE_FOR = 0x2E,
|
||||||
OPCODE_PUSHT = 0x2F,
|
OPCODE_PUSHT = 0x2F,
|
||||||
OPCODE_PUSHFL = 0x30,
|
OPCODE_PUSHFL = 0x30,
|
||||||
|
OPCODE_LEN = 0x31,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -2757,6 +2785,7 @@ bool is_expr(Node *node)
|
|||||||
case NODE_SELECT:
|
case NODE_SELECT:
|
||||||
case NODE_NESTED:
|
case NODE_NESTED:
|
||||||
case NODE_FUNC_CALL:
|
case NODE_FUNC_CALL:
|
||||||
|
case NODE_OPER_LEN:
|
||||||
case NODE_OPER_POS:
|
case NODE_OPER_POS:
|
||||||
case NODE_OPER_NEG:
|
case NODE_OPER_NEG:
|
||||||
case NODE_OPER_ASS:
|
case NODE_OPER_ASS:
|
||||||
@@ -3002,6 +3031,20 @@ void assemble_expr(Assembler *a, Node *node, int num_results)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case NODE_OPER_LEN:
|
||||||
|
assemble_expr(a, node->left, 1);
|
||||||
|
append_u8(&a->out, OPCODE_LEN);
|
||||||
|
|
||||||
|
if (num_results == 0)
|
||||||
|
append_u8(&a->out, OPCODE_POP);
|
||||||
|
else if (num_results != -1 && num_results != 1) {
|
||||||
|
append_u8(&a->out, OPCODE_GROUP);
|
||||||
|
append_u8(&a->out, OPCODE_GTRUNC);
|
||||||
|
append_u32(&a->out, num_results-1);
|
||||||
|
append_u8(&a->out, OPCODE_GCOALESCE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case NODE_OPER_POS:
|
case NODE_OPER_POS:
|
||||||
assemble_expr(a, node->left, num_results);
|
assemble_expr(a, node->left, num_results);
|
||||||
break;
|
break;
|
||||||
@@ -5574,6 +5617,24 @@ int step(WL_State *state)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPCODE_LEN:
|
||||||
|
{
|
||||||
|
Value set = state->eval_stack[state->eval_depth-1];
|
||||||
|
|
||||||
|
Type type = type_of(set);
|
||||||
|
if (type != TYPE_ARRAY && type != TYPE_MAP) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
Value len = make_int(state->a, value_length(set));
|
||||||
|
if (len == VALUE_ERROR) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
state->eval_stack[state->eval_depth++] = len;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
eval_report(state, "Invalid opcode (offset %d)", state->off-1);
|
eval_report(state, "Invalid opcode (offset %d)", state->off-1);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1,6 +1,23 @@
|
|||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
EXT = .exe
|
||||||
|
LFLAGS = -lws2_32
|
||||||
|
else
|
||||||
|
EXT = .out
|
||||||
|
LFLAGS =
|
||||||
|
endif
|
||||||
|
|
||||||
all: sqlite3.o
|
CFLAGS = -Wall -Wextra -O0 -g3 -I3p
|
||||||
gcc main.c variadic.c sqlite3utils.c chttp.c WL.c sqlite3.o -o main -Wall -Wextra -g3 -O0 -lws2_32
|
|
||||||
|
|
||||||
sqlite3.o:
|
HFILES = $(shell find src 3p -name "*.h")
|
||||||
gcc -c sqlite3.c -o sqlite3.o
|
CFILES = $(filter-out 3p/sqlite3.c, $(shell find src 3p -name "*.c"))
|
||||||
|
|
||||||
|
all: cozisnews$(EXT)
|
||||||
|
|
||||||
|
cozisnews$(EXT): $(CFILES) $(HFILES) sqlite3.o
|
||||||
|
gcc -o $@ $(CFILES) sqlite3.o $(CFLAGS) $(LFLAGS)
|
||||||
|
|
||||||
|
sqlite3.o: 3p/sqlite3.c
|
||||||
|
gcc -o $@ -c $<
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm *.o *.out *.exe
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "file.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int file_open(String path, File *handle, int *size)
|
|
||||||
{
|
|
||||||
char zt[1<<10];
|
|
||||||
if (path.len >= COUNT(zt))
|
|
||||||
return -1;
|
|
||||||
memcpy(zt, path.ptr, path.len);
|
|
||||||
zt[path.len] = '\0';
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
*handle = CreateFileA(
|
|
||||||
zt,
|
|
||||||
GENERIC_READ,
|
|
||||||
FILE_SHARE_READ,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL
|
|
||||||
);
|
|
||||||
if (*handle == INVALID_HANDLE_VALUE) {
|
|
||||||
DWORD error = GetLastError();
|
|
||||||
if (error == ERROR_FILE_NOT_FOUND ||
|
|
||||||
error == ERROR_ACCESS_DENIED)
|
|
||||||
return 1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
LARGE_INTEGER fileSize;
|
|
||||||
if (!GetFileSizeEx(*handle, &fileSize)) {
|
|
||||||
CloseHandle(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (fileSize.QuadPart > INT_MAX) {
|
|
||||||
CloseHandle(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*size = (int) fileSize.QuadPart;
|
|
||||||
#else
|
|
||||||
*handle = open(zt, O_RDONLY);
|
|
||||||
if (*handle < 0) {
|
|
||||||
if (errno == ENOENT)
|
|
||||||
return 1;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
struct stat info;
|
|
||||||
if (fstat(*handle, &info) < 0) {
|
|
||||||
close(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (S_ISDIR(info.st_mode)) {
|
|
||||||
close(*handle);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (info.st_size > INT_MAX) {
|
|
||||||
close(*handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
*size = (int) info.st_size;
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void file_close(File file)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
CloseHandle(file);
|
|
||||||
#else
|
|
||||||
close(file);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_read(File file, char *dst, int max)
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
DWORD num;
|
|
||||||
BOOL ok = ReadFile(file, dst, max, &num, NULL);
|
|
||||||
if (!ok)
|
|
||||||
return -1;
|
|
||||||
return (int) num;
|
|
||||||
#else
|
|
||||||
return read(file, dst, max);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int file_read_all(String path, String *dst)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
File handle;
|
|
||||||
if (file_open(path, &handle, &len) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
char *ptr = malloc(len+1);
|
|
||||||
if (ptr == NULL) {
|
|
||||||
file_close(handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int copied = 0; copied < len; ) {
|
|
||||||
int ret = file_read(handle, ptr + copied, len - copied);
|
|
||||||
if (ret <= 0) {
|
|
||||||
free(ptr);
|
|
||||||
file_close(handle);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
copied += ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
*dst = (String) { ptr, len };
|
|
||||||
file_close(handle);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef WL_FILE_INCLUDED
|
|
||||||
#define WL_FILE_INCLUDED
|
|
||||||
|
|
||||||
#ifndef WL_AMALGAMATION
|
|
||||||
#include "includes.h"
|
|
||||||
#include "basic.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
typedef HANDLE File;
|
|
||||||
#else
|
|
||||||
typedef int File;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int file_open(String path, File *handle, int *size);
|
|
||||||
void file_close(File file);
|
|
||||||
int file_read(File file, char *dst, int max);
|
|
||||||
int file_read_all(String path, String *dst);
|
|
||||||
|
|
||||||
#endif // WL_FILE_INCLUDED
|
|
||||||
@@ -9,6 +9,7 @@ CREATE TABLE IF NOT EXISTS Users (
|
|||||||
CREATE TABLE IF NOT EXISTS Posts (
|
CREATE TABLE IF NOT EXISTS Posts (
|
||||||
id INTEGER PRIMARY KEY,
|
id INTEGER PRIMARY KEY,
|
||||||
author INTEGER NOT NULL,
|
author INTEGER NOT NULL,
|
||||||
|
title TEXT NOT NULL,
|
||||||
is_link BOOLEAN NOT NULL,
|
is_link BOOLEAN NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
+19
-8
@@ -1,10 +1,9 @@
|
|||||||
include "pages/page.wl"
|
include "pages/page.wl"
|
||||||
|
|
||||||
let posts = [
|
let posts = $query("SELECT id, title, is_link, content, 0 as num_comments, CURRENT_TIMESTAMP as date FROM Posts")
|
||||||
{ title: "Show HN: Kitten TTS - 25MB CPU-Only, Open-Source TTS Model", date: "3 hours ago", num_comments: 127, link: "https://github.com/KittenML/KittenTTS" },
|
|
||||||
{ title: "Open models by OpenAI", date: "15 hours ago", num_comments: 651, link: "https://openai.com/open-models/" },
|
if posts == none:
|
||||||
{ title: "Anthropic rejects the main developer of the library they use", date: "40 minutes ago", num_comments: 437, link: "https://grell.dev/blog/ai_rejection" }
|
posts = []
|
||||||
]
|
|
||||||
|
|
||||||
let style =
|
let style =
|
||||||
<style>
|
<style>
|
||||||
@@ -35,15 +34,27 @@ let style =
|
|||||||
|
|
||||||
let main =
|
let main =
|
||||||
<main>
|
<main>
|
||||||
\for post in posts:
|
|
||||||
|
\if len(posts) == 0:
|
||||||
|
<div>There are no posts yet!</div>
|
||||||
|
|
||||||
|
\for post in posts: {
|
||||||
|
|
||||||
|
let link
|
||||||
|
if post.is_link != 0:
|
||||||
|
link = post.content
|
||||||
|
else
|
||||||
|
link = ["/post?id=", post.id]
|
||||||
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div>
|
<div>
|
||||||
<a href=post.link>\post.title</a>
|
<a href=link>\post.title</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>\post.date</span> | <span><a href="/thread">\post.num_comments comments</a></span>
|
<span>\post.date</span> | <span><a href=link>\post.num_comments comments</a></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
page("Index", $login_user_id, style, main)
|
page("Index", $login_user_id, style, main)
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
include "pages/page.wl"
|
||||||
|
|
||||||
|
let posts = $query("SELECT title, content FROM Posts WHERE id=?", $post_id)
|
||||||
|
let post = posts[0]
|
||||||
|
|
||||||
|
let style =
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
let main =
|
||||||
|
<main>
|
||||||
|
<h3>\post.title</h3>
|
||||||
|
<p>\post.content</p>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
page(post.title, $login_user_id, style, main)
|
||||||
+81
-4
@@ -2,15 +2,92 @@ include "pages/page.wl"
|
|||||||
|
|
||||||
let style =
|
let style =
|
||||||
<style>
|
<style>
|
||||||
|
form {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 30px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input,
|
||||||
|
form textarea {
|
||||||
|
border: 0;
|
||||||
|
outline: 0;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
background: #E8D4A9;
|
||||||
|
border: 1px solid #D4C298;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input:focus,
|
||||||
|
form textarea:focus {
|
||||||
|
border-color: #5780C9;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
form textarea {
|
||||||
|
height: 120px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input[type=submit] {
|
||||||
|
cursor: pointer;
|
||||||
|
background: #5780C9;
|
||||||
|
color: #F7E6C0;
|
||||||
|
}
|
||||||
|
|
||||||
|
form input[type=submit]:hover {
|
||||||
|
background: #1D2B42;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-row {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-row input[type="checkbox"] {
|
||||||
|
width: auto;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-row label {
|
||||||
|
color: #1D2B42;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
let main =
|
let main =
|
||||||
<main>
|
<main>
|
||||||
<form action="api/post" method="POST">
|
<form action="/api/post" method="POST">
|
||||||
<input type="text" name="title" />
|
<input type="text" id="title" name="title" placeholder="Title" required />
|
||||||
<textarea name="content"></textarea>
|
|
||||||
<input type="submit" value="Publish" />
|
<div class="checkbox-row">
|
||||||
|
<input type="checkbox" id="is_link" name="is_link" onchange="togglePostType()" />
|
||||||
|
<label>This is a link post</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="url" id="url" name="link" placeholder="URL" style="display: none;" />
|
||||||
|
|
||||||
|
<textarea id="content" name="content" placeholder="Write your post here..."></textarea>
|
||||||
|
|
||||||
|
<input type="submit" value="Submit Post" />
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function togglePostType() {
|
||||||
|
const checkbox = document.getElementById('is_link');
|
||||||
|
const urlInput = document.getElementById('url');
|
||||||
|
const contentTextarea = document.getElementById('content');
|
||||||
|
|
||||||
|
if (checkbox.checked) {
|
||||||
|
urlInput.style.display = 'block';
|
||||||
|
contentTextarea.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
urlInput.style.display = 'none';
|
||||||
|
contentTextarea.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
page("Write Post", $login_user_id, style, main)
|
page("Write Post", $login_user_id, style, main)
|
||||||
+116
-15
@@ -161,7 +161,7 @@ static int query_routine(WL_State *state)
|
|||||||
|
|
||||||
int evaluate_template(HTTP_ResponseBuilder builder,
|
int evaluate_template(HTTP_ResponseBuilder builder,
|
||||||
WL_Program program, WL_Arena arena,
|
WL_Program program, WL_Arena arena,
|
||||||
char *err, int errmax, int user_id)
|
char *err, int errmax, int user_id, int post_id)
|
||||||
{
|
{
|
||||||
//WL_dump_program(program);
|
//WL_dump_program(program);
|
||||||
|
|
||||||
@@ -186,10 +186,18 @@ int evaluate_template(HTTP_ResponseBuilder builder,
|
|||||||
|
|
||||||
case WL_VAR:
|
case WL_VAR:
|
||||||
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_pushnone(state);
|
||||||
else
|
else
|
||||||
WL_pushint(state, user_id);
|
WL_pushint(state, user_id);
|
||||||
|
|
||||||
|
} else if (WL_streq(result.str, "post_id", -1)) {
|
||||||
|
|
||||||
|
if (post_id < 0)
|
||||||
|
WL_pushnone(state);
|
||||||
|
else
|
||||||
|
WL_pushint(state, post_id);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -209,7 +217,7 @@ int evaluate_template(HTTP_ResponseBuilder builder,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *file, int user_id)
|
void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *file, int user_id, int post_id)
|
||||||
{
|
{
|
||||||
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"));
|
||||||
@@ -284,7 +292,7 @@ void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *fil
|
|||||||
WL_Program program = result.program;
|
WL_Program program = result.program;
|
||||||
|
|
||||||
char err[1<<9];
|
char err[1<<9];
|
||||||
if (evaluate_template(builder, program, arena, err, (int) sizeof(err), user_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_body(builder, (HTTP_String) { err, (int) strlen(err) });
|
||||||
@@ -531,6 +539,12 @@ bool valid_post_content(HTTP_String str)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool valid_link(HTTP_String str)
|
||||||
|
{
|
||||||
|
(void) str;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
http_global_init();
|
http_global_init();
|
||||||
@@ -546,7 +560,7 @@ int main(void)
|
|||||||
|
|
||||||
char *schema_data;
|
char *schema_data;
|
||||||
long schema_size;
|
long schema_size;
|
||||||
ret = load_file("schema.sql", &schema_data, &schema_size);
|
ret = load_file("misc/schema.sql", &schema_data, &schema_size);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
printf("Couldn't load schema\n");
|
printf("Couldn't load schema\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -738,28 +752,75 @@ int main(void)
|
|||||||
} else if (http_streq(path, HTTP_STR("/api/post"))) {
|
} else if (http_streq(path, HTTP_STR("/api/post"))) {
|
||||||
|
|
||||||
if (req->method != HTTP_METHOD_POST) {
|
if (req->method != HTTP_METHOD_POST) {
|
||||||
assert(0); // TODO
|
http_response_builder_status(builder, 405);
|
||||||
|
http_response_builder_done(builder);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user_id == -1) {
|
if (user_id == -1) {
|
||||||
assert(0); // TODO
|
http_response_builder_status(builder, 303);
|
||||||
|
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||||
|
http_response_builder_done(builder);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTP_String title = http_getparam(req->body, HTTP_STR("title"));
|
HTTP_String title = http_getparam(req->body, HTTP_STR("title"));
|
||||||
|
HTTP_String link = http_getparam(req->body, HTTP_STR("link"));
|
||||||
HTTP_String content = http_getparam(req->body, HTTP_STR("content"));
|
HTTP_String content = http_getparam(req->body, HTTP_STR("content"));
|
||||||
|
|
||||||
title = http_trim(title);
|
title = http_trim(title);
|
||||||
|
link = http_trim(link);
|
||||||
content = http_trim(content);
|
content = http_trim(content);
|
||||||
|
|
||||||
if (!valid_post_title(title) || !valid_post_content(content)) {
|
if (!valid_post_title(title) || !valid_link(link) || !valid_post_content(content)) {
|
||||||
assert(0); // TODO
|
assert(0); // TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO
|
if (content.len == 0 && link.len == 0) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_link = false;
|
||||||
|
if (link.len > 0) {
|
||||||
|
is_link = true;
|
||||||
|
content = link;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int ret = sqlite3utils_prepare(db, &stmt, "INSERT INTO Posts(author, title, is_link, content) VALUES (?, ?, ?, ?)", user_id, title, is_link, content);
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t tmp = sqlite3_last_insert_rowid(db);
|
||||||
|
if (tmp < 0 || tmp > INT_MAX) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
int post_id = (int) tmp;
|
||||||
|
|
||||||
|
char location[1<<9];
|
||||||
|
ret = snprintf(location, sizeof(location), "Location: /post?id=%d", post_id);
|
||||||
|
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);
|
evaluate_template_2(builder, arena, "pages/index.wl", user_id, -1);
|
||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/write"))) {
|
} else if (http_streq(path, HTTP_STR("/write"))) {
|
||||||
|
|
||||||
@@ -771,7 +832,7 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/write.wl", user_id);
|
evaluate_template_2(builder, arena, "pages/write.wl", user_id, -1);
|
||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/login"))) {
|
} else if (http_streq(path, HTTP_STR("/login"))) {
|
||||||
|
|
||||||
@@ -783,7 +844,7 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/login.wl", user_id);
|
evaluate_template_2(builder, arena, "pages/login.wl", user_id, -1);
|
||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/signup"))) {
|
} else if (http_streq(path, HTTP_STR("/signup"))) {
|
||||||
|
|
||||||
@@ -795,15 +856,55 @@ int main(void)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/signup.wl", user_id);
|
evaluate_template_2(builder, arena, "pages/signup.wl", user_id, -1);
|
||||||
|
|
||||||
} else if (http_streq(path, HTTP_STR("/thread"))) {
|
} else if (http_streq(path, HTTP_STR("/post"))) {
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/thread.wl", user_id);
|
HTTP_String idstr = http_getparam(req->url.query, HTTP_STR("id"));
|
||||||
|
|
||||||
|
char buf[32];
|
||||||
|
if (idstr.len == 0 || idstr.len >= (int) sizeof(buf)) {
|
||||||
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
memcpy(buf, idstr.ptr, idstr.len);
|
||||||
|
buf[idstr.len] = '\0';
|
||||||
|
|
||||||
|
int post_id = atoi(buf);
|
||||||
|
if (post_id == 0) {
|
||||||
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlite3_stmt *stmt;
|
||||||
|
int ret = sqlite3utils_prepare(db, &stmt, "SELECT COUNT(*) FROM Posts WHERE id=?", post_id);
|
||||||
|
if (ret != SQLITE_OK) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sqlite3_step(stmt);
|
||||||
|
if (ret != SQLITE_ROW) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
int64_t num = sqlite3_column_int64(stmt, 0);
|
||||||
|
|
||||||
|
ret = sqlite3_finalize(stmt);
|
||||||
|
if (ret != SQLITE_OK) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num < 0) {
|
||||||
|
assert(0); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num == 0)
|
||||||
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
|
else
|
||||||
|
evaluate_template_2(builder, arena, "pages/post.wl", user_id, post_id);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id);
|
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user