summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cgit.mk1
-rw-r--r--cmd.c7
-rw-r--r--ui-shared.c10
-rw-r--r--ui-shared.h3
-rw-r--r--ui-wiki.c213
-rw-r--r--ui-wiki.h6
6 files changed, 240 insertions, 0 deletions
diff --git a/cgit.mk b/cgit.mk
index 3fcc1ca..6c9e610 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -94,6 +94,7 @@ CGIT_OBJ_NAMES += ui-stats.o
CGIT_OBJ_NAMES += ui-summary.o
CGIT_OBJ_NAMES += ui-tag.o
CGIT_OBJ_NAMES += ui-tree.o
+CGIT_OBJ_NAMES += ui-wiki.o
CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
diff --git a/cmd.c b/cmd.c
index 3342843..64a00f6 100644
--- a/cmd.c
+++ b/cmd.c
@@ -26,6 +26,7 @@
#include "ui-summary.h"
#include "ui-tag.h"
#include "ui-tree.h"
+#include "ui-wiki.h"
static void HEAD_fn(void)
{
@@ -164,6 +165,11 @@ static void tree_fn(void)
cgit_print_tree(ctx.qry.oid, ctx.qry.path);
}
+static void wiki_fn(void)
+{
+ cgit_print_wiki(ctx.qry.oid);
+}
+
#define def_cmd(name, want_repo, want_vpath, is_clone) \
{#name, name##_fn, want_repo, want_vpath, is_clone}
@@ -191,6 +197,7 @@ struct cgit_cmd *cgit_get_cmd(void)
def_cmd(summary, 1, 0, 0),
def_cmd(tag, 1, 0, 0),
def_cmd(tree, 1, 1, 0),
+ def_cmd(wiki, 1, 0, 0),
};
int i;
diff --git a/ui-shared.c b/ui-shared.c
index 93ab93c..d3cf24b 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -10,6 +10,7 @@
#include "cgit.h"
#include "ui-shared.h"
+#include "ui-blob.h"
#include "cmd.h"
#include "html.h"
#include "version.h"
@@ -348,6 +349,12 @@ void cgit_tree_link(const char *name, const char *title, const char *class,
reporevlink("tree", name, title, class, head, rev, path);
}
+void cgit_wiki_link(const char *name, const char *title, const char *class,
+ const char *head, const char *rev, const char *path)
+{
+ reporevlink("wiki", name, title, class, head, rev, path);
+}
+
void cgit_plain_link(const char *name, const char *title, const char *class,
const char *head, const char *rev, const char *path)
{
@@ -1099,6 +1106,9 @@ void cgit_print_pageheader(void)
else
cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
ctx.qry.oid, ctx.qry.vpath);
+ if (cgit_ref_path_exists(".wiki", ctx.qry.head, 0))
+ cgit_wiki_link("wiki", NULL, hc("wiki"), ctx.qry.head,
+ ctx.qry.oid, NULL);
cgit_commit_link("commit", NULL, hc("commit"),
ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
diff --git a/ui-shared.h b/ui-shared.h
index f12fa99..65d8f75 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -24,6 +24,9 @@ extern void cgit_tag_link(const char *name, const char *title,
extern void cgit_tree_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
+extern void cgit_wiki_link(const char *name, const char *title,
+ const char *class, const char *head,
+ const char *rev, const char *path);
extern void cgit_plain_link(const char *name, const char *title,
const char *class, const char *head,
const char *rev, const char *path);
diff --git a/ui-wiki.c b/ui-wiki.c
new file mode 100644
index 0000000..9fdb4b9
--- /dev/null
+++ b/ui-wiki.c
@@ -0,0 +1,213 @@
+/* ui-wiki.c: functions for wiki output
+ *
+ * Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
+ * Copyright (C) 2025 Freya Murphy <freya@freyacat.org>
+ *
+ * Licensed under GNU General Public License v2
+ * (see COPYING for full license text)
+ */
+
+ #define USE_THE_REPOSITORY_VARIABLE
+
+#include "cgit.h"
+#include "ui-wiki.h"
+#include "html.h"
+#include "ui-shared.h"
+
+ #define WIKI_PATH ".wiki"
+
+struct walk_wiki_context {
+ char *curr_rev;
+ int state;
+ int index;
+};
+
+static void print_page(const struct object_id *oid, const char *path, const char *basename, const char *rev)
+{
+ enum object_type type;
+ unsigned long size;
+ char *buf;
+
+ type = oid_object_info(the_repository, oid, &size);
+ if (type == OBJ_BAD) {
+ cgit_print_error_page(404, "Not found",
+ "Bad object name: %s", oid_to_hex(oid));
+ return;
+ }
+
+ buf = repo_read_object_file(the_repository, oid, &type, &size);
+ if (!buf) {
+ cgit_print_error_page(500, "Internal server error",
+ "Error reading object %s", oid_to_hex(oid));
+ return;
+ }
+
+ html("<div id='summary'>");
+ cgit_open_filter(ctx.repo->about_filter, basename);
+ html_raw(buf, size);
+ cgit_close_filter(ctx.repo->about_filter);
+ html("</div>");
+
+ free(buf);
+}
+
+static int ls_wiki(const struct object_id *oid, const char *pathname, struct walk_wiki_context *walk_wiki_ctx)
+{
+ char *name, *link, *ptr;
+
+ if (isdigit(*pathname)) {
+ long index = strtol(pathname, &ptr, 10);
+ if (ptr && *ptr == '-') {
+ pathname = ptr + 1;
+ walk_wiki_ctx->index = index;
+ }
+ }
+
+ name = xstrdup(pathname);
+ ptr = strrchr(name, '.');
+ if (ptr)
+ *ptr = '\0';
+ link = xstrdup(name);
+ for (ptr = name; *ptr; ptr++) {
+ if (*ptr == '-')
+ *ptr = ' ';
+ }
+ for (ptr = link; *ptr; ptr++)
+ *ptr = tolower(*ptr);
+
+ html("<tr><td class='ls-index'>");
+ htmlf("%d", walk_wiki_ctx->index);
+ html("</td><td class>");
+ cgit_wiki_link(name, NULL, "ls-link", ctx.qry.head, walk_wiki_ctx->curr_rev, link);
+ html("</td></tr>\n");
+
+ walk_wiki_ctx->index++;
+ free(name);
+ free(link);
+ return 0;
+}
+
+static void ls_head(void)
+{
+ cgit_print_layout_start();
+ html("<table summary='wiki listing' class='list'>\n");
+ html("<tr class='nohover'>");
+ html("<th class='left'>Index</th>");
+ html("<th class='left'>Title</th>");
+ html("<th/>");
+ html("</tr>\n");
+}
+
+static void ls_tail(void)
+{
+ html("</table>\n");
+}
+
+static int walk_wiki(const struct object_id *oid, struct strbuf *base,
+ const char *pathname, unsigned mode, void *cbdata)
+{
+ struct walk_wiki_context *walk_wiki_ctx = cbdata;
+
+ if (!strcmp(pathname, WIKI_PATH))
+ return READ_TREE_RECURSIVE;
+
+ if (walk_wiki_ctx->state < 2) {
+ if (walk_wiki_ctx->state == 0) {
+ walk_wiki_ctx->state = 1;
+ ls_head();
+ }
+
+ ls_wiki(oid, pathname, walk_wiki_ctx);
+ } else if (walk_wiki_ctx->state == 2) {
+ const char *name;
+
+ name = pathname;
+ if (isdigit(*name)) {
+ char *ptr;
+ strtol(name, &ptr, 10);
+ if (ptr && *ptr == '-')
+ name = ptr + 1;
+ }
+
+ if (!strncasecmp(name, ctx.qry.path, strlen(ctx.qry.path))) {
+ struct strbuf buffer = STRBUF_INIT;
+
+ strbuf_addbuf(&buffer, base);
+ strbuf_addstr(&buffer, pathname);
+
+ walk_wiki_ctx->state = 3;
+ print_page(oid, pathname, buffer.buf, walk_wiki_ctx->curr_rev);
+ strbuf_release(&buffer);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Show repo wiki
+ * rev: the commit pointing at the root ui object
+ * path: partial name of a wiki md file
+ */
+void cgit_print_wiki(const char *rev)
+{
+ struct object_id oid;
+ struct commit *commit;
+ struct pathspec_item path_items = {
+ .match = WIKI_PATH,
+ .len = sizeof(WIKI_PATH) - 1,
+ };
+ struct pathspec paths = {
+ .nr = 1,
+ .items = &path_items
+ };
+ struct walk_wiki_context walk_wiki_ctx = {
+ .state = 0,
+ .index = 1
+ };
+
+ if (!rev)
+ rev = ctx.qry.head;
+
+ if (repo_get_oid(the_repository, rev, &oid)) {
+ cgit_print_error_page(404, "Not found",
+ "Invalid revision name: %s", rev);
+ return;
+ }
+ commit = lookup_commit_reference(the_repository, &oid);
+ if (!commit || repo_parse_commit(the_repository, commit)) {
+ cgit_print_error_page(404, "Not found",
+ "Invalid commit reference: %s", rev);
+ return;
+ }
+
+ walk_wiki_ctx.curr_rev = xstrdup(rev);
+
+ // print wiki pages
+ read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+ &paths, walk_wiki, &walk_wiki_ctx);
+
+ if (walk_wiki_ctx.state == 0) {
+ cgit_print_error_page(404, "Not found", "Wiki not found");
+ goto cleanup;
+ }
+
+ ls_tail();
+
+ // print active wiki page
+ if (ctx.qry.path) {
+ html("<hr>\n");
+
+ walk_wiki_ctx.state = 2;
+ read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
+ &paths, walk_wiki, &walk_wiki_ctx);
+
+ if (walk_wiki_ctx.state == 2)
+ cgit_print_error("Wiki page not found");
+ }
+
+ cgit_print_layout_end();
+
+cleanup:
+ free(walk_wiki_ctx.curr_rev);
+}
diff --git a/ui-wiki.h b/ui-wiki.h
new file mode 100644
index 0000000..7160010
--- /dev/null
+++ b/ui-wiki.h
@@ -0,0 +1,6 @@
+#ifndef UI_WIKI_H
+#define UI_WIKI_H
+
+extern void cgit_print_wiki(const char *rev);
+
+#endif /* UI_TREE_H */