List for cgit developers and users
 help / color / mirror / Atom feed
* [PATCH 00/12] filter framework and lua integration: complete
@ 2014-01-13  4:11 Jason
  2014-01-13  4:11 ` [PATCH 01/12] filter: add fprintf_filter function Jason
                   ` (11 more replies)
  0 siblings, 12 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


The beginnings of the filter framework I merged yesterday. This is
the second half. It is a combination of my work and John's, and
allows for a variety of different types of filters to be used with
cgit. We support long lived ones as well as one-off ones, complete
with support for redirecting write(), cleanup functions, init
functions, and more...

This patch set begins with finishing the infrastructure for more
advanced filter types. Then I add lua support, during which time I
make a few changes to the infrastructure. I should have split this out
into a separate commit, but I didn't. It should be fairly straight-
forward though. Finally, this series ends with adding support for the
email-filter option and writing both an old style "exec:" script for
adding gravatars, as well as a new style "lua:" script for the same
thing.

We managed to do all of this in under 500 lines of additions, evidently.

Jason A. Donenfeld (9):
  filter: allow for cleanup hook for filter types
  filter: basic write hooking infrastructure
  filter: add preliminary lua support
  filter: document lua filter type
  filter: lua error reporting
  filter: return on null filter from open and close
  filter: add support for email filter
  filter: add simple gravatar email filter
  filter: add gravatar lua script

John Keeping (3):
  filter: add fprintf_filter function
  filter: add interface layer
  filter: introduce "filter type" prefix

 cgit.c                     |  15 +-
 cgit.h                     |  18 ++-
 cgit.mk                    |  13 +-
 cgitrc.5.txt               |  50 +++++++
 filter.c                   | 340 ++++++++++++++++++++++++++++++++++++++++++---
 filters/email-gravatar.lua |  25 ++++
 filters/email-gravatar.py  |  33 +++++
 shared.c                   |   1 +
 ui-commit.c                |  22 ++-
 ui-log.c                   |   2 +
 ui-refs.c                  |   9 +-
 ui-repolist.c              |   6 +-
 ui-snapshot.c              |  11 +-
 ui-summary.c               |   8 +-
 ui-tag.c                   |   2 +
 15 files changed, 499 insertions(+), 56 deletions(-)
 create mode 100644 filters/email-gravatar.lua
 create mode 100755 filters/email-gravatar.py

-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 01/12] filter: add fprintf_filter function
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 02/12] filter: add interface layer Jason
                   ` (10 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


From: John Keeping <john at keeping.me.uk>

This stops the code in cgit.c::print_repo needing to inspect the
cgit_filter structure, meaning that we can abstract out different filter
types that will have different fields that need to be printed.

Signed-off-by: John Keeping <john at keeping.me.uk>
---
 cgit.c   | 6 +++---
 cgit.h   | 1 +
 filter.c | 5 +++++
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/cgit.c b/cgit.c
index 0be41b8..29b658e 100644
--- a/cgit.c
+++ b/cgit.c
@@ -706,11 +706,11 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
 	fprintf(f, "repo.enable-log-linecount=%d\n",
 	        repo->enable_log_linecount);
 	if (repo->about_filter && repo->about_filter != ctx.cfg.about_filter)
-		fprintf(f, "repo.about-filter=%s\n", repo->about_filter->cmd);
+		cgit_fprintf_filter(repo->about_filter, f, "repo.about-filter=");
 	if (repo->commit_filter && repo->commit_filter != ctx.cfg.commit_filter)
-		fprintf(f, "repo.commit-filter=%s\n", repo->commit_filter->cmd);
+		cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter=");
 	if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
-		fprintf(f, "repo.source-filter=%s\n", repo->source_filter->cmd);
+		cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter=");
 	if (repo->snapshots != ctx.cfg.snapshots) {
 		char *tmp = build_snapshot_setting(repo->snapshots);
 		fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
diff --git a/cgit.h b/cgit.h
index e6e7715..9b4be26 100644
--- a/cgit.h
+++ b/cgit.h
@@ -345,6 +345,7 @@ extern int cgit_parse_snapshots_mask(const char *str);
 
 extern int cgit_open_filter(struct cgit_filter *filter, ...);
 extern int cgit_close_filter(struct cgit_filter *filter);
+extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix);
 extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
 
 extern void cgit_prepare_repo_env(struct cgit_repo * repo);
diff --git a/filter.c b/filter.c
index d8c0116..80cf689 100644
--- a/filter.c
+++ b/filter.c
@@ -63,6 +63,11 @@ done:
 
 }
 
+void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
+{
+	fprintf(f, "%s%s\n", prefix, filter->cmd);
+}
+
 struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 {
 	struct cgit_filter *f;
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 02/12] filter: add interface layer
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
  2014-01-13  4:11 ` [PATCH 01/12] filter: add fprintf_filter function Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 03/12] filter: introduce "filter type" prefix Jason
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


From: John Keeping <john at keeping.me.uk>

Change the existing cgit_{open,close,fprintf}_filter functions to
delegate to filter-specific implementations accessed via function
pointers on the cgit_filter object.

We treat the "exec" filter type slightly specially here by putting its
structure definition in the header file and providing an "init" function
to set up the function pointers.  This is required so that the
ui-snapshot.c code that applies a compression filter can continue to use
the filter interface to do so.

Signed-off-by: John Keeping <john at keeping.me.uk>
---
 cgit.h        |  8 ++++++++
 filter.c      | 66 ++++++++++++++++++++++++++++++++++++++++++++---------------
 ui-snapshot.c | 11 +++++-----
 3 files changed, 63 insertions(+), 22 deletions(-)

diff --git a/cgit.h b/cgit.h
index 9b4be26..92e8c55 100644
--- a/cgit.h
+++ b/cgit.h
@@ -57,6 +57,13 @@ typedef enum {
 } filter_type;
 
 struct cgit_filter {
+	int (*open)(struct cgit_filter *, va_list ap);
+	int (*close)(struct cgit_filter *);
+	void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
+};
+
+struct cgit_exec_filter {
+	struct cgit_filter base;
 	char *cmd;
 	char **argv;
 	int extra_args;
@@ -346,6 +353,7 @@ extern int cgit_parse_snapshots_mask(const char *str);
 extern int cgit_open_filter(struct cgit_filter *filter, ...);
 extern int cgit_close_filter(struct cgit_filter *filter);
 extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix);
+extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
 extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
 
 extern void cgit_prepare_repo_env(struct cgit_repo * repo);
diff --git a/filter.c b/filter.c
index 80cf689..0f3edb0 100644
--- a/filter.c
+++ b/filter.c
@@ -13,15 +13,13 @@
 #include <string.h>
 #include <stdlib.h>
 
-int cgit_open_filter(struct cgit_filter *filter, ...)
+static int open_exec_filter(struct cgit_filter *base, va_list ap)
 {
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 	int i;
-	va_list ap;
 
-	va_start(ap, filter);
 	for (i = 0; i < filter->extra_args; i++)
 		filter->argv[i+1] = va_arg(ap, char *);
-	va_end(ap);
 
 	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
 		"Unable to duplicate STDOUT");
@@ -41,9 +39,9 @@ int cgit_open_filter(struct cgit_filter *filter, ...)
 	return 0;
 }
 
-
-int cgit_close_filter(struct cgit_filter *filter)
+static int close_exec_filter(struct cgit_filter *base)
 {
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 	int i, exit_status;
 
 	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
@@ -63,21 +61,50 @@ done:
 
 }
 
-void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
+static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
 {
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 	fprintf(f, "%s%s\n", prefix, filter->cmd);
 }
 
-struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
+int cgit_open_filter(struct cgit_filter *filter, ...)
 {
-	struct cgit_filter *f;
-	int args_size = 0;
+	int result;
+	va_list ap;
+	va_start(ap, filter);
+	result = filter->open(filter, ap);
+	va_end(ap);
+	return result;
+}
 
-	if (!cmd || !cmd[0])
-		return NULL;
+int cgit_close_filter(struct cgit_filter *filter)
+{
+	return filter->close(filter);
+}
+
+void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
+{
+	filter->fprintf(filter, f, prefix);
+}
+
+void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
+{
+	memset(filter, 0, sizeof(*filter));
+	filter->base.open = open_exec_filter;
+	filter->base.close = close_exec_filter;
+	filter->base.fprintf = fprintf_exec_filter;
+	filter->cmd = cmd;
+	filter->argv = argv;
+}
+
+static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype)
+{
+	struct cgit_exec_filter *f;
+	int args_size = 0;
 
-	f = xmalloc(sizeof(struct cgit_filter));
-	memset(f, 0, sizeof(struct cgit_filter));
+	f = xmalloc(sizeof(*f));
+	/* We leave argv for now and assign it below. */
+	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
 
 	switch (filtertype) {
 		case SOURCE:
@@ -91,10 +118,17 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 			break;
 	}
 
-	f->cmd = xstrdup(cmd);
 	args_size = (2 + f->extra_args) * sizeof(char *);
 	f->argv = xmalloc(args_size);
 	memset(f->argv, 0, args_size);
 	f->argv[0] = f->cmd;
-	return f;
+	return &f->base;
+}
+
+struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
+{
+	if (!cmd || !cmd[0])
+		return NULL;
+
+	return new_exec_filter(cmd, filtertype);
 }
