changeset 77:fe78b16c694d

pasterd: refactor json utilities
author David Demelier <markand@malikania.fr>
date Thu, 16 Mar 2023 15:05:26 +0100
parents 9643962908ab
children 9bfe5ce3cc45
files GNUmakefile html/new.html html/search.html json-util.c json-util.h page-index.c page-new.c page-paste.c page-search.c page-static.c themes/default/style.css util.c util.h
diffstat 13 files changed, 230 insertions(+), 100 deletions(-) [+]
line wrap: on
line diff
--- a/GNUmakefile	Thu Mar 16 13:35:17 2023 +0100
+++ b/GNUmakefile	Thu Mar 16 15:05:26 2023 +0100
@@ -48,6 +48,7 @@
 LIBPASTER_SRCS +=       config.c
 LIBPASTER_SRCS +=       database.c
 LIBPASTER_SRCS +=       http.c
+LIBPASTER_SRCS +=       json-util.c
 LIBPASTER_SRCS +=       log.c
 LIBPASTER_SRCS +=       page-download.c
 LIBPASTER_SRCS +=       page-fork.c
@@ -116,9 +117,9 @@
 pasterd: $(LIBPASTER)
 
 clean:
-	rm -f extern/bcc/bcc
+	rm -f extern/bcc/bcc extern/bcc/bcc.d
 	rm -f $(LIBPASTER) $(LIBPASTER_OBJS) $(LIBPASTER_DEPS) $(LIBPASTER_HTML_OBJS)
-	rm -f paster pasterd
+	rm -f paster pasterd pasterd.d
 	rm -f test.db $(TESTS_OBJS)
 
 install-paster:
--- a/html/new.html	Thu Mar 16 13:35:17 2023 +0100
+++ b/html/new.html	Thu Mar 16 15:05:26 2023 +0100
@@ -41,6 +41,5 @@
 				</table>
 
 				<textarea id="code" class="textarea" placeholder="What do you want to share?" rows="10" name="code">{{code}}</textarea>
-				<br>
 				<input class="submit" type="submit" value="paste" />
 			</form>
--- a/html/search.html	Thu Mar 16 13:35:17 2023 +0100
+++ b/html/search.html	Thu Mar 16 15:05:26 2023 +0100
@@ -16,7 +16,9 @@
 				<td>Language</td>
 				<td>
 					<select name="language">
-						<option name="{{value}}">{{value}}</option>
+						{{#languages}}
+						<option name="{{name}}">{{name}}</option>
+						{{/languages}}
 					</select>
 				</td>
 			</tr>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json-util.c	Thu Mar 16 15:05:26 2023 +0100
@@ -0,0 +1,75 @@
+/*
+ * json-util.c -- utilities for JSON
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include "json-util.h"
+#include "util.h"
+#include "paste.h"
+
+json_t *
+ju_languages(const char *selected)
+{
+	json_t *array, *obj;
+
+	array = json_array();
+
+	for (size_t i = 0; i < languagesz; ++i) {
+		if (selected && strcmp(languages[i], selected) == 0)
+			obj = json_pack("{ss ss}",
+				"name",         languages[i],
+				"selected",     "selected"
+			);
+		else
+			obj = json_pack("{ss}", "name", languages[i]);
+
+		json_array_append_new(array, obj);
+	}
+
+	return array;
+}
+
+json_t *
+ju_durations(void)
+{
+	json_t *array = json_array();
+
+	for (size_t i = 0; i < durationsz; ++i)
+		json_array_append_new(array, json_pack("{ss}",
+		    "value", durations[i].title)
+		);
+
+	return array;
+}
+
+json_t *
+ju_date(const struct paste *paste)
+{
+	assert(paste);
+
+	return json_string(bstrftime("%c", localtime(&paste->timestamp)));
+}
+
+json_t *
+ju_expiration(const struct paste *paste)
+{
+	assert(paste);
+
+	return json_string(ttl(paste->timestamp, paste->duration));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/json-util.h	Thu Mar 16 15:05:26 2023 +0100
@@ -0,0 +1,98 @@
+/*
+ * json-util.h -- utilities for JSON
+ *
+ * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef PASTER_PASTER_JSON_UTIL_H
+#define PASTER_PASTER_JSON_UTIL_H
+
+#include <jansson.h>
+
+struct paste;
+
+/**
+ * Create an array of all possible languages supported by the application. If
+ * the selected argument is not null it will also add a JSON property selected
+ * (mostly used when rendering an existing paste).
+ *
+ * Example of generated schema:
+ *
+ * ```javascript
+ * [
+ *   {
+ *     "name": "nohighlight"
+ *   }
+ *   {
+ *     "name": "c",
+ *     "selected": "selected"
+ *   }
+ *   {
+ *     "name": "cpp"
+ *   }
+ * ]
+ * ```
+ *
+ * \param selected the current selected language (or NULL if none)
+ * \return a JSON array of objects
+ */
+json_t *
+ju_languages(const char *selected);
+
+/**
+ * Create a list of duration in the form:
+ *
+ * ```javascript
+ * [
+ *   {
+ *     "value": "day"
+ *   }
+ *   {
+ *     "value": "hour"
+ *   }
+ * ]
+ * ```
+ *
+ * \return a JSON array of objects
+ */
+json_t *
+ju_durations(void);
+
+/**
+ * Create a convenient ISO date string containing the paste creation date.
+ *
+ * \pre paste != NULL
+ * \param paste this paste
+ * \return a string with an ISO date
+ */
+json_t *
+ju_date(const struct paste *paste);
+
+/**
+ * Create a convenient remaining time for the given paste.
+ *
+ * Returns strings in the form:
+ *
+ * - `2 day(s)`
+ * - `3 hours(s)`
+ *
+ * \pre paste != NULL
+ * \param paste this paste
+ * \return a string containing the expiration time
+ */
+json_t *
+ju_expiration(const struct paste *paste);
+
+#endif /* !PASTER_PASTER_JSON_UTIL_H */
--- a/page-index.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/page-index.c	Thu Mar 16 15:05:26 2023 +0100
@@ -19,6 +19,7 @@
 #include <assert.h>
 
 #include "database.h"
+#include "json-util.h"
 #include "page-index.h"
 #include "page.h"
 #include "paste.h"
@@ -43,18 +44,6 @@
 }
 
 static inline json_t *
-create_date(const struct paste *paste)
-{
-	return json_string(bstrftime("%c", localtime(&paste->timestamp)));
-}
-
-static inline json_t *
-create_expiration(const struct paste *paste)
-{
-	return json_string(ttl(paste->timestamp, paste->duration));
-}
-
-static inline json_t *
 create_pastes(const struct paste *pastes, size_t pastesz)
 {
 	json_t *array = json_array();
@@ -67,8 +56,8 @@
 			"id",           paste->id,
 			"author",       paste->author,
 			"title",        paste->title,
-			"date",         create_date(paste),
-			"expiration",   create_expiration(paste)
+			"date",         ju_date(paste),
+			"expiration",   ju_expiration(paste)
 		));
 	}
 
