cgit

commit d6f6072560c963065b13c704fa1fa6f8950e4bac

Author: Lars Hjemli <hjemli@gmail.com>

Add generic filter/plugin infrastructure

The functions cgit_open_filter() and cgit_close_filter() can be used to
execute filters on the output stream from cgit.

Signed-off-by: Lars Hjemli <hjemli@gmail.com>

 cgit.c | 15 +++++++++++++++
 cgit.h | 12 ++++++++++++
 shared.c | 35 +++++++++++++++++++++++++++++++++++


diff --git a/cgit.c b/cgit.c
index 2039ab1de53699b6d29a23f1409461386589a227..779a4643abb87e84aef93dc40239c26a4a17d63b 100644
--- a/cgit.c
+++ b/cgit.c
@@ -17,6 +17,21 @@ #include "scan-tree.h"
 
 const char *cgit_version = CGIT_VERSION;
 
+struct cgit_filter *new_filter(const char *cmd, int extra_args)
+{
+	struct cgit_filter *f;
+
+	if (!cmd)
+		return NULL;
+
+	f = xmalloc(sizeof(struct cgit_filter));
+	f->cmd = xstrdup(cmd);
+	f->argv = xmalloc((2 + extra_args) * sizeof(char *));
+	f->argv[0] = f->cmd;
+	f->argv[1] = NULL;
+	return f;
+}
+
 void config_cb(const char *name, const char *value)
 {
 	if (!strcmp(name, "root-title"))




diff --git a/cgit.h b/cgit.h
index 8c64efeaa6e7f5698751e202b64765280e3985cf..d0fff1f7f8cd5a0af073afce2d8ea23301811154 100644
--- a/cgit.h
+++ b/cgit.h
@@ -129,6 +129,15 @@ 	char *sort;
 	int showmsg;
 };
 
+struct cgit_filter {
+	char *cmd;
+	char **argv;
+	int old_stdout;
+	int pipe_fh[2];
+	int pid;
+	int exitstatus;
+};
+
 struct cgit_config {
 	char *agefile;
 	char *cache_root;
@@ -247,6 +256,9 @@
 extern const char *cgit_repobasename(const char *reponame);
 
 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);
 
 
 #endif /* CGIT_H */




diff --git a/shared.c b/shared.c
index cce0af40db29b77527fe1f6a7a897b4076c9621b..288cfa22b4888ee8b773188089d2ecc496714f94 100644
--- a/shared.c
+++ b/shared.c
@@ -355,3 +355,38 @@ 		str += tl;
 	}
 	return rv;
 }
+
+int cgit_open_filter(struct cgit_filter *filter)
+{
+
+	filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
+		"Unable to duplicate STDOUT");
+	chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
+	filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
+	if (filter->pid == 0) {
+		close(filter->pipe_fh[1]);
+		chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
+			"Unable to use pipe as STDIN");
+		execvp(filter->cmd, filter->argv);
+		die("Unable to exec subprocess %s: %s (%d)", filter->cmd,
+			strerror(errno), errno);
+	}
+	close(filter->pipe_fh[0]);
+	chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
+		"Unable to use pipe as STDOUT");
+	close(filter->pipe_fh[1]);
+	return 0;
+}
+
+int cgit_close_filter(struct cgit_filter *filter)
+{
+	chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO),
+		"Unable to restore STDOUT");
+	close(filter->old_stdout);
+	if (filter->pid < 0)
+		return 0;
+	waitpid(filter->pid, &filter->exitstatus, 0);
+	if (WIFEXITED(filter->exitstatus) && !WEXITSTATUS(filter->exitstatus))
+		return 0;
+	die("Subprocess %s exited abnormally", filter->cmd);
+}