diff --git a/ui-snapshot.c b/ui-snapshot.c
index 5136c49..7115ec4 100644
--- a/ui-snapshot.c
+++ b/ui-snapshot.c
@@ -58,13 +58,12 @@ static int write_compressed_tar_archive(const char *hex,
 					char *filter_argv[])
 {
 	int rv;
-	struct cgit_filter f = {
-		.cmd = filter_argv[0],
-		.argv = filter_argv,
-	};
-	cgit_open_filter(&f);
+	struct cgit_exec_filter f;
+	cgit_exec_filter_init(&f, filter_argv[0], filter_argv);
+
+	cgit_open_filter(&f.base);
 	rv = write_tar_archive(hex, prefix);
-	cgit_close_filter(&f);
+	cgit_close_filter(&f.base);
 	return rv;
 }
 
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 03/12] filter: introduce "filter type" prefix
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
  2014-01-13  4:11 ` [PATCH 01/12] filter: add fprintf_filter function Jason
  2014-01-13  4:11 ` [PATCH 02/12] filter: add interface layer Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 04/12] filter: allow for cleanup hook for filter types Jason
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


From: John Keeping <john at keeping.me.uk>

This allows different filter implementations to be specified in the
configuration file.  Currently only "exec" is supported, but it may now
be specified either with or without the "exec:" prefix.

Signed-off-by: John Keeping <john at keeping.me.uk>
---
 cgitrc.5.txt |  9 +++++++++
 filter.c     | 33 +++++++++++++++++++++++++++++++--
 2 files changed, 40 insertions(+), 2 deletions(-)

diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 52caed0..60159f6 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -557,6 +557,15 @@ config files, e.g. "repo.desc" becomes "desc".
 
 FILTER API
 ----------
+By default, filters are separate processes that are executed each time they
+are needed.  Alternative technologies may be used by prefixing the filter
+specification with the relevant string; available values are:
+
+'exec:'::
+	The default "one process per filter" mode.
+
+Parameters are provided to filters as follows.
+
 about filter::
 	This filter is given a single parameter: the filename of the source
 	file to filter. The filter can use the filename to determine (for
diff --git a/filter.c b/filter.c
index 0f3edb0..ba66e46 100644
--- a/filter.c
+++ b/filter.c
@@ -64,7 +64,7 @@ done:
 static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *prefix)
 {
 	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
-	fprintf(f, "%s%s\n", prefix, filter->cmd);
+	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
 }
 
 int cgit_open_filter(struct cgit_filter *filter, ...)
@@ -125,10 +125,39 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty
 	return &f->base;
 }
 
+static const struct {
+	const char *prefix;
+	struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype);
+} filter_specs[] = {
+	{ "exec", new_exec_filter },
+};
+
 struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 {
+	char *colon;
+	int i;
+	size_t len;
 	if (!cmd || !cmd[0])
 		return NULL;
 
-	return new_exec_filter(cmd, filtertype);
+	colon = strchr(cmd, ':');
+	len = colon - cmd;
+	/*
+	 * In case we're running on Windows, don't allow a single letter before
+	 * the colon.
+	 */
+	if (len == 1)
+		colon = NULL;
+
+	/* If no prefix is given, exec filter is the default. */
+	if (!colon)
+		return new_exec_filter(cmd, filtertype);
+
+	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
+		if (len == strlen(filter_specs[i].prefix) &&
+		    !strncmp(filter_specs[i].prefix, cmd, len))
+			return filter_specs[i].ctor(colon + 1, filtertype);
+	}
+
+	die("Invalid filter type: %.*s", (int) len, cmd);
 }
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 04/12] filter: allow for cleanup hook for filter types
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (2 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 03/12] filter: introduce "filter type" prefix Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 05/12] filter: basic write hooking infrastructure Jason
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


At some point, we're going to want to do lazy deallocation of filters.
For example, if we implement lua, we'll want to load the lua runtime
once for each filter, even if that filter is called many times.
Similarly, for persistent exec filters, we'll want to load it once,
despite many open_filter and close_filter calls, and only reap the child
process at the end of the cgit process. For this reason, we add here a
cleanup function that is called at the end of cgit's main().

Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 cgit.c   |  1 +
 cgit.h   |  2 ++
 filter.c | 85 +++++++++++++++++++++++++++++++++++++++++++---------------------
 3 files changed, 61 insertions(+), 27 deletions(-)

diff --git a/cgit.c b/cgit.c
index 29b658e..4f31e58 100644
--- a/cgit.c
+++ b/cgit.c
@@ -951,6 +951,7 @@ int main(int argc, const char **argv)
 		ctx.cfg.cache_size = 0;
 	err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
 			    ctx.qry.raw, ttl, process_request, &ctx);
+	cgit_cleanup_filters();
 	if (err)
 		cgit_print_error("Error processing page: %s (%d)",
 				 strerror(err), err);
diff --git a/cgit.h b/cgit.h
index 92e8c55..893c38f 100644
--- a/cgit.h
+++ b/cgit.h
@@ -60,6 +60,7 @@ struct cgit_filter {
 	int (*open)(struct cgit_filter *, va_list ap);
 	int (*close)(struct cgit_filter *);
 	void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
+	void (*cleanup)(struct cgit_filter *);
 };
 
 struct cgit_exec_filter {
@@ -355,6 +356,7 @@ extern int cgit_close_filter(struct cgit_filter *filter);
 extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix);
 extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
 extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
+extern void cgit_cleanup_filters(void);
 
 extern void cgit_prepare_repo_env(struct cgit_repo * repo);
 
diff --git a/filter.c b/filter.c
index ba66e46..86c1d5d 100644
--- a/filter.c
+++ b/filter.c
@@ -67,34 +67,13 @@ static void fprintf_exec_filter(struct cgit_filter *base, FILE *f, const char *p
 	fprintf(f, "%sexec:%s\n", prefix, filter->cmd);
 }
 
-int cgit_open_filter(struct cgit_filter *filter, ...)
-{
-	int result;
-	va_list ap;
-	va_start(ap, filter);
-	result = filter->open(filter, ap);
-	va_end(ap);
-	return result;
-}
-
-int cgit_close_filter(struct cgit_filter *filter)
+static void cleanup_exec_filter(struct cgit_filter *base)
 {
-	return filter->close(filter);
-}
-
-void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
-{
-	filter->fprintf(filter, f, prefix);
-}
-
-void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
-{
-	memset(filter, 0, sizeof(*filter));
-	filter->base.open = open_exec_filter;
-	filter->base.close = close_exec_filter;
-	filter->base.fprintf = fprintf_exec_filter;
-	filter->cmd = cmd;
-	filter->argv = argv;
+	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
+	if (filter->argv) {
+		free(filter->argv);
+		filter->argv = NULL;
+	}
 }
 
 static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype)
@@ -125,6 +104,39 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty
 	return &f->base;
 }
 
+void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv)
+{
+	memset(filter, 0, sizeof(*filter));
+	filter->base.open = open_exec_filter;
+	filter->base.close = close_exec_filter;
+	filter->base.fprintf = fprintf_exec_filter;
+	filter->base.cleanup = cleanup_exec_filter;
+	filter->cmd = cmd;
+	filter->argv = argv;
+}
+
+int cgit_open_filter(struct cgit_filter *filter, ...)
+{
+	int result;
+	va_list ap;
+	va_start(ap, filter);
+	result = filter->open(filter, ap);
+	va_end(ap);
+	return result;
+}
+
+int cgit_close_filter(struct cgit_filter *filter)
+{
+	return filter->close(filter);
+}
+
+void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
+{
+	filter->fprintf(filter, f, prefix);
+}
+
+
+
 static const struct {
 	const char *prefix;
 	struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype);
@@ -161,3 +173,22 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 
 	die("Invalid filter type: %.*s", (int) len, cmd);
 }