--- a/page-new.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/page-new.c	Thu Mar 16 15:05:26 2023 +0100
@@ -20,23 +20,14 @@
 #include <string.h>
 
 #include "database.h"
-#include "paste.h"
+#include "json-util.h"
 #include "page-new.h"
 #include "page.h"
+#include "paste.h"
 #include "util.h"
 
 #include "html/new.h"
 
-static const struct {
-	const char *title;
-	long long int secs;
-} durations[] = {
-	{ "day",        PASTE_DURATION_DAY      },
-	{ "hour",       PASTE_DURATION_HOUR     },
-	{ "week",       PASTE_DURATION_WEEK     },
-	{ "month",      PASTE_DURATION_MONTH    },
-};
-
 static const struct paste paste_default = {
 	.id = "",
 	.title = "unknown",
@@ -48,7 +39,7 @@
 static long long int
 duration(const char *val)
 {
-	for (size_t i = 0; i < NELEM(durations); ++i)
+	for (size_t i = 0; i < durationsz; ++i)
 		if (strcmp(val, durations[i].title) == 0)
 			return durations[i].secs;
 
@@ -119,39 +110,6 @@
 	paste_finish(&paste);
 }
 
-static json_t *
-create_languages(const struct paste *paste)
-{
-	json_t *array, *obj;
-
-	array = json_array();
-
-	for (size_t i = 0; i < languagesz; ++i) {
-		if (strcmp(languages[i], paste->language) == 0)
-			obj = json_pack("{ss ss}",
-				"name",         languages[i],
-				"selected",     "selected"
-			);
-		else
-			obj = json_pack("{ss}", "name", languages[i]);
-
-		json_array_append_new(array, obj);
-	}
-
-	return array;
-}
-
-static inline json_t *
-create_durations(void)
-{
-	json_t *array = json_array();
-
-	for (size_t i = 0; i < NELEM(durations); ++i)
-		json_array_append_new(array, json_pack("{ss}", "value", durations[i].title));
-
-	return array;
-}
-
 void
 page_new_render(struct kreq *req, const struct paste *paste)
 {
@@ -163,8 +121,8 @@
 	page(req, KHTTP_200, html_new, json_pack("{ss ss so so ss}",
 		"pagetitle",    "paster -- create new paste",
 		"title",        paste->title,
-		"languages",    create_languages(paste),
-		"durations",    create_durations(),
+		"languages",    ju_languages(paste->language),
+		"durations",    ju_durations(),
 		"code",         paste->code
 	));
 }
--- a/page-paste.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/page-paste.c	Thu Mar 16 15:05:26 2023 +0100
@@ -19,26 +19,14 @@
 #include <assert.h>
 
 #include "database.h"
-#include "paste.h"
+#include "json-util.h"
 #include "page-paste.h"
 #include "page.h"
+#include "paste.h"
 #include "util.h"
 
 #include "html/paste.h"
 
-// TODO: share this.
-static inline json_t *
-create_date(const struct paste *paste)
-{
-	return json_string(bstrftime("%c", localtime(&paste->timestamp)));
-}
-
-static inline json_t *
-create_expiration(const struct paste *paste)
-{
-	return json_string(ttl(paste->timestamp, paste->duration));
-}
-
 static inline json_t *
 create_pagetitle(const struct paste *paste)
 {
@@ -56,8 +44,8 @@
 		"language",     paste->language,
 		"code",         paste->code,
 		"public",       paste->visible ? "Yes" : "No",
-		"date",         create_date(paste),
-		"expiration",   create_expiration(paste)
+		"date",         ju_date(paste),
+		"expiration",   ju_expiration(paste)
 	);
 }
 
