commit 25431f2fde33ec1fc58d5c7bd27142356f4a1b03
parent e46326f376410983fc97685498425a6c5a361760
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Tue, 5 Mar 2024 22:55:12 -0800
Move most page functions into the pages directory
To support this, CGI/HTTP keys were moved to key.c & key.h. Also,
a list of page names was added to pagedata.
Diffstat:
14 files changed, 321 insertions(+), 262 deletions(-)
diff --git a/Makefile b/Makefile
@@ -4,8 +4,9 @@ cflags = ${CFLAGS} -O0 -Wall -Wextra -I/usr/local/include
lddflags = ${LDDFLAGS} -static -L/usr/local/lib -lkcgi -lkcgihtml -lz -lsqlbox -lsqlite3 -lm -lpthread
prefix = /var/www/htdocs/${name}.primus.lan
-src = page.c html.c user.c stmt.c pages/util.c
-hdr = ${src:.c=.h} util.h
+pages = pages/index.c pages/404.c pages/login.c pages/logout.c pages/account.c
+src = page.c html.c user.c stmt.c key.c pages/util.c ${pages}
+hdr = page.h html.h user.h stmt.h key.h util.h pages/util.h pages/pages.h
obj = ${src:.c=.o}
all: ${name}
diff --git a/key.c b/key.c
@@ -0,0 +1,43 @@
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <kcgi.h>
+#include <kcgihtml.h>
+
+#include <ctype.h>
+
+#include "key.h"
+
+#include "page.h"
+#include "user.h"
+
+int
+kvalid_name(struct kpair *p)
+{
+ char *s;
+
+ if (!kvalid_stringne(p) || p->valsz < NameMin || p->valsz > NameMax)
+ return 0;
+
+ for (s = p->val; *s; ++s)
+ if (!isalnum(*s) && *s != '-')
+ return 0;
+ return 1;
+}
+
+int
+kvalid_pass(struct kpair *p)
+{
+ if (!kvalid_stringne(p) || p->valsz < PassMin || p->valsz > PassMax)
+ return 0;
+ return 1;
+}
+
+struct kvalid keys[] = {
+ [KeyUsername] = { kvalid_name, "username" },
+ [KeyPassword] = { kvalid_pass, "password" },
+ [KeyHash] = { kvalid_stringne, "hash" },
+ [KeyCreate] = { kvalid_stringne, "create" },
+ [KeyDelete] = { kvalid_stringne, "delete" },
+ [KeyTime] = { kvalid_stringne, "time" }
+};
diff --git a/key.h b/key.h
@@ -0,0 +1,11 @@
+enum Field {
+ KeyUsername,
+ KeyPassword,
+ KeyHash, /* Authentication hash */
+ KeyCreate, /* Whether to create a new user in login page */
+ KeyDelete, /* Delete account in account page */
+ KeyTime, /* one of start, startbreak, endbreak, end */
+ KeyMax
+};
+
+extern struct kvalid keys[];
diff --git a/page.h b/page.h
@@ -4,6 +4,7 @@ struct pagedata {
struct sqlbox *db;
size_t dbid;
struct kvalid *keys;
+ char **pages; /* page names */
};
enum kcgi_err showpage(struct pagedata *pd, char **names,
diff --git a/pages/404.c b/pages/404.c
@@ -0,0 +1,7 @@
+#include "common.h"
+
+enum kcgi_err
+page404(struct pagedata *pd)
+{
+ return errorpage(pd, KHTTP_404);
+}
diff --git a/pages/account.c b/pages/account.c
@@ -0,0 +1,50 @@
+#include <string.h>
+
+#include "common.h"
+#include "pages.h"
+
+enum kcgi_err
+pageaccount(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ struct user *user;
+
+ user = sitegetlogin(pd);
+ if (!user)
+ return errorpage(pd, KHTTP_401);
+
+ if (pd->req.fieldmap[KeyDelete] && strcmp(pd->req.fieldmap[KeyDelete]->parsed.s, "yes") == 0) {
+ if (deleteuser(pd, user->hash) == 0)
+ return redirect(pd, pd->pages[PageIndex], "Account deleted");
+ else
+ return errorpage(pd, KHTTP_500);
+ }
+
+ if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK ||
+ (status = htmlwithin(pd, KELEM_H1, "Account")) != KCGI_OK ||
+ (status = htmlwithin(pd, KELEM_H2, "Delete account")) != KCGI_OK ||
+ (status = htmlwithin(pd, KELEM_P, "Deleting an account is irreversible:"
+ " no account data can be restored after deletion.")) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
+ (status = khtml_puts(&pd->html, "Confirm permanent deletion:")) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "checkbox",
+ KATTR_NAME, pd->keys[KeyDelete].name,
+ KATTR_VALUE, "yes",
+ KATTR_REQUIRED, "true",
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK ||
+ (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "submit",
+ KATTR_VALUE, "Delete",
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK) {
+ freeuser(user);
+ return status;
+ }
+
+ freeuser(user);
+ return khtml_close(&pd->html);
+}
diff --git a/pages/common.h b/pages/common.h
@@ -0,0 +1,12 @@
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <kcgi.h>
+#include <kcgihtml.h>
+
+#include "../page.h"
+#include "../html.h"
+#include "../user.h"
+#include "../key.h"
+#include "util.h"
diff --git a/pages/index.c b/pages/index.c
@@ -0,0 +1,34 @@
+#include "common.h"
+
+#include "pages.h"
+
+enum kcgi_err
+pageindex(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ struct user *user;
+
+ user = sitegetlogin(pd);
+
+ if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK)
+ return status;
+
+ khtml_elem(&pd->html, KELEM_H1);
+ khtml_printf(&pd->html, "Welcome %s!", user ? user->name : "friend");
+ khtml_closeelem(&pd->html, 1);
+
+ if ((status = htmllink(pd, user ? "/logout" : "/login",
+ user ? "Logout" : "Login")) != KCGI_OK)
+ return status;
+
+ if (user) {
+ if ((status = khtml_puts(&pd->html, " | ")) != KCGI_OK ||
+ (status = htmllink(pd, pd->pages[PageAccount], "Manage account")))
+ return status;
+ if ((status = khtml_puts(&pd->html, " | ")) != KCGI_OK ||
+ (status = htmllink(pd, pd->pages[PageMain], "Timekeeper")))
+ return status;
+ }
+
+ return khtml_close(&pd->html);
+}
diff --git a/pages/login.c b/pages/login.c
@@ -0,0 +1,107 @@
+#include <assert.h>
+
+#define const
+
+#include <ctype.h>
+
+#include "common.h"
+#include "pages.h"
+
+enum kcgi_err
+htmlinput(struct pagedata *pd, enum Field field, char *type, int64_t minl, int64_t maxl)
+{
+ enum kcgi_err status;
+
+ assert(pd->keys[field].name[0] != 0);
+
+ if ((status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
+ (status = khtml_printf(&pd->html, "%c%s: ",
+ toupper(pd->keys[field].name[0]), pd->keys[field].name + 1)) != KCGI_OK ||
+ (status = khtml_attrx(&pd->html, KELEM_INPUT,
+ KATTR_NAME, KATTRX_STRING, pd->keys[field].name,
+ KATTR_TYPE, KATTRX_STRING, type,
+ KATTR_MINLENGTH, KATTRX_INT, minl,
+ KATTR_MAXLENGTH, KATTRX_INT, maxl,
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+
+ return KCGI_OK;
+}
+
+enum kcgi_err
+pagelogin(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ struct user *user;
+ char *fuser, *fpass;
+ enum LoginStatus ls;
+ char *msg;
+
+ user = NULL;
+ ls = LoginUntried;
+ if (pd->req.fieldmap[KeyUsername] && pd->req.fieldmap[KeyPassword]) {
+ fuser = pd->req.fieldmap[KeyUsername]->parsed.s;
+ fpass = pd->req.fieldmap[KeyPassword]->parsed.s;
+
+ if (pd->req.fieldmap[KeyCreate]) {
+ if (adduser(pd, fuser, fpass))
+ ls = LoginError;
+
+ }
+ if (ls == LoginUntried) {
+ ls = loginuser(&user, pd, fuser, fpass);
+ if (ls == LoginValid) {
+ status = khttp_head(&pd->req, kresps[KRESP_SET_COOKIE], "%s=%s; Path=/",
+ pd->keys[KeyHash].name, user->hash);
+ if (status != KCGI_OK)
+ return status;
+ }
+ }
+ } else {
+ user = sitegetlogin(pd);
+ }
+
+ if (user)
+ return redirect(pd, pd->pages[PageIndex], "Logged in");
+
+ if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK)
+ return status;
+
+ if ((status = htmlwithin(pd, KELEM_H1, "Login")) != KCGI_OK)
+ return status;
+
+ if (ls > 0) {
+ if (ls == LoginInvalid)
+ msg = "Error: Invalid credentials";
+ else if (ls == LoginError)
+ msg = "Error: System error";
+ else
+ msg = "Error: Undefined error";
+
+ if ((status = htmlwithin(pd, KELEM_P, msg)) != KCGI_OK)
+ return status;
+ }
+
+ if ((status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
+ (status = htmlinput(pd, KeyUsername, "text", NameMin, NameMax)) != KCGI_OK ||
+ (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
+ (status = htmlinput(pd, KeyPassword, "password", PassMin, PassMax)) != KCGI_OK ||
+ (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
+ (status = khtml_puts(&pd->html, "Create user ")) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "checkbox",
+ KATTR_NAME, pd->keys[KeyCreate].name,
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK ||
+ (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "submit",
+ KATTR_VALUE, "Submit",
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+
+ return khtml_close(&pd->html);
+}
diff --git a/pages/logout.c b/pages/logout.c
@@ -0,0 +1,19 @@
+#include "common.h"
+
+#include "pages.h"
+
+enum kcgi_err
+pagelogout(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ struct user *user;
+
+ if ((user = sitegetlogin(pd))) {
+ freeuser(user);
+ if ((status = khttp_head(&pd->req, kresps[KRESP_SET_COOKIE],
+ "%s=; Path=/", pd->keys[KeyHash].name)) != KCGI_OK)
+ return status;
+ }
+
+ return redirect(pd, pd->pages[PageIndex], "You are being logged out");
+}
diff --git a/pages/pages.h b/pages/pages.h
@@ -0,0 +1,15 @@
+enum Page {
+ PageIndex,
+ PageLogin,
+ PageLogout,
+ PageAccount,
+ PageMain,
+ Page404,
+ PageMax
+};
+
+enum kcgi_err page404(struct pagedata *pd);
+enum kcgi_err pageindex(struct pagedata *pd);
+enum kcgi_err pagelogin(struct pagedata *pd);
+enum kcgi_err pagelogout(struct pagedata *pd);
+enum kcgi_err pageaccount(struct pagedata *pd);
diff --git a/pages/util.c b/pages/util.c
@@ -1,10 +1,15 @@
+#define const
+
#include <sys/types.h>
#include <stdarg.h>
+#include <stddef.h>
#include <stdint.h>
#include <kcgi.h>
#include <kcgihtml.h>
#include "../page.h"
+#include "../user.h"
+#include "../key.h"
enum kcgi_err
errorpage(struct pagedata *pd, enum khttp code)
@@ -64,3 +69,12 @@ redirect(struct pagedata *pd, char *to, char *msg)
return status;
return khtml_close(&pd->html);
}
+
+/* getlogin is taken */
+struct user *
+sitegetlogin(struct pagedata *pd)
+{
+ if (!pd->req.cookiemap[KeyHash])
+ return NULL;
+ return getuser(pd, "hash", pd->req.cookiemap[KeyHash]->parsed.s);
+}
diff --git a/pages/util.h b/pages/util.h
@@ -1,3 +1,4 @@
enum kcgi_err errorpage(struct pagedata *pd, enum khttp code);
enum kcgi_err starthtmldoc(struct pagedata *pd, enum khttp code);
enum kcgi_err redirect(struct pagedata *pd, char *to, char *msg);
+struct user *sitegetlogin(struct pagedata *pd);
diff --git a/timekeeper.c b/timekeeper.c
@@ -3,10 +3,8 @@
#define const
#include <sys/types.h>
-#include <ctype.h>
#include <err.h>
#include <limits.h>
-#include <pwd.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
@@ -24,32 +22,14 @@
#include "html.h"
#include "user.h"
#include "stmt.h"
+#include "key.h"
#include "pages/util.h"
+#include "pages/pages.h"
#include "util.h"
#define Promises "stdio rpath wpath cpath proc recvfd unix sendfd"
-enum Field {
- KeyUsername,
- KeyPassword,
- KeyHash, /* Authentication hash */
- KeyCreate, /* Whether to create a new user in login page */
- KeyDelete, /* Delete account in account page */
- KeyTime, /* one of start, startbreak, endbreak, end */
- KeyMax
-};
-
-enum Page {
- PageIndex,
- PageLogin,
- PageLogout,
- PageAccount,
- PageMain,
- Page404,
- PageMax
-};
-
enum TimeField {
StartTime,
BreakStartTime,
@@ -74,28 +54,6 @@ char *timefields[] = {
};
int
-kvalid_name(struct kpair *p)
-{
- char *s;
-
- if (!kvalid_stringne(p) || p->valsz < NameMin || p->valsz > NameMax)
- return 0;
-
- for (s = p->val; *s; ++s)
- if (!isalnum(*s) && *s != '-')
- return 0;
- return 1;
-}
-
-int
-kvalid_pass(struct kpair *p)
-{
- if (!kvalid_stringne(p) || p->valsz < PassMin || p->valsz > PassMax)
- return 0;
- return 1;
-}
-
-int
initdb(struct pagedata *pd)
{
enum StmtID i;
@@ -106,213 +64,6 @@ initdb(struct pagedata *pd)
return 0;
}
-/* getlogin is taken */
-struct user *
-sitegetlogin(struct pagedata *pd)
-{
- if (!pd->req.cookiemap[KeyHash])
- return NULL;
- return getuser(pd, "hash", pd->req.cookiemap[KeyHash]->parsed.s);
-}
-
-enum kcgi_err
-page404(struct pagedata *pd)
-{
- return errorpage(pd, KHTTP_404);
-}
-
-enum kcgi_err
-pageindex(struct pagedata *pd)
-{
- enum kcgi_err status;
- struct user *user;
-
- user = sitegetlogin(pd);
-
- if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK)
- return status;
-
- khtml_elem(&pd->html, KELEM_H1);
- khtml_printf(&pd->html, "Welcome %s!", user ? user->name : "friend");
- khtml_closeelem(&pd->html, 1);
-
- if ((status = htmllink(pd, user ? "/logout" : "/login",
- user ? "Logout" : "Login")) != KCGI_OK)
- return status;
-
- if (user) {
- if ((status = khtml_puts(&pd->html, " | ")) != KCGI_OK ||
- (status = htmllink(pd, pages[PageAccount], "Manage account")))
- return status;
- if ((status = khtml_puts(&pd->html, " | ")) != KCGI_OK ||
- (status = htmllink(pd, pages[PageMain], "Timekeeper")))
- return status;
- }
-
- return khtml_close(&pd->html);
-}
-
-enum kcgi_err
-htmlinput(struct pagedata *pd, enum Field field, char *type, int64_t minl, int64_t maxl)
-{
- enum kcgi_err status;
-
- assert(pd->keys[field].name[0] != 0);
-
- if ((status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
- (status = khtml_printf(&pd->html, "%c%s: ",
- toupper(pd->keys[field].name[0]), pd->keys[field].name + 1)) != KCGI_OK ||
- (status = khtml_attrx(&pd->html, KELEM_INPUT,
- KATTR_NAME, KATTRX_STRING, pd->keys[field].name,
- KATTR_TYPE, KATTRX_STRING, type,
- KATTR_MINLENGTH, KATTRX_INT, minl,
- KATTR_MAXLENGTH, KATTRX_INT, maxl,
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
-
- return KCGI_OK;
-}
-
-enum kcgi_err
-pagelogin(struct pagedata *pd)
-{
- enum kcgi_err status;
- struct user *user;
- char *fuser, *fpass;
- enum LoginStatus ls;
- char *msg;
-
- user = NULL;
- ls = LoginUntried;
- if (pd->req.fieldmap[KeyUsername] && pd->req.fieldmap[KeyPassword]) {
- fuser = pd->req.fieldmap[KeyUsername]->parsed.s;
- fpass = pd->req.fieldmap[KeyPassword]->parsed.s;
-
- if (pd->req.fieldmap[KeyCreate]) {
- if (adduser(pd, fuser, fpass))
- ls = LoginError;
-
- }
- if (ls == LoginUntried) {
- ls = loginuser(&user, pd, fuser, fpass);
- if (ls == LoginValid) {
- status = khttp_head(&pd->req, kresps[KRESP_SET_COOKIE], "%s=%s; Path=/",
- pd->keys[KeyHash].name, user->hash);
- if (status != KCGI_OK)
- return status;
- }
- }
- } else {
- user = sitegetlogin(pd);
- }
-
- if (user)
- return redirect(pd, pages[PageIndex], "Logged in");
-
- if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK)
- return status;
-
- if ((status = htmlwithin(pd, KELEM_H1, "Login")) != KCGI_OK)
- return status;
-
- if (ls > 0) {
- if (ls == LoginInvalid)
- msg = "Error: Invalid credentials";
- else if (ls == LoginError)
- msg = "Error: System error";
- else
- msg = "Error: Undefined error";
-
- if ((status = htmlwithin(pd, KELEM_P, msg)) != KCGI_OK)
- return status;
- }
-
- if ((status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
- (status = htmlinput(pd, KeyUsername, "text", NameMin, NameMax)) != KCGI_OK ||
- (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
- (status = htmlinput(pd, KeyPassword, "password", PassMin, PassMax)) != KCGI_OK ||
- (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
- (status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
- (status = khtml_puts(&pd->html, "Create user ")) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "checkbox",
- KATTR_NAME, pd->keys[KeyCreate].name,
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK ||
- (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "submit",
- KATTR_VALUE, "Submit",
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
-
- return khtml_close(&pd->html);
-}
-
-enum kcgi_err
-pagelogout(struct pagedata *pd)
-{
- enum kcgi_err status;
- struct user *user;
-
- if ((user = sitegetlogin(pd))) {
- freeuser(user);
- if ((status = khttp_head(&pd->req, kresps[KRESP_SET_COOKIE],
- "%s=; Path=/", pd->keys[KeyHash].name)) != KCGI_OK)
- return status;
- }
-
- return redirect(pd, pages[PageIndex], "You are being logged out");
-}
-
-enum kcgi_err
-pageaccount(struct pagedata *pd)
-{
- enum kcgi_err status;
- struct user *user;
-
- user = sitegetlogin(pd);
- if (!user)
- return errorpage(pd, KHTTP_401);
-
- if (pd->req.fieldmap[KeyDelete] && strcmp(pd->req.fieldmap[KeyDelete]->parsed.s, "yes") == 0) {
- if (deleteuser(pd, user->hash) == 0)
- return redirect(pd, pages[PageIndex], "Account deleted");
- else
- return errorpage(pd, KHTTP_500);
- }
-
- if ((status = starthtmldoc(pd, KHTTP_200)) != KCGI_OK ||
- (status = htmlwithin(pd, KELEM_H1, "Account")) != KCGI_OK ||
- (status = htmlwithin(pd, KELEM_H2, "Delete account")) != KCGI_OK ||
- (status = htmlwithin(pd, KELEM_P, "Deleting an account is irreversible:"
- " no account data can be restored after deletion.")) != KCGI_OK ||
- (status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
- (status = khtml_elem(&pd->html, KELEM_LABEL)) != KCGI_OK ||
- (status = khtml_puts(&pd->html, "Confirm permanent deletion:")) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "checkbox",
- KATTR_NAME, pd->keys[KeyDelete].name,
- KATTR_VALUE, "yes",
- KATTR_REQUIRED, "true",
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK ||
- (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "submit",
- KATTR_VALUE, "Delete",
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK) {
- freeuser(user);
- return status;
- }
-
- freeuser(user);
- return khtml_close(&pd->html);
-}
-
enum sqlbox_code
newrow(struct pagedata *pd, char *hash)
{
@@ -660,16 +411,9 @@ main(void)
{
enum kcgi_err status;
struct kfcgi *fcgi;
- struct kvalid keys[] = {
- [KeyUsername] = { kvalid_name, "username" },
- [KeyPassword] = { kvalid_pass, "password" },
- [KeyHash] = { kvalid_stringne, "hash" },
- [KeyCreate] = { kvalid_stringne, "create" },
- [KeyDelete] = { kvalid_stringne, "delete" },
- [KeyTime] = { kvalid_stringne, "time" }
- };
struct pagedata pd = {
- .keys = keys
+ .keys = keys,
+ .pages = pages
};
struct sqlbox_cfg cfg;
struct sqlbox_src srcs[] = {