+
+static inline void reap_filter(struct cgit_filter *filter)
+{
+	if (filter && filter->cleanup)
+		filter->cleanup(filter);
+}
+
+void cgit_cleanup_filters(void)
+{
+	int i;
+	reap_filter(ctx.cfg.about_filter);
+	reap_filter(ctx.cfg.commit_filter);
+	reap_filter(ctx.cfg.source_filter);
+	for (i = 0; i < cgit_repolist.count; ++i) {
+		reap_filter(cgit_repolist.repos[i].about_filter);
+		reap_filter(cgit_repolist.repos[i].commit_filter);
+		reap_filter(cgit_repolist.repos[i].source_filter);
+	}
+}
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 05/12] filter: basic write hooking infrastructure
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (3 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 04/12] filter: allow for cleanup hook for filter types Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  8:19   ` cgit
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
                   ` (6 subsequent siblings)
  11 siblings, 1 reply; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Filters can now call hook_write and unhook_write if they want to
redirect writing to stdout to a different function. This saves us from
potential file descriptor pipes and other less efficient mechanisms.

We do this instead of replacing the call in html_raw because some places
stdlib's printf functions are used (ui-patch or within git itself),
which has its own internal buffering, which makes it difficult to
interlace our function calls. So, we dlsym libc's write and then
override it in the link stage.

Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 cgit.c   |  2 ++
 cgit.h   |  1 +
 cgit.mk  |  4 +++-
 filter.c | 30 ++++++++++++++++++++++++++++++
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/cgit.c b/cgit.c
index 4f31e58..725fd65 100644
--- a/cgit.c
+++ b/cgit.c
@@ -904,6 +904,8 @@ int main(int argc, const char **argv)
 	const char *path;
 	int err, ttl;
 
+	cgit_init_filters();
+
 	prepare_context(&ctx);
 	cgit_repolist.length = 0;
 	cgit_repolist.count = 0;
diff --git a/cgit.h b/cgit.h
index 893c38f..db34493 100644
--- a/cgit.h
+++ b/cgit.h
@@ -357,6 +357,7 @@ extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char
 extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv);
 extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype);
 extern void cgit_cleanup_filters(void);
+extern void cgit_init_filters(void);
 
 extern void cgit_prepare_repo_env(struct cgit_repo * repo);
 
