diff options
context:
space:
mode:
Diffstat (limited to 'source/ui-atom.c')
-rw-r--r--source/ui-atom.c107
1 file changed, 66 insertions, 41 deletions
diff --git a/source/ui-atom.c b/source/ui-atom.c
index f968e24..aa9237a 100644
--- a/source/ui-atom.c
+++ b/source/ui-atom.c
@@ -1,24 +1,57 @@
-/* ui-atom.c: functions for atom feeds
- *
- * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
- *
- * Licensed under GNU General Public License v2
- * (see LICENSE.txt for full license text)
+/*
+ * The Atom feed for a repository, which lists recent commits so a reader can
+ * follow the project from a feed reader instead of the browsable pages. The
+ * response is XML rather than a page, so this file writes its own HTTP headers
+ * and never goes through the shared HTML layout.
*/
#define USE_THE_REPOSITORY_VARIABLE
#include "cgit.h"
-#include "ui-atom.h"
#include "html.h"
+#include "parsing.h"
+#include "shared.h"
+#include "ui-atom.h"
#include "ui-shared.h"
-static void add_entry(struct commit *commit, const char *host)
+/*
+ * Atom timestamps have to be RFC 3339, so a feed ignores the date-format and
+ * local-time settings the browsable pages honour. The zero is the timezone
+ * offset, which pins every feed date to UTC.
+ */
+static const char *feed_date(timestamp_t when)
+{
+ return show_date(when, 0, date_mode_from_type(DATE_ISO8601_STRICT));
+}
+
+/*
+ * cgit keeps an address with the angle brackets it was written with, while
+ * Atom wants the bare address.
+ */
+static void print_email(const char *email)
+{
+ char *copy = xstrdup(email);
+ char *start, *end;
+
+ start = strchr(copy, '<');
+ if (start)
+ start++;
+ else
+ start = copy;
+ end = strchr(start, '>');
+ if (end)
+ *end = '\0';
+
+ html("<email>");
+ html_txt(start);
+ html("</email>\n");
+ free(copy);
+}
+
+static void print_entry(struct commit *commit, const char *host)
{
- char delim = '&';
- char *hex;
- char *mail, *t, *t2;
struct commitinfo *info;
+ char *hex;
info = cgit_parse_commit(commit);
hex = oid_to_hex(&commit->object.oid);
@@ -27,8 +60,7 @@ static void add_entry(struct commit *commit, const char *host)
html_txt(info->subject);
html("</title>\n");
html("<updated>");
- html_txt(show_date(info->committer_date, 0,
- date_mode_from_type(DATE_ISO8601_STRICT)));
+ html_txt(feed_date(info->committer_date));
html("</updated>\n");
html("<author>\n");
if (info->author) {
@@ -36,33 +68,24 @@ static void add_entry(struct commit *commit, const char *host)
html_txt(info->author);
html("</name>\n");
}
- if (info->author_email && !ctx.cfg.noplainemail) {
- mail = xstrdup(info->author_email);
- t = strchr(mail, '<');
- if (t)
- t++;
- else
- t = mail;
- t2 = strchr(t, '>');
- if (t2)
- *t2 = '\0';
- html("<email>");
- html_txt(t);
- html("</email>\n");
- free(mail);
- }
+ if (info->author_email && !ctx.cfg.noplainemail)
+ print_email(info->author_email);
html("</author>\n");
html("<published>");
- html_txt(show_date(info->author_date, 0,
- date_mode_from_type(DATE_ISO8601_STRICT)));
+ html_txt(feed_date(info->author_date));
html("</published>\n");
if (host) {
char *pageurl;
+ char delim = '&';
+
html("<link rel='alternate' type='text/html' href='");
html(cgit_httpscheme());
html_attr(host);
pageurl = cgit_pageurl(ctx.repo->url, "commit", NULL);
html_attr(pageurl);
+ // Without a virtual root the page url is already a query
+ // string, so the commit id continues it instead of opening
+ // one.
if (ctx.cfg.virtual_root)
delim = '?';
html_attrf("%cid=%s", delim, hex);
@@ -79,15 +102,16 @@ static void add_entry(struct commit *commit, const char *host)
cgit_free_commitinfo(info);
}
-
void cgit_print_atom(char *tip, const char *path, int max_count)
{
char *host;
+ // setup_revisions reads a command line, so the first slot is the
+ // unused program name and parsing starts at the second.
const char *argv[] = {NULL, tip, NULL, NULL, NULL};
struct commit *commit;
struct rev_info rev;
int argc = 2;
- bool first = true;
+ bool need_updated = true;
if (ctx.qry.show_all)
argv[1] = "--all";
@@ -143,22 +167,23 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
free(repourl);
}
while ((commit = get_revision(&rev)) != NULL) {
- if (first) {
+ if (need_updated) {
html("<updated>");
- html_txt(show_date(commit->date, 0,
- date_mode_from_type(DATE_ISO8601_STRICT)));
+ html_txt(feed_date(commit->date));
html("</updated>\n");
- first = false;
+ need_updated = false;
}
- add_entry(commit, host);
+ print_entry(commit, host);
+ // release_commit_memory frees the parent list without clearing
+ // the pointer to it, so drop the dangling reference here.
release_commit_memory(the_repository->parsed_objects, commit);
commit->parents = NULL;
}
- if (first) {
- /* An empty feed still needs one feed-level <updated>. */
+ if (need_updated) {
+ // Atom makes a feed level updated mandatory, and an empty feed
+ // has no commit to take one from.
html("<updated>");
- html_txt(show_date(time(NULL), 0,
- date_mode_from_type(DATE_ISO8601_STRICT)));
+ html_txt(feed_date(time(NULL)));
html("</updated>\n");
}
html("</feed>\n");