timekeeper

[Abandoned unfinished] CGI web application in C for time tracking. (My first, just a learning project)
Log | Files | Refs | README

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:
MMakefile | 8+++++---
Apages/main.c | 285+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpages/pages.h | 1+
Mtimekeeper.c | 360+------------------------------------------------------------------------------
Atimes.c | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atimes.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(&times[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(&times[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, &times[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(&times[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(&times[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, &times[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);