diff --git a/cgit.mk b/cgit.mk
index 19a76e7..9d6dea8 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -61,6 +61,8 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
 $(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
 	-DCGIT_VERSION='"$(CGIT_VERSION)"'
 
+CGIT_LIBS += -ldl
+
 
 # Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
 # handled by that and we must handle them ourselves.
@@ -88,4 +90,4 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
 	$(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
 
 $(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
-	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
+	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
diff --git a/filter.c b/filter.c
index 86c1d5d..8990575 100644
--- a/filter.c
+++ b/filter.c
@@ -12,6 +12,10 @@
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
+#include <dlfcn.h>
+
+static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
+static ssize_t (*filter_write)(const void *buf, size_t count) = NULL;
 
 static int open_exec_filter(struct cgit_filter *base, va_list ap)
 {
@@ -192,3 +196,29 @@ void cgit_cleanup_filters(void)
 		reap_filter(cgit_repolist.repos[i].source_filter);
 	}
 }
+
+void cgit_init_filters(void)
+{
+	libc_write = dlsym(RTLD_NEXT, "write");
+	if (!libc_write)
+		die("Could not locate libc's write function");
+}
+
+ssize_t write(int fd, const void *buf, size_t count)
+{
+	if (fd != STDOUT_FILENO || !filter_write)
+		return libc_write(fd, buf, count);
+	return filter_write(buf, count);
+}
+
+static inline void hook_write(ssize_t (*new_write)(const void *buf, size_t count))
+{
+	/* We want to avoid buggy nested patterns. */
+	assert(filter_write == NULL);
+	filter_write = new_write;
+}
+static inline void unhook_write()
+{
+	assert(filter_write != NULL);
+	filter_write = NULL;
+}
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (4 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 05/12] filter: basic write hooking infrastructure Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  8:31   ` cgit
                     ` (3 more replies)
  2014-01-13  4:11 ` [PATCH 07/12] filter: document lua filter type Jason
                   ` (5 subsequent siblings)
  11 siblings, 4 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 cgit.h   |   2 +-
 cgit.mk  |  13 ++-
 filter.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 230 insertions(+), 69 deletions(-)

diff --git a/cgit.h b/cgit.h
index db34493..519d2af 100644
--- a/cgit.h
+++ b/cgit.h
@@ -61,13 +61,13 @@ struct cgit_filter {
 	int (*close)(struct cgit_filter *);
 	void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
 	void (*cleanup)(struct cgit_filter *);
+	int argument_count;
 };
 
 struct cgit_exec_filter {
 	struct cgit_filter base;
 	char *cmd;
 	char **argv;
-	int extra_args;
 	int old_stdout;
 	int pipe_fh[2];
 	int pid;
diff --git a/cgit.mk b/cgit.mk
index 9d6dea8..71bbe8b 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -25,6 +25,16 @@ ifdef NO_C99_FORMAT
 	CFLAGS += -DNO_C99_FORMAT
 endif
 
+ifdef NO_LUA
+	CFLAGS += -DNO_LUA
+else
+	CGIT_LIBS += -llua
+endif
+
+CGIT_LIBS += -ldl
+
+
+
 CGIT_OBJ_NAMES += cgit.o
 CGIT_OBJ_NAMES += cache.o
 CGIT_OBJ_NAMES += cmd.o
@@ -61,9 +71,6 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
 $(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
 	-DCGIT_VERSION='"$(CGIT_VERSION)"'
 
-CGIT_LIBS += -ldl
-
-
 # Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
 # handled by that and we must handle them ourselves.
 cgit_dep_files := $(foreach f,$(CGIT_OBJS),$(dir $f).depend/$(notdir $f).d)
diff --git a/filter.c b/filter.c
index 8990575..6f0e298 100644
--- a/filter.c
+++ b/filter.c
@@ -13,16 +13,73 @@
 #include <string.h>
 #include <stdlib.h>
 #include <dlfcn.h>
+#include <errno.h>
+#ifndef NO_LUA
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#endif
 
 static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
-static ssize_t (*filter_write)(const void *buf, size_t count) = NULL;
+static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
+static struct cgit_filter *current_write_filter = NULL;
+
+static inline void reap_filter(struct cgit_filter *filter)
+{
+	if (filter && filter->cleanup)
+		filter->cleanup(filter);
+}
+
+void cgit_cleanup_filters(void)
+{
+	int i;
+	reap_filter(ctx.cfg.about_filter);
+	reap_filter(ctx.cfg.commit_filter);
+	reap_filter(ctx.cfg.source_filter);
+	for (i = 0; i < cgit_repolist.count; ++i) {
+		reap_filter(cgit_repolist.repos[i].about_filter);
+		reap_filter(cgit_repolist.repos[i].commit_filter);
+		reap_filter(cgit_repolist.repos[i].source_filter);
+	}
+}
+
+void cgit_init_filters(void)
+{
+	libc_write = dlsym(RTLD_NEXT, "write");
+	if (!libc_write)
+		die("Could not locate libc's write function");
+}
+
+ssize_t write(int fd, const void *buf, size_t count)
+{
+	if (fd != STDOUT_FILENO || !filter_write)
+		return libc_write(fd, buf, count);
+	return filter_write(current_write_filter, buf, count);
+}
+
+static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
+{
+	/* We want to avoid buggy nested patterns. */
+	assert(filter_write == NULL);
+	assert(current_write_filter == NULL);
+	current_write_filter = filter;
+	filter_write = new_write;
+}
+
+static inline void unhook_write()
+{
+	assert(filter_write != NULL);
+	assert(current_write_filter != NULL);
+	filter_write = NULL;
+	current_write_filter = NULL;
+}
 
 static int open_exec_filter(struct cgit_filter *base, va_list ap)
 {
 	struct cgit_exec_filter *filter = (struct cgit_exec_filter *) base;
 	int i;
 
-	for (i = 0; i < filter->extra_args; i++)
+	for (i = 0; i < filter->base.argument_count; i++)
 		filter->argv[i+1] = va_arg(ap, char *);
 
 	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
@@ -59,7 +116,7 @@ static int close_exec_filter(struct cgit_filter *base)
 	die("Subprocess %s exited abnormally", filter->cmd);
 
 done:
-	for (i = 0; i < filter->extra_args; i++)
+	for (i = 0; i < filter->base.argument_count; i++)
 		filter->argv[i+1] = NULL;
 	return 0;
 
@@ -78,9 +135,18 @@ static void cleanup_exec_filter(struct cgit_filter *base)
 		free(filter->argv);
 		filter->argv = NULL;
 	}
+
+/* We can't enable this code block because cgit_exec_filter_init sometimes takes
+ * non-malloc'd cmd. Refactor the callers of that before uncommenting this block */
+#if 0
+	if (filter->cmd) {
+		free(filter->cmd);
+		filter->cmd = NULL;
+	}
+#endif
 }
 
-static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filtertype)
+static struct cgit_filter *new_exec_filter(const char *cmd, int argument_count)
 {
 	struct cgit_exec_filter *f;
 	int args_size = 0;
@@ -88,20 +154,8 @@ static struct cgit_filter *new_exec_filter(const char *cmd, filter_type filterty
 	f = xmalloc(sizeof(*f));
 	/* We leave argv for now and assign it below. */
 	cgit_exec_filter_init(f, xstrdup(cmd), NULL);
-
-	switch (filtertype) {
-		case SOURCE:
-		case ABOUT:
-			f->extra_args = 1;
-			break;
-
-		case COMMIT:
-		default:
-			f->extra_args = 0;
-			break;
-	}
-
-	args_size = (2 + f->extra_args) * sizeof(char *);
+	f->base.argument_count = argument_count;
+	args_size = (2 + argument_count) * sizeof(char *);
 	f->argv = xmalloc(args_size);
 	memset(f->argv, 0, args_size);
 	f->argv[0] = f->cmd;
@@ -117,8 +171,135 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
 	filter->base.cleanup = cleanup_exec_filter;
 	filter->cmd = cmd;
 	filter->argv = argv;
+	/* The argument count for open_filter is zero by default, unless called from new_filter, above. */
+	filter->base.argument_count = 0;
+}
+
+#ifndef NO_LUA
+struct lua_filter {
+	struct cgit_filter base;
+	char *script_file;
+	lua_State *lua_state;
+};
+
+static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
+{
+	struct lua_filter *filter = (struct lua_filter *) base;
+
+	lua_getglobal(filter->lua_state, "filter_write");
+	lua_pushlstring(filter->lua_state, buf, count);
+	if (lua_pcall(filter->lua_state, 1, 0, 0)) {
+		errno = EPROTO;
+		return -1;
+	}
+	return count;
+}
+
+static int html_lua_filter(lua_State *lua_state)
+{
+	size_t len;
+	const char *str;
+
+	str = lua_tostring(lua_state, 1);
+	if (!str)
+		return 0;
+	len = strlen(str);
+	libc_write(STDOUT_FILENO, str, len);
+	return 0;
+}
+
+static void cleanup_lua_filter(struct cgit_filter *base)
+{
+	struct lua_filter *filter = (struct lua_filter *) base;
+
+	if (!filter->lua_state)
+		return;
+
+	lua_close(filter->lua_state);
+	filter->lua_state = NULL;
+	if (filter->script_file) {
+		free(filter->script_file);
+		filter->script_file = NULL;
+	}
+}
+
+static int init_lua_filter(struct lua_filter *filter)
+{
+	if (filter->lua_state)
+		return 0;
+
+	if (!(filter->lua_state = luaL_newstate()))
+		return 1;
+
+	luaL_openlibs(filter->lua_state);
+
+	lua_pushcfunction(filter->lua_state, html_lua_filter);
+	lua_setglobal(filter->lua_state, "html");
+
+	if (luaL_dofile(filter->lua_state, filter->script_file)) {
+		lua_close(filter->lua_state);
+		filter->lua_state = NULL;
+		return 1;
+	}
+	return 0;
+}
+
+static int open_lua_filter(struct cgit_filter *base, va_list ap)
+{
+	struct lua_filter *filter = (struct lua_filter *) base;
+	int i;
+
+	if (init_lua_filter(filter))
+		return 1;
+
+	hook_write(base, write_lua_filter);
+
+	lua_getglobal(filter->lua_state, "filter_open");
+	for (i = 0; i < filter->base.argument_count; ++i)
+		lua_pushstring(filter->lua_state, va_arg(ap, char *));
+	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0))
+		return 1;
+	return 0;
+}
+
+static int close_lua_filter(struct cgit_filter *base)
+{
+	struct lua_filter *filter = (struct lua_filter *) base;
+	int ret = 0;
+
+	lua_getglobal(filter->lua_state, "filter_close");
+	if (lua_pcall(filter->lua_state, 0, 0, 0))
+		ret = 1;
+	unhook_write();
+	return ret;
+}
+
+static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
+{
+	struct lua_filter *filter = (struct lua_filter *) base;
+	fprintf(f, "%slua:%s\n", prefix, filter->script_file);
+}
+
+
+static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
+{
+	struct lua_filter *filter;
+
+	filter = xmalloc(sizeof(*filter));
+	memset(filter, 0, sizeof(*filter));
+	filter->base.open = open_lua_filter;
+	filter->base.close = close_lua_filter;
+	filter->base.fprintf = fprintf_lua_filter;
+	filter->base.cleanup = cleanup_lua_filter;
+	filter->base.argument_count = argument_count;
+	filter->script_file = xstrdup(cmd);
+
+	return &filter->base;
 }
 
+#endif
+
+
 int cgit_open_filter(struct cgit_filter *filter, ...)
 {
 	int result;
@@ -143,9 +324,12 @@ void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix
 
 static const struct {
 	const char *prefix;
-	struct cgit_filter *(*ctor)(const char *cmd, filter_type filtertype);
+	struct cgit_filter *(*ctor)(const char *cmd, int argument_count);
 } filter_specs[] = {
 	{ "exec", new_exec_filter },
+#ifndef NO_LUA
+	{ "lua", new_lua_filter },
+#endif
 };
 
 struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
@@ -153,6 +337,8 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 	char *colon;
 	int i;
 	size_t len;
+	int argument_count;
+
 	if (!cmd || !cmd[0])
 		return NULL;
 
@@ -165,60 +351,28 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 	if (len == 1)
 		colon = NULL;
 
+	switch (filtertype) {
+		case SOURCE:
+		case ABOUT:
+			argument_count = 1;
+			break;
+
+		case COMMIT:
+		default:
+			argument_count = 0;
+			break;
+	}
+
 	/* If no prefix is given, exec filter is the default. */
 	if (!colon)
-		return new_exec_filter(cmd, filtertype);
+		return new_exec_filter(cmd, argument_count);
 
 	for (i = 0; i < ARRAY_SIZE(filter_specs); i++) {
 		if (len == strlen(filter_specs[i].prefix) &&
 		    !strncmp(filter_specs[i].prefix, cmd, len))
-			return filter_specs[i].ctor(colon + 1, filtertype);
+			return filter_specs[i].ctor(colon + 1, argument_count);
 	}
 
 	die("Invalid filter type: %.*s", (int) len, cmd);
 }
 
-static inline void reap_filter(struct cgit_filter *filter)
-{
-	if (filter && filter->cleanup)
-		filter->cleanup(filter);
-}
-
-void cgit_cleanup_filters(void)
-{
-	int i;
-	reap_filter(ctx.cfg.about_filter);
-	reap_filter(ctx.cfg.commit_filter);
-	reap_filter(ctx.cfg.source_filter);
-	for (i = 0; i < cgit_repolist.count; ++i) {
-		reap_filter(cgit_repolist.repos[i].about_filter);
-		reap_filter(cgit_repolist.repos[i].commit_filter);
-		reap_filter(cgit_repolist.repos[i].source_filter);
-	}
-}
-
-void cgit_init_filters(void)
-{
-	libc_write = dlsym(RTLD_NEXT, "write");
-	if (!libc_write)
-		die("Could not locate libc's write function");
-}
-
-ssize_t write(int fd, const void *buf, size_t count)
-{
-	if (fd != STDOUT_FILENO || !filter_write)
-		return libc_write(fd, buf, count);
-	return filter_write(buf, count);
-}
-
-static inline void hook_write(ssize_t (*new_write)(const void *buf, size_t count))
-{
-	/* We want to avoid buggy nested patterns. */
-	assert(filter_write == NULL);
-	filter_write = new_write;
-}
-static inline void unhook_write()
-{
-	assert(filter_write != NULL);
-	filter_write = NULL;
-}
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 07/12] filter: document lua filter type
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (5 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 08/12] filter: lua error reporting Jason
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 cgitrc.5.txt | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index 60159f6..d1e872f 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -564,6 +564,29 @@ specification with the relevant string; available values are:
 'exec:'::
 	The default "one process per filter" mode.
 