--- a/page-search.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/page-search.c	Thu Mar 16 15:05:26 2023 +0100
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include "database.h"
+#include "json-util.h"
 #include "page-index.h"
 #include "page-search.h"
 #include "page.h"
@@ -29,22 +30,11 @@
 #include "html/search.h"
 
 static inline json_t *
-create_languages(void)
-{
-	json_t *array = json_array();
-
-	for (size_t i = 0; i < languagesz; ++i)
-		json_array_append_new(array, json_pack("{ss}", "value", languages[i]));
-
-	return array;
-}
-
-static inline json_t *
 create_root(void)
 {
 	return json_pack("{ss so}",
 		"pagetitle",    "paster -- search",
-		"languages",    create_languages()
+		"languages",    ju_languages(NULL)
 	);
 }
 
--- a/page-static.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/page-static.c	Thu Mar 16 15:05:26 2023 +0100
@@ -27,6 +27,18 @@
 #include "config.h"
 #include "page.h"
 
+static inline enum kmime
+mimetype(const struct kreq *req)
+{
+	switch (req->mime) {
+	case KMIME_TEXT_HTML:
+	case KMIME_TEXT_CSS:
+		return req->mime;
+	default:
+		return KMIME_APP_OCTET_STREAM;
+	}
+}
+
 static void
 get(struct kreq *req)
 {
@@ -40,7 +52,7 @@
 		page_status(req, KHTTP_404);
 	else {
 		khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[KHTTP_200]);
-		khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[req->mime]);
+		khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[mimetype(req)]);
 		khttp_head(req, kresps[KRESP_CONTENT_LENGTH],
 		    "%llu", (unsigned long long)(st.st_size));
 		khttp_body(req);
--- a/themes/default/style.css	Thu Mar 16 13:35:17 2023 +0100
+++ b/themes/default/style.css	Thu Mar 16 15:05:26 2023 +0100
@@ -111,6 +111,7 @@
 	border: 0;
 	border-radius: 2px;
 	padding: 0.5em;
+	margin-top: 1em;
 	font-size: medium;
 }
 
--- a/util.c	Thu Mar 16 13:35:17 2023 +0100
+++ b/util.c	Thu Mar 16 15:05:26 2023 +0100
@@ -2,11 +2,11 @@
  * util.c -- various utilities
  *
  * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- * 
+ *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
@@ -30,7 +30,7 @@
 #include "util.h"
 #include "paste.h"
 
-const char *languages[] = {
+const char * const languages[] = {
 	"nohighlight",
 	"1c",
 	"abnf",
@@ -212,6 +212,15 @@
 
 const size_t languagesz = NELEM(languages);
 
+const struct duration durations[] = {
+	{ "day",        PASTE_DURATION_DAY      },
+	{ "hour",       PASTE_DURATION_HOUR     },
+	{ "week",       PASTE_DURATION_WEEK     },
+	{ "month",      PASTE_DURATION_MONTH    }
+};
+
+const size_t durationsz = NELEM(durations);
+
 void
 die(const char *fmt, ...)
 {
--- a/util.h	Thu Mar 16 13:35:17 2023 +0100
+++ b/util.h	Thu Mar 16 15:05:26 2023 +0100
@@ -2,11 +2,11 @@
  * util.h -- various utilities
  *
  * Copyright (c) 2020-2023 David Demelier <markand@malikania.fr>
- * 
+ *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
+ *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
@@ -27,9 +27,17 @@
 struct tm;
 struct kreq;
 
-extern const char *languages[];
+struct duration {
+	const char *title;
+	long long int secs;
+};
+
+extern const char * const languages[];
 extern const size_t languagesz;
 
+extern const struct duration durations[];
+extern const size_t durationsz;
+
 void
 die(const char *, ...);