first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
*.o
|
||||
*.exe
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
all: sqlite3.o
|
||||
gcc main.c variadic.c sqlite3utils.c chttp.c WL.c sqlite3.o -o main -Wall -Wextra -g3 -O0 -lws2_32
|
||||
|
||||
sqlite3.o:
|
||||
gcc -c sqlite3.c -o sqlite3.o
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef WL_AMALGAMATION
|
||||
#define WL_AMALGAMATION
|
||||
|
||||
// This file was generated automatically. Do not modify directly!
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 {
|
||||
char *ptr;
|
||||
int len;
|
||||
} WL_String;
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
int len;
|
||||
} WL_Program;
|
||||
|
||||
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 {
|
||||
char *ptr;
|
||||
int len;
|
||||
int cur;
|
||||
} WL_Arena;
|
||||
|
||||
typedef struct WL_Compiler WL_Compiler;
|
||||
|
||||
typedef enum {
|
||||
WL_COMPILE_RESULT_DONE,
|
||||
WL_COMPILE_RESULT_FILE,
|
||||
WL_COMPILE_RESULT_ERROR,
|
||||
} WL_CompileResultType;
|
||||
|
||||
typedef struct {
|
||||
WL_CompileResultType type;
|
||||
WL_Program program;
|
||||
WL_String path;
|
||||
} WL_CompileResult;
|
||||
|
||||
WL_Compiler* WL_Compiler_init (WL_Arena *arena);
|
||||
void WL_Compiler_free (WL_Compiler *compiler);
|
||||
WL_CompileResult WL_compile (WL_Compiler *compiler, WL_String file, WL_String content);
|
||||
WL_State* WL_State_init (WL_Arena *a, WL_Program p, char *err, int errmax);
|
||||
void WL_State_free (WL_State *state);
|
||||
void WL_State_trace (WL_State *state, int trace);
|
||||
WL_Result WL_eval (WL_State *state);
|
||||
|
||||
void WL_dump_program(WL_Program program);
|
||||
|
||||
int WL_streq (WL_String a, char *b, int blen);
|
||||
int WL_peeknone (WL_State *state, int off);
|
||||
int WL_peekint (WL_State *state, int off, long long *x);
|
||||
int WL_peekfloat (WL_State *state, int off, float *x);
|
||||
int WL_peekstr (WL_State *state, int off, WL_String *str);
|
||||
int WL_popnone (WL_State *state);
|
||||
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
|
||||
#endif // WL_AMALGAMATION
|
||||
@@ -0,0 +1,522 @@
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#define HTTP_AMALGAMATION
|
||||
|
||||
// This file was generated automatically. Do not modify directly!
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/basic.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/basic.h"
|
||||
#ifndef CHTTP_BASIC_INCLUDED
|
||||
#define CHTTP_BASIC_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// String type used throughout cHTTP.
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
int len;
|
||||
} HTTP_String;
|
||||
|
||||
// Compare two strings and return true iff they have
|
||||
// the same contents.
|
||||
bool http_streq(HTTP_String s1, HTTP_String s2);
|
||||
|
||||
// Compre two strings case-insensitively (uppercase and
|
||||
// lowercase versions of a letter are considered the same)
|
||||
// and return true iff they have the same contents.
|
||||
bool http_streqcase(HTTP_String s1, HTTP_String s2);
|
||||
|
||||
// Remove spaces and tabs from the start and the end of
|
||||
// a string. This doesn't change the original string and
|
||||
// the new one references the contents of the original one.
|
||||
HTTP_String http_trim(HTTP_String s);
|
||||
|
||||
// TODO: comment
|
||||
void print_bytes(HTTP_String prefix, HTTP_String src);
|
||||
|
||||
// Macro to simplify converting string literals to
|
||||
// HTTP_String.
|
||||
//
|
||||
// Instead of doing this:
|
||||
//
|
||||
// char *s = "some string";
|
||||
//
|
||||
// You do this:
|
||||
//
|
||||
// HTTP_String s = HTTP_STR("some string")
|
||||
//
|
||||
// This is a bit cumbersome, but better than null-terminated
|
||||
// strings, having a pointer and length variable pairs whenever
|
||||
// a function operates on a string. If this wasn't a library
|
||||
// I would have done for
|
||||
//
|
||||
// #define S(X) ...
|
||||
//
|
||||
// But I don't want to cause collisions with user code.
|
||||
#define HTTP_STR(X) ((HTTP_String) {(X), sizeof(X)-1})
|
||||
|
||||
// Returns the number of items of a static array.
|
||||
#define HTTP_COUNT(X) (sizeof(X) / sizeof((X)[0]))
|
||||
|
||||
// TODO: comment
|
||||
#define HTTP_UNPACK(X) (X).len, (X).ptr
|
||||
|
||||
// Macro used to make invariants of the code more explicit.
|
||||
//
|
||||
// Say you have some function that operates on two integers
|
||||
// and that by design their sum is always 100. This macro is
|
||||
// useful to make that explicit:
|
||||
//
|
||||
// void func(int a, int b)
|
||||
// {
|
||||
// HTTP_ASSERT(a + b == 100);
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// Assertions are about documentation, *not* error management.
|
||||
//
|
||||
// In non-release builds (where NDEBUG is not defined) asserted
|
||||
// expressions are evaluated and if not true, the program is halted.
|
||||
// This is quite nice as they offer a way to document code in
|
||||
// a way that can be checked at runtime, unlike regular comments
|
||||
// like this one.
|
||||
#ifdef NDEBUG
|
||||
#define HTTP_ASSERT(X) ((void) 0)
|
||||
#else
|
||||
#define HTTP_ASSERT(X) {if (!(X)) { __builtin_trap(); }}
|
||||
#endif
|
||||
|
||||
#endif // CHTTP_BASIC_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/parse.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/parse.h"
|
||||
#ifndef PARSE_INCLUDED
|
||||
#define PARSE_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "basic.h"
|
||||
#endif
|
||||
|
||||
#define HTTP_MAX_HEADERS 32
|
||||
|
||||
typedef struct {
|
||||
unsigned int data;
|
||||
} HTTP_IPv4;
|
||||
|
||||
typedef struct {
|
||||
unsigned short data[8];
|
||||
} HTTP_IPv6;
|
||||
|
||||
typedef enum {
|
||||
HTTP_HOST_MODE_VOID = 0,
|
||||
HTTP_HOST_MODE_NAME,
|
||||
HTTP_HOST_MODE_IPV4,
|
||||
HTTP_HOST_MODE_IPV6,
|
||||
} HTTP_HostMode;
|
||||
|
||||
typedef struct {
|
||||
HTTP_HostMode mode;
|
||||
HTTP_String text;
|
||||
union {
|
||||
HTTP_String name;
|
||||
HTTP_IPv4 ipv4;
|
||||
HTTP_IPv6 ipv6;
|
||||
};
|
||||
} HTTP_Host;
|
||||
|
||||
typedef struct {
|
||||
HTTP_String userinfo;
|
||||
HTTP_Host host;
|
||||
int port;
|
||||
} HTTP_Authority;
|
||||
|
||||
// ZII
|
||||
typedef struct {
|
||||
HTTP_String scheme;
|
||||
HTTP_Authority authority;
|
||||
HTTP_String path;
|
||||
HTTP_String query;
|
||||
HTTP_String fragment;
|
||||
} HTTP_URL;
|
||||
|
||||
typedef enum {
|
||||
HTTP_METHOD_GET,
|
||||
HTTP_METHOD_HEAD,
|
||||
HTTP_METHOD_POST,
|
||||
HTTP_METHOD_PUT,
|
||||
HTTP_METHOD_DELETE,
|
||||
HTTP_METHOD_CONNECT,
|
||||
HTTP_METHOD_OPTIONS,
|
||||
HTTP_METHOD_TRACE,
|
||||
HTTP_METHOD_PATCH,
|
||||
} HTTP_Method;
|
||||
|
||||
typedef struct {
|
||||
HTTP_String name;
|
||||
HTTP_String value;
|
||||
} HTTP_Header;
|
||||
|
||||
typedef struct {
|
||||
HTTP_Method method;
|
||||
HTTP_URL url;
|
||||
int minor;
|
||||
int num_headers;
|
||||
HTTP_Header headers[HTTP_MAX_HEADERS];
|
||||
HTTP_String body;
|
||||
HTTP_String raw;
|
||||
} HTTP_Request;
|
||||
|
||||
typedef struct {
|
||||
void* context;
|
||||
int minor;
|
||||
int status;
|
||||
HTTP_String reason;
|
||||
int num_headers;
|
||||
HTTP_Header headers[HTTP_MAX_HEADERS];
|
||||
HTTP_String body;
|
||||
} HTTP_Response;
|
||||
|
||||
int http_parse_ipv4 (char *src, int len, HTTP_IPv4 *ipv4);
|
||||
int http_parse_ipv6 (char *src, int len, HTTP_IPv6 *ipv6);
|
||||
int http_parse_url (char *src, int len, HTTP_URL *url);
|
||||
int http_parse_request (char *src, int len, HTTP_Request *req);
|
||||
int http_parse_response (char *src, int len, HTTP_Response *res);
|
||||
|
||||
int http_find_header (HTTP_Header *headers, int num_headers, HTTP_String name);
|
||||
|
||||
#endif // PARSE_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/engine.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/engine.h"
|
||||
#ifndef HTTP_ENGINE_INCLUDED
|
||||
#define HTTP_ENGINE_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
HTTP_MEMFUNC_MALLOC,
|
||||
HTTP_MEMFUNC_FREE,
|
||||
} HTTP_MemoryFuncTag;
|
||||
|
||||
typedef void*(*HTTP_MemoryFunc)(HTTP_MemoryFuncTag tag,
|
||||
void *ptr, int len, void *data);
|
||||
|
||||
typedef struct {
|
||||
|
||||
HTTP_MemoryFunc memfunc;
|
||||
void *memfuncdata;
|
||||
|
||||
unsigned long long curs;
|
||||
|
||||
char* data;
|
||||
unsigned int head;
|
||||
unsigned int size;
|
||||
unsigned int used;
|
||||
unsigned int limit;
|
||||
|
||||
char* read_target;
|
||||
unsigned int read_target_size;
|
||||
|
||||
int flags;
|
||||
} HTTP_ByteQueue;
|
||||
|
||||
typedef unsigned long long HTTP_ByteQueueOffset;
|
||||
|
||||
#define HTTP_ENGINE_STATEBIT_CLIENT (1 << 0)
|
||||
#define HTTP_ENGINE_STATEBIT_CLOSED (1 << 1)
|
||||
#define HTTP_ENGINE_STATEBIT_RECV_BUF (1 << 2)
|
||||
#define HTTP_ENGINE_STATEBIT_RECV_ACK (1 << 3)
|
||||
#define HTTP_ENGINE_STATEBIT_SEND_BUF (1 << 4)
|
||||
#define HTTP_ENGINE_STATEBIT_SEND_ACK (1 << 5)
|
||||
#define HTTP_ENGINE_STATEBIT_REQUEST (1 << 6)
|
||||
#define HTTP_ENGINE_STATEBIT_RESPONSE (1 << 7)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP (1 << 8)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_HEADER (1 << 9)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_BODY_BUF (1 << 10)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_BODY_ACK (1 << 11)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_ERROR (1 << 12)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_URL (1 << 13)
|
||||
#define HTTP_ENGINE_STATEBIT_PREP_STATUS (1 << 14)
|
||||
#define HTTP_ENGINE_STATEBIT_CLOSING (1 << 15)
|
||||
|
||||
typedef enum {
|
||||
HTTP_ENGINE_STATE_NONE = 0,
|
||||
HTTP_ENGINE_STATE_CLIENT_PREP_URL = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_URL,
|
||||
HTTP_ENGINE_STATE_CLIENT_PREP_HEADER = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER,
|
||||
HTTP_ENGINE_STATE_CLIENT_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF,
|
||||
HTTP_ENGINE_STATE_CLIENT_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK,
|
||||
HTTP_ENGINE_STATE_CLIENT_PREP_ERROR = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR,
|
||||
HTTP_ENGINE_STATE_CLIENT_SEND_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_BUF,
|
||||
HTTP_ENGINE_STATE_CLIENT_SEND_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_SEND_ACK,
|
||||
HTTP_ENGINE_STATE_CLIENT_RECV_BUF = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_BUF,
|
||||
HTTP_ENGINE_STATE_CLIENT_RECV_ACK = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RECV_ACK,
|
||||
HTTP_ENGINE_STATE_CLIENT_READY = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_RESPONSE,
|
||||
HTTP_ENGINE_STATE_CLIENT_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT | HTTP_ENGINE_STATEBIT_CLOSED,
|
||||
HTTP_ENGINE_STATE_SERVER_RECV_BUF = HTTP_ENGINE_STATEBIT_RECV_BUF,
|
||||
HTTP_ENGINE_STATE_SERVER_RECV_ACK = HTTP_ENGINE_STATEBIT_RECV_ACK,
|
||||
HTTP_ENGINE_STATE_SERVER_PREP_STATUS = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_STATUS,
|
||||
HTTP_ENGINE_STATE_SERVER_PREP_HEADER = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_HEADER,
|
||||
HTTP_ENGINE_STATE_SERVER_PREP_BODY_BUF = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_BUF,
|
||||
HTTP_ENGINE_STATE_SERVER_PREP_BODY_ACK = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_BODY_ACK,
|
||||
HTTP_ENGINE_STATE_SERVER_PREP_ERROR = HTTP_ENGINE_STATEBIT_REQUEST | HTTP_ENGINE_STATEBIT_PREP | HTTP_ENGINE_STATEBIT_PREP_ERROR,
|
||||
HTTP_ENGINE_STATE_SERVER_SEND_BUF = HTTP_ENGINE_STATEBIT_SEND_BUF,
|
||||
HTTP_ENGINE_STATE_SERVER_SEND_ACK = HTTP_ENGINE_STATEBIT_SEND_ACK,
|
||||
HTTP_ENGINE_STATE_SERVER_CLOSED = HTTP_ENGINE_STATEBIT_CLIENT,
|
||||
} HTTP_EngineState;
|
||||
|
||||
typedef struct {
|
||||
HTTP_EngineState state;
|
||||
HTTP_ByteQueue input;
|
||||
HTTP_ByteQueue output;
|
||||
int numexch;
|
||||
int reqsize;
|
||||
int closing;
|
||||
int keepalive;
|
||||
HTTP_ByteQueueOffset response_offset;
|
||||
HTTP_ByteQueueOffset content_length_offset;
|
||||
HTTP_ByteQueueOffset content_length_value_offset;
|
||||
union {
|
||||
HTTP_Request req;
|
||||
HTTP_Response res;
|
||||
} result;
|
||||
} HTTP_Engine;
|
||||
|
||||
void http_engine_init (HTTP_Engine *eng, int client, HTTP_MemoryFunc memfunc, void *memfuncdata);
|
||||
void http_engine_free (HTTP_Engine *eng);
|
||||
|
||||
void http_engine_close (HTTP_Engine *eng);
|
||||
HTTP_EngineState http_engine_state (HTTP_Engine *eng);
|
||||
|
||||
const char* http_engine_statestr(HTTP_EngineState state); // TODO: remove
|
||||
|
||||
char* http_engine_recvbuf (HTTP_Engine *eng, int *cap);
|
||||
void http_engine_recvack (HTTP_Engine *eng, int num);
|
||||
char* http_engine_sendbuf (HTTP_Engine *eng, int *len);
|
||||
void http_engine_sendack (HTTP_Engine *eng, int num);
|
||||
|
||||
HTTP_Request* http_engine_getreq (HTTP_Engine *eng);
|
||||
HTTP_Response* http_engine_getres (HTTP_Engine *eng);
|
||||
|
||||
void http_engine_url (HTTP_Engine *eng, HTTP_Method method, HTTP_String url, int minor);
|
||||
void http_engine_status (HTTP_Engine *eng, int status);
|
||||
void http_engine_header (HTTP_Engine *eng, HTTP_String str);
|
||||
void http_engine_body (HTTP_Engine *eng, HTTP_String str);
|
||||
void http_engine_bodycap (HTTP_Engine *eng, int mincap);
|
||||
char* http_engine_bodybuf (HTTP_Engine *eng, int *cap);
|
||||
void http_engine_bodyack (HTTP_Engine *eng, int num);
|
||||
void http_engine_done (HTTP_Engine *eng);
|
||||
void http_engine_undo (HTTP_Engine *eng);
|
||||
|
||||
#endif // HTTP_ENGINE_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/cert.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/cert.h"
|
||||
#ifndef CERT_INCLUDED
|
||||
#define CERT_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "basic.h"
|
||||
#endif
|
||||
|
||||
// This is an utility to create self-signed certificates
|
||||
// useful when testing HTTPS servers locally. This is only
|
||||
// meant to be used by people starting out with a library
|
||||
// and simplifying the zero to one phase.
|
||||
//
|
||||
// The C, O, and CN are respectively country name, organization name,
|
||||
// and common name of the certificate. For instance:
|
||||
//
|
||||
// C="IT"
|
||||
// O="My Organization"
|
||||
// CN="my_website.com"
|
||||
//
|
||||
// The output is a certificate file in PEM format and a private
|
||||
// key file with the key used to sign the certificate.
|
||||
int http_create_test_certificate(HTTP_String C, HTTP_String O, HTTP_String CN,
|
||||
HTTP_String cert_file, HTTP_String key_file);
|
||||
|
||||
#endif // CERT_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/client.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/client.h"
|
||||
#ifndef CLIENT_INCLUDED
|
||||
#define CLIENT_INCLUDED
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
// Initialize the global state of cHTTP.
|
||||
//
|
||||
// cHTTP tries to avoid global state. What this function
|
||||
// does is call the global initialization functions of
|
||||
// its dependencies (OpenSSL and Winsock)
|
||||
int http_global_init(void);
|
||||
|
||||
// Free the global state of cHTTP.
|
||||
void http_global_free(void);
|
||||
|
||||
// Opaque type describing an "HTTP client". Any request
|
||||
// that is started must always be associated to an HTTP
|
||||
// client object.
|
||||
typedef struct HTTP_Client HTTP_Client;
|
||||
|
||||
// Handle for a pending request. This should be considered
|
||||
// opaque. Don't read or modify its fields!
|
||||
typedef struct {
|
||||
void *data0;
|
||||
int data1;
|
||||
int data2;
|
||||
} HTTP_RequestBuilder;
|
||||
|
||||
// Initialize a client object. If something goes wrong,
|
||||
// NULL is returned.
|
||||
HTTP_Client *http_client_init(void);
|
||||
|
||||
// Deinitialize a client object
|
||||
void http_client_free(HTTP_Client *client);
|
||||
|
||||
// Create a request object associated to the given client.
|
||||
// On success, 0 is returned and the handle is initialized.
|
||||
// On error, -1 is returned.
|
||||
int http_client_get_builder(HTTP_Client *client, HTTP_RequestBuilder *builder);
|
||||
|
||||
void http_request_builder_user_data(HTTP_RequestBuilder builder, void *user_data);
|
||||
|
||||
// Enable/disable I/O tracing for the specified request.
|
||||
// This must be done when the request is in the initialization
|
||||
// phase.
|
||||
void http_request_builder_trace(HTTP_RequestBuilder builder, bool trace);
|
||||
|
||||
// Set the method and URL of the specified request object.
|
||||
// This must be the first thing you do after http_client_request
|
||||
// is called (you may http_request_trace before, but nothing
|
||||
// else!)
|
||||
void http_request_builder_line(HTTP_RequestBuilder builder, HTTP_Method method, HTTP_String url);
|
||||
|
||||
// Append a header to the specified request. You must call
|
||||
// this after http_request_line and may do so multiple times.
|
||||
void http_request_builder_header(HTTP_RequestBuilder builder, HTTP_String str);
|
||||
|
||||
// Append some data to the request's body. You must call
|
||||
// this after either http_request_line or http_request_header.
|
||||
void http_request_builder_body(HTTP_RequestBuilder builder, HTTP_String str);
|
||||
|
||||
// Mark the initialization of the request as completed and
|
||||
// perform the request.
|
||||
void http_request_builder_submit(HTTP_RequestBuilder builder);
|
||||
|
||||
// Free resources associated to a request. This must be called
|
||||
// after the request has completed.
|
||||
//
|
||||
// TODO: allow aborting pending requests
|
||||
void http_response_free(HTTP_Response *res);
|
||||
|
||||
// Wait for the completion of one request associated to
|
||||
// the client. The handle of the resolved request is returned
|
||||
// through the handle output parameter. If you're not
|
||||
// interested in which request completed (like when you
|
||||
// have only one pending request), you can pass NULL.
|
||||
//
|
||||
// On error -1 is retutned, else 0 is returned and the
|
||||
// handle is initialized.
|
||||
//
|
||||
// Note that calling this function when no requests are
|
||||
// pending is considered an error.
|
||||
int http_client_wait(HTTP_Client *client, HTTP_Response **res, void **user_data);
|
||||
|
||||
// TODO: comment
|
||||
HTTP_Response *http_get(HTTP_String url,
|
||||
HTTP_String *headers, int num_headers);
|
||||
|
||||
// TODO: comment
|
||||
HTTP_Response *http_post(HTTP_String url,
|
||||
HTTP_String *headers, int num_headers,
|
||||
HTTP_String body);
|
||||
|
||||
#endif // CLIENT_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/server.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/server.h"
|
||||
#ifndef HTTP_SERVER_INCLUDED
|
||||
#define HTTP_SERVER_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "parse.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
void *data0;
|
||||
int data1;
|
||||
int data2;
|
||||
} HTTP_ResponseBuilder;
|
||||
|
||||
typedef struct HTTP_Server HTTP_Server;
|
||||
|
||||
HTTP_Server *http_server_init(HTTP_String addr, uint16_t port);
|
||||
|
||||
HTTP_Server *http_server_init_ex(HTTP_String addr, uint16_t port,
|
||||
uint16_t secure_port, HTTP_String cert_key, HTTP_String private_key);
|
||||
|
||||
void http_server_free (HTTP_Server *server);
|
||||
int http_server_wait (HTTP_Server *server, HTTP_Request **req, HTTP_ResponseBuilder *handle);
|
||||
void http_server_set_trace (HTTP_Server *server, bool trace);
|
||||
int http_server_add_website (HTTP_Server *server, HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
|
||||
void http_response_builder_status (HTTP_ResponseBuilder res, int status);
|
||||
void http_response_builder_header (HTTP_ResponseBuilder res, HTTP_String str);
|
||||
void http_response_builder_body (HTTP_ResponseBuilder res, HTTP_String str);
|
||||
void http_response_builder_bodycap (HTTP_ResponseBuilder res, int mincap);
|
||||
char* http_response_builder_bodybuf (HTTP_ResponseBuilder res, int *cap);
|
||||
void http_response_builder_bodyack (HTTP_ResponseBuilder res, int num);
|
||||
void http_response_builder_undo (HTTP_ResponseBuilder res);
|
||||
void http_response_builder_done (HTTP_ResponseBuilder res);
|
||||
|
||||
#endif // HTTP_SERVER_INCLUDED
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// src/router.h
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#line 1 "src/router.h"
|
||||
#ifndef HTTP_ROUTER_INCLUDED
|
||||
#define HTTP_ROUTER_INCLUDED
|
||||
|
||||
#ifndef HTTP_AMALGAMATION
|
||||
#include "server.h"
|
||||
#endif
|
||||
|
||||
typedef struct HTTP_Router HTTP_Router;
|
||||
typedef void (*HTTP_RouterFunc)(HTTP_Request*, HTTP_ResponseBuilder, void*);;
|
||||
|
||||
HTTP_Router* http_router_init (void);
|
||||
void http_router_free (HTTP_Router *router);
|
||||
void http_router_resolve (HTTP_Router *router, HTTP_Request *req, HTTP_ResponseBuilder res);
|
||||
void http_router_dir (HTTP_Router *router, HTTP_String endpoint, HTTP_String path);
|
||||
void http_router_func (HTTP_Router *router, HTTP_Method method, HTTP_String endpoint, HTTP_RouterFunc func, void*);
|
||||
int http_serve (char *addr, int port, HTTP_Router *router);
|
||||
|
||||
#endif // HTTP_ROUTER_INCLUDED
|
||||
#endif // HTTP_AMALGAMATION
|
||||
@@ -0,0 +1,114 @@
|
||||
#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;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#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
|
||||
@@ -0,0 +1,814 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include "sqlite3.h"
|
||||
#include "chttp.h"
|
||||
#include "WL.h"
|
||||
#include "sqlite3utils.h"
|
||||
|
||||
sqlite3 *db;
|
||||
|
||||
int load_file(char *file, char **data, long *size)
|
||||
{
|
||||
FILE *f = fopen(file, "rb");
|
||||
if (f == NULL) return -1;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
*size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
*data = malloc(*size + 1);
|
||||
|
||||
fread(*data, 1, *size, f);
|
||||
(*data)[*size] = '\0';
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int query_routine(WL_State *state)
|
||||
{
|
||||
long long num_args;
|
||||
if (!WL_popint(state, &num_args)) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
if (num_args == 0) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
WL_String format;
|
||||
if (!WL_peekstr(state, -num_args, &format)) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
sqlite3_stmt *res;
|
||||
int ret = sqlite3_prepare_v2(db, format.ptr, format.len, &res, 0);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_args-1; i++) {
|
||||
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
if (WL_peeknone(state, -num_args + i + 1)) {
|
||||
ret = sqlite3_bind_null(res, i+1);
|
||||
continue;
|
||||
}
|
||||
|
||||
long long ival;
|
||||
if (WL_peekint(state, -num_args + i + 1, &ival)) {
|
||||
ret = sqlite3_bind_int64(res, i+1, ival);
|
||||
continue;
|
||||
}
|
||||
|
||||
float fval;
|
||||
if (WL_peekfloat(state, -num_args + i + 1, &fval)) {
|
||||
ret = sqlite3_bind_double(res, i+1, fval);
|
||||
continue;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const char *name = sqlite3_column_name(res, i);
|
||||
|
||||
WL_pushstr(state, (WL_String) { (char*) name, strlen(name) });
|
||||
WL_insert(state);
|
||||
}
|
||||
|
||||
WL_append(state);
|
||||
|
||||
} while (sqlite3_step(res) == SQLITE_ROW);
|
||||
}
|
||||
|
||||
WL_pushint(state, 1);
|
||||
|
||||
sqlite3_finalize(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int evaluate_template(HTTP_ResponseBuilder builder,
|
||||
WL_Program program, WL_Arena arena,
|
||||
char *err, int errmax, int user_id)
|
||||
{
|
||||
//WL_dump_program(program);
|
||||
|
||||
WL_State *state = WL_State_init(&arena, program, err, errmax);
|
||||
if (state == NULL)
|
||||
return -1;
|
||||
|
||||
WL_State_trace(state, 0);
|
||||
|
||||
for (;;) {
|
||||
|
||||
WL_Result result = WL_eval(state);
|
||||
switch (result.type) {
|
||||
|
||||
case WL_DONE:
|
||||
WL_State_free(state);
|
||||
return 0;
|
||||
|
||||
case WL_ERROR:
|
||||
WL_State_free(state);
|
||||
return -1;
|
||||
|
||||
case WL_VAR:
|
||||
if (WL_streq(result.str, "login_user_id", -1)) {
|
||||
if (user_id < 0)
|
||||
WL_pushnone(state);
|
||||
else
|
||||
WL_pushint(state, user_id);
|
||||
}
|
||||
break;
|
||||
|
||||
case WL_CALL:
|
||||
if (WL_streq(result.str, "query", -1)) {
|
||||
query_routine(state);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WL_OUTPUT:
|
||||
http_response_builder_body(builder, (HTTP_String) { result.str.ptr, result.str.len });
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void evaluate_template_2(HTTP_ResponseBuilder builder, WL_Arena arena, char *file, int user_id)
|
||||
{
|
||||
http_response_builder_status(builder, 200);
|
||||
http_response_builder_header(builder, HTTP_STR("Content-Type: text/html"));
|
||||
|
||||
WL_Compiler *compiler = WL_Compiler_init(&arena);
|
||||
if (compiler == NULL) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
char *loaded_files[128];
|
||||
int num_loaded_files = 0;
|
||||
|
||||
WL_CompileResult result;
|
||||
WL_String path = { file, strlen(file) };
|
||||
for (int i = 0;; i++) {
|
||||
|
||||
char buf[1<<10];
|
||||
if (path.len >= (int) sizeof(buf)) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
memcpy(buf, path.ptr, path.len);
|
||||
buf[path.len] = '\0';
|
||||
|
||||
FILE *f = fopen(buf, "rb");
|
||||
if (f == NULL) {
|
||||
http_response_builder_undo(builder);
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_body(builder, HTTP_STR("Couldn't find file '"));
|
||||
http_response_builder_body(builder, (HTTP_String) { path.ptr, path.len });
|
||||
http_response_builder_body(builder, HTTP_STR("'"));
|
||||
http_response_builder_done(builder);
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long file_size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char *file_data = malloc(file_size);
|
||||
|
||||
fread(file_data, 1, file_size, f);
|
||||
fclose(f);
|
||||
|
||||
result = WL_compile(compiler, path, (WL_String) { file_data, file_size });
|
||||
|
||||
loaded_files[num_loaded_files++] = file_data;
|
||||
|
||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
||||
printf("Compilation of '%.*s' failed\n", path.len, path.ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.type == WL_COMPILE_RESULT_DONE)
|
||||
break;
|
||||
|
||||
assert(result.type == WL_COMPILE_RESULT_FILE);
|
||||
path = result.path;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_loaded_files; i++)
|
||||
free(loaded_files[i]);
|
||||
|
||||
WL_Compiler_free(compiler);
|
||||
|
||||
if (result.type == WL_COMPILE_RESULT_ERROR) {
|
||||
http_response_builder_undo(builder);
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_body(builder, HTTP_STR("Couldn't compile the template"));
|
||||
http_response_builder_done(builder);
|
||||
return;
|
||||
}
|
||||
WL_Program program = result.program;
|
||||
|
||||
char err[1<<9];
|
||||
if (evaluate_template(builder, program, arena, err, (int) sizeof(err), user_id) < 0) {
|
||||
http_response_builder_undo(builder);
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_body(builder, (HTTP_String) { err, (int) strlen(err) });
|
||||
http_response_builder_done(builder);
|
||||
return;
|
||||
}
|
||||
|
||||
http_response_builder_done(builder);
|
||||
}
|
||||
|
||||
int create_user(HTTP_String name, HTTP_String email, HTTP_String pass)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int ret = sqlite3utils_prepare(db, &stmt,
|
||||
"INSERT INTO Users(username, email, password) VALUES (?, ?, ?)", name, email, pass);
|
||||
if (ret != SQLITE_OK)
|
||||
return -500;
|
||||
|
||||
ret = sqlite3_step(stmt);
|
||||
if (ret != SQLITE_DONE) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||
// TODO: What if the user exists?
|
||||
sqlite3_finalize(stmt);
|
||||
return -500;
|
||||
}
|
||||
|
||||
int64_t tmp = sqlite3_last_insert_rowid(db);
|
||||
if (tmp < 0 || tmp > INT_MAX) {
|
||||
sqlite3_finalize(stmt);
|
||||
return -500;
|
||||
}
|
||||
int user_id = (int) tmp;
|
||||
|
||||
ret = sqlite3_finalize(stmt);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_finalize(stmt);
|
||||
return -500;
|
||||
}
|
||||
|
||||
return user_id;
|
||||
}
|
||||
|
||||
int user_exists(HTTP_String name, HTTP_String pass)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int ret = sqlite3utils_prepare(db, &stmt,
|
||||
"SELECT id FROM Users WHERE username=? AND password=?", name, pass);
|
||||
if (ret != SQLITE_OK)
|
||||
return -500;
|
||||
|
||||
ret = sqlite3_step(stmt);
|
||||
if (ret == SQLITE_DONE) {
|
||||
sqlite3_finalize(stmt);
|
||||
return -404;
|
||||
}
|
||||
if (ret != SQLITE_ROW) {
|
||||
sqlite3_finalize(stmt);
|
||||
return -500;
|
||||
}
|
||||
|
||||
int user_id = sqlite3_column_int(stmt, 0);
|
||||
if (user_id < 0) {
|
||||
sqlite3_finalize(stmt);
|
||||
return -500;
|
||||
}
|
||||
|
||||
ret = sqlite3_finalize(stmt);
|
||||
if (ret != SQLITE_OK)
|
||||
return -500;
|
||||
|
||||
return user_id;
|
||||
}
|
||||
|
||||
HTTP_String http_getcookie(HTTP_Request *req, HTTP_String name)
|
||||
{
|
||||
// TODO: best-effort implementation
|
||||
|
||||
for (int i = 0; i < req->num_headers; i++) {
|
||||
|
||||
if (!http_streqcase(req->headers[i].name, HTTP_STR("Cookie")))
|
||||
continue;
|
||||
|
||||
char *src = req->headers[i].value.ptr;
|
||||
int len = req->headers[i].value.len;
|
||||
int cur = 0;
|
||||
|
||||
// Cookie: name1=value1; name2=value2; name3=value3
|
||||
|
||||
for (;;) {
|
||||
|
||||
while (cur < len && src[cur] == ' ')
|
||||
cur++;
|
||||
|
||||
int off = cur;
|
||||
while (cur < len && src[cur] != '=')
|
||||
cur++;
|
||||
|
||||
HTTP_String cookie_name = { src + off, cur - off };
|
||||
|
||||
if (cur == len)
|
||||
break;
|
||||
cur++;
|
||||
|
||||
off = cur;
|
||||
while (cur < len && src[cur] != ';')
|
||||
cur++;
|
||||
|
||||
HTTP_String cookie_value = { src + off, cur - off };
|
||||
|
||||
if (http_streq(name, cookie_name))
|
||||
return cookie_value;
|
||||
|
||||
if (cur == len)
|
||||
break;
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
|
||||
return HTTP_STR("");
|
||||
}
|
||||
|
||||
HTTP_String http_getparam(HTTP_String body, HTTP_String str)
|
||||
{
|
||||
// This is just a best-effort implementation
|
||||
|
||||
char *src = body.ptr;
|
||||
int len = body.len;
|
||||
int cur = 0;
|
||||
|
||||
if (cur < len && src[cur] == '?')
|
||||
cur++;
|
||||
|
||||
while (cur < len) {
|
||||
|
||||
HTTP_String name;
|
||||
{
|
||||
int off = cur;
|
||||
while (cur < len && src[cur] != '=' && src[cur] != '&')
|
||||
cur++;
|
||||
name = (HTTP_String) { src + off, cur - off };
|
||||
}
|
||||
|
||||
HTTP_String body = HTTP_STR("");
|
||||
if (cur < len) {
|
||||
cur++;
|
||||
if (src[cur-1] == '=') {
|
||||
int off = cur;
|
||||
while (cur < len && src[cur] != '&')
|
||||
cur++;
|
||||
body = (HTTP_String) { src + off, cur - off };
|
||||
|
||||
if (cur < len)
|
||||
cur++;
|
||||
}
|
||||
}
|
||||
|
||||
if (http_streq(str, name))
|
||||
return body;
|
||||
}
|
||||
|
||||
return HTTP_STR("");
|
||||
}
|
||||
|
||||
#define SESSION_LIMIT 1024
|
||||
#define USERNAME_LIMIT 64
|
||||
|
||||
typedef struct {
|
||||
int sess_id;
|
||||
int user_id;
|
||||
} Session;
|
||||
|
||||
typedef struct {
|
||||
Session sessions[SESSION_LIMIT];
|
||||
int count;
|
||||
int next_sess_id; // TODO: this should be choosen randomly and on 64 bits
|
||||
} SessionSet;
|
||||
|
||||
void session_set_init(SessionSet *set)
|
||||
{
|
||||
set->count = 0;
|
||||
set->next_sess_id = 1;
|
||||
}
|
||||
|
||||
int session_set_add(SessionSet *set, int user_id)
|
||||
{
|
||||
if (set->count == SESSION_LIMIT)
|
||||
return -1;
|
||||
int sess_id = set->next_sess_id++;
|
||||
set->sessions[set->count++] = (Session) {
|
||||
.sess_id=sess_id,
|
||||
.user_id=user_id,
|
||||
};
|
||||
return sess_id;
|
||||
}
|
||||
|
||||
void session_set_remove(SessionSet *set, int sess_id)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < set->count && set->sessions[i].sess_id != sess_id)
|
||||
i++;
|
||||
if (i == set->count)
|
||||
return;
|
||||
set->sessions[i] = set->sessions[--set->count];
|
||||
}
|
||||
|
||||
int session_set_find(SessionSet *set, int sess_id)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < set->count && set->sessions[i].sess_id != sess_id)
|
||||
i++;
|
||||
if (i == set->count)
|
||||
return -1;
|
||||
return set->sessions[i].user_id;
|
||||
}
|
||||
|
||||
bool valid_name(HTTP_String str)
|
||||
{
|
||||
(void) str; // TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valid_email(HTTP_String str)
|
||||
{
|
||||
(void) str; // TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valid_pass(HTTP_String str)
|
||||
{
|
||||
(void) str; // TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valid_post_title(HTTP_String str)
|
||||
{
|
||||
(void) str; // TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
bool valid_post_content(HTTP_String str)
|
||||
{
|
||||
(void) str; // TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
http_global_init();
|
||||
|
||||
printf("%s\n", sqlite3_libversion());
|
||||
|
||||
int ret = sqlite3_open(":memory:", &db);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *schema_data;
|
||||
long schema_size;
|
||||
ret = load_file("schema.sql", &schema_data, &schema_size);
|
||||
if (ret < 0) {
|
||||
printf("Couldn't load schema\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = sqlite3_exec(db, schema_data, NULL, NULL, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Cannot run schema: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(schema_data);
|
||||
|
||||
HTTP_String addr = HTTP_STR("127.0.0.1");
|
||||
uint16_t port = 8080;
|
||||
|
||||
HTTP_Server *server = http_server_init(addr, port);
|
||||
if (server == NULL) return -1;
|
||||
|
||||
http_server_set_trace(server, false);
|
||||
|
||||
int pool_cap = 1<<20;
|
||||
char *pool = malloc(pool_cap);
|
||||
|
||||
WL_Arena arena = { pool, pool_cap, 0 };
|
||||
|
||||
SessionSet sessions;
|
||||
session_set_init(&sessions);
|
||||
|
||||
for (;;) {
|
||||
|
||||
HTTP_Request *req;
|
||||
HTTP_ResponseBuilder builder;
|
||||
|
||||
int ret = http_server_wait(server, &req, &builder);
|
||||
if (ret < 0) return -1;
|
||||
|
||||
//printf("%.*s\n", req->raw.len, req->raw.ptr); // TODO
|
||||
|
||||
// If logged in, these are set to non-negative values
|
||||
int sess_id = -1;
|
||||
int user_id = -1;
|
||||
|
||||
{
|
||||
HTTP_String str = http_getcookie(req, HTTP_STR("sess_id"));
|
||||
if (str.len > 0) {
|
||||
|
||||
char tmp[1<<9]; // TODO
|
||||
memcpy(tmp, str.ptr, str.len);
|
||||
tmp[str.len] = '\0';
|
||||
|
||||
sess_id = atoi(tmp);
|
||||
if (sess_id == 0)
|
||||
sess_id = -1;
|
||||
}
|
||||
|
||||
user_id = session_set_find(&sessions, sess_id);
|
||||
}
|
||||
|
||||
HTTP_String path = req->url.path;
|
||||
if (http_streq(path, HTTP_STR("/api/login"))) {
|
||||
|
||||
if (req->method != HTTP_METHOD_POST) {
|
||||
http_response_builder_status(builder, 405);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (user_id != -1) {
|
||||
// Already logged in
|
||||
http_response_builder_status(builder, 303);
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
HTTP_String name = http_getparam(req->body, HTTP_STR("username"));
|
||||
HTTP_String pass = http_getparam(req->body, HTTP_STR("password"));
|
||||
|
||||
name = http_trim(name);
|
||||
pass = http_trim(pass);
|
||||
|
||||
if (!valid_name(name) || !valid_pass(pass)) {
|
||||
http_response_builder_status(builder, 400);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
int ret = user_exists(name, pass);
|
||||
if (ret < 0) {
|
||||
http_response_builder_status(builder, -ret);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
int user_id = ret;
|
||||
|
||||
int sess_id = session_set_add(&sessions, user_id);
|
||||
if (sess_id < 0) {
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
char cookie[1<<9];
|
||||
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%d; Path=/; HttpOnly", sess_id);
|
||||
if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) {
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
http_response_builder_status(builder, 303); // TODO: Whats the correct code here?
|
||||
http_response_builder_header(builder, (HTTP_String) { cookie, cookie_len });
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/api/signup"))) {
|
||||
|
||||
if (req->method != HTTP_METHOD_POST) {
|
||||
http_response_builder_status(builder, 405);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (user_id != -1) {
|
||||
// Already logged in
|
||||
http_response_builder_status(builder, 303);
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
HTTP_String name = http_getparam(req->body, HTTP_STR("username"));
|
||||
HTTP_String email = http_getparam(req->body, HTTP_STR("email"));
|
||||
HTTP_String pass1 = http_getparam(req->body, HTTP_STR("password1"));
|
||||
HTTP_String pass2 = http_getparam(req->body, HTTP_STR("password2"));
|
||||
|
||||
if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) {
|
||||
http_response_builder_status(builder, 400);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!http_streq(pass1, pass2)) {
|
||||
http_response_builder_status(builder, 400);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
int ret = create_user(name, email, pass1);
|
||||
if (ret < 0) {
|
||||
http_response_builder_status(builder, -ret);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
user_id = ret;
|
||||
|
||||
int sess_id = session_set_add(&sessions, user_id);
|
||||
if (sess_id < 0) {
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
char cookie[1<<9];
|
||||
int cookie_len = snprintf(cookie, sizeof(cookie), "Set-Cookie: sess_id=%d; Path=/; HttpOnly", sess_id);
|
||||
if (cookie_len < 0 || cookie_len >= (int) sizeof(cookie)) {
|
||||
http_response_builder_status(builder, 500);
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
http_response_builder_status(builder, 303); // TODO: Whats the correct code here?
|
||||
http_response_builder_header(builder, (HTTP_String) { cookie, cookie_len });
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/api/logout"))) {
|
||||
|
||||
if (sess_id != -1)
|
||||
session_set_remove(&sessions, sess_id);
|
||||
|
||||
http_response_builder_status(builder, 303); // TODO: Whats the correct code here?
|
||||
http_response_builder_header(builder, HTTP_STR("Set-Cookie: sess_id=; Path=/; HttpOnly"));
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/api/post"))) {
|
||||
|
||||
if (req->method != HTTP_METHOD_POST) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
if (user_id == -1) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
HTTP_String title = http_getparam(req->body, HTTP_STR("title"));
|
||||
HTTP_String content = http_getparam(req->body, HTTP_STR("content"));
|
||||
|
||||
title = http_trim(title);
|
||||
content = http_trim(content);
|
||||
|
||||
if (!valid_post_title(title) || !valid_post_content(content)) {
|
||||
assert(0); // TODO
|
||||
}
|
||||
|
||||
// TODO
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/index"))) {
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/index.wl", user_id);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/write"))) {
|
||||
|
||||
if (user_id == -1) {
|
||||
// Not logged in
|
||||
http_response_builder_status(builder, 303);
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/write.wl", user_id);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/login"))) {
|
||||
|
||||
if (user_id != -1) {
|
||||
// Already logged in
|
||||
http_response_builder_status(builder, 303);
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/login.wl", user_id);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/signup"))) {
|
||||
|
||||
if (user_id != -1) {
|
||||
// Already logged in
|
||||
http_response_builder_status(builder, 303);
|
||||
http_response_builder_header(builder, HTTP_STR("Location: /index"));
|
||||
http_response_builder_done(builder);
|
||||
continue;
|
||||
}
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/signup.wl", user_id);
|
||||
|
||||
} else if (http_streq(path, HTTP_STR("/thread"))) {
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/thread.wl", user_id);
|
||||
|
||||
} else {
|
||||
|
||||
evaluate_template_2(builder, arena, "pages/notfound.wl", user_id);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_close(db);
|
||||
http_server_free(server);
|
||||
http_global_free();
|
||||
return 0;
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>cozis news - login</title>
|
||||
<style>
|
||||
body {
|
||||
line-height: 200%;
|
||||
font-family: monospace;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
nav {
|
||||
overflow: auto;
|
||||
background: #5780C9;
|
||||
color: #6E93D4;
|
||||
padding: 5px 10px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
nav div {
|
||||
float: left
|
||||
}
|
||||
nav div:last-child {
|
||||
float: right
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
}
|
||||
nav a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
main {
|
||||
padding: 5px 10px;
|
||||
background: #F7E6C0;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
footer {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* Login form styles */
|
||||
.login-container {
|
||||
max-width: 400px;
|
||||
margin: 40px auto;
|
||||
padding: 30px;
|
||||
background: #E8D4A9;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #D4C298;
|
||||
}
|
||||
.login-container h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
color: #1D2B42;
|
||||
font-size: 18px;
|
||||
}
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
color: #7A5F2A;
|
||||
font-size: 14px;
|
||||
}
|
||||
.form-group input {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
background: #F7E6C0;
|
||||
border: 1px solid #D4C298;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.form-group input:focus {
|
||||
outline: none;
|
||||
border-color: #5780C9;
|
||||
background: #fff;
|
||||
}
|
||||
.login-button {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: #5780C9;
|
||||
color: #F7E6C0;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.login-button:hover {
|
||||
background: #1D2B42;
|
||||
}
|
||||
.form-links {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-links a {
|
||||
color: #7A5F2A;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.form-links a:hover {
|
||||
color: #1D2B42;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div>
|
||||
<a href="/new">new</a>
|
||||
|
|
||||
<a href="">hot</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">settings</a>
|
||||
|
|
||||
<a href="/login" class="current">login</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<div class="login-container">
|
||||
<h2>Welcome back!</h2>
|
||||
<form action="/api/login" method="POST">
|
||||
<div class="form-group">
|
||||
<label for_="username">Username:</label>
|
||||
<input type="text" id="username" name="username" required />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for_="password">Password:</label>
|
||||
<input type="password" id="password" name="password" required />
|
||||
</div>
|
||||
<button type="submit" class="login-button">log in</button>
|
||||
<div class="form-links">
|
||||
<a href="">forgot password?</a>
|
||||
|
|
||||
<a href="">create account</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Made with love by cozis
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
|
||||
fun page(title, style, content)
|
||||
<html>
|
||||
<head>
|
||||
<title>CN - \title</title>
|
||||
<style>
|
||||
body {
|
||||
line-height: 200%;
|
||||
font-family: monospace;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
nav {
|
||||
overflow: auto;
|
||||
background: #5780C9;
|
||||
color: #6E93D4;
|
||||
padding: 5px 10px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
nav div {
|
||||
float: left
|
||||
}
|
||||
nav div:last-child {
|
||||
float: right
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
}
|
||||
nav a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
main {
|
||||
padding: 5px 10px;
|
||||
background: #F7E6C0;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
footer {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
</style>
|
||||
\(style)
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div>
|
||||
<a href="/new" class="current">new</a>
|
||||
|
|
||||
<a href="">hot</a>
|
||||
</div>
|
||||
\if $login_username == none:
|
||||
<div>
|
||||
<a href="/login">log-in</a>
|
||||
|
|
||||
<a href="/signup">sign-up</a>
|
||||
</div>
|
||||
else
|
||||
<div>
|
||||
<a href="">settings</a>
|
||||
|
|
||||
<a href="/logout">log-out</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
\content
|
||||
</main>
|
||||
<footer>
|
||||
Made with love by cozis
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
let 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/" },
|
||||
{ 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" },
|
||||
]
|
||||
|
||||
let style =
|
||||
<style>
|
||||
.item {
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #E8D4A9;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.item span {
|
||||
color: #7A5F2A;
|
||||
text-decoration: none;
|
||||
}
|
||||
.item div {
|
||||
color: #E8D4A9;
|
||||
float: left
|
||||
}
|
||||
.item div:first-child {
|
||||
width: calc(100% - 250px);
|
||||
}
|
||||
.item div:last-child {
|
||||
width: 250px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
let content =
|
||||
<div>
|
||||
\for post in posts:
|
||||
<div class="item">
|
||||
<div>
|
||||
<a href=post.link>\post.title</a>
|
||||
</div>
|
||||
<div>
|
||||
<span>\post.date</span> | <span><a href="/thread">\post.num_comments comments</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
page("new", style, content)
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>cozis news - not found</title>
|
||||
<style>
|
||||
body {
|
||||
line-height: 200%;
|
||||
font-family: monospace;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
nav {
|
||||
overflow: auto;
|
||||
background: #5780C9;
|
||||
color: #6E93D4;
|
||||
padding: 5px 10px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
nav div {
|
||||
float: left
|
||||
}
|
||||
nav div:last-child {
|
||||
float: right
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
}
|
||||
nav a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
main {
|
||||
padding: 5px 10px;
|
||||
background: #F7E6C0;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
footer {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* 404 page styles */
|
||||
.not-found-container {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
.error-code {
|
||||
font-size: 72px;
|
||||
font-weight: bold;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 20px;
|
||||
line-height: 100%;
|
||||
}
|
||||
.error-message {
|
||||
font-size: 24px;
|
||||
color: #1D2B42;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.error-description {
|
||||
font-size: 14px;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 40px;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
line-height: 160%;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
display: inline-block;
|
||||
background: #5780C9;
|
||||
color: #F7E6C0;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
margin-top: 20px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.home-button:hover {
|
||||
background: #1D2B42;
|
||||
color: #F7E6C0;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div>
|
||||
<a href="/new">new</a>
|
||||
|
|
||||
<a href="">hot</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">settings</a>
|
||||
|
|
||||
<a href="/login">log-out</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<div class="not-found-container">
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-message">Page Not Found</div>
|
||||
<div class="error-description">
|
||||
Looks like this page wandered off somewhere.
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<footer>
|
||||
Made with love by cozis
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
include "pages/page.wl"
|
||||
include "pages/login_and_signup_style.wl"
|
||||
|
||||
let main =
|
||||
<main>
|
||||
<span>Welcome!</span>
|
||||
<form action="/api/signup" method="POST">
|
||||
<input type="text" name="username" placeholder="username" />
|
||||
<input type="email" name="email" placeholder="email" />
|
||||
<input type="password" name="password1" placeholder="password" />
|
||||
<input type="password" name="password2" placeholder="repeat password" />
|
||||
<input type="submit" value="Sign-Up" />
|
||||
<div class="form-links">
|
||||
<a href="">forgot password?</a>
|
||||
|
|
||||
<a href="">already have an account?</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
page("Log-In", none, style, main)
|
||||
+340
@@ -0,0 +1,340 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>cozis news - thread</title>
|
||||
<style>
|
||||
body {
|
||||
line-height: 200%;
|
||||
font-family: monospace;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
nav {
|
||||
overflow: auto;
|
||||
background: #5780C9;
|
||||
color: #6E93D4;
|
||||
padding: 5px 10px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
nav div {
|
||||
float: left
|
||||
}
|
||||
nav div:last-child {
|
||||
float: right
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
}
|
||||
nav a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
main {
|
||||
padding: 5px 10px;
|
||||
background: #F7E6C0;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
footer {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* Thread styles */
|
||||
.thread-header {
|
||||
padding: 15px 0;
|
||||
border-bottom: 2px solid #E8D4A9;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.thread-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.thread-title a {
|
||||
color: #1D2B42;
|
||||
text-decoration: none;
|
||||
}
|
||||
.thread-title a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.thread-meta {
|
||||
font-size: 12px;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.thread-meta a {
|
||||
color: #7A5F2A;
|
||||
}
|
||||
.thread-text {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
line-height: 160%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.thread-actions {
|
||||
font-size: 12px;
|
||||
}
|
||||
.thread-actions a {
|
||||
color: #7A5F2A;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Comment styles */
|
||||
.comment {
|
||||
margin-bottom: 15px;
|
||||
border-left: 1px solid #E8D4A9;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.comment.level-0 { margin-left: 0px; }
|
||||
.comment.level-1 { margin-left: 20px; }
|
||||
.comment.level-2 { margin-left: 40px; }
|
||||
.comment.level-3 { margin-left: 60px; }
|
||||
.comment.level-4 { margin-left: 80px; }
|
||||
|
||||
.comment-meta {
|
||||
font-size: 11px;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.comment-meta a {
|
||||
color: #7A5F2A;
|
||||
}
|
||||
.comment-text {
|
||||
font-size: 13px;
|
||||
color: #1D2B42;
|
||||
line-height: 150%;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.comment-actions {
|
||||
font-size: 11px;
|
||||
}
|
||||
.comment-actions a {
|
||||
color: #7A5F2A;
|
||||
margin-right: 8px;
|
||||
}
|
||||
.comment-actions a:hover {
|
||||
color: #1D2B42;
|
||||
}
|
||||
|
||||
.vote-buttons {
|
||||
float: left;
|
||||
width: 15px;
|
||||
margin-right: 8px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
.vote-buttons a {
|
||||
display: block;
|
||||
color: #7A5F2A;
|
||||
text-decoration: none;
|
||||
line-height: 100%;
|
||||
}
|
||||
.vote-buttons a:hover {
|
||||
color: #1D2B42;
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
margin-left: 23px;
|
||||
}
|
||||
|
||||
.add-comment {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
background: #E8D4A9;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #D4C298;
|
||||
}
|
||||
.add-comment textarea {
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
background: #F7E6C0;
|
||||
border: 1px solid #D4C298;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
}
|
||||
.add-comment button {
|
||||
background: #5780C9;
|
||||
color: #F7E6C0;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
padding: 6px 12px;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.add-comment button:hover {
|
||||
background: #1D2B42;
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
color: #7A5F2A;
|
||||
font-size: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.collapsed:hover {
|
||||
color: #1D2B42;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div>
|
||||
<a href="/new" class="current">new</a>
|
||||
|
|
||||
<a href="">hot</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="">settings</a>
|
||||
|
|
||||
<a href="/login">log-out</a>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<div class="thread-header">
|
||||
<div class="thread-title">
|
||||
<a href="https://github.com/KittenML/KittenTTS">Show HN: Kitten TTS - 25MB CPU-Only, Open-Source TTS Model</a>
|
||||
</div>
|
||||
<div class="thread-meta">
|
||||
submitted 3 hours ago by <a href="">kittenlover42</a> | <a href="">127 comments</a>
|
||||
</div>
|
||||
<div class="thread-text">
|
||||
Hey HN! I've been working on Kitten TTS for the past 6 months - a tiny text-to-speech model that runs entirely on CPU. At just 25MB, it's perfect for embedded applications and privacy-focused projects where you don't want to send text to external services.
|
||||
<br/><br/>
|
||||
The model is trained on a diverse dataset and supports multiple languages. Performance is surprisingly good for the size - would love to hear your thoughts!
|
||||
</div>
|
||||
<div class="thread-actions">
|
||||
<a href="">reply</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="add-comment">
|
||||
<textarea placeholder="Add a comment..."></textarea>
|
||||
<button>add comment</button>
|
||||
</div>
|
||||
|
||||
<div class="comment level-0">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">techgeek99</a> 2 hours ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
This is incredible! I've been looking for something exactly like this for my IoT project. The fact that it's only 25MB and runs on CPU is a game changer. How does the voice quality compare to larger models?
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-1">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">kittenlover42</a> 2 hours ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
Thanks! The voice quality is obviously not as good as larger models like Tacotron2 or the commercial APIs, but it's surprisingly decent for most use cases. I'd say it's about 75% of the quality at 1% of the size. Perfect for things like reading notifications aloud or simple voice interfaces.
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-2">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">embedded_dev</a> 1 hour ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
That sounds perfect for embedded applications! What's the inference speed like on something like a Raspberry Pi?
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-0">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">ml_researcher</a> 2 hours ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
Very impressive work! I'm curious about the architecture - are you using knowledge distillation from a larger model, or did you train this from scratch? The 25MB constraint must have required some clever optimizations.
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-1">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">kittenlover42</a> 1 hour ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
Great question! It's a combination approach. I started with a modified Tacotron architecture but with much smaller hidden dimensions, then used knowledge distillation from WaveNet to get the vocoder down to size. Also heavily quantized the weights and used some pruning techniques. Happy to share more technical details if you're interested!
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-0">
|
||||
<div class="vote-buttons">
|
||||
<a href="">▲</a>
|
||||
<a href="">▼</a>
|
||||
</div>
|
||||
<div class="comment-content">
|
||||
<div class="comment-meta">
|
||||
<a href="">privacy_advocate</a> 1 hour ago
|
||||
</div>
|
||||
<div class="comment-text">
|
||||
Love that this runs locally! So tired of having to send my text to Google/Amazon/etc just to get speech synthesis. This is exactly what we need more of in the open source community.
|
||||
</div>
|
||||
<div class="comment-actions">
|
||||
<a href="">reply</a> | <a href="">flag</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comment level-0">
|
||||
<div class="collapsed">
|
||||
[5 more comments]
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
<footer>
|
||||
Made with love by cozis
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
include "pages/page.wl"
|
||||
|
||||
let 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/" },
|
||||
{ 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" }
|
||||
]
|
||||
|
||||
let style =
|
||||
<style>
|
||||
.item {
|
||||
overflow: auto;
|
||||
border-bottom: 1px solid #E8D4A9;
|
||||
font-size: 14px;
|
||||
}
|
||||
.item:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.item span {
|
||||
color: #7A5F2A;
|
||||
text-decoration: none;
|
||||
}
|
||||
.item div {
|
||||
color: #E8D4A9;
|
||||
float: left
|
||||
}
|
||||
.item div:first-child {
|
||||
width: calc(100% - 250px);
|
||||
}
|
||||
.item div:last-child {
|
||||
width: 250px;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
let main =
|
||||
<main>
|
||||
\for post in posts:
|
||||
<div class="item">
|
||||
<div>
|
||||
<a href=post.link>\post.title</a>
|
||||
</div>
|
||||
<div>
|
||||
<span>\post.date</span> | <span><a href="/thread">\post.num_comments comments</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
page("Index", $login_user_id, style, main)
|
||||
@@ -0,0 +1,19 @@
|
||||
include "pages/page.wl"
|
||||
include "pages/login_and_signup_style.wl"
|
||||
|
||||
let main =
|
||||
<main>
|
||||
<span>Welcome back!</span>
|
||||
<form action="/api/login" method="POST">
|
||||
<input type="text" name="username" placeholder="username" />
|
||||
<input type="password" name="password" placeholder="password" />
|
||||
<input type="submit" value="Log-In" />
|
||||
<div class="form-links">
|
||||
<a href="">forgot password?</a>
|
||||
|
|
||||
<a href="">create account</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
page("Log-In", none, style, main)
|
||||
@@ -0,0 +1,62 @@
|
||||
let style =
|
||||
<style>
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
color: #1D2B42;
|
||||
font-size: 18px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
form {
|
||||
max-width: 300px;
|
||||
margin: 30px auto;
|
||||
}
|
||||
|
||||
form input {
|
||||
border: 0;
|
||||
outline: 0;
|
||||
width: 100%;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
form input[type=text],
|
||||
form input[type=email],
|
||||
form input[type=password] {
|
||||
background: #E8D4A9;
|
||||
border: 1px solid #D4C298;
|
||||
}
|
||||
|
||||
form input[type=text]:focus,
|
||||
form input[type=email]:focus,
|
||||
form input[type=password]:focus {
|
||||
border-color: #5780C9;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
form input[type=submit] {
|
||||
cursor: pointer;
|
||||
background: #5780C9;
|
||||
color: #F7E6C0;
|
||||
}
|
||||
|
||||
form input[type=submit]:hover {
|
||||
background: #1D2B42;
|
||||
}
|
||||
|
||||
.form-links {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
.form-links a {
|
||||
color: #7A5F2A;
|
||||
margin: 0 10px;
|
||||
}
|
||||
.form-links a:hover {
|
||||
color: #1D2B42;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,60 @@
|
||||
include "pages/page.wl"
|
||||
|
||||
let style =
|
||||
<style>
|
||||
.not-found-container {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
}
|
||||
.error-code {
|
||||
font-size: 72px;
|
||||
font-weight: bold;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 20px;
|
||||
line-height: 100%;
|
||||
}
|
||||
.error-message {
|
||||
font-size: 24px;
|
||||
color: #1D2B42;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.error-description {
|
||||
font-size: 14px;
|
||||
color: #7A5F2A;
|
||||
margin-bottom: 40px;
|
||||
max-width: 500px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
line-height: 160%;
|
||||
}
|
||||
|
||||
.home-button {
|
||||
display: inline-block;
|
||||
background: #5780C9;
|
||||
color: #F7E6C0;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
margin-top: 20px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.home-button:hover {
|
||||
background: #1D2B42;
|
||||
color: #F7E6C0;
|
||||
}
|
||||
</style>
|
||||
|
||||
let main =
|
||||
<main>
|
||||
<div class="not-found-container">
|
||||
<div class="error-code">404</div>
|
||||
<div class="error-message">Page Not Found</div>
|
||||
<div class="error-description">
|
||||
Looks like this page wandered off somewhere.
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
page("Not Found", $login_user_id, style, main)
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
fun page(title, login_user_id, style, main)
|
||||
<html>
|
||||
<head>
|
||||
<title>CN - \title</title>
|
||||
<style>
|
||||
body {
|
||||
line-height: 200%;
|
||||
font-family: monospace;
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
nav {
|
||||
overflow: auto;
|
||||
background: #5780C9;
|
||||
color: #6E93D4;
|
||||
padding: 5px 10px;
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
}
|
||||
nav div {
|
||||
float: left
|
||||
}
|
||||
nav div:last-child {
|
||||
float: right
|
||||
}
|
||||
nav a {
|
||||
font-size: 14px;
|
||||
color: #1D2B42;
|
||||
}
|
||||
nav a.current {
|
||||
font-weight: bold;
|
||||
}
|
||||
main {
|
||||
padding: 5px 10px;
|
||||
background: #F7E6C0;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
footer {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
</style>
|
||||
\(style)
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<div>
|
||||
<a href="/index">index</a>
|
||||
\if login_user_id != none: {
|
||||
"|\n"
|
||||
<a href="/write">write</a>
|
||||
}
|
||||
</div>
|
||||
\if login_user_id == none:
|
||||
<div>
|
||||
<a href="/login">log-in</a>
|
||||
|
|
||||
<a href="/signup">sign-up</a>
|
||||
</div>
|
||||
else
|
||||
<div>
|
||||
<a href="">settings</a>
|
||||
|
|
||||
<a href="/api/logout">log-out</a>
|
||||
</div>
|
||||
</nav>
|
||||
\main
|
||||
<footer>
|
||||
Made with love by cozis
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
include "pages/page.wl"
|
||||
include "pages/login_and_signup_style.wl"
|
||||
|
||||
let main =
|
||||
<main>
|
||||
<span>Welcome!</span>
|
||||
<form action="/api/signup" method="POST">
|
||||
<input type="text" name="username" placeholder="username" />
|
||||
<input type="email" name="email" placeholder="email" />
|
||||
<input type="password" name="password1" placeholder="password" />
|
||||
<input type="password" name="password2" placeholder="repeat password" />
|
||||
<input type="submit" value="Sign-Up" />
|
||||
<div class="form-links">
|
||||
<a href="">forgot password?</a>
|
||||
|
|
||||
<a href="">already have an account?</a>
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
|
||||
page("Log-In", none, style, main)
|
||||
@@ -0,0 +1,5 @@
|
||||
<ul>
|
||||
<li>1. Post A</li>
|
||||
<li>2. Post B</li>
|
||||
<li>3. Post C</li>
|
||||
</ul>
|
||||
@@ -0,0 +1,16 @@
|
||||
include "pages/page.wl"
|
||||
|
||||
let style =
|
||||
<style>
|
||||
</style>
|
||||
|
||||
let main =
|
||||
<main>
|
||||
<form action="api/post" method="POST">
|
||||
<input type="text" name="title" />
|
||||
<textarea name="content"></textarea>
|
||||
<input type="submit" value="Publish" />
|
||||
</form>
|
||||
</main>
|
||||
|
||||
page("Write Post", $login_user_id, style, main)
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
CREATE TABLE IF NOT EXISTS Users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT NOT NULL UNIQUE,
|
||||
password TEXT NOT NULL,
|
||||
email TEXT NOT NULL UNIQUE,
|
||||
signup_time DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Posts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
author INTEGER NOT NULL,
|
||||
is_link BOOLEAN NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (author) REFERENCES Users(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS Comments (
|
||||
id INTEGER PRIMARY KEY,
|
||||
parent_post INTEGER NOT NULL,
|
||||
parent_comment INTEGER,
|
||||
author INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
submit_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (author) REFERENCES Users(id),
|
||||
FOREIGN KEY (parent_post) REFERENCES Posts(id),
|
||||
FOREIGN KEY (parent_comment) REFERENCES Comments(id)
|
||||
);
|
||||
+723
@@ -0,0 +1,723 @@
|
||||
/*
|
||||
** 2006 June 7
|
||||
**
|
||||
** The author disclaims copyright to this source code. In place of
|
||||
** a legal notice, here is a blessing:
|
||||
**
|
||||
** May you do good and not evil.
|
||||
** May you find forgiveness for yourself and forgive others.
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** This header file defines the SQLite interface for use by
|
||||
** shared libraries that want to be imported as extensions into
|
||||
** an SQLite instance. Shared libraries that intend to be loaded
|
||||
** as extensions by SQLite should #include this file instead of
|
||||
** sqlite3.h.
|
||||
*/
|
||||
#ifndef SQLITE3EXT_H
|
||||
#define SQLITE3EXT_H
|
||||
#include "sqlite3.h"
|
||||
|
||||
/*
|
||||
** The following structure holds pointers to all of the SQLite API
|
||||
** routines.
|
||||
**
|
||||
** WARNING: In order to maintain backwards compatibility, add new
|
||||
** interfaces to the end of this structure only. If you insert new
|
||||
** interfaces in the middle of this structure, then older different
|
||||
** versions of SQLite will not be able to load each other's shared
|
||||
** libraries!
|
||||
*/
|
||||
struct sqlite3_api_routines {
|
||||
void * (*aggregate_context)(sqlite3_context*,int nBytes);
|
||||
int (*aggregate_count)(sqlite3_context*);
|
||||
int (*bind_blob)(sqlite3_stmt*,int,const void*,int n,void(*)(void*));
|
||||
int (*bind_double)(sqlite3_stmt*,int,double);
|
||||
int (*bind_int)(sqlite3_stmt*,int,int);
|
||||
int (*bind_int64)(sqlite3_stmt*,int,sqlite_int64);
|
||||
int (*bind_null)(sqlite3_stmt*,int);
|
||||
int (*bind_parameter_count)(sqlite3_stmt*);
|
||||
int (*bind_parameter_index)(sqlite3_stmt*,const char*zName);
|
||||
const char * (*bind_parameter_name)(sqlite3_stmt*,int);
|
||||
int (*bind_text)(sqlite3_stmt*,int,const char*,int n,void(*)(void*));
|
||||
int (*bind_text16)(sqlite3_stmt*,int,const void*,int,void(*)(void*));
|
||||
int (*bind_value)(sqlite3_stmt*,int,const sqlite3_value*);
|
||||
int (*busy_handler)(sqlite3*,int(*)(void*,int),void*);
|
||||
int (*busy_timeout)(sqlite3*,int ms);
|
||||
int (*changes)(sqlite3*);
|
||||
int (*close)(sqlite3*);
|
||||
int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const char*));
|
||||
int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
|
||||
int eTextRep,const void*));
|
||||
const void * (*column_blob)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes)(sqlite3_stmt*,int iCol);
|
||||
int (*column_bytes16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_count)(sqlite3_stmt*pStmt);
|
||||
const char * (*column_database_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_database_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_decltype)(sqlite3_stmt*,int i);
|
||||
const void * (*column_decltype16)(sqlite3_stmt*,int);
|
||||
double (*column_double)(sqlite3_stmt*,int iCol);
|
||||
int (*column_int)(sqlite3_stmt*,int iCol);
|
||||
sqlite_int64 (*column_int64)(sqlite3_stmt*,int iCol);
|
||||
const char * (*column_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_origin_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_origin_name16)(sqlite3_stmt*,int);
|
||||
const char * (*column_table_name)(sqlite3_stmt*,int);
|
||||
const void * (*column_table_name16)(sqlite3_stmt*,int);
|
||||
const unsigned char * (*column_text)(sqlite3_stmt*,int iCol);
|
||||
const void * (*column_text16)(sqlite3_stmt*,int iCol);
|
||||
int (*column_type)(sqlite3_stmt*,int iCol);
|
||||
sqlite3_value* (*column_value)(sqlite3_stmt*,int iCol);
|
||||
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
|
||||
int (*complete)(const char*sql);
|
||||
int (*complete16)(const void*sql);
|
||||
int (*create_collation)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_collation16)(sqlite3*,const void*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*));
|
||||
int (*create_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_function16)(sqlite3*,const void*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*));
|
||||
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
|
||||
int (*data_count)(sqlite3_stmt*pStmt);
|
||||
sqlite3 * (*db_handle)(sqlite3_stmt*);
|
||||
int (*declare_vtab)(sqlite3*,const char*);
|
||||
int (*enable_shared_cache)(int);
|
||||
int (*errcode)(sqlite3*db);
|
||||
const char * (*errmsg)(sqlite3*);
|
||||
const void * (*errmsg16)(sqlite3*);
|
||||
int (*exec)(sqlite3*,const char*,sqlite3_callback,void*,char**);
|
||||
int (*expired)(sqlite3_stmt*);
|
||||
int (*finalize)(sqlite3_stmt*pStmt);
|
||||
void (*free)(void*);
|
||||
void (*free_table)(char**result);
|
||||
int (*get_autocommit)(sqlite3*);
|
||||
void * (*get_auxdata)(sqlite3_context*,int);
|
||||
int (*get_table)(sqlite3*,const char*,char***,int*,int*,char**);
|
||||
int (*global_recover)(void);
|
||||
void (*interruptx)(sqlite3*);
|
||||
sqlite_int64 (*last_insert_rowid)(sqlite3*);
|
||||
const char * (*libversion)(void);
|
||||
int (*libversion_number)(void);
|
||||
void *(*malloc)(int);
|
||||
char * (*mprintf)(const char*,...);
|
||||
int (*open)(const char*,sqlite3**);
|
||||
int (*open16)(const void*,sqlite3**);
|
||||
int (*prepare)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
void * (*profile)(sqlite3*,void(*)(void*,const char*,sqlite_uint64),void*);
|
||||
void (*progress_handler)(sqlite3*,int,int(*)(void*),void*);
|
||||
void *(*realloc)(void*,int);
|
||||
int (*reset)(sqlite3_stmt*pStmt);
|
||||
void (*result_blob)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_double)(sqlite3_context*,double);
|
||||
void (*result_error)(sqlite3_context*,const char*,int);
|
||||
void (*result_error16)(sqlite3_context*,const void*,int);
|
||||
void (*result_int)(sqlite3_context*,int);
|
||||
void (*result_int64)(sqlite3_context*,sqlite_int64);
|
||||
void (*result_null)(sqlite3_context*);
|
||||
void (*result_text)(sqlite3_context*,const char*,int,void(*)(void*));
|
||||
void (*result_text16)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16be)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
|
||||
void (*result_value)(sqlite3_context*,sqlite3_value*);
|
||||
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
|
||||
int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
|
||||
const char*,const char*),void*);
|
||||
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
|
||||
char * (*xsnprintf)(int,char*,const char*,...);
|
||||
int (*step)(sqlite3_stmt*);
|
||||
int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
|
||||
char const**,char const**,int*,int*,int*);
|
||||
void (*thread_cleanup)(void);
|
||||
int (*total_changes)(sqlite3*);
|
||||
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
|
||||
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
|
||||
void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
|
||||
sqlite_int64),void*);
|
||||
void * (*user_data)(sqlite3_context*);
|
||||
const void * (*value_blob)(sqlite3_value*);
|
||||
int (*value_bytes)(sqlite3_value*);
|
||||
int (*value_bytes16)(sqlite3_value*);
|
||||
double (*value_double)(sqlite3_value*);
|
||||
int (*value_int)(sqlite3_value*);
|
||||
sqlite_int64 (*value_int64)(sqlite3_value*);
|
||||
int (*value_numeric_type)(sqlite3_value*);
|
||||
const unsigned char * (*value_text)(sqlite3_value*);
|
||||
const void * (*value_text16)(sqlite3_value*);
|
||||
const void * (*value_text16be)(sqlite3_value*);
|
||||
const void * (*value_text16le)(sqlite3_value*);
|
||||
int (*value_type)(sqlite3_value*);
|
||||
char *(*vmprintf)(const char*,va_list);
|
||||
/* Added ??? */
|
||||
int (*overload_function)(sqlite3*, const char *zFuncName, int nArg);
|
||||
/* Added by 3.3.13 */
|
||||
int (*prepare_v2)(sqlite3*,const char*,int,sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
|
||||
int (*clear_bindings)(sqlite3_stmt*);
|
||||
/* Added by 3.4.1 */
|
||||
int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
|
||||
void (*xDestroy)(void *));
|
||||
/* Added by 3.5.0 */
|
||||
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
|
||||
int (*blob_bytes)(sqlite3_blob*);
|
||||
int (*blob_close)(sqlite3_blob*);
|
||||
int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
|
||||
int,sqlite3_blob**);
|
||||
int (*blob_read)(sqlite3_blob*,void*,int,int);
|
||||
int (*blob_write)(sqlite3_blob*,const void*,int,int);
|
||||
int (*create_collation_v2)(sqlite3*,const char*,int,void*,
|
||||
int(*)(void*,int,const void*,int,const void*),
|
||||
void(*)(void*));
|
||||
int (*file_control)(sqlite3*,const char*,int,void*);
|
||||
sqlite3_int64 (*memory_highwater)(int);
|
||||
sqlite3_int64 (*memory_used)(void);
|
||||
sqlite3_mutex *(*mutex_alloc)(int);
|
||||
void (*mutex_enter)(sqlite3_mutex*);
|
||||
void (*mutex_free)(sqlite3_mutex*);
|
||||
void (*mutex_leave)(sqlite3_mutex*);
|
||||
int (*mutex_try)(sqlite3_mutex*);
|
||||
int (*open_v2)(const char*,sqlite3**,int,const char*);
|
||||
int (*release_memory)(int);
|
||||
void (*result_error_nomem)(sqlite3_context*);
|
||||
void (*result_error_toobig)(sqlite3_context*);
|
||||
int (*sleep)(int);
|
||||
void (*soft_heap_limit)(int);
|
||||
sqlite3_vfs *(*vfs_find)(const char*);
|
||||
int (*vfs_register)(sqlite3_vfs*,int);
|
||||
int (*vfs_unregister)(sqlite3_vfs*);
|
||||
int (*xthreadsafe)(void);
|
||||
void (*result_zeroblob)(sqlite3_context*,int);
|
||||
void (*result_error_code)(sqlite3_context*,int);
|
||||
int (*test_control)(int, ...);
|
||||
void (*randomness)(int,void*);
|
||||
sqlite3 *(*context_db_handle)(sqlite3_context*);
|
||||
int (*extended_result_codes)(sqlite3*,int);
|
||||
int (*limit)(sqlite3*,int,int);
|
||||
sqlite3_stmt *(*next_stmt)(sqlite3*,sqlite3_stmt*);
|
||||
const char *(*sql)(sqlite3_stmt*);
|
||||
int (*status)(int,int*,int*,int);
|
||||
int (*backup_finish)(sqlite3_backup*);
|
||||
sqlite3_backup *(*backup_init)(sqlite3*,const char*,sqlite3*,const char*);
|
||||
int (*backup_pagecount)(sqlite3_backup*);
|
||||
int (*backup_remaining)(sqlite3_backup*);
|
||||
int (*backup_step)(sqlite3_backup*,int);
|
||||
const char *(*compileoption_get)(int);
|
||||
int (*compileoption_used)(const char*);
|
||||
int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void(*xDestroy)(void*));
|
||||
int (*db_config)(sqlite3*,int,...);
|
||||
sqlite3_mutex *(*db_mutex)(sqlite3*);
|
||||
int (*db_status)(sqlite3*,int,int*,int*,int);
|
||||
int (*extended_errcode)(sqlite3*);
|
||||
void (*log)(int,const char*,...);
|
||||
sqlite3_int64 (*soft_heap_limit64)(sqlite3_int64);
|
||||
const char *(*sourceid)(void);
|
||||
int (*stmt_status)(sqlite3_stmt*,int,int);
|
||||
int (*strnicmp)(const char*,const char*,int);
|
||||
int (*unlock_notify)(sqlite3*,void(*)(void**,int),void*);
|
||||
int (*wal_autocheckpoint)(sqlite3*,int);
|
||||
int (*wal_checkpoint)(sqlite3*,const char*);
|
||||
void *(*wal_hook)(sqlite3*,int(*)(void*,sqlite3*,const char*,int),void*);
|
||||
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
|
||||
int (*vtab_config)(sqlite3*,int op,...);
|
||||
int (*vtab_on_conflict)(sqlite3*);
|
||||
/* Version 3.7.16 and later */
|
||||
int (*close_v2)(sqlite3*);
|
||||
const char *(*db_filename)(sqlite3*,const char*);
|
||||
int (*db_readonly)(sqlite3*,const char*);
|
||||
int (*db_release_memory)(sqlite3*);
|
||||
const char *(*errstr)(int);
|
||||
int (*stmt_busy)(sqlite3_stmt*);
|
||||
int (*stmt_readonly)(sqlite3_stmt*);
|
||||
int (*stricmp)(const char*,const char*);
|
||||
int (*uri_boolean)(const char*,const char*,int);
|
||||
sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
|
||||
const char *(*uri_parameter)(const char*,const char*);
|
||||
char *(*xvsnprintf)(int,char*,const char*,va_list);
|
||||
int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
|
||||
/* Version 3.8.7 and later */
|
||||
int (*auto_extension)(void(*)(void));
|
||||
int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
|
||||
void(*)(void*),unsigned char);
|
||||
int (*cancel_auto_extension)(void(*)(void));
|
||||
int (*load_extension)(sqlite3*,const char*,const char*,char**);
|
||||
void *(*malloc64)(sqlite3_uint64);
|
||||
sqlite3_uint64 (*msize)(void*);
|
||||
void *(*realloc64)(void*,sqlite3_uint64);
|
||||
void (*reset_auto_extension)(void);
|
||||
void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
|
||||
void(*)(void*));
|
||||
void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
|
||||
void(*)(void*), unsigned char);
|
||||
int (*strglob)(const char*,const char*);
|
||||
/* Version 3.8.11 and later */
|
||||
sqlite3_value *(*value_dup)(const sqlite3_value*);
|
||||
void (*value_free)(sqlite3_value*);
|
||||
int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
|
||||
int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
|
||||
/* Version 3.9.0 and later */
|
||||
unsigned int (*value_subtype)(sqlite3_value*);
|
||||
void (*result_subtype)(sqlite3_context*,unsigned int);
|
||||
/* Version 3.10.0 and later */
|
||||
int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
|
||||
int (*strlike)(const char*,const char*,unsigned int);
|
||||
int (*db_cacheflush)(sqlite3*);
|
||||
/* Version 3.12.0 and later */
|
||||
int (*system_errno)(sqlite3*);
|
||||
/* Version 3.14.0 and later */
|
||||
int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
|
||||
char *(*expanded_sql)(sqlite3_stmt*);
|
||||
/* Version 3.18.0 and later */
|
||||
void (*set_last_insert_rowid)(sqlite3*,sqlite3_int64);
|
||||
/* Version 3.20.0 and later */
|
||||
int (*prepare_v3)(sqlite3*,const char*,int,unsigned int,
|
||||
sqlite3_stmt**,const char**);
|
||||
int (*prepare16_v3)(sqlite3*,const void*,int,unsigned int,
|
||||
sqlite3_stmt**,const void**);
|
||||
int (*bind_pointer)(sqlite3_stmt*,int,void*,const char*,void(*)(void*));
|
||||
void (*result_pointer)(sqlite3_context*,void*,const char*,void(*)(void*));
|
||||
void *(*value_pointer)(sqlite3_value*,const char*);
|
||||
int (*vtab_nochange)(sqlite3_context*);
|
||||
int (*value_nochange)(sqlite3_value*);
|
||||
const char *(*vtab_collation)(sqlite3_index_info*,int);
|
||||
/* Version 3.24.0 and later */
|
||||
int (*keyword_count)(void);
|
||||
int (*keyword_name)(int,const char**,int*);
|
||||
int (*keyword_check)(const char*,int);
|
||||
sqlite3_str *(*str_new)(sqlite3*);
|
||||
char *(*str_finish)(sqlite3_str*);
|
||||
void (*str_appendf)(sqlite3_str*, const char *zFormat, ...);
|
||||
void (*str_vappendf)(sqlite3_str*, const char *zFormat, va_list);
|
||||
void (*str_append)(sqlite3_str*, const char *zIn, int N);
|
||||
void (*str_appendall)(sqlite3_str*, const char *zIn);
|
||||
void (*str_appendchar)(sqlite3_str*, int N, char C);
|
||||
void (*str_reset)(sqlite3_str*);
|
||||
int (*str_errcode)(sqlite3_str*);
|
||||
int (*str_length)(sqlite3_str*);
|
||||
char *(*str_value)(sqlite3_str*);
|
||||
/* Version 3.25.0 and later */
|
||||
int (*create_window_function)(sqlite3*,const char*,int,int,void*,
|
||||
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
|
||||
void (*xFinal)(sqlite3_context*),
|
||||
void (*xValue)(sqlite3_context*),
|
||||
void (*xInv)(sqlite3_context*,int,sqlite3_value**),
|
||||
void(*xDestroy)(void*));
|
||||
/* Version 3.26.0 and later */
|
||||
const char *(*normalized_sql)(sqlite3_stmt*);
|
||||
/* Version 3.28.0 and later */
|
||||
int (*stmt_isexplain)(sqlite3_stmt*);
|
||||
int (*value_frombind)(sqlite3_value*);
|
||||
/* Version 3.30.0 and later */
|
||||
int (*drop_modules)(sqlite3*,const char**);
|
||||
/* Version 3.31.0 and later */
|
||||
sqlite3_int64 (*hard_heap_limit64)(sqlite3_int64);
|
||||
const char *(*uri_key)(const char*,int);
|
||||
const char *(*filename_database)(const char*);
|
||||
const char *(*filename_journal)(const char*);
|
||||
const char *(*filename_wal)(const char*);
|
||||
/* Version 3.32.0 and later */
|
||||
const char *(*create_filename)(const char*,const char*,const char*,
|
||||
int,const char**);
|
||||
void (*free_filename)(const char*);
|
||||
sqlite3_file *(*database_file_object)(const char*);
|
||||
/* Version 3.34.0 and later */
|
||||
int (*txn_state)(sqlite3*,const char*);
|
||||
/* Version 3.36.1 and later */
|
||||
sqlite3_int64 (*changes64)(sqlite3*);
|
||||
sqlite3_int64 (*total_changes64)(sqlite3*);
|
||||
/* Version 3.37.0 and later */
|
||||
int (*autovacuum_pages)(sqlite3*,
|
||||
unsigned int(*)(void*,const char*,unsigned int,unsigned int,unsigned int),
|
||||
void*, void(*)(void*));
|
||||
/* Version 3.38.0 and later */
|
||||
int (*error_offset)(sqlite3*);
|
||||
int (*vtab_rhs_value)(sqlite3_index_info*,int,sqlite3_value**);
|
||||
int (*vtab_distinct)(sqlite3_index_info*);
|
||||
int (*vtab_in)(sqlite3_index_info*,int,int);
|
||||
int (*vtab_in_first)(sqlite3_value*,sqlite3_value**);
|
||||
int (*vtab_in_next)(sqlite3_value*,sqlite3_value**);
|
||||
/* Version 3.39.0 and later */
|
||||
int (*deserialize)(sqlite3*,const char*,unsigned char*,
|
||||
sqlite3_int64,sqlite3_int64,unsigned);
|
||||
unsigned char *(*serialize)(sqlite3*,const char *,sqlite3_int64*,
|
||||
unsigned int);
|
||||
const char *(*db_name)(sqlite3*,int);
|
||||
/* Version 3.40.0 and later */
|
||||
int (*value_encoding)(sqlite3_value*);
|
||||
/* Version 3.41.0 and later */
|
||||
int (*is_interrupted)(sqlite3*);
|
||||
/* Version 3.43.0 and later */
|
||||
int (*stmt_explain)(sqlite3_stmt*,int);
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
/* Version 3.50.0 and later */
|
||||
int (*setlk_timeout)(sqlite3*,int,int);
|
||||
};
|
||||
|
||||
/*
|
||||
** This is the function signature used for all extension entry points. It
|
||||
** is also defined in the file "loadext.c".
|
||||
*/
|
||||
typedef int (*sqlite3_loadext_entry)(
|
||||
sqlite3 *db, /* Handle to the database. */
|
||||
char **pzErrMsg, /* Used to set error string on failure. */
|
||||
const sqlite3_api_routines *pThunk /* Extension API function pointers. */
|
||||
);
|
||||
|
||||
/*
|
||||
** The following macros redefine the API routines so that they are
|
||||
** redirected through the global sqlite3_api structure.
|
||||
**
|
||||
** This header file is also used by the loadext.c source file
|
||||
** (part of the main SQLite library - not an extension) so that
|
||||
** it can get access to the sqlite3_api_routines structure
|
||||
** definition. But the main library does not want to redefine
|
||||
** the API. So the redefinition macros are only valid if the
|
||||
** SQLITE_CORE macros is undefined.
|
||||
*/
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
|
||||
#endif
|
||||
#define sqlite3_bind_blob sqlite3_api->bind_blob
|
||||
#define sqlite3_bind_double sqlite3_api->bind_double
|
||||
#define sqlite3_bind_int sqlite3_api->bind_int
|
||||
#define sqlite3_bind_int64 sqlite3_api->bind_int64
|
||||
#define sqlite3_bind_null sqlite3_api->bind_null
|
||||
#define sqlite3_bind_parameter_count sqlite3_api->bind_parameter_count
|
||||
#define sqlite3_bind_parameter_index sqlite3_api->bind_parameter_index
|
||||
#define sqlite3_bind_parameter_name sqlite3_api->bind_parameter_name
|
||||
#define sqlite3_bind_text sqlite3_api->bind_text
|
||||
#define sqlite3_bind_text16 sqlite3_api->bind_text16
|
||||
#define sqlite3_bind_value sqlite3_api->bind_value
|
||||
#define sqlite3_busy_handler sqlite3_api->busy_handler
|
||||
#define sqlite3_busy_timeout sqlite3_api->busy_timeout
|
||||
#define sqlite3_changes sqlite3_api->changes
|
||||
#define sqlite3_close sqlite3_api->close
|
||||
#define sqlite3_collation_needed sqlite3_api->collation_needed
|
||||
#define sqlite3_collation_needed16 sqlite3_api->collation_needed16
|
||||
#define sqlite3_column_blob sqlite3_api->column_blob
|
||||
#define sqlite3_column_bytes sqlite3_api->column_bytes
|
||||
#define sqlite3_column_bytes16 sqlite3_api->column_bytes16
|
||||
#define sqlite3_column_count sqlite3_api->column_count
|
||||
#define sqlite3_column_database_name sqlite3_api->column_database_name
|
||||
#define sqlite3_column_database_name16 sqlite3_api->column_database_name16
|
||||
#define sqlite3_column_decltype sqlite3_api->column_decltype
|
||||
#define sqlite3_column_decltype16 sqlite3_api->column_decltype16
|
||||
#define sqlite3_column_double sqlite3_api->column_double
|
||||
#define sqlite3_column_int sqlite3_api->column_int
|
||||
#define sqlite3_column_int64 sqlite3_api->column_int64
|
||||
#define sqlite3_column_name sqlite3_api->column_name
|
||||
#define sqlite3_column_name16 sqlite3_api->column_name16
|
||||
#define sqlite3_column_origin_name sqlite3_api->column_origin_name
|
||||
#define sqlite3_column_origin_name16 sqlite3_api->column_origin_name16
|
||||
#define sqlite3_column_table_name sqlite3_api->column_table_name
|
||||
#define sqlite3_column_table_name16 sqlite3_api->column_table_name16
|
||||
#define sqlite3_column_text sqlite3_api->column_text
|
||||
#define sqlite3_column_text16 sqlite3_api->column_text16
|
||||
#define sqlite3_column_type sqlite3_api->column_type
|
||||
#define sqlite3_column_value sqlite3_api->column_value
|
||||
#define sqlite3_commit_hook sqlite3_api->commit_hook
|
||||
#define sqlite3_complete sqlite3_api->complete
|
||||
#define sqlite3_complete16 sqlite3_api->complete16
|
||||
#define sqlite3_create_collation sqlite3_api->create_collation
|
||||
#define sqlite3_create_collation16 sqlite3_api->create_collation16
|
||||
#define sqlite3_create_function sqlite3_api->create_function
|
||||
#define sqlite3_create_function16 sqlite3_api->create_function16
|
||||
#define sqlite3_create_module sqlite3_api->create_module
|
||||
#define sqlite3_create_module_v2 sqlite3_api->create_module_v2
|
||||
#define sqlite3_data_count sqlite3_api->data_count
|
||||
#define sqlite3_db_handle sqlite3_api->db_handle
|
||||
#define sqlite3_declare_vtab sqlite3_api->declare_vtab
|
||||
#define sqlite3_enable_shared_cache sqlite3_api->enable_shared_cache
|
||||
#define sqlite3_errcode sqlite3_api->errcode
|
||||
#define sqlite3_errmsg sqlite3_api->errmsg
|
||||
#define sqlite3_errmsg16 sqlite3_api->errmsg16
|
||||
#define sqlite3_exec sqlite3_api->exec
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_expired sqlite3_api->expired
|
||||
#endif
|
||||
#define sqlite3_finalize sqlite3_api->finalize
|
||||
#define sqlite3_free sqlite3_api->free
|
||||
#define sqlite3_free_table sqlite3_api->free_table
|
||||
#define sqlite3_get_autocommit sqlite3_api->get_autocommit
|
||||
#define sqlite3_get_auxdata sqlite3_api->get_auxdata
|
||||
#define sqlite3_get_table sqlite3_api->get_table
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_global_recover sqlite3_api->global_recover
|
||||
#endif
|
||||
#define sqlite3_interrupt sqlite3_api->interruptx
|
||||
#define sqlite3_last_insert_rowid sqlite3_api->last_insert_rowid
|
||||
#define sqlite3_libversion sqlite3_api->libversion
|
||||
#define sqlite3_libversion_number sqlite3_api->libversion_number
|
||||
#define sqlite3_malloc sqlite3_api->malloc
|
||||
#define sqlite3_mprintf sqlite3_api->mprintf
|
||||
#define sqlite3_open sqlite3_api->open
|
||||
#define sqlite3_open16 sqlite3_api->open16
|
||||
#define sqlite3_prepare sqlite3_api->prepare
|
||||
#define sqlite3_prepare16 sqlite3_api->prepare16
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_profile sqlite3_api->profile
|
||||
#define sqlite3_progress_handler sqlite3_api->progress_handler
|
||||
#define sqlite3_realloc sqlite3_api->realloc
|
||||
#define sqlite3_reset sqlite3_api->reset
|
||||
#define sqlite3_result_blob sqlite3_api->result_blob
|
||||
#define sqlite3_result_double sqlite3_api->result_double
|
||||
#define sqlite3_result_error sqlite3_api->result_error
|
||||
#define sqlite3_result_error16 sqlite3_api->result_error16
|
||||
#define sqlite3_result_int sqlite3_api->result_int
|
||||
#define sqlite3_result_int64 sqlite3_api->result_int64
|
||||
#define sqlite3_result_null sqlite3_api->result_null
|
||||
#define sqlite3_result_text sqlite3_api->result_text
|
||||
#define sqlite3_result_text16 sqlite3_api->result_text16
|
||||
#define sqlite3_result_text16be sqlite3_api->result_text16be
|
||||
#define sqlite3_result_text16le sqlite3_api->result_text16le
|
||||
#define sqlite3_result_value sqlite3_api->result_value
|
||||
#define sqlite3_rollback_hook sqlite3_api->rollback_hook
|
||||
#define sqlite3_set_authorizer sqlite3_api->set_authorizer
|
||||
#define sqlite3_set_auxdata sqlite3_api->set_auxdata
|
||||
#define sqlite3_snprintf sqlite3_api->xsnprintf
|
||||
#define sqlite3_step sqlite3_api->step
|
||||
#define sqlite3_table_column_metadata sqlite3_api->table_column_metadata
|
||||
#define sqlite3_thread_cleanup sqlite3_api->thread_cleanup
|
||||
#define sqlite3_total_changes sqlite3_api->total_changes
|
||||
#define sqlite3_trace sqlite3_api->trace
|
||||
#ifndef SQLITE_OMIT_DEPRECATED
|
||||
#define sqlite3_transfer_bindings sqlite3_api->transfer_bindings
|
||||
#endif
|
||||
#define sqlite3_update_hook sqlite3_api->update_hook
|
||||
#define sqlite3_user_data sqlite3_api->user_data
|
||||
#define sqlite3_value_blob sqlite3_api->value_blob
|
||||
#define sqlite3_value_bytes sqlite3_api->value_bytes
|
||||
#define sqlite3_value_bytes16 sqlite3_api->value_bytes16
|
||||
#define sqlite3_value_double sqlite3_api->value_double
|
||||
#define sqlite3_value_int sqlite3_api->value_int
|
||||
#define sqlite3_value_int64 sqlite3_api->value_int64
|
||||
#define sqlite3_value_numeric_type sqlite3_api->value_numeric_type
|
||||
#define sqlite3_value_text sqlite3_api->value_text
|
||||
#define sqlite3_value_text16 sqlite3_api->value_text16
|
||||
#define sqlite3_value_text16be sqlite3_api->value_text16be
|
||||
#define sqlite3_value_text16le sqlite3_api->value_text16le
|
||||
#define sqlite3_value_type sqlite3_api->value_type
|
||||
#define sqlite3_vmprintf sqlite3_api->vmprintf
|
||||
#define sqlite3_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_overload_function sqlite3_api->overload_function
|
||||
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
|
||||
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
|
||||
#define sqlite3_clear_bindings sqlite3_api->clear_bindings
|
||||
#define sqlite3_bind_zeroblob sqlite3_api->bind_zeroblob
|
||||
#define sqlite3_blob_bytes sqlite3_api->blob_bytes
|
||||
#define sqlite3_blob_close sqlite3_api->blob_close
|
||||
#define sqlite3_blob_open sqlite3_api->blob_open
|
||||
#define sqlite3_blob_read sqlite3_api->blob_read
|
||||
#define sqlite3_blob_write sqlite3_api->blob_write
|
||||
#define sqlite3_create_collation_v2 sqlite3_api->create_collation_v2
|
||||
#define sqlite3_file_control sqlite3_api->file_control
|
||||
#define sqlite3_memory_highwater sqlite3_api->memory_highwater
|
||||
#define sqlite3_memory_used sqlite3_api->memory_used
|
||||
#define sqlite3_mutex_alloc sqlite3_api->mutex_alloc
|
||||
#define sqlite3_mutex_enter sqlite3_api->mutex_enter
|
||||
#define sqlite3_mutex_free sqlite3_api->mutex_free
|
||||
#define sqlite3_mutex_leave sqlite3_api->mutex_leave
|
||||
#define sqlite3_mutex_try sqlite3_api->mutex_try
|
||||
#define sqlite3_open_v2 sqlite3_api->open_v2
|
||||
#define sqlite3_release_memory sqlite3_api->release_memory
|
||||
#define sqlite3_result_error_nomem sqlite3_api->result_error_nomem
|
||||
#define sqlite3_result_error_toobig sqlite3_api->result_error_toobig
|
||||
#define sqlite3_sleep sqlite3_api->sleep
|
||||
#define sqlite3_soft_heap_limit sqlite3_api->soft_heap_limit
|
||||
#define sqlite3_vfs_find sqlite3_api->vfs_find
|
||||
#define sqlite3_vfs_register sqlite3_api->vfs_register
|
||||
#define sqlite3_vfs_unregister sqlite3_api->vfs_unregister
|
||||
#define sqlite3_threadsafe sqlite3_api->xthreadsafe
|
||||
#define sqlite3_result_zeroblob sqlite3_api->result_zeroblob
|
||||
#define sqlite3_result_error_code sqlite3_api->result_error_code
|
||||
#define sqlite3_test_control sqlite3_api->test_control
|
||||
#define sqlite3_randomness sqlite3_api->randomness
|
||||
#define sqlite3_context_db_handle sqlite3_api->context_db_handle
|
||||
#define sqlite3_extended_result_codes sqlite3_api->extended_result_codes
|
||||
#define sqlite3_limit sqlite3_api->limit
|
||||
#define sqlite3_next_stmt sqlite3_api->next_stmt
|
||||
#define sqlite3_sql sqlite3_api->sql
|
||||
#define sqlite3_status sqlite3_api->status
|
||||
#define sqlite3_backup_finish sqlite3_api->backup_finish
|
||||
#define sqlite3_backup_init sqlite3_api->backup_init
|
||||
#define sqlite3_backup_pagecount sqlite3_api->backup_pagecount
|
||||
#define sqlite3_backup_remaining sqlite3_api->backup_remaining
|
||||
#define sqlite3_backup_step sqlite3_api->backup_step
|
||||
#define sqlite3_compileoption_get sqlite3_api->compileoption_get
|
||||
#define sqlite3_compileoption_used sqlite3_api->compileoption_used
|
||||
#define sqlite3_create_function_v2 sqlite3_api->create_function_v2
|
||||
#define sqlite3_db_config sqlite3_api->db_config
|
||||
#define sqlite3_db_mutex sqlite3_api->db_mutex
|
||||
#define sqlite3_db_status sqlite3_api->db_status
|
||||
#define sqlite3_extended_errcode sqlite3_api->extended_errcode
|
||||
#define sqlite3_log sqlite3_api->log
|
||||
#define sqlite3_soft_heap_limit64 sqlite3_api->soft_heap_limit64
|
||||
#define sqlite3_sourceid sqlite3_api->sourceid
|
||||
#define sqlite3_stmt_status sqlite3_api->stmt_status
|
||||
#define sqlite3_strnicmp sqlite3_api->strnicmp
|
||||
#define sqlite3_unlock_notify sqlite3_api->unlock_notify
|
||||
#define sqlite3_wal_autocheckpoint sqlite3_api->wal_autocheckpoint
|
||||
#define sqlite3_wal_checkpoint sqlite3_api->wal_checkpoint
|
||||
#define sqlite3_wal_hook sqlite3_api->wal_hook
|
||||
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
|
||||
#define sqlite3_vtab_config sqlite3_api->vtab_config
|
||||
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
|
||||
/* Version 3.7.16 and later */
|
||||
#define sqlite3_close_v2 sqlite3_api->close_v2
|
||||
#define sqlite3_db_filename sqlite3_api->db_filename
|
||||
#define sqlite3_db_readonly sqlite3_api->db_readonly
|
||||
#define sqlite3_db_release_memory sqlite3_api->db_release_memory
|
||||
#define sqlite3_errstr sqlite3_api->errstr
|
||||
#define sqlite3_stmt_busy sqlite3_api->stmt_busy
|
||||
#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
|
||||
#define sqlite3_stricmp sqlite3_api->stricmp
|
||||
#define sqlite3_uri_boolean sqlite3_api->uri_boolean
|
||||
#define sqlite3_uri_int64 sqlite3_api->uri_int64
|
||||
#define sqlite3_uri_parameter sqlite3_api->uri_parameter
|
||||
#define sqlite3_uri_vsnprintf sqlite3_api->xvsnprintf
|
||||
#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
|
||||
/* Version 3.8.7 and later */
|
||||
#define sqlite3_auto_extension sqlite3_api->auto_extension
|
||||
#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
|
||||
#define sqlite3_bind_text64 sqlite3_api->bind_text64
|
||||
#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
|
||||
#define sqlite3_load_extension sqlite3_api->load_extension
|
||||
#define sqlite3_malloc64 sqlite3_api->malloc64
|
||||
#define sqlite3_msize sqlite3_api->msize
|
||||
#define sqlite3_realloc64 sqlite3_api->realloc64
|
||||
#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
|
||||
#define sqlite3_result_blob64 sqlite3_api->result_blob64
|
||||
#define sqlite3_result_text64 sqlite3_api->result_text64
|
||||
#define sqlite3_strglob sqlite3_api->strglob
|
||||
/* Version 3.8.11 and later */
|
||||
#define sqlite3_value_dup sqlite3_api->value_dup
|
||||
#define sqlite3_value_free sqlite3_api->value_free
|
||||
#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
|
||||
#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
|
||||
/* Version 3.9.0 and later */
|
||||
#define sqlite3_value_subtype sqlite3_api->value_subtype
|
||||
#define sqlite3_result_subtype sqlite3_api->result_subtype
|
||||
/* Version 3.10.0 and later */
|
||||
#define sqlite3_status64 sqlite3_api->status64
|
||||
#define sqlite3_strlike sqlite3_api->strlike
|
||||
#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
|
||||
/* Version 3.12.0 and later */
|
||||
#define sqlite3_system_errno sqlite3_api->system_errno
|
||||
/* Version 3.14.0 and later */
|
||||
#define sqlite3_trace_v2 sqlite3_api->trace_v2
|
||||
#define sqlite3_expanded_sql sqlite3_api->expanded_sql
|
||||
/* Version 3.18.0 and later */
|
||||
#define sqlite3_set_last_insert_rowid sqlite3_api->set_last_insert_rowid
|
||||
/* Version 3.20.0 and later */
|
||||
#define sqlite3_prepare_v3 sqlite3_api->prepare_v3
|
||||
#define sqlite3_prepare16_v3 sqlite3_api->prepare16_v3
|
||||
#define sqlite3_bind_pointer sqlite3_api->bind_pointer
|
||||
#define sqlite3_result_pointer sqlite3_api->result_pointer
|
||||
#define sqlite3_value_pointer sqlite3_api->value_pointer
|
||||
/* Version 3.22.0 and later */
|
||||
#define sqlite3_vtab_nochange sqlite3_api->vtab_nochange
|
||||
#define sqlite3_value_nochange sqlite3_api->value_nochange
|
||||
#define sqlite3_vtab_collation sqlite3_api->vtab_collation
|
||||
/* Version 3.24.0 and later */
|
||||
#define sqlite3_keyword_count sqlite3_api->keyword_count
|
||||
#define sqlite3_keyword_name sqlite3_api->keyword_name
|
||||
#define sqlite3_keyword_check sqlite3_api->keyword_check
|
||||
#define sqlite3_str_new sqlite3_api->str_new
|
||||
#define sqlite3_str_finish sqlite3_api->str_finish
|
||||
#define sqlite3_str_appendf sqlite3_api->str_appendf
|
||||
#define sqlite3_str_vappendf sqlite3_api->str_vappendf
|
||||
#define sqlite3_str_append sqlite3_api->str_append
|
||||
#define sqlite3_str_appendall sqlite3_api->str_appendall
|
||||
#define sqlite3_str_appendchar sqlite3_api->str_appendchar
|
||||
#define sqlite3_str_reset sqlite3_api->str_reset
|
||||
#define sqlite3_str_errcode sqlite3_api->str_errcode
|
||||
#define sqlite3_str_length sqlite3_api->str_length
|
||||
#define sqlite3_str_value sqlite3_api->str_value
|
||||
/* Version 3.25.0 and later */
|
||||
#define sqlite3_create_window_function sqlite3_api->create_window_function
|
||||
/* Version 3.26.0 and later */
|
||||
#define sqlite3_normalized_sql sqlite3_api->normalized_sql
|
||||
/* Version 3.28.0 and later */
|
||||
#define sqlite3_stmt_isexplain sqlite3_api->stmt_isexplain
|
||||
#define sqlite3_value_frombind sqlite3_api->value_frombind
|
||||
/* Version 3.30.0 and later */
|
||||
#define sqlite3_drop_modules sqlite3_api->drop_modules
|
||||
/* Version 3.31.0 and later */
|
||||
#define sqlite3_hard_heap_limit64 sqlite3_api->hard_heap_limit64
|
||||
#define sqlite3_uri_key sqlite3_api->uri_key
|
||||
#define sqlite3_filename_database sqlite3_api->filename_database
|
||||
#define sqlite3_filename_journal sqlite3_api->filename_journal
|
||||
#define sqlite3_filename_wal sqlite3_api->filename_wal
|
||||
/* Version 3.32.0 and later */
|
||||
#define sqlite3_create_filename sqlite3_api->create_filename
|
||||
#define sqlite3_free_filename sqlite3_api->free_filename
|
||||
#define sqlite3_database_file_object sqlite3_api->database_file_object
|
||||
/* Version 3.34.0 and later */
|
||||
#define sqlite3_txn_state sqlite3_api->txn_state
|
||||
/* Version 3.36.1 and later */
|
||||
#define sqlite3_changes64 sqlite3_api->changes64
|
||||
#define sqlite3_total_changes64 sqlite3_api->total_changes64
|
||||
/* Version 3.37.0 and later */
|
||||
#define sqlite3_autovacuum_pages sqlite3_api->autovacuum_pages
|
||||
/* Version 3.38.0 and later */
|
||||
#define sqlite3_error_offset sqlite3_api->error_offset
|
||||
#define sqlite3_vtab_rhs_value sqlite3_api->vtab_rhs_value
|
||||
#define sqlite3_vtab_distinct sqlite3_api->vtab_distinct
|
||||
#define sqlite3_vtab_in sqlite3_api->vtab_in
|
||||
#define sqlite3_vtab_in_first sqlite3_api->vtab_in_first
|
||||
#define sqlite3_vtab_in_next sqlite3_api->vtab_in_next
|
||||
/* Version 3.39.0 and later */
|
||||
#ifndef SQLITE_OMIT_DESERIALIZE
|
||||
#define sqlite3_deserialize sqlite3_api->deserialize
|
||||
#define sqlite3_serialize sqlite3_api->serialize
|
||||
#endif
|
||||
#define sqlite3_db_name sqlite3_api->db_name
|
||||
/* Version 3.40.0 and later */
|
||||
#define sqlite3_value_encoding sqlite3_api->value_encoding
|
||||
/* Version 3.41.0 and later */
|
||||
#define sqlite3_is_interrupted sqlite3_api->is_interrupted
|
||||
/* Version 3.43.0 and later */
|
||||
#define sqlite3_stmt_explain sqlite3_api->stmt_explain
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
/* Version 3.50.0 and later */
|
||||
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
/* This case when the file really is being compiled as a loadable
|
||||
** extension */
|
||||
# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
|
||||
# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
|
||||
# define SQLITE_EXTENSION_INIT3 \
|
||||
extern const sqlite3_api_routines *sqlite3_api;
|
||||
#else
|
||||
/* This case when the file is being statically linked into the
|
||||
** application */
|
||||
# define SQLITE_EXTENSION_INIT1 /*no-op*/
|
||||
# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
|
||||
# define SQLITE_EXTENSION_INIT3 /*no-op*/
|
||||
#endif
|
||||
|
||||
#endif /* SQLITE3EXT_H */
|
||||
@@ -0,0 +1,46 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include "sqlite3utils.h"
|
||||
|
||||
int sqlite3utils_prepare_impl(sqlite3 *db, sqlite3_stmt **pstmt, const char *fmt, VArgs args)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int ret = sqlite3_prepare_v2(db, fmt, -1, &stmt, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.len; i++) {
|
||||
VArg arg = args.ptr[i];
|
||||
switch (arg.type) {
|
||||
case VARG_TYPE_C : ret = sqlite3_bind_text(stmt, i+1, &arg.c, 1, NULL); break;
|
||||
case VARG_TYPE_S : ret = sqlite3_bind_int(stmt, i+1, arg.s); break;
|
||||
case VARG_TYPE_I : ret = sqlite3_bind_int(stmt, i+1, arg.i); break;
|
||||
case VARG_TYPE_L : ret = sqlite3_bind_int64(stmt, i+1, arg.l); break;
|
||||
case VARG_TYPE_LL : ret = sqlite3_bind_int64(stmt, i+1, arg.ll); break;
|
||||
case VARG_TYPE_SC : ret = sqlite3_bind_int(stmt, i+1, arg.sc); break;
|
||||
case VARG_TYPE_SS : ret = sqlite3_bind_int(stmt, i+1, arg.ss); break;
|
||||
case VARG_TYPE_SI : ret = sqlite3_bind_int(stmt, i+1, arg.si); break;
|
||||
case VARG_TYPE_SL : ret = sqlite3_bind_int64(stmt, i+1, arg.sl); break;
|
||||
case VARG_TYPE_SLL: ret = sqlite3_bind_int(stmt, i+1, arg.sll); break;
|
||||
case VARG_TYPE_UC : ret = sqlite3_bind_int(stmt, i+1, arg.uc); break;
|
||||
case VARG_TYPE_US : ret = sqlite3_bind_int(stmt, i+1, arg.us); break;
|
||||
case VARG_TYPE_UI : ret = sqlite3_bind_int64(stmt, i+1, arg.ui); break;
|
||||
case VARG_TYPE_UL : ret = sqlite3_bind_int64(stmt, i+1, arg.ul); break;
|
||||
case VARG_TYPE_ULL: ret = sqlite3_bind_int64(stmt, i+1, arg.ull); break;
|
||||
case VARG_TYPE_F : ret = sqlite3_bind_double(stmt, i+1, arg.f); break;
|
||||
case VARG_TYPE_D : ret = sqlite3_bind_double(stmt, i+1, arg.d); break;
|
||||
case VARG_TYPE_B : ret = sqlite3_bind_int(stmt, i+1, arg.b); break;
|
||||
case VARG_TYPE_STR: ret = sqlite3_bind_text(stmt, i+1, arg.str.ptr, arg.str.len, NULL); break;
|
||||
}
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
|
||||
sqlite3_finalize(stmt);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
*pstmt = stmt;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#include "sqlite3.h"
|
||||
#include "variadic.h"
|
||||
|
||||
int sqlite3utils_prepare_impl(sqlite3 *db, sqlite3_stmt **pstmt, const char *fmt, VArgs args);
|
||||
#define sqlite3utils_prepare(db, pstmt, fmt, ...) sqlite3utils_prepare_impl((db), (pstmt), (fmt), VARGS(__VA_ARGS__))
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
#include "variadic.h"
|
||||
|
||||
VArg varg_from_c (char c) { return (VArg) { VARG_TYPE_C, .c=c }; }
|
||||
VArg varg_from_s (short s) { return (VArg) { VARG_TYPE_S, .s=s }; }
|
||||
VArg varg_from_i (int i) { return (VArg) { VARG_TYPE_I, .i=i }; }
|
||||
VArg varg_from_l (long l) { return (VArg) { VARG_TYPE_L, .l=l }; }
|
||||
VArg varg_from_ll (long long ll) { return (VArg) { VARG_TYPE_LL, .ll=ll }; }
|
||||
VArg varg_from_sc (char sc) { return (VArg) { VARG_TYPE_SC, .sc=sc }; }
|
||||
VArg varg_from_ss (short ss) { return (VArg) { VARG_TYPE_SS, .ss=ss }; }
|
||||
VArg varg_from_si (int si) { return (VArg) { VARG_TYPE_SI, .si=si }; }
|
||||
VArg varg_from_sl (long sl) { return (VArg) { VARG_TYPE_SL, .sl=sl }; }
|
||||
VArg varg_from_sll (long long sll) { return (VArg) { VARG_TYPE_SLL, .sll=sll }; }
|
||||
VArg varg_from_uc (char uc) { return (VArg) { VARG_TYPE_UC, .uc=uc }; }
|
||||
VArg varg_from_us (short us) { return (VArg) { VARG_TYPE_US, .us=us }; }
|
||||
VArg varg_from_ui (int ui) { return (VArg) { VARG_TYPE_UI, .ui=ui }; }
|
||||
VArg varg_from_ul (long ul) { return (VArg) { VARG_TYPE_UL, .ul=ul }; }
|
||||
VArg varg_from_ull (long long ull) { return (VArg) { VARG_TYPE_ULL, .ull=ull }; }
|
||||
VArg varg_from_f (float f) { return (VArg) { VARG_TYPE_F, .f=f }; }
|
||||
VArg varg_from_d (double d) { return (VArg) { VARG_TYPE_D, .d=d }; }
|
||||
VArg varg_from_b (bool b) { return (VArg) { VARG_TYPE_B, .b=b }; }
|
||||
VArg varg_from_str (HTTP_String str) { return (VArg) { VARG_TYPE_STR, .str=str }; }
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
#include <stdbool.h>
|
||||
#include "chttp.h"
|
||||
|
||||
typedef enum {
|
||||
VARG_TYPE_C,
|
||||
VARG_TYPE_S,
|
||||
VARG_TYPE_I,
|
||||
VARG_TYPE_L,
|
||||
VARG_TYPE_LL,
|
||||
VARG_TYPE_SC,
|
||||
VARG_TYPE_SS,
|
||||
VARG_TYPE_SI,
|
||||
VARG_TYPE_SL,
|
||||
VARG_TYPE_SLL,
|
||||
VARG_TYPE_UC,
|
||||
VARG_TYPE_US,
|
||||
VARG_TYPE_UI,
|
||||
VARG_TYPE_UL,
|
||||
VARG_TYPE_ULL,
|
||||
VARG_TYPE_F,
|
||||
VARG_TYPE_D,
|
||||
VARG_TYPE_B,
|
||||
VARG_TYPE_STR,
|
||||
} VArgType;
|
||||
|
||||
typedef struct {
|
||||
VArgType type;
|
||||
union {
|
||||
char c;
|
||||
short s;
|
||||
int i;
|
||||
long l;
|
||||
long long ll;
|
||||
signed char sc;
|
||||
signed short ss;
|
||||
signed int si;
|
||||
signed long sl;
|
||||
signed long long sll;
|
||||
unsigned char uc;
|
||||
unsigned short us;
|
||||
unsigned int ui;
|
||||
unsigned long ul;
|
||||
unsigned long long ull;
|
||||
float f;
|
||||
double d;
|
||||
bool b;
|
||||
HTTP_String str;
|
||||
};
|
||||
} VArg;
|
||||
|
||||
VArg varg_from_c (char c);
|
||||
VArg varg_from_s (short s);
|
||||
VArg varg_from_i (int i);
|
||||
VArg varg_from_l (long l);
|
||||
VArg varg_from_ll (long long ll);
|
||||
VArg varg_from_sc (char sc);
|
||||
VArg varg_from_ss (short ss);
|
||||
VArg varg_from_si (int si);
|
||||
VArg varg_from_sl (long sl);
|
||||
VArg varg_from_sll (long long sll);
|
||||
VArg varg_from_uc (char uc);
|
||||
VArg varg_from_us (short us);
|
||||
VArg varg_from_ui (int ui);
|
||||
VArg varg_from_ul (long ul);
|
||||
VArg varg_from_ull (long long ull);
|
||||
VArg varg_from_f (float f);
|
||||
VArg varg_from_d (double d);
|
||||
VArg varg_from_b (bool b);
|
||||
VArg varg_from_str (HTTP_String str);
|
||||
|
||||
#define VARG(X) (_Generic((X), \
|
||||
char : varg_from_c, \
|
||||
short : varg_from_s, \
|
||||
int : varg_from_i, \
|
||||
long : varg_from_l, \
|
||||
long long : varg_from_ll, \
|
||||
signed char : varg_from_sc, \
|
||||
/*signed short : varg_from_ss,*/ \
|
||||
/*signed int : varg_from_si,*/ \
|
||||
/*signed long : varg_from_sl,*/ \
|
||||
/*signed long long : varg_from_sll,*/ \
|
||||
unsigned char : varg_from_uc, \
|
||||
unsigned short : varg_from_us, \
|
||||
unsigned int : varg_from_ui, \
|
||||
unsigned long : varg_from_ul, \
|
||||
unsigned long long: varg_from_ull, \
|
||||
float : varg_from_f, \
|
||||
double : varg_from_d, \
|
||||
bool : varg_from_b, \
|
||||
HTTP_String : varg_from_str \
|
||||
))(X)
|
||||
|
||||
typedef struct {
|
||||
int len;
|
||||
VArg *ptr;
|
||||
} VArgs;
|
||||
|
||||
#define VARGS_1(a) (VArgs) {1, (VArg[]) { VARG(a) } }
|
||||
#define VARGS_2(a, b) (VArgs) {2, (VArg[]) { VARG(a), VARG(b) } }
|
||||
#define VARGS_3(a, b, c) (VArgs) {3, (VArg[]) { VARG(a), VARG(b), VARG(c) } }
|
||||
#define VARGS_4(a, b, c, d) (VArgs) {4, (VArg[]) { VARG(a), VARG(b), VARG(c), VARG(d) } }
|
||||
#define VARGS_5(a, b, c, d, e) (VArgs) {5, (VArg[]) { VARG(a), VARG(b), VARG(c), VARG(d), VARG(e) } }
|
||||
|
||||
#define DISPATCH__(_1, _2, _3, _4, _5, NAME, ...) NAME
|
||||
#define VARGS(...) DISPATCH__(__VA_ARGS__, VARGS_5, VARGS_4, VARGS_3, VARGS_2, VARGS_1)(__VA_ARGS__)
|
||||
Reference in New Issue
Block a user