+'lua:'::
+	Executes the script using a built-in Lua interpreter. The script is
+	loaded once per execution of cgit, and may be called multiple times
+	during cgit's lifetime, making it a good choice for repeated filters
+	such as the 'email filter'. It responds to three functions:
+
+	'filter_open(argument1, argument2, argument3, ...)'::
+		This is called upon activation of the filter for a particular
+		set of data.
+	
+	'filter_write(buffer)'::
+		This is called whenever cgit writes data to the webpage.
+	
+	'filter_close()'::
+		This is called when the current filtering operation is
+		completed.
+	
+	Additionally, cgit exposes to the Lua the following built-in function:
+
+	'html(str)'::
+		Writes 'str' to the webpage.
+
+
 Parameters are provided to filters as follows.
 
 about filter::
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 08/12] filter: lua error reporting
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (6 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 07/12] filter: document lua filter type Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 09/12] filter: return on null filter from open and close Jason
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 filter.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/filter.c b/filter.c
index 6f0e298..d2f6a4f 100644
--- a/filter.c
+++ b/filter.c
@@ -182,6 +182,12 @@ struct lua_filter {
 	lua_State *lua_state;
 };
 
+static void error_lua_filter(struct lua_filter *filter)
+{
+	die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1));
+	lua_pop(filter->lua_state, 1);
+}
+
 static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
 {
 	struct lua_filter *filter = (struct lua_filter *) base;
@@ -189,6 +195,7 @@ static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_
 	lua_getglobal(filter->lua_state, "filter_write");
 	lua_pushlstring(filter->lua_state, buf, count);
 	if (lua_pcall(filter->lua_state, 1, 0, 0)) {
+		error_lua_filter(filter);
 		errno = EPROTO;
 		return -1;
 	}
@@ -237,6 +244,7 @@ static int init_lua_filter(struct lua_filter *filter)
 	lua_setglobal(filter->lua_state, "html");
 
 	if (luaL_dofile(filter->lua_state, filter->script_file)) {
+		error_lua_filter(filter);
 		lua_close(filter->lua_state);
 		filter->lua_state = NULL;
 		return 1;
@@ -257,8 +265,10 @@ static int open_lua_filter(struct cgit_filter *base, va_list ap)
 	lua_getglobal(filter->lua_state, "filter_open");
 	for (i = 0; i < filter->base.argument_count; ++i)
 		lua_pushstring(filter->lua_state, va_arg(ap, char *));
-	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0))
+	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) {
+		error_lua_filter(filter);
 		return 1;
+	}
 	return 0;
 }
 
@@ -268,8 +278,10 @@ static int close_lua_filter(struct cgit_filter *base)
 	int ret = 0;
 
 	lua_getglobal(filter->lua_state, "filter_close");
-	if (lua_pcall(filter->lua_state, 0, 0, 0))
+	if (lua_pcall(filter->lua_state, 0, 0, 0)) {
+		error_lua_filter(filter);
 		ret = 1;
+	}
 	unhook_write();
 	return ret;
 }
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 09/12] filter: return on null filter from open and close
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (7 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 08/12] filter: lua error reporting Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 10/12] filter: add support for email filter Jason
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


So that we don't have to include the if(filter) open_filter(filter)
block everywhere, we introduce the guard in the function itself. This
should simplify quite a bit of code.

Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 filter.c      |  4 ++++
 ui-commit.c   | 18 ++++++------------
 ui-repolist.c |  6 ++----
 ui-summary.c  |  8 ++------
 4 files changed, 14 insertions(+), 22 deletions(-)

diff --git a/filter.c b/filter.c
index d2f6a4f..7db98b1 100644
--- a/filter.c
+++ b/filter.c
@@ -316,6 +316,8 @@ int cgit_open_filter(struct cgit_filter *filter, ...)
 {
 	int result;
 	va_list ap;
+	if (!filter)
+		return 0;
 	va_start(ap, filter);
 	result = filter->open(filter, ap);
 	va_end(ap);
@@ -324,6 +326,8 @@ int cgit_open_filter(struct cgit_filter *filter, ...)
 
 int cgit_close_filter(struct cgit_filter *filter)
 {
+	if (!filter)
+		return 0;
 	return filter->close(filter);
 }
 
diff --git a/ui-commit.c b/ui-commit.c
index aa1892f..5ac79c0 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -107,28 +107,22 @@ void cgit_print_commit(char *hex, const char *prefix)
 	}
 	html("</table>\n");
 	html("<div class='commit-subject'>");
-	if (ctx.repo->commit_filter)
-		cgit_open_filter(ctx.repo->commit_filter);
+	cgit_open_filter(ctx.repo->commit_filter);
 	html_txt(info->subject);
-	if (ctx.repo->commit_filter)
-		cgit_close_filter(ctx.repo->commit_filter);
+	cgit_close_filter(ctx.repo->commit_filter);
 	show_commit_decorations(commit);
 	html("</div>");
 	html("<div class='commit-msg'>");
-	if (ctx.repo->commit_filter)
-		cgit_open_filter(ctx.repo->commit_filter);
+	cgit_open_filter(ctx.repo->commit_filter);
 	html_txt(info->msg);
-	if (ctx.repo->commit_filter)
-		cgit_close_filter(ctx.repo->commit_filter);
+	cgit_close_filter(ctx.repo->commit_filter);
 	html("</div>");
 	if (notes.len != 0) {
 		html("<div class='notes-header'>Notes</div>");
 		html("<div class='notes'>");
-		if (ctx.repo->commit_filter)
-			cgit_open_filter(ctx.repo->commit_filter);
+		cgit_open_filter(ctx.repo->commit_filter);
 		html_txt(notes.buf);
-		if (ctx.repo->commit_filter)
-			cgit_close_filter(ctx.repo->commit_filter);
+		cgit_close_filter(ctx.repo->commit_filter);
 		html("</div>");
 		html("<div class='notes-footer'></div>");
 	}
diff --git a/ui-repolist.c b/ui-repolist.c
index 7b1fec3..f9cb21a 100644
--- a/ui-repolist.c
+++ b/ui-repolist.c
@@ -333,9 +333,7 @@ void cgit_print_site_readme()
 {
 	if (!ctx.cfg.root_readme)
 		return;
-	if (ctx.cfg.about_filter)
-		cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
+	cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
 	html_include(ctx.cfg.root_readme);
-	if (ctx.cfg.about_filter)
-		cgit_close_filter(ctx.cfg.about_filter);
+	cgit_close_filter(ctx.cfg.about_filter);
 }
diff --git a/ui-summary.c b/ui-summary.c
index 725f3ab..ddd8f1b 100644
--- a/ui-summary.c
+++ b/ui-summary.c
@@ -151,16 +151,12 @@ void cgit_print_repo_readme(char *path)
 	 * filesystem, while applying the about-filter.
 	 */
 	html("<div id='summary'>");
-	if (ctx.repo->about_filter)
-		cgit_open_filter(ctx.repo->about_filter, filename);
-
+	cgit_open_filter(ctx.repo->about_filter, filename);
 	if (ref)
 		cgit_print_file(filename, ref, 1);
 	else
 		html_include(filename);
