commit e9a907e110171e1e0d046423488298013b7636e6
parent 25431f2fde33ec1fc58d5c7bd27142356f4a1b03
Author: Jacob R. Edwards <jacob@jacobedwards.org>
Date: Tue, 5 Mar 2024 23:14:25 -0800
Move main page to pages directory
This required moving times database related functions to times.c.
Diffstat:
M | Makefile | | | 8 | +++++--- |
A | pages/main.c | | | 285 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | pages/pages.h | | | 1 | + |
M | timekeeper.c | | | 360 | +------------------------------------------------------------------------------ |
A | times.c | | | 104 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | times.h | | | 12 | ++++++++++++ |
6 files changed, 408 insertions(+), 362 deletions(-)
diff --git a/Makefile b/Makefile
@@ -4,9 +4,11 @@ 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
-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
+pages = pages/index.c pages/404.c pages/login.c pages/logout.c \
+ pages/account.c pages/main.c
+hdrsrc = page.c html.c user.c stmt.c key.c times.c
+src = ${hdrsrc} ${pages} pages/util.c
+hdr = ${hdrsrc:.c=.h} util.h pages/util.h pages/pages.h
obj = ${src:.c=.o}
all: ${name}
diff --git a/pages/main.c b/pages/main.c
@@ -0,0 +1,285 @@
+#include <assert.h>
+
+#define const
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <time.h>
+#include <err.h>
+#include <kcgi.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sqlbox.h>
+
+#include "common.h"
+#include "pages.h"
+
+#include "../times.h"
+#include "../util.h"
+
+#define DateTimeSize 25
+
+static char *datetime_fmt = "%FT%T+0000";
+
+enum kcgi_err
+printhours(struct pagedata *pd, time_t *times, int len)
+{
+ enum kcgi_err status;
+ time_t elapsed;
+ time_t hr, mn, sc;
+ struct tm *tm;
+ char datetime[13]; /* allows up to four digits for hours */
+ time_t to, from, breaktime;
+
+ assert(len > 0);
+ from = times[StartTime];
+ assert(from);
+ to = ((len - 1) >= EndTime) ? times[EndTime] : 0;
+ breaktime = 0;
+
+ if ((len - 1) == BreakStartTime && times[BreakStartTime])
+ to = times[BreakStartTime];
+ else if ((len - 1) >= BreakEndTime &&
+ times[BreakStartTime] && times[BreakEndTime])
+ breaktime = times[BreakEndTime] - times[BreakStartTime];
+
+ elapsed = ((to ? to : time(NULL)) - from) - breaktime;
+ kutil_info(&pd->req, NULL, "printhours: from %lld, to %lld, elapsed %lld, len %d",
+ from, to, elapsed, len);
+ assert(elapsed >= 0);
+
+ mn = elapsed / 60;
+ sc = elapsed % 60;
+ hr = mn / 60;
+ mn = mn % 60;
+
+ tm = gmtime(&from);
+ if (!tm)
+ err(1, "gmtime");
+
+ if (snprintf(datetime, sizeof(datetime), "%lldh%lldm%llds",
+ hr, mn, sc) >= (int)sizeof(datetime)) {
+ err(1, "snprintf (%lldh %lldm %llds)", hr, mn, sc);
+ }
+
+ if (!to)
+ status = khtml_attr(&pd->html, KELEM_TIME,
+ KATTR_ID, "counter", KATTR_DATETIME, datetime, KATTR__MAX);
+ else
+ status = khtml_attr(&pd->html, KELEM_TIME,
+ KATTR_DATETIME, datetime, KATTR__MAX);
+ if (status)
+ return status;
+
+ if ((status = khtml_printf(&pd->html, "%.2lld:%.2lld:%.2lld", hr, mn, sc)) != KCGI_OK)
+ return status;
+
+ if (!to) {
+ if ((status = khtml_elem(&pd->html, KELEM_NOSCRIPT)) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_A,
+ KATTR_HREF, pd->pages[PageMain], KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
+ (status = htmlwithin(pd, KELEM_BUTTON, "Reload")) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 2)) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_SCRIPT,
+ KATTR_SRC, "scripts/main.js", KATTR__MAX)) != KCGI_OK)
+ return status;
+ }
+
+ return khtml_closeelem(&pd->html, 1);
+}
+
+enum kcgi_err
+printtimebutton(struct pagedata *pd, enum TimeField tf)
+{
+ enum kcgi_err status;
+ static char *buttons[] = {
+ [StartTime] = "Start",
+ [BreakStartTime] = "Start Break",
+ [BreakEndTime] = "End Break",
+ [EndTime] = "Stop Time"
+ };
+
+ if ((status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "hidden",
+ KATTR_NAME, pd->keys[KeyTime].name,
+ KATTR_VALUE, timefields[tf],
+ KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_INPUT,
+ KATTR_TYPE, "submit",
+ KATTR_VALUE, buttons[tf], KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+ return KCGI_OK;
+}
+
+/* prints one row in times table */
+enum kcgi_err
+printtime(struct pagedata *pd, time_t *times, unsigned int len)
+{
+ unsigned int i;
+ struct tm *tm;
+ char datetime[DateTimeSize];
+ char date[11]; /* %F */
+ char time[6]; /* %R */
+ enum kcgi_err status;
+
+ if (len > 4)
+ return KCGI_SYSTEM;
+
+ if (len > 0) {
+ tm = gmtime(×[0]);
+ if (!tm)
+ return KCGI_SYSTEM;
+ if (strftime(date, sizeof(date), "%F", tm) >= sizeof(date))
+ return KCGI_SYSTEM;
+ } else {
+ date[0] = 0;
+ }
+
+ if ((status = khtml_attr(&pd->html, KELEM_TH,
+ KATTR_SCOPE, "row", KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_puts(&pd->html, date)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+
+ for (i = 0; i < 4; ++i) {
+ if (i >= len || !times[i]) {
+ if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK)
+ return status;
+
+ if (len == StartTime && i == StartTime)
+ status = printtimebutton(pd, StartTime);
+ else if (len == BreakStartTime && (i == BreakStartTime || i == EndTime))
+ status = printtimebutton(pd, i);
+ else if (len > BreakStartTime && ((times[BreakStartTime] && i == BreakEndTime) || i == EndTime))
+ status = printtimebutton(pd, i);
+ else
+ status = khtml_putc(&pd->html, '-');
+
+ if (status != KCGI_OK || (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+ continue;
+ }
+
+ if (i) {
+ tm = gmtime(×[i]);
+ if (!tm)
+ return KCGI_SYSTEM;
+ }
+
+ if (strftime(datetime, sizeof(datetime), datetime_fmt, tm) >= sizeof(datetime) ||
+ strftime(time, sizeof(time), "%R", tm) >= sizeof(time))
+ return KCGI_SYSTEM;
+
+ if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK ||
+ (status = khtml_attr(&pd->html, KELEM_TIME,
+ KATTR_DATETIME, datetime, KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_puts(&pd->html, time)) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 2)) != KCGI_OK)
+ return status;
+ }
+
+ if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK ||
+ (len && (status = printhours(pd, times, len)) != KCGI_OK) ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+ return KCGI_OK;
+}
+
+enum kcgi_err
+printtimes(struct pagedata *pd, time_t *times, size_t len)
+{
+ unsigned int i;
+ enum kcgi_err status;
+ char *headers[] = {
+ "Date", "Start time", "Break start", "Break end", "End time", "Hours"
+ };
+
+ if ((status = khtml_elem(&pd->html, KELEM_TABLE)) != KCGI_OK ||
+ (status = htmlwithin(pd, KELEM_CAPTION, "Timesheets")) != KCGI_OK)
+ return status;
+
+ if ((status = khtml_elem(&pd->html, KELEM_THEAD)) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_TR)) != KCGI_OK)
+ return status;
+
+ for (i = 0; i < Len(headers); ++i) {
+ if ((status = khtml_attr(&pd->html, KELEM_TH,
+ KATTR_SCOPE, "col", KATTR__MAX)) != KCGI_OK ||
+ (status = khtml_puts(&pd->html, headers[i])) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+ }
+
+ if ((status = khtml_closeelem(&pd->html, 2)) != KCGI_OK ||
+ (status = khtml_elem(&pd->html, KELEM_TBODY)) != KCGI_OK)
+ return status;
+
+ for (i = 0; i <= len; i += 4) {
+ if ((status = khtml_elem(&pd->html, KELEM_TR)) != KCGI_OK ||
+ (status = printtime(pd, ×[i], Min(len - i, 4))) != KCGI_OK ||
+ (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
+ return status;
+ }
+
+ return khtml_closeelem(&pd->html, 2);
+}
+
+int
+gettf(char *tfs)
+{
+ int i;
+
+ for (i = 0; i <= EndTime; ++i) {
+ if (strcmp(timefields[i], tfs) == 0)
+ return i;
+ }
+ return -1;
+}
+
+enum kcgi_err
+pagemain(struct pagedata *pd)
+{
+ enum kcgi_err status;
+ struct user *user;
+ enum TimeField tf;
+ time_t *times;
+ size_t ntimes;
+
+ user = sitegetlogin(pd);
+ if (!user)
+ return errorpage(pd, KHTTP_401);
+
+ if (pd->req.fieldmap[KeyTime]) {
+ tf = gettf(pd->req.fieldmap[KeyTime]->parsed.s);
+ if (tf < 0)
+ err(1, "Invalid time field");
+ if (tf == StartTime && newrow(pd, user->hash) != SQLBOX_CODE_OK)
+ err(1, "New time");
+ if (settime(pd, user->hash, tf, time(NULL)))
+ return KCGI_SYSTEM;
+ }
+
+ status = starthtmldoc(pd, KHTTP_200);
+ if (status != KCGI_OK)
+ return status;
+
+ times = gettimes(pd, user->hash, &ntimes);
+ if (!times)
+ err(1, "gettimes");
+
+ if ((status = htmlwithin(pd, KELEM_H1, "Timekeeper")) != KCGI_OK ||
+ (status = printtimes(pd, times, ntimes)) != KCGI_OK) {
+ free(times);
+ return status;
+ }
+
+ free(times);
+ freeuser(user);
+ return khtml_close(&pd->html);
+}
diff --git a/pages/pages.h b/pages/pages.h
@@ -13,3 +13,4 @@ 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);
+enum kcgi_err pagemain(struct pagedata *pd);
diff --git a/timekeeper.c b/timekeeper.c
@@ -8,10 +8,8 @@
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
#include <unistd.h>
+#include <string.h>
#include <kcgi.h>
#include <kcgihtml.h>
@@ -30,13 +28,6 @@
#define Promises "stdio rpath wpath cpath proc recvfd unix sendfd"
-enum TimeField {
- StartTime,
- BreakStartTime,
- BreakEndTime,
- EndTime
-};
-
static char *pages[] = {
[PageIndex] = "index",
[PageLogin] = "login",
@@ -46,13 +37,6 @@ static char *pages[] = {
[Page404] = "404"
};
-char *timefields[] = {
- [StartTime] = "start",
- [BreakStartTime] = "startbreak",
- [BreakEndTime] = "endbreak",
- [EndTime] = "end"
-};
-
int
initdb(struct pagedata *pd)
{
@@ -64,348 +48,6 @@ initdb(struct pagedata *pd)
return 0;
}
-enum sqlbox_code
-newrow(struct pagedata *pd, char *hash)
-{
- struct sqlbox_parm ps[] = {
- { .sparm = hash, .type = SQLBOX_PARM_STRING }
- };
-
- return sqlbox_exec(pd->db, pd->dbid, StmtNewTimeRow,
- Len(ps), ps, 0) != SQLBOX_CODE_OK;
-}
-
-enum sqlbox_code
-settime(struct pagedata *pd, char *hash, enum TimeField f, time_t time)
-{
- struct sqlbox_parm ps[] = {
- { .iparm = f, .type = SQLBOX_PARM_INT },
- { .iparm = time, .type = SQLBOX_PARM_INT },
- { .sparm = hash, .type = SQLBOX_PARM_STRING },
- { .iparm = f, .type = SQLBOX_PARM_INT }
- };
-
- if (!time)
- return SQLBOX_CODE_ERROR;
- return sqlbox_exec(pd->db, pd->dbid, StmtSetTime,
- Len(ps), ps, 0) != SQLBOX_CODE_OK;
-}
-
-time_t *
-gettimes(struct pagedata *pd, char *hash, size_t *relem)
-{
- size_t stmtid;
- struct sqlbox_parm p = {
- .sparm = hash, .type = SQLBOX_PARM_STRING
- };
- struct sqlbox_parmset *r;
- time_t *times;
- size_t i;
-
- assert(relem);
-
- stmtid = sqlbox_prepare_bind(pd->db, pd->dbid, StmtGetTimes, 1, &p, 0);
- if (stmtid == 0)
- err(1, "prepare bind");
-
- r = sqlbox_step(pd->db, stmtid);
- if (!r || r->code != SQLBOX_CODE_OK)
- err(1, "step");
-
- assert(r->psz == 1 && r->ps[0].type == SQLBOX_PARM_INT);
- assert(r->ps[0].iparm >= 0);
-
- *relem = r->ps[0].iparm;
- kutil_info(NULL,NULL,"Number of times: %zu", *relem);
- times = calloc(*relem, sizeof(*times));
- if (!times)
- err(1, "calloc");
-
- for (i = 0; i < *relem && (r = sqlbox_step(pd->db, stmtid)) &&
- !(!r->psz && r->code == SQLBOX_CODE_OK); ++i) {
- assert(r->psz == 1);
- assert(r->ps[0].type == SQLBOX_PARM_INT ||
- r->ps[0].type == SQLBOX_PARM_NULL);
- times[i] = (r->ps[0].type == SQLBOX_PARM_NULL) ? 0 : r->ps[0].iparm;
- kutil_info(NULL, NULL, "(%zu)time[%zu] = %lld", *relem, i, times[i]);
- }
- assert(r);
-
- while (*relem > 0 && !times[*relem - 1])
- --*relem;
-
- if (!sqlbox_finalise(pd->db, stmtid)) {
- free(times);
- err(1, "finalise");
- }
-
- return times;
-}
-
-#define DateTimeSize 25
-
-static char *datetime_fmt = "%FT%T+0000";
-
-enum kcgi_err
-printhours(struct pagedata *pd, time_t *times, int len)
-{
- enum kcgi_err status;
- time_t elapsed;
- time_t hr, mn, sc;
- struct tm *tm;
- char datetime[13]; /* allows up to four digits for hours */
- time_t to, from, breaktime;
-
- assert(len > 0);
- from = times[StartTime];
- assert(from);
- to = ((len - 1) >= EndTime) ? times[EndTime] : 0;
- breaktime = 0;
-
- if ((len - 1) == BreakStartTime && times[BreakStartTime])
- to = times[BreakStartTime];
- else if ((len - 1) >= BreakEndTime &&
- times[BreakStartTime] && times[BreakEndTime])
- breaktime = times[BreakEndTime] - times[BreakStartTime];
-
- elapsed = ((to ? to : time(NULL)) - from) - breaktime;
- kutil_info(&pd->req, NULL, "printhours: from %lld, to %lld, elapsed %lld, len %d",
- from, to, elapsed, len);
- assert(elapsed >= 0);
-
- mn = elapsed / 60;
- sc = elapsed % 60;
- hr = mn / 60;
- mn = mn % 60;
-
- tm = gmtime(&from);
- if (!tm)
- err(1, "gmtime");
-
- if (snprintf(datetime, sizeof(datetime), "%lldh%lldm%llds",
- hr, mn, sc) >= (int)sizeof(datetime)) {
- err(1, "snprintf (%lldh %lldm %llds)", hr, mn, sc);
- }
-
- if (!to)
- status = khtml_attr(&pd->html, KELEM_TIME,
- KATTR_ID, "counter", KATTR_DATETIME, datetime, KATTR__MAX);
- else
- status = khtml_attr(&pd->html, KELEM_TIME,
- KATTR_DATETIME, datetime, KATTR__MAX);
- if (status)
- return status;
-
- if ((status = khtml_printf(&pd->html, "%.2lld:%.2lld:%.2lld", hr, mn, sc)) != KCGI_OK)
- return status;
-
- if (!to) {
- if ((status = khtml_elem(&pd->html, KELEM_NOSCRIPT)) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_A,
- KATTR_HREF, pages[PageMain], KATTR__MAX)) != KCGI_OK ||
- (status = khtml_putc(&pd->html, ' ')) != KCGI_OK ||
- (status = htmlwithin(pd, KELEM_BUTTON, "Reload")) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 2)) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_SCRIPT,
- KATTR_SRC, "scripts/main.js", KATTR__MAX)) != KCGI_OK)
- return status;
- }
-
- return khtml_closeelem(&pd->html, 1);
-}
-
-enum kcgi_err
-printtimebutton(struct pagedata *pd, enum TimeField tf)
-{
- enum kcgi_err status;
- static char *buttons[] = {
- [StartTime] = "Start",
- [BreakStartTime] = "Start Break",
- [BreakEndTime] = "End Break",
- [EndTime] = "Stop Time"
- };
-
- if ((status = khtml_elem(&pd->html, KELEM_FORM)) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "hidden",
- KATTR_NAME, pd->keys[KeyTime].name,
- KATTR_VALUE, timefields[tf],
- KATTR__MAX)) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_INPUT,
- KATTR_TYPE, "submit",
- KATTR_VALUE, buttons[tf], KATTR__MAX)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
- return KCGI_OK;
-}
-
-/* prints one row in times table */
-enum kcgi_err
-printtime(struct pagedata *pd, time_t *times, unsigned int len)
-{
- unsigned int i;
- struct tm *tm;
- char datetime[DateTimeSize];
- char date[11]; /* %F */
- char time[6]; /* %R */
- enum kcgi_err status;
-
- if (len > 4)
- return KCGI_SYSTEM;
-
- if (len > 0) {
- tm = gmtime(×[0]);
- if (!tm)
- return KCGI_SYSTEM;
- if (strftime(date, sizeof(date), "%F", tm) >= sizeof(date))
- return KCGI_SYSTEM;
- } else {
- date[0] = 0;
- }
-
- if ((status = khtml_attr(&pd->html, KELEM_TH,
- KATTR_SCOPE, "row", KATTR__MAX)) != KCGI_OK ||
- (status = khtml_puts(&pd->html, date)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
-
- for (i = 0; i < 4; ++i) {
- if (i >= len || !times[i]) {
- if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK)
- return status;
-
- if (len == StartTime && i == StartTime)
- status = printtimebutton(pd, StartTime);
- else if (len == BreakStartTime && (i == BreakStartTime || i == EndTime))
- status = printtimebutton(pd, i);
- else if (len > BreakStartTime && ((times[BreakStartTime] && i == BreakEndTime) || i == EndTime))
- status = printtimebutton(pd, i);
- else
- status = khtml_putc(&pd->html, '-');
-
- if (status != KCGI_OK || (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
- continue;
- }
-
- if (i) {
- tm = gmtime(×[i]);
- if (!tm)
- return KCGI_SYSTEM;
- }
-
- if (strftime(datetime, sizeof(datetime), datetime_fmt, tm) >= sizeof(datetime) ||
- strftime(time, sizeof(time), "%R", tm) >= sizeof(time))
- return KCGI_SYSTEM;
-
- if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK ||
- (status = khtml_attr(&pd->html, KELEM_TIME,
- KATTR_DATETIME, datetime, KATTR__MAX)) != KCGI_OK ||
- (status = khtml_puts(&pd->html, time)) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 2)) != KCGI_OK)
- return status;
- }
-
- if ((status = khtml_elem(&pd->html, KELEM_TH)) != KCGI_OK ||
- (len && (status = printhours(pd, times, len)) != KCGI_OK) ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
- return KCGI_OK;
-}
-
-enum kcgi_err
-printtimes(struct pagedata *pd, time_t *times, size_t len)
-{
- unsigned int i;
- enum kcgi_err status;
- char *headers[] = {
- "Date", "Start time", "Break start", "Break end", "End time", "Hours"
- };
-
- if ((status = khtml_elem(&pd->html, KELEM_TABLE)) != KCGI_OK ||
- (status = htmlwithin(pd, KELEM_CAPTION, "Timesheets")) != KCGI_OK)
- return status;
-
- if ((status = khtml_elem(&pd->html, KELEM_THEAD)) != KCGI_OK ||
- (status = khtml_elem(&pd->html, KELEM_TR)) != KCGI_OK)
- return status;
-
- for (i = 0; i < Len(headers); ++i) {
- if ((status = khtml_attr(&pd->html, KELEM_TH,
- KATTR_SCOPE, "col", KATTR__MAX)) != KCGI_OK ||
- (status = khtml_puts(&pd->html, headers[i])) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
- }
-
- if ((status = khtml_closeelem(&pd->html, 2)) != KCGI_OK ||
- (status = khtml_elem(&pd->html, KELEM_TBODY)) != KCGI_OK)
- return status;
-
- for (i = 0; i <= len; i += 4) {
- if ((status = khtml_elem(&pd->html, KELEM_TR)) != KCGI_OK ||
- (status = printtime(pd, ×[i], Min(len - i, 4))) != KCGI_OK ||
- (status = khtml_closeelem(&pd->html, 1)) != KCGI_OK)
- return status;
- }
-
- return khtml_closeelem(&pd->html, 2);
-}
-
-int
-gettf(char *tfs)
-{
- int i;
-
- for (i = 0; i < (int)Len(timefields); ++i) {
- if (strcmp(timefields[i], tfs) == 0)
- return i;
- }
- return -1;
-}
-
-enum kcgi_err
-pagemain(struct pagedata *pd)
-{
- enum kcgi_err status;
- struct user *user;
- enum TimeField tf;
- time_t *times;
- size_t ntimes;
-
- user = sitegetlogin(pd);
- if (!user)
- return errorpage(pd, KHTTP_401);
-
- if (pd->req.fieldmap[KeyTime]) {
- tf = gettf(pd->req.fieldmap[KeyTime]->parsed.s);
- if (tf < 0)
- err(1, "Invalid time field");
- if (tf == StartTime && newrow(pd, user->hash) != SQLBOX_CODE_OK)
- err(1, "New time");
- if (settime(pd, user->hash, tf, time(NULL)))
- return KCGI_SYSTEM;
- }
-
- status = starthtmldoc(pd, KHTTP_200);
- if (status != KCGI_OK)
- return status;
-
- times = gettimes(pd, user->hash, &ntimes);
- if (!times)
- err(1, "gettimes");
-
- if ((status = htmlwithin(pd, KELEM_H1, "Timekeeper")) != KCGI_OK ||
- (status = printtimes(pd, times, ntimes)) != KCGI_OK) {
- free(times);
- return status;
- }
-
- free(times);
- freeuser(user);
- return khtml_close(&pd->html);
-}
-
int
main(void)
{
diff --git a/times.c b/times.c
@@ -0,0 +1,104 @@
+#include <assert.h>
+
+#define const
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <err.h>
+
+#include <kcgi.h>
+#include <kcgihtml.h>
+#include <sqlbox.h>
+
+#include "util.h"
+#include "stmt.h"
+#include "page.h"
+
+#include "times.h"
+
+char *timefields[] = {
+ [StartTime] = "start",
+ [BreakStartTime] = "startbreak",
+ [BreakEndTime] = "endbreak",
+ [EndTime] = "end"
+};
+
+enum sqlbox_code
+newrow(struct pagedata *pd, char *hash)
+{
+ struct sqlbox_parm ps[] = {
+ { .sparm = hash, .type = SQLBOX_PARM_STRING }
+ };
+
+ return sqlbox_exec(pd->db, pd->dbid, StmtNewTimeRow,
+ Len(ps), ps, 0) != SQLBOX_CODE_OK;
+}
+
+enum sqlbox_code
+settime(struct pagedata *pd, char *hash, enum TimeField f, time_t time)
+{
+ struct sqlbox_parm ps[] = {
+ { .iparm = f, .type = SQLBOX_PARM_INT },
+ { .iparm = time, .type = SQLBOX_PARM_INT },
+ { .sparm = hash, .type = SQLBOX_PARM_STRING },
+ { .iparm = f, .type = SQLBOX_PARM_INT }
+ };
+
+ if (!time)
+ return SQLBOX_CODE_ERROR;
+ return sqlbox_exec(pd->db, pd->dbid, StmtSetTime,
+ Len(ps), ps, 0) != SQLBOX_CODE_OK;
+}
+
+time_t *
+gettimes(struct pagedata *pd, char *hash, size_t *relem)
+{
+ size_t stmtid;
+ struct sqlbox_parm p = {
+ .sparm = hash, .type = SQLBOX_PARM_STRING
+ };
+ struct sqlbox_parmset *r;
+ time_t *times;
+ size_t i;
+
+ assert(relem);
+
+ stmtid = sqlbox_prepare_bind(pd->db, pd->dbid, StmtGetTimes, 1, &p, 0);
+ if (stmtid == 0)
+ err(1, "prepare bind");
+
+ r = sqlbox_step(pd->db, stmtid);
+ if (!r || r->code != SQLBOX_CODE_OK)
+ err(1, "step");
+
+ assert(r->psz == 1 && r->ps[0].type == SQLBOX_PARM_INT);
+ assert(r->ps[0].iparm >= 0);
+
+ *relem = r->ps[0].iparm;
+ kutil_info(NULL,NULL,"Number of times: %zu", *relem);
+ times = calloc(*relem, sizeof(*times));
+ if (!times)
+ err(1, "calloc");
+
+ for (i = 0; i < *relem && (r = sqlbox_step(pd->db, stmtid)) &&
+ !(!r->psz && r->code == SQLBOX_CODE_OK); ++i) {
+ assert(r->psz == 1);
+ assert(r->ps[0].type == SQLBOX_PARM_INT ||
+ r->ps[0].type == SQLBOX_PARM_NULL);
+ times[i] = (r->ps[0].type == SQLBOX_PARM_NULL) ? 0 : r->ps[0].iparm;
+ kutil_info(NULL, NULL, "(%zu)time[%zu] = %lld", *relem, i, times[i]);
+ }
+ assert(r);
+
+ while (*relem > 0 && !times[*relem - 1])
+ --*relem;
+
+ if (!sqlbox_finalise(pd->db, stmtid)) {
+ free(times);
+ err(1, "finalise");
+ }
+
+ return times;
+}
diff --git a/times.h b/times.h
@@ -0,0 +1,12 @@
+enum TimeField {
+ StartTime,
+ BreakStartTime,
+ BreakEndTime,
+ EndTime
+};
+
+extern char *timefields[];
+
+enum sqlbox_code newrow(struct pagedata *pd, char *hash);
+enum sqlbox_code settime(struct pagedata *pd, char *hash, enum TimeField f, time_t time);
+time_t *gettimes(struct pagedata *pd, char *hash, size_t *relem);