commit - cf0bb562ad08cfea921890bb5060eb1cd68f8023
commit + efaf222244a17c530f5e2150244957d925fbb58a
blob - f4933bb712cfd193bb25477a22f5c3cba52a632e
blob + 5f8e39bd2ff40baa5509001d77ceec8213ab29de
--- compat.h
+++ compat.h
#include <stdint.h>
#include <stdlib.h>
+#include <string.h>
#include <errno.h>
#ifdef __OpenBSD__
#endif
/*
+ * strsep - BSD extension to tokenize strings.
+ * POSIX only provides strtok, which is not thread-safe and less flexible.
+ */
+#if !defined(__OpenBSD__) && !defined(__APPLE__) && !defined(__GLIBC__)
+static inline char *
+strsep(char **stringp, const char *delim)
+{
+ char *s;
+ const char *spanp;
+ int c, sc;
+ char *tok;
+
+ if ((s = *stringp) == NULL)
+ return NULL;
+
+ for (tok = s;;) {
+ c = *s++;
+ spanp = delim;
+ do {
+ if ((sc = *spanp++) == c) {
+ if (c == 0)
+ s = NULL;
+ else
+ s[-1] = 0;
+ *stringp = s;
+ return tok;
+ }
+ } while (sc != 0);
+ }
+}
+#endif
+
+/*
* reallocarray - safe realloc with overflow checking.
* Available in OpenBSD and glibc >= 2.26; provide a shim elsewhere
* (including macOS/Apple libc which does not provide it).
blob - 1e5f76636cf06ad3a70eead626f995e49af9bbde
blob + ed56e216636e3503d6e9ba1f5b58bb02d5f98a0f
--- db.c
+++ db.c
, '\0'
};
-static const char sql_select_day_bookings[] = {
-#embed "sql/select_day_bookings.sql"
- , '\0'
-};
-
-static const char sql_select_available_desks[] = {
-#embed "sql/select_available_desks.sql"
- , '\0'
-};
-
static const char sql_select_desks_with_bookings[] = {
#embed "sql/select_desks_with_bookings.sql"
, '\0'
}
/*
- * Append a desk name to a dynamically growing desk list.
- * The list doubles in capacity when full, starting at 8 entries.
- * The name is copied via strdup. Returns 0 on success, -1 if
- * any allocation fails.
- */
-static int
-desk_list_add(struct desk_list *dl, const char *desk)
-{
- if (dl->count >= dl->cap) {
- const int newcap = dl->cap == 0 ? 8 : dl->cap * 2;
- char **new_items = reallocarray(dl->items, newcap,
- sizeof(char *));
- if (new_items == NULL)
- return -1;
- dl->items = new_items;
- dl->cap = newcap;
- }
-
- dl->items[dl->count] = strdup(desk);
- if (dl->items[dl->count] == NULL)
- return -1;
-
- dl->count++;
- return 0;
-}
-
-/*
- * Free all memory owned by a desk list and reset its fields
- * to zero so it can be safely reused or ignored.
- */
-void
-desk_list_free(struct desk_list *dl)
-{
- for (int i = 0; i < dl->count; i++)
- free(dl->items[i]);
- free(dl->items);
- dl->items = NULL;
- dl->count = 0;
- dl->cap = 0;
-}
-
-/*
* Query future bookings for a given user, populating bl with the
* results ordered by date. The caller must call booking_list_free
* when done. Returns 0 on success, -1 on failure.
}
/*
- * Query all bookings for a given day, populating bl with results
- * ordered by desk name using natural sort collation. The caller
- * must call booking_list_free when done. Returns 0 on success,
- * -1 on failure.
- */
-int
-db_query_day_bookings(sqlite3 *db, const char *day, struct booking_list *bl)
-{
- sqlite3_stmt *stmt;
- int rc;
-
- memset(bl, 0, sizeof(*bl));
-
- if (sqlite3_prepare_v2(db, sql_select_day_bookings, -1, &stmt,
- NULL) != SQLITE_OK) {
- fprintf(stderr, "prepare select day bookings: %s\n",
- sqlite3_errmsg(db));
- return -1;
- }
-
- sqlite3_bind_text(stmt, 1, day, -1, SQLITE_STATIC);
-
- while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
- if (booking_list_add(bl,
- (const char *)sqlite3_column_text(stmt, 0),
- (const char *)sqlite3_column_text(stmt, 1),
- (const char *)sqlite3_column_text(stmt, 2)) != 0) {
- sqlite3_finalize(stmt);
- booking_list_free(bl);
- return -1;
- }
- }
-
- sqlite3_finalize(stmt);
- if (rc != SQLITE_DONE) {
- fprintf(stderr, "step select day bookings: %s\n",
- sqlite3_errmsg(db));
- booking_list_free(bl);
- return -1;
- }
-
- return 0;
-}
-
-/*
- * Query desks that have no booking on the given day, populating
- * dl with results ordered by desk name using natural sort
- * collation. The caller must call desk_list_free when done.
- * Returns 0 on success, -1 on failure.
- */
-int
-db_query_available_desks(sqlite3 *db, const char *day, struct desk_list *dl)
-{
- sqlite3_stmt *stmt;
- int rc;
-
- memset(dl, 0, sizeof(*dl));
-
- if (sqlite3_prepare_v2(db, sql_select_available_desks, -1, &stmt,
- NULL) != SQLITE_OK) {
- fprintf(stderr, "prepare select available desks: %s\n",
- sqlite3_errmsg(db));
- return -1;
- }
-
- sqlite3_bind_text(stmt, 1, day, -1, SQLITE_STATIC);
-
- while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
- if (desk_list_add(dl,
- (const char *)sqlite3_column_text(stmt, 0)) != 0) {
- sqlite3_finalize(stmt);
- desk_list_free(dl);
- return -1;
- }
- }
-
- sqlite3_finalize(stmt);
- if (rc != SQLITE_DONE) {
- fprintf(stderr, "step select available desks: %s\n",
- sqlite3_errmsg(db));
- desk_list_free(dl);
- return -1;
- }
-
- return 0;
-}
-
-/*
* Query all desks with their booking status for a given day.
* Each desk appears exactly once: booked desks have a non-empty
* user field, available desks have an empty user string. Results
blob - 64e97bac5c093cc1a50f1b48b114f6b4267d24d3
blob + 8cbca1a3ba79fb9e3b2e32f80fd7f3c596f9e256
--- db.h
+++ db.h
/* Cleanup. */
void booking_list_free(struct booking_list *);
-void desk_list_free(struct desk_list *);
#endif /* DESKD_DB_H */
blob - cecae24048e49100c93e09b9902341ffe235d6d1 (mode 644)
blob + /dev/null
--- sql/select_available_desks.sql
+++ /dev/null
-SELECT desk FROM desks WHERE desk NOT IN (SELECT desk FROM bookings WHERE day = ?) ORDER BY desk COLLATE "natural"
\ No newline at end of file
blob - 9bb4a56f8f7cf57baa21bf12a3c040ea42ca732a (mode 644)
blob + /dev/null
--- sql/select_day_bookings.sql
+++ /dev/null
-SELECT user, desk, day FROM bookings WHERE day = ? ORDER BY desk COLLATE "natural"
\ No newline at end of file