-
-	if (ctx.repo->about_filter)
-		cgit_close_filter(ctx.repo->about_filter);
+	cgit_close_filter(ctx.repo->about_filter);
 
 	html("</div>");
 	if (free_filename)
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 10/12] filter: add support for email filter
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (8 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 09/12] filter: return on null filter from open and close Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 11/12] filter: add simple gravatar " Jason
  2014-01-13  4:11 ` [PATCH 12/12] filter: add gravatar lua script Jason
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 cgit.c       |  6 ++++++
 cgit.h       |  4 +++-
 cgitrc.5.txt | 18 ++++++++++++++++++
 filter.c     |  1 +
 shared.c     |  1 +
 ui-commit.c  |  4 ++++
 ui-log.c     |  2 ++
 ui-refs.c    |  9 ++++++++-
 ui-tag.c     |  2 ++
 9 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/cgit.c b/cgit.c
index 725fd65..f3fe56b 100644
--- a/cgit.c
+++ b/cgit.c
@@ -89,6 +89,8 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
 			repo->commit_filter = cgit_new_filter(value, COMMIT);
 		else if (!strcmp(name, "source-filter"))
 			repo->source_filter = cgit_new_filter(value, SOURCE);
+		else if (!strcmp(name, "email-filter"))
+			repo->email_filter = cgit_new_filter(value, EMAIL);
 	}
 }
 
@@ -188,6 +190,8 @@ static void config_cb(const char *name, const char *value)
 		ctx.cfg.about_filter = cgit_new_filter(value, ABOUT);
 	else if (!strcmp(name, "commit-filter"))
 		ctx.cfg.commit_filter = cgit_new_filter(value, COMMIT);
+	else if (!strcmp(name, "email-filter"))
+		ctx.cfg.email_filter = cgit_new_filter(value, EMAIL);
 	else if (!strcmp(name, "embedded"))
 		ctx.cfg.embedded = atoi(value);
 	else if (!strcmp(name, "max-atom-items"))
@@ -711,6 +715,8 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
 		cgit_fprintf_filter(repo->commit_filter, f, "repo.commit-filter=");
 	if (repo->source_filter && repo->source_filter != ctx.cfg.source_filter)
 		cgit_fprintf_filter(repo->source_filter, f, "repo.source-filter=");
+	if (repo->email_filter && repo->email_filter != ctx.cfg.email_filter)
+		cgit_fprintf_filter(repo->email_filter, f, "repo.email-filter=");
 	if (repo->snapshots != ctx.cfg.snapshots) {
 		char *tmp = build_snapshot_setting(repo->snapshots);
 		fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
diff --git a/cgit.h b/cgit.h
index 519d2af..e200a06 100644
--- a/cgit.h
+++ b/cgit.h
@@ -53,7 +53,7 @@ typedef void (*filepair_fn)(struct diff_filepair *pair);
 typedef void (*linediff_fn)(char *line, int len);
 
 typedef enum {
-	ABOUT, COMMIT, SOURCE
+	ABOUT, COMMIT, SOURCE, EMAIL
 } filter_type;
 
 struct cgit_filter {
@@ -99,6 +99,7 @@ struct cgit_repo {
 	struct cgit_filter *about_filter;
 	struct cgit_filter *commit_filter;
 	struct cgit_filter *source_filter;
+	struct cgit_filter *email_filter;
 	struct string_list submodules;
 };
 
@@ -250,6 +251,7 @@ struct cgit_config {
 	struct cgit_filter *about_filter;
 	struct cgit_filter *commit_filter;
 	struct cgit_filter *source_filter;
+	struct cgit_filter *email_filter;
 };
 
 struct cgit_page {
diff --git a/cgitrc.5.txt b/cgitrc.5.txt
index d1e872f..f462349 100644
--- a/cgitrc.5.txt
+++ b/cgitrc.5.txt
@@ -117,6 +117,14 @@ css::
 	Url which specifies the css document to include in all cgit pages.
 	Default value: "/cgit.css".
 
+email-filter::
+	Specifies a command which will be invoked to format names and email
+	address of committers, authors, and taggers, as represented in various
+	places throughout the cgit interface. This command will receive an
+	email address as its only command line argument, and the text to
+	format on STDIN. It is to write the formatted text back out onto
+	STDOUT. Default value: none. See also: "FILTER API".
+
 embedded::
 	Flag which, when set to "1", will make cgit generate a html fragment
 	suitable for embedding in other html pages. Default value: none. See
@@ -457,6 +465,10 @@ repo.defbranch::
 repo.desc::
 	The value to show as repository description. Default value: none.
 
+repo.email-filter::
+	Override the default email-filter. Default value: none. See also:
+	"enable-filter-overrides". See also: "FILTER API".
+
 repo.enable-commit-graph::
 	A flag which can be used to disable the global setting
 	`enable-commit-graph'. Default value: none.
@@ -601,6 +613,12 @@ commit filter::
 	be filtered is available on standard input and the filtered text is
 	expected on standard output.
 
+email filter::
+	This filter is given a single parameter: the email address of the
+	relevent user. The filter will then receive the text string to format
+	on standard input and is expected to write to standard output the
+	formatted text to be included in the page.
+
 source filter::
 	This filter is given a single parameter: the filename of the source
 	file to filter. The filter can use the filename to determine (for
diff --git a/filter.c b/filter.c
index 7db98b1..e895587 100644
--- a/filter.c
+++ b/filter.c
@@ -368,6 +368,7 @@ struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype)
 		colon = NULL;
 
 	switch (filtertype) {
+		case EMAIL:
 		case SOURCE:
 		case ABOUT:
 			argument_count = 1;
diff --git a/shared.c b/shared.c
index 4626148..7e88bbd 100644
--- a/shared.c
+++ b/shared.c
@@ -71,6 +71,7 @@ struct cgit_repo *cgit_add_repo(const char *url)
 	ret->about_filter = ctx.cfg.about_filter;
 	ret->commit_filter = ctx.cfg.commit_filter;
 	ret->source_filter = ctx.cfg.source_filter;
+	ret->email_filter = ctx.cfg.email_filter;
 	ret->clone_url = ctx.cfg.clone_url;
 	ret->submodules.strdup_strings = 1;
 	return ret;
diff --git a/ui-commit.c b/ui-commit.c
index 5ac79c0..bd14ef0 100644
--- a/ui-commit.c
+++ b/ui-commit.c
@@ -44,20 +44,24 @@ void cgit_print_commit(char *hex, const char *prefix)
 	cgit_print_diff_ctrls();
 	html("<table summary='commit info' class='commit-info'>\n");
 	html("<tr><th>author</th><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->author_email);
 	html_txt(info->author);
 	if (!ctx.cfg.noplainemail) {
 		html(" ");
 		html_txt(info->author_email);
 	}
+	cgit_close_filter(ctx.repo->email_filter);
 	html("</td><td class='right'>");
 	cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
 	html("</td></tr>\n");
 	html("<tr><th>committer</th><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->committer_email);
 	html_txt(info->committer);
 	if (!ctx.cfg.noplainemail) {
 		html(" ");
 		html_txt(info->committer_email);
 	}
+	cgit_close_filter(ctx.repo->email_filter);
 	html("</td><td class='right'>");
 	cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
 	html("</td></tr>\n");
diff --git a/ui-log.c b/ui-log.c
index 584336a..957d887 100644
--- a/ui-log.c
+++ b/ui-log.c
@@ -168,7 +168,9 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
 			 sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0);
 	show_commit_decorations(commit);
 	html("</td><td>");
+	cgit_open_filter(ctx.repo->email_filter, info->author_email);
 	html_txt(info->author);
+	cgit_close_filter(ctx.repo->email_filter);
 
 	if (revs->graph) {
 		html("</td><td>");
diff --git a/ui-refs.c b/ui-refs.c
index c97b0c6..d125459 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -77,7 +77,9 @@ static int print_branch(struct refinfo *ref)
 	if (ref->object->type == OBJ_COMMIT) {
 		cgit_commit_link(info->subject, NULL, NULL, name, NULL, NULL, 0);
 		html("</td><td>");
+		cgit_open_filter(ctx.repo->email_filter, info->author_email);
 		html_txt(info->author);
+		cgit_close_filter(ctx.repo->email_filter);
 		html("</td><td colspan='2'>");
 		cgit_print_age(info->commit->date, -1, NULL);
 	} else {
@@ -154,10 +156,15 @@ static int print_tag(struct refinfo *ref)
 		cgit_object_link(obj);
 	html("</td><td>");
 	if (info) {
-		if (info->tagger)
+		if (info->tagger) {
+			cgit_open_filter(ctx.repo->email_filter, info->tagger_email);
 			html_txt(info->tagger);
+			cgit_close_filter(ctx.repo->email_filter);
+		}
 	} else if (ref->object->type == OBJ_COMMIT) {
+		cgit_open_filter(ctx.repo->email_filter, ref->commit->author_email);
 		html_txt(ref->commit->author);
+		cgit_close_filter(ctx.repo->email_filter);
 	}
 	html("</td><td colspan='2'>");
 	if (info) {
diff --git a/ui-tag.c b/ui-tag.c
index ec9c757..adbdb90 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -77,11 +77,13 @@ void cgit_print_tag(char *revname)
 		}
 		if (info->tagger) {
 			html("<tr><td>tagged by</td><td>");
+			cgit_open_filter(ctx.repo->email_filter, info->tagger_email);
 			html_txt(info->tagger);
 			if (info->tagger_email && !ctx.cfg.noplainemail) {
 				html(" ");
 				html_txt(info->tagger_email);
 			}
+			cgit_close_filter(ctx.repo->email_filter);
 			html("</td></tr>\n");
 		}
 		html("<tr><td>tagged object</td><td class='sha1'>");
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 11/12] filter: add simple gravatar email filter
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (9 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 10/12] filter: add support for email filter Jason
@ 2014-01-13  4:11 ` Jason
  2014-01-13  4:11 ` [PATCH 12/12] filter: add gravatar lua script Jason
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 filters/email-gravatar.py | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100755 filters/email-gravatar.py

diff --git a/filters/email-gravatar.py b/filters/email-gravatar.py
new file mode 100755
index 0000000..d635b45
--- /dev/null
+++ b/filters/email-gravatar.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+# This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+#
+# The following environment variables can be used to retrieve the configuration
+# of the repository for which this script is called:
+# CGIT_REPO_URL        ( = repo.url       setting )
+# CGIT_REPO_NAME       ( = repo.name      setting )
+# CGIT_REPO_PATH       ( = repo.path      setting )
+# CGIT_REPO_OWNER      ( = repo.owner     setting )
+# CGIT_REPO_DEFBRANCH  ( = repo.defbranch setting )
+# CGIT_REPO_SECTION    ( = section        setting )
+# CGIT_REPO_CLONE_URL  ( = repo.clone-url setting )
+#
+# It receives an email address on argv[1] and text on stdin. It prints
+# to stdout that text prepended by a gravatar at 10pt.
+
+import sys
+import hashlib
+
+email = sys.argv[1].lower().strip()
+if email[0] == '<':
+        email = email[1:]
+if email[-1] == '>':
+        email = email[0:-1]
+
+md5 = hashlib.md5(email.encode()).hexdigest()
+text = sys.stdin.read().strip()
+
+print("<img src='//www.gravatar.com/avatar/" + md5 + "?s=16&d=retro' style='height:10pt;width:10pt'> " + text)
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 12/12] filter: add gravatar lua script
  2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
                   ` (10 preceding siblings ...)
  2014-01-13  4:11 ` [PATCH 11/12] filter: add simple gravatar " Jason
@ 2014-01-13  4:11 ` Jason
  11 siblings, 0 replies; 19+ messages in thread
From: Jason @ 2014-01-13  4:11 UTC (permalink / raw)


Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
---
 filters/email-gravatar.lua | 25 +++++++++++++++++++++++++
 filters/email-gravatar.py  |  3 +++
 2 files changed, 28 insertions(+)
 create mode 100644 filters/email-gravatar.lua

diff --git a/filters/email-gravatar.lua b/filters/email-gravatar.lua
new file mode 100644
index 0000000..ef1bdbc
--- /dev/null
+++ b/filters/email-gravatar.lua
@@ -0,0 +1,25 @@
+-- This script may be used with the email-filter or repo.email-filter settings in cgitrc.
+-- It adds gravatar icons to author names. It is designed to be used with the lua:
+-- prefix in filters. It is much faster than the corresponding python script.
+--
+-- Requirements:
+-- 	luacrypto >= 0.3
+-- 	<http://mkottman.github.io/luacrypto/>
+--
+
+require("crypto")
+
+function filter_open(email)
+	buffer = ""
+	md5 = crypto.digest("md5", email:sub(2, -2):lower())
+end
+
+function filter_close()
+	html("<img src='//www.gravatar.com/avatar/" .. md5 .. "?s=16&d=retro' style='height:10pt;width:10pt'> " .. buffer)
+end
+
+function filter_write(str)
+	buffer = buffer .. str
+end
+
+
diff --git a/filters/email-gravatar.py b/filters/email-gravatar.py
index d635b45..52a184b 100755
--- a/filters/email-gravatar.py
+++ b/filters/email-gravatar.py
@@ -1,5 +1,8 @@
 #!/usr/bin/env python3
 
+# Please prefer the email-gravatar.lua using lua: as a prefix over this script. This
+# script is very slow, in comparison.
+#
 # This script may be used with the email-filter or repo.email-filter settings in cgitrc.
 #
 # The following environment variables can be used to retrieve the configuration
-- 
1.8.5.2



^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 05/12] filter: basic write hooking infrastructure
  2014-01-13  4:11 ` [PATCH 05/12] filter: basic write hooking infrastructure Jason
@ 2014-01-13  8:19   ` cgit
  0 siblings, 0 replies; 19+ messages in thread
From: cgit @ 2014-01-13  8:19 UTC (permalink / raw)


On Mon, 13 Jan 2014 at 05:11:12, Jason A. Donenfeld wrote:
> Filters can now call hook_write and unhook_write if they want to
> redirect writing to stdout to a different function. This saves us from
> potential file descriptor pipes and other less efficient mechanisms.
> 
> We do this instead of replacing the call in html_raw because some places
> stdlib's printf functions are used (ui-patch or within git itself),
> which has its own internal buffering, which makes it difficult to
> interlace our function calls. So, we dlsym libc's write and then
> override it in the link stage.

Clever. Not sure whether I like it, though. I think it would be much
better to have a more transparent hooking mechanism that does use any
"tricks". Could you elaborate on where Git directly prints for us?

> 
> Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
> ---
>  cgit.c   |  2 ++
>  cgit.h   |  1 +
>  cgit.mk  |  4 +++-
>  filter.c | 30 ++++++++++++++++++++++++++++++
>  4 files changed, 36 insertions(+), 1 deletion(-)
> 
> [...]
> diff --git a/cgit.mk b/cgit.mk
> index 19a76e7..9d6dea8 100644
> --- a/cgit.mk
> +++ b/cgit.mk
> @@ -61,6 +61,8 @@ $(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
>  $(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
>         -DCGIT_VERSION='"$(CGIT_VERSION)"'
>  
> +CGIT_LIBS += -ldl

This breaks compilation at least on FreeBSD and OpenBSD which do no have
libdl (and have dlsym() built-in as a part of libc). I am not sure about
other platforms. So if we go this way, we should check how to make this
cross-platform compatible.

> +

Stray newline?

>  
>  # Git handles dependencies using ":=" so dependencies in CGIT_OBJ are not
>  # handled by that and we must handle them ourselves.
> @@ -88,4 +90,4 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
>         $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $<
>  
>  $(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
> -       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
> +       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
> diff --git a/filter.c b/filter.c
> index 86c1d5d..8990575 100644
> --- a/filter.c
> +++ b/filter.c
> @@ -12,6 +12,10 @@
>  #include <unistd.h>
>  #include <string.h>
>  #include <stdlib.h>
> +#include <dlfcn.h>
> +
> +static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
> +static ssize_t (*filter_write)(const void *buf, size_t count) = NULL;
>  
>  static int open_exec_filter(struct cgit_filter *base, va_list ap)
>  {
> @@ -192,3 +196,29 @@ void cgit_cleanup_filters(void)
>                 reap_filter(cgit_repolist.repos[i].source_filter);
>         }
>  }
> +
> +void cgit_init_filters(void)
> +{
> +       libc_write = dlsym(RTLD_NEXT, "write");
> +       if (!libc_write)
> +               die("Could not locate libc's write function");
> +}
> +
> +ssize_t write(int fd, const void *buf, size_t count)
> +{
> +       if (fd != STDOUT_FILENO || !filter_write)
> +               return libc_write(fd, buf, count);
> +       return filter_write(buf, count);
> +}
> +
> +static inline void hook_write(ssize_t (*new_write)(const void *buf, size_t count))
> +{
> +       /* We want to avoid buggy nested patterns. */
> +       assert(filter_write == NULL);
> +       filter_write = new_write;
> +}
> +static inline void unhook_write()
> +{
> +       assert(filter_write != NULL);
> +       filter_write = NULL;
> +}
> -- 
> 1.8.5.2
> 
> _______________________________________________
> CGit mailing list
> CGit at lists.zx2c4.com
> http://lists.zx2c4.com/mailman/listinfo/cgit


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
@ 2014-01-13  8:31   ` cgit
  2014-01-13  8:53     ` john
  2014-01-13  8:39   ` cgit
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 19+ messages in thread
From: cgit @ 2014-01-13  8:31 UTC (permalink / raw)


This patch is quite messy and hard to read. I read your cover-letter but
maybe you still want to clean this up when dealing with the other
suggestions during a rebase -- shouldn't be too hard when using an
editor with good Git integration (like fugitive for Vim).

On Mon, 13 Jan 2014 at 05:11:13, Jason A. Donenfeld wrote:
> [...]
> +ifdef NO_LUA

We should document this in the installation instructions section in the
README. I also wonder whether this should made an opt-in feature?

> +       CFLAGS += -DNO_LUA
> +else
> +       CGIT_LIBS += -llua

Similar: Add Lua/LuaJIT (Will you squash the LuaJIT Makefile fix into
this one? Or is there any reason to use Lua first and switch to LuaJIT
later?) to the dependencies section of the README file and mention that
it is optional.

> +endif
> +
> +CGIT_LIBS += -ldl
> +
> +
> +
>  CGIT_OBJ_NAMES += cgit.o
>  CGIT_OBJ_NAMES += cache.o
>  CGIT_OBJ_NAMES += cmd.o
> [...]


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
  2014-01-13  8:31   ` cgit
@ 2014-01-13  8:39   ` cgit
  2014-01-13  8:55   ` john
  2014-01-13  9:41   ` bluewind
  3 siblings, 0 replies; 19+ messages in thread
From: cgit @ 2014-01-13  8:39 UTC (permalink / raw)


On Mon, 13 Jan 2014 at 05:11:13, Jason A. Donenfeld wrote:
> Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
> ---
>  cgit.h   |   2 +-
>  cgit.mk  |  13 ++-
>  filter.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
>  3 files changed, 230 insertions(+), 69 deletions(-)
> 
> [...]
> +static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count)
> +{
> +       struct lua_filter *filter = (struct lua_filter *) base;
> +
> +       lua_getglobal(filter->lua_state, "filter_write");
> +       lua_pushlstring(filter->lua_state, buf, count);
> +       if (lua_pcall(filter->lua_state, 1, 0, 0)) {
> +               errno = EPROTO;

Just noticed that this also breaks compilation under OpenBSD:

    ../filter.c:199: error: 'EPROTO' undeclared (first use in this function)

The error number seems to be part of POSIX, though, so not sure if we
should care about OpenBSD being non-compliant here.

> +               return -1;
> +       }
> +       return count;
> +}
> [...]


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  8:31   ` cgit
@ 2014-01-13  8:53     ` john
  0 siblings, 0 replies; 19+ messages in thread
From: john @ 2014-01-13  8:53 UTC (permalink / raw)


On Mon, Jan 13, 2014 at 09:31:39AM +0100, Lukas Fleischer wrote:
> This patch is quite messy and hard to read. I read your cover-letter but
> maybe you still want to clean this up when dealing with the other
> suggestions during a rebase -- shouldn't be too hard when using an
> editor with good Git integration (like fugitive for Vim).
> 
> On Mon, 13 Jan 2014 at 05:11:13, Jason A. Donenfeld wrote:
> > [...]
> > +ifdef NO_LUA
> 
> We should document this in the installation instructions section in the
> README. I also wonder whether this should made an opt-in feature?
> 
> > +       CFLAGS += -DNO_LUA
> > +else
> > +       CGIT_LIBS += -llua
> 
> Similar: Add Lua/LuaJIT (Will you squash the LuaJIT Makefile fix into
> this one? Or is there any reason to use Lua first and switch to LuaJIT
> later?) to the dependencies section of the README file and mention that
> it is optional.

I think we should support both vanilla Lua and LuaJIT if we can (I
believe LuaJIT can be used as a drop-in replacement, so there's no
reason this shouldn't be possible).

> > +endif
> > +
> > +CGIT_LIBS += -ldl
> > +
> > +
> > +
> >  CGIT_OBJ_NAMES += cgit.o
> >  CGIT_OBJ_NAMES += cache.o
> >  CGIT_OBJ_NAMES += cmd.o
> > [...]


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
  2014-01-13  8:31   ` cgit
  2014-01-13  8:39   ` cgit
@ 2014-01-13  8:55   ` john
  2014-01-13  9:41   ` bluewind
  3 siblings, 0 replies; 19+ messages in thread
From: john @ 2014-01-13  8:55 UTC (permalink / raw)


On Mon, Jan 13, 2014 at 05:11:13AM +0100, Jason A. Donenfeld wrote:
[snip]
> +static int html_lua_filter(lua_State *lua_state)
> +{
> +	size_t len;
> +	const char *str;
> +
> +	str = lua_tostring(lua_state, 1);
> +	if (!str)
> +		return 0;
> +	len = strlen(str);
> +	libc_write(STDOUT_FILENO, str, len);
> +	return 0;
> +}
> +
> +static void cleanup_lua_filter(struct cgit_filter *base)
> +{
> +	struct lua_filter *filter = (struct lua_filter *) base;
> +
> +	if (!filter->lua_state)
> +		return;
> +
> +	lua_close(filter->lua_state);
> +	filter->lua_state = NULL;
> +	if (filter->script_file) {
> +		free(filter->script_file);
> +		filter->script_file = NULL;
> +	}
> +}
> +
> +static int init_lua_filter(struct lua_filter *filter)
> +{
> +	if (filter->lua_state)
> +		return 0;
> +
> +	if (!(filter->lua_state = luaL_newstate()))
> +		return 1;
> +
> +	luaL_openlibs(filter->lua_state);
> +
> +	lua_pushcfunction(filter->lua_state, html_lua_filter);
> +	lua_setglobal(filter->lua_state, "html");

It would be good to provide some of the other html_* functions here,
particularly html_txt so that filters don't need to re-invent the wheel
for escaping output.  That's probably slightly harder than the plain
HTML, but I suspect we just need to wrap the call to the underlying
function in an unhook_write/hook_write pair.

> +
> +	if (luaL_dofile(filter->lua_state, filter->script_file)) {
> +		lua_close(filter->lua_state);
> +		filter->lua_state = NULL;
> +		return 1;
> +	}
> +	return 0;
> +}
> +
> +static int open_lua_filter(struct cgit_filter *base, va_list ap)
> +{
> +	struct lua_filter *filter = (struct lua_filter *) base;
> +	int i;
> +
> +	if (init_lua_filter(filter))
> +		return 1;
> +
> +	hook_write(base, write_lua_filter);
> +
> +	lua_getglobal(filter->lua_state, "filter_open");
> +	for (i = 0; i < filter->base.argument_count; ++i)
> +		lua_pushstring(filter->lua_state, va_arg(ap, char *));
> +	if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0))
> +		return 1;
> +	return 0;
> +}
> +
> +static int close_lua_filter(struct cgit_filter *base)
> +{
> +	struct lua_filter *filter = (struct lua_filter *) base;
> +	int ret = 0;
> +
> +	lua_getglobal(filter->lua_state, "filter_close");
> +	if (lua_pcall(filter->lua_state, 0, 0, 0))
> +		ret = 1;
> +	unhook_write();
> +	return ret;
> +}
> +
> +static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix)
> +{
> +	struct lua_filter *filter = (struct lua_filter *) base;
> +	fprintf(f, "%slua:%s\n", prefix, filter->script_file);
> +}
> +
> +
> +static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
> +{
> +	struct lua_filter *filter;
> +
> +	filter = xmalloc(sizeof(*filter));
> +	memset(filter, 0, sizeof(*filter));
> +	filter->base.open = open_lua_filter;
> +	filter->base.close = close_lua_filter;
> +	filter->base.fprintf = fprintf_lua_filter;
> +	filter->base.cleanup = cleanup_lua_filter;
> +	filter->base.argument_count = argument_count;
> +	filter->script_file = xstrdup(cmd);
> +
> +	return &filter->base;
>  }
>  
> +#endif


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH 06/12] filter: add preliminary lua support
  2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
                     ` (2 preceding siblings ...)
  2014-01-13  8:55   ` john
@ 2014-01-13  9:41   ` bluewind
  3 siblings, 0 replies; 19+ messages in thread
From: bluewind @ 2014-01-13  9:41 UTC (permalink / raw)


On 13.01.2014 05:11, Jason A. Donenfeld wrote:
> Signed-off-by: Jason A. Donenfeld <Jason at zx2c4.com>
> ---
>  cgit.h   |   2 +-
>  cgit.mk  |  13 ++-
>  filter.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++++---------------

All those *_lua_filter functions look rather self contained, maybe they
should be split into filter_lua.c for readability?

Also I'm not sure why documenting the filter and adding error reporting
to totally new code go into separate commits. They are one logical
change after all.

As Lukas already said it's probably a lot better if you split the
cleanup and moving-stuff-around into it's own patch. (Or merge it into
"basic write hooking infrastructure" since you create that code there so
it should be created at the correct place)


-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: OpenPGP digital signature
URL: <http://lists.zx2c4.com/pipermail/cgit/attachments/20140113/714403af/attachment-0001.asc>


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2014-01-13  9:41 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-13  4:11 [PATCH 00/12] filter framework and lua integration: complete Jason
2014-01-13  4:11 ` [PATCH 01/12] filter: add fprintf_filter function Jason
2014-01-13  4:11 ` [PATCH 02/12] filter: add interface layer Jason
2014-01-13  4:11 ` [PATCH 03/12] filter: introduce "filter type" prefix Jason
2014-01-13  4:11 ` [PATCH 04/12] filter: allow for cleanup hook for filter types Jason
2014-01-13  4:11 ` [PATCH 05/12] filter: basic write hooking infrastructure Jason
2014-01-13  8:19   ` cgit
2014-01-13  4:11 ` [PATCH 06/12] filter: add preliminary lua support Jason
2014-01-13  8:31   ` cgit
2014-01-13  8:53     ` john
2014-01-13  8:39   ` cgit
2014-01-13  8:55   ` john
2014-01-13  9:41   ` bluewind
2014-01-13  4:11 ` [PATCH 07/12] filter: document lua filter type Jason
2014-01-13  4:11 ` [PATCH 08/12] filter: lua error reporting Jason
2014-01-13  4:11 ` [PATCH 09/12] filter: return on null filter from open and close Jason
2014-01-13  4:11 ` [PATCH 10/12] filter: add support for email filter Jason
2014-01-13  4:11 ` [PATCH 11/12] filter: add simple gravatar " Jason
2014-01-13  4:11 ` [PATCH 12/12] filter: add gravatar lua script Jason

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).