do not edit — generated by btf.
git.druid.rocksindexdruid520wgitsrc/webgit.c

src/webgit.c


#define NULL ((void*)0)
 
#define SYS_read 0
#define SYS_write 1
#define SYS_open 2
#define SYS_close 3
#define SYS_pipe 22
#define SYS_dup2 33
#define SYS_fork 57
#define SYS_execve 59
#define SYS_exit 60
#define SYS_wait4 61
#define SYS_mkdir 83
#define SYS_rmdir 84
#define SYS_unlink 87
#define SYS_getdents64 217
 
#define O_RDONLY 0
#define O_WRONLY 1
#define O_CREAT 64
#define O_TRUNC 512
 
#define DT_DIR 4
 
#define REPOS "/srv/repos"
#define WEB   "/srv/git"
#define GIT   "/usr/bin/git"
 
struct commit
{
	long time;
	char hash[64];
	char author[128];
	char subject[512];
	char user[128];
	char repo[128];
};
 
static struct commit commits[512];
static long ncommits;
 
static char obuf[262144];
static volatile long olen;
 
static char lb[4096];
static long lblen;
static long last_empty;
 
static void oreset(void);
static void oput(const char* s);
static void oesc(const char* s, long n);
static void onum(long n);
static void onum2(long n);
static void lbset(const char* s);
static void lbcat(const char* s);
static void lbesc(const char* s);
static void lbnum(long n);
static void lbnump(long n);
static void lbflush(void);
static long write(long fd, const void* buf, long n);
static long open(const char* path, long flags, long mode);
static long close(long fd);
static void mkdir1(const char* path);
static void mkpath(const char* p);
static void mkfilepath(const char* p);
static void unlink1(const char* path);
static void rmdir1(const char* path);
static void rmtree(const char* dir);
static void oflush(const char* path);
static long strlen(const char* s);
static int strcmp(const char* a, const char* b);
static long strlcpy(char* d, const char* s, long size);
static long strlcat(char* d, const char* s, long size);
static char* strncpy(char* d, const char* s, long n);
static void* memcpy(void* d, const void* s, long n);
static void trim(char* s);
static long atoi(const char* s);
static int isrepo(const char* name);
static void stripgit(char* name);
static long rungit(const char* gitdir, const char* a, const char* b, const char* c, const char* d, const char* e, const char* f);
static void reap(void);
static long readall(long fd, char* buf, long max);
static void ymd(long days, long* y, long* m, long* d);
static long splitfields(char* s, char** f, long want);
static void readdesc(const char* user, const char* repo, char* desc, long sz);
static long showfile(const char* gitdir, const char* branch, const char* path, char* out, long max);
static void readacl(const char* user, const char* repo, char* out, long max);
static long listbranches(const char* gitdir, char branches[][128], long max);
static void emit_filetree(const char* user, const char* repo, const char* gitdir, const char* branch, const char* dir, long shallow);
static void emit_branchnav(const char* user, const char* repo, const char* branch, long nbranches, char branches[][128]);
static void emit_nav_user(const char* user);
static void emit_pathnav(const char* user, const char* repo, const char* branch, const char* path);
static void emit_dir(const char* user, const char* repo, const char* gitdir, const char* branch, const char* dir);
static void emit_file(const char* user, const char* repo, const char* branch, const char* path, const char* gitdir);
static void emit_repo_clean(const char* user, const char* repo);
static void emit_repo_files(const char* user, const char* repo);
static void emit_repo_dirs(const char* user, const char* repo);
static void emit_log(const char* user, const char* repo, const char* branch, long record);
static void emit_profile(const char* user);
static void emit_repo_page(const char* user, const char* repo, const char* branch, long nbranches, char branches[][128], const char* gitdir, const char* desc, const char* acl, const char* outpath, long record, long shallow);
static void emit_repo(const char* user, const char* repo);
static void emit_user_repos(const char* user);
static void swap(struct commit* a, struct commit* b);
static void sortcommits(void);
static void emit_rss(void);
static void emit_global(void);
int main(void);
 
long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6);
 
static void
oreset(void)
{
	olen = 0;
	last_empty = 1;
}
 
static void
oput(const char* s)
{
	long i = 0;
 
	while(s[i] != '\0')
	{
		obuf[olen] = s[i];
		olen = olen + 1;
		i = i + 1;
	}
}
 
static void
oesc(const char* s, long n)
{
	long i = 0;
 
	while(i < n)
	{
		if(s[i] == '\\' || s[i] == '[')
		{
			obuf[olen] = '\\';
			olen = olen + 1;
		}
		obuf[olen] = s[i];
		olen = olen + 1;
		i = i + 1;
	}
}
 
static void
onum(long n)
{
	char tmp[24];
	long i = 0;
	long j;
 
	if(n == 0)
	{
		obuf[olen] = '0';
		olen = olen + 1;
		return;
	}
	if(n < 0)
	{
		obuf[olen] = '-';
		olen = olen + 1;
		n = -n;
	}
	while(n > 0)
	{
		tmp[i] = (char)('0' + (n % 10));
		i = i + 1;
		n = n / 10;
	}
	j = i;
	while(j > 0)
	{
		j = j - 1;
		obuf[olen] = tmp[j];
		olen = olen + 1;
	}
}
 
static void
onum2(long n)
{
	if(n < 10)
	{
		obuf[olen] = '0';
		olen = olen + 1;
	}
	onum(n);
}
 
static void
lbset(const char* s)
{
	lblen = 0;
	lbcat(s);
}
 
static void
lbcat(const char* s)
{
	long i = 0;
 
	while(s[i] != '\0' && lblen < 4095)
	{
		lb[lblen] = s[i];
		lblen = lblen + 1;
		i = i + 1;
	}
}
 
static void
lbesc(const char* s)
{
	long i = 0;
 
	while(s[i] != '\0')
	{
		if(s[i] == '\\' || s[i] == '[')
		{
			if(lblen > 4092)
			{
				break;
			}
			lb[lblen] = '\\';
			lblen = lblen + 1;
		}
		else if(lblen > 4094)
		{
			break;
		}
		lb[lblen] = s[i];
		lblen = lblen + 1;
		i = i + 1;
	}
}
 
static void
lbnum(long n)
{
	char tmp[24];
	long i = 0;
	long j;
 
	if(n == 0)
	{
		tmp[i] = '0';
		i = i + 1;
	}
	if(n < 0)
	{
		tmp[i] = '-';
		i = i + 1;
		n = -n;
	}
	while(n > 0)
	{
		tmp[i] = (char)('0' + (n % 10));
		i = i + 1;
		n = n / 10;
	}
	j = i;
	while(j > 0 && lblen < 4095)
	{
		j = j - 1;
		lb[lblen] = tmp[j];
		lblen = lblen + 1;
	}
}
 
static void
lbnump(long n)
{
	char tmp[24];
	long i = 0;
	long j;
 
	if(n == 0)
	{
		tmp[i] = '0';
		i = i + 1;
	}
	while(n > 0)
	{
		tmp[i] = (char)('0' + (n % 10));
		i = i + 1;
		n = n / 10;
	}
	while(i < 2)
	{
		tmp[i] = '0';
		i = i + 1;
	}
	j = i;
	while(j > 0 && lblen < 4095)
	{
		j = j - 1;
		lb[lblen] = tmp[j];
		lblen = lblen + 1;
	}
}
 
static void
lbflush(void)
{
	if(lblen > 0 && last_empty == 0)
	{
		oput("[br]");
	}
	lb[lblen] = '\0';
	oput(lb);
	oput("\n");
	if(lblen == 0)
	{
		last_empty = 1;
	}
	else
	{
		last_empty = 0;
	}
	lblen = 0;
}
 
static long
write(long fd, const void* buf, long n)
{
	return syscall6(SYS_write, fd, (long)buf, n, 0, 0, 0);
}
 
static long
open(const char* path, long flags, long mode)
{
	return syscall6(SYS_open, (long)path, flags, mode, 0, 0, 0);
}
 
static long
close(long fd)
{
	return syscall6(SYS_close, fd, 0, 0, 0, 0, 0);
}
 
static void
mkdir1(const char* path)
{
	syscall6(SYS_mkdir, (long)path, 0755, 0, 0, 0, 0);
}
 
static void
mkpath(const char* p)
{
	char buf[1024];
	long i = 1;
	long n = 0;
 
	while(p[n] != '\0')
	{
		n = n + 1;
	}
	while(i <= n)
	{
		if(p[i] == '/' || i == n)
		{
			memcpy(buf, p, i);
			buf[i] = '\0';
			mkdir1(buf);
		}
		i = i + 1;
	}
}
 
static void
mkfilepath(const char* p)
{
	char buf[2048];
	long n = 0;
	long i = 0;
	long last = -1;
 
	while(p[n] != '\0')
	{
		n = n + 1;
	}
	while(i < n)
	{
		if(p[i] == '/')
		{
			last = i;
		}
		i = i + 1;
	}
	if(last < 1)
	{
		return;
	}
	memcpy(buf, p, last);
	buf[last] = '\0';
	mkpath(buf);
}
 
static void
unlink1(const char* path)
{
	syscall6(SYS_unlink, (long)path, 0, 0, 0, 0, 0);
}
 
static void
rmdir1(const char* path)
{
	syscall6(SYS_rmdir, (long)path, 0, 0, 0, 0, 0);
}
 
static void
rmtree(const char* dir)
{
	char dbuf[16384];
	char sub[2048];
	unsigned short reclen;
	char type;
	char* name;
	long fd;
	long dn;
	long off = 0;
 
	fd = open(dir, O_RDONLY, 0);
	if(fd < 0)
	{
		return;
	}
	dn = syscall6(SYS_getdents64, fd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
	close(fd);
	if(dn < 0)
	{
		return;
	}
	while(off < dn)
	{
		reclen = *(unsigned short*)(dbuf + off + 16);
		type = dbuf[off + 18];
		name = dbuf + off + 19;
		off = off + reclen;
		if(name[0] == '.' && name[1] == '\0')
		{
			continue;
		}
		if(name[0] == '.' && name[1] == '.' && name[2] == '\0')
		{
			continue;
		}
		strlcpy(sub, dir, sizeof(sub));
		strlcat(sub, "/", sizeof(sub));
		strlcat(sub, name, sizeof(sub));
		if(type == DT_DIR)
		{
			rmtree(sub);
			rmdir1(sub);
		}
		else
		{
			unlink1(sub);
		}
	}
}
 
static void
oflush(const char* path)
{
	long fd;
 
	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if(fd < 0)
	{
		oreset();
		return;
	}
	write(fd, obuf, olen);
	close(fd);
	oreset();
}
 
static long
strlen(const char* s)
{
	long n = 0;
 
	while(s[n] != '\0')
	{
		n = n + 1;
	}
	return n;
}
 
static int
strcmp(const char* a, const char* b)
{
	long i = 0;
 
	while(a[i] != '\0' && a[i] == b[i])
	{
		i = i + 1;
	}
	return (unsigned char)a[i] - (unsigned char)b[i];
}
 
static long
strlcpy(char* d, const char* s, long size)
{
	long slen = strlen(s);
	long n = slen;
 
	if(n > size - 1)
	{
		n = size - 1;
	}
	if(n > 0)
	{
		memcpy(d, s, n);
	}
	if(size > 0)
	{
		d[n] = '\0';
	}
	return slen;
}
 
static long
strlcat(char* d, const char* s, long size)
{
	long dlen = strlen(d);
	long slen = strlen(s);
	long n = slen;
 
	if(dlen >= size)
	{
		return dlen + slen;
	}
	if(n > size - dlen - 1)
	{
		n = size - dlen - 1;
	}
	if(n > 0)
	{
		memcpy(d + dlen, s, n);
	}
	d[dlen + n] = '\0';
	return dlen + slen;
}
 
static char*
strncpy(char* d, const char* s, long n)
{
	long i = 0;
 
	while(i < n && s[i] != '\0')
	{
		d[i] = s[i];
		i = i + 1;
	}
	while(i < n)
	{
		d[i] = '\0';
		i = i + 1;
	}
	return d;
}
 
static void*
memcpy(void* d, const void* s, long n)
{
	char* dd = d;
	const char* ss = s;
	long i = 0;
 
	while(i < n)
	{
		dd[i] = ss[i];
		i = i + 1;
	}
	return d;
}
 
static void
trim(char* s)
{
	long n = strlen(s);
 
	while(n > 0)
	{
		if(s[n - 1] != '\n' && s[n - 1] != '\r' &&
		    s[n - 1] != ' ' && s[n - 1] != '\t')
		{
			break;
		}
		n = n - 1;
		s[n] = '\0';
	}
}
 
static long
atoi(const char* s)
{
	long n = 0;
	long i = 0;
 
	while(s[i] >= '0' && s[i] <= '9')
	{
		n = (n * 10) + (s[i] - '0');
		i = i + 1;
	}
	return n;
}
 
static int
isrepo(const char* name)
{
	long n = strlen(name);
 
	if(n > 4 && strcmp(name + n - 4, ".git") == 0)
	{
		return 1;
	}
	return 0;
}
 
static void
stripgit(char* name)
{
	long n = strlen(name);
 
	if(n > 4)
	{
		name[n - 4] = '\0';
	}
}
 
static long
rungit(const char* gitdir, const char* a, const char* b, const char* c, const char* d, const char* e, const char* f)
{
	char* argv[9];
	char* envp[3];
	char garg[2048];
	int p[2];
	long pid;
	long dn;
 
	if(syscall6(SYS_pipe, (long)p, 0, 0, 0, 0, 0) < 0)
	{
		return -1;
	}
	pid = syscall6(SYS_fork, 0, 0, 0, 0, 0, 0);
	if(pid == 0)
	{
		syscall6(SYS_dup2, p[1], 1, 0, 0, 0, 0);
		close(p[0]);
		close(p[1]);
		dn = open("/dev/null", O_WRONLY, 0);
		if(dn >= 0)
		{
			syscall6(SYS_dup2, dn, 2, 0, 0, 0, 0);
			close(dn);
		}
		strlcpy(garg, "--git-dir=", sizeof(garg));
		strlcat(garg, gitdir, sizeof(garg));
		argv[0] = "git";
		argv[1] = garg;
		argv[2] = (char*)a;
		argv[3] = (char*)b;
		argv[4] = (char*)c;
		argv[5] = (char*)d;
		argv[6] = (char*)e;
		argv[7] = (char*)f;
		argv[8] = NULL;
		envp[0] = "PATH=/usr/bin:/bin";
		envp[1] = "HOME=/srv/repos";
		envp[2] = NULL;
		syscall6(SYS_execve, (long)GIT, (long)argv, (long)envp, 0, 0, 0);
		syscall6(SYS_exit, 1, 0, 0, 0, 0, 0);
	}
	close(p[1]);
	return p[0];
}
 
static void
reap(void)
{
	syscall6(SYS_wait4, -1, 0, 0, 0, 0, 0);
}
 
static long
readall(long fd, char* buf, long max)
{
	long total = 0;
	long n;
 
	while(total < max)
	{
		n = syscall6(SYS_read, fd, (long)(buf + total),
		    max - total, 0, 0, 0);
		if(n <= 0)
		{
			break;
		}
		total = total + n;
	}
	return total;
}
 
static void
ymd(long days, long* y, long* m, long* d)
{
	long z;
	long era;
	long doe;
	long yoe;
	long doy;
	long mp;
 
	z = days + 719468;
	if(z >= 0)
	{
		era = z / 146097;
	}
	else
	{
		era = (z - 146096) / 146097;
	}
	doe = z - (era * 146097);
	yoe = (doe - (doe / 1460) + (doe / 36524) - (doe / 146096)) / 365;
	*y = yoe + (era * 400);
	doy = doe - ((365 * yoe) + (yoe / 4) - (yoe / 100));
	mp = ((5 * doy) + 2) / 153;
	*d = doy - (((153 * mp) + 2) / 5) + 1;
	if(mp < 10)
	{
		*m = mp + 3;
	}
	else
	{
		*m = mp - 9;
	}
	if(*m <= 2)
	{
		*y = *y + 1;
	}
}
 
static char* mons[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static char* wdays[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
 
static long
splitfields(char* s, char** f, long want)
{
	long nf = 1;
	long i = 0;
 
	f[0] = s;
	while(s[i] != '\0' && nf < want)
	{
		if(s[i] == '\t')
		{
			s[i] = '\0';
			f[nf] = s + i + 1;
			nf = nf + 1;
		}
		i = i + 1;
	}
	return nf;
}
 
static void
readdesc(const char* user, const char* repo, char* desc, long sz)
{
	char path[1024];
	long fd;
	long n;
 
	strlcpy(path, REPOS "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	strlcat(path, "/", sizeof(path));
	strlcat(path, repo, sizeof(path));
	strlcat(path, ".git/description", sizeof(path));
	desc[0] = '\0';
	fd = open(path, O_RDONLY, 0);
	if(fd < 0)
	{
		return;
	}
	n = syscall6(SYS_read, fd, (long)desc, sz - 1, 0, 0, 0);
	close(fd);
	if(n > 0)
	{
		desc[n] = '\0';
		trim(desc);
	}
}
 
static long
showfile(const char* gitdir, const char* branch, const char* path,
    char* out, long max)
{
	char ref[2048];
	long fd;
	long n;
 
	strlcpy(ref, branch, sizeof(ref));
	strlcat(ref, ":", sizeof(ref));
	strlcat(ref, path, sizeof(ref));
	fd = rungit(gitdir, "show", ref, NULL, NULL, NULL, NULL);
	if(fd < 0)
	{
		return -1;
	}
	n = readall(fd, out, max - 1);
	close(fd);
	reap();
	out[n] = '\0';
	return n;
}
 
static void
readacl(const char* user, const char* repo, char* out, long max)
{
	char path[1024];
	long fd;
	long n;
 
	strlcpy(path, REPOS "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	strlcat(path, "/", sizeof(path));
	strlcat(path, repo, sizeof(path));
	strlcat(path, ".git/acl", sizeof(path));
	out[0] = '\0';
	fd = open(path, O_RDONLY, 0);
	if(fd < 0)
	{
		return;
	}
	n = syscall6(SYS_read, fd, (long)out, max - 1, 0, 0, 0);
	close(fd);
	out[n] = '\0';
}
 
static long
listbranches(const char* gitdir, char branches[][128], long max)
{
	char rbuf[65536];
	long nb = 0;
	long fd;
	long n;
	long i = 0;
	long j;
 
	fd = rungit(gitdir, "for-each-ref", "--format=%(refname:short)",
	    "refs/heads", NULL, NULL, NULL);
	if(fd < 0)
	{
		return 0;
	}
	n = readall(fd, rbuf, sizeof(rbuf) - 1);
	close(fd);
	reap();
	rbuf[n] = '\0';
	while(i < n && nb < max)
	{
		j = 0;
		while(i < n && rbuf[i] != '\n')
		{
			if(j < 126)
			{
				branches[nb][j] = rbuf[i];
				j = j + 1;
			}
			i = i + 1;
		}
		if(i < n)
		{
			i = i + 1;
		}
		branches[nb][j] = '\0';
		if(branches[nb][0] != '\0')
		{
			nb = nb + 1;
		}
	}
	return nb;
}
 
static void
emit_filetree(const char* user, const char* repo, const char* gitdir,
    const char* branch, const char* dir, long shallow)
{
	char rbuf[65536];
	char line[1024];
	char name[512];
	char type[16];
	char ref[2048];
	long fd;
	long n;
	long i;
	long j;
 
	if(dir[0] != '\0')
	{
		strlcpy(ref, branch, sizeof(ref));
		strlcat(ref, ":", sizeof(ref));
		strlcat(ref, dir, sizeof(ref));
	}
	else
	{
		strlcpy(ref, branch, sizeof(ref));
	}
	fd = rungit(gitdir, "ls-tree", ref, NULL, NULL, NULL, NULL);
	if(fd < 0)
	{
		return;
	}
	n = readall(fd, rbuf, sizeof(rbuf) - 1);
	close(fd);
	reap();
	rbuf[n] = '\0';
	i = 0;
	while(i < n)
	{
		j = 0;
		while(i < n && rbuf[i] != ' ')
		{
			i = i + 1;
		}
		if(i < n)
		{
			i = i + 1;
		}
		while(i < n && rbuf[i] != ' ')
		{
			if(j < 16)
			{
				type[j] = rbuf[i];
				j = j + 1;
			}
			i = i + 1;
		}
		type[j] = '\0';
		while(i < n && rbuf[i] != '\t')
		{
			i = i + 1;
		}
		if(i < n)
		{
			i = i + 1;
		}
		j = 0;
		while(i < n && rbuf[i] != '\n')
		{
			if(j < 510)
			{
				name[j] = rbuf[i];
				j = j + 1;
			}
			i = i + 1;
		}
		name[j] = '\0';
		if(i < n)
		{
			i = i + 1;
		}
		if(name[0] == '\0')
		{
			continue;
		}
		if(shallow != 0)
		{
			lbset("  ");
			lbesc(name);
			if(strcmp(type, "tree") == 0)
			{
				lbcat("/");
			}
			lbflush();
		}
		else if(strcmp(type, "tree") == 0)
		{
			lbset("  [url=\"/");
			lbcat(user);
			lbcat("/");
			lbcat(repo);
			lbcat(".git/");
			lbcat(branch);
			if(dir[0] != '\0')
			{
				lbcat("/");
				lbcat(dir);
			}
			lbcat("/");
			lbcat(name);
			lbcat("/\"]");
			lbesc(name);
			lbcat("/[e]");
			lbflush();
		}
		else
		{
			lbset("  [url=\"/");
			lbcat(user);
			lbcat("/");
			lbcat(repo);
			lbcat(".git/");
			lbcat(branch);
			if(dir[0] != '\0')
			{
				lbcat("/");
				lbcat(dir);
			}
			lbcat("/");
			lbcat(name);
			lbcat(".html\"]");
			lbesc(name);
			lbcat("[e]");
			lbflush();
		}
	}
}
 
static void
emit_branchnav(const char* user, const char* repo, const char* branch,
    long nbranches, char branches[][128])
{
	long b;
 
	lbset("[table class=\"topnav\"][tr][td class=\"logotab\"]git.druid.rocks[e][td][url=\"/\"]index[e][e][td][url=\"/");
	lbcat(user);
	lbcat("\"]");
	lbesc(user);
	lbcat("[e][e][td][url=\"/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git/");
	lbcat(branch);
	lbcat("/\"]");
	lbesc(repo);
	lbcat("[e][e]");
	b = 0;
	while(b < nbranches)
	{
		lbcat("[td][url=\"/");
		lbcat(user);
		lbcat("/");
		lbcat(repo);
		lbcat(".git/");
		lbcat(branches[b]);
		lbcat("/\"]");
		lbesc(branches[b]);
		lbcat("[e][e]");
		b = b + 1;
	}
	lbcat("[e][e]");
	lbflush();
}
 
static void
emit_nav_user(const char* user)
{
	lbset("[table class=\"topnav\"][tr][td class=\"logotab\"]git.druid.rocks[e][td][url=\"/\"]index[e][e][td][url=\"/");
	lbcat(user);
	lbcat("\"]");
	lbesc(user);
	lbcat("[e][e][e][e]");
	lbflush();
}
 
static void
emit_pathnav(const char* user, const char* repo, const char* branch,
    const char* path)
{
	char prefix[2048];
	char seg[512];
	long i = 0;
	long j;
	long n;
 
	n = strlen(path);
 
	lbset("[table class=\"topnav\"][tr][td class=\"logotab\"]git.druid.rocks[e][td][url=\"/\"]index[e][e][td][url=\"/");
	lbcat(user);
	lbcat("\"]");
	lbesc(user);
	lbcat("[e][e][td][url=\"/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git/");
	lbcat(branch);
	lbcat("/\"]");
	lbesc(repo);
	lbcat("[e][e]");
 
	while(i < n)
	{
		j = 0;
		while(i < n && path[i] != '/')
		{
			if(j < 510)
			{
				seg[j] = path[i];
				j = j + 1;
			}
			i = i + 1;
		}
		seg[j] = '\0';
		if(i < n)
		{
			memcpy(prefix, path, i);
			prefix[i] = '\0';
			lbcat("[td][url=\"/");
			lbcat(user);
			lbcat("/");
			lbcat(repo);
			lbcat(".git/");
			lbcat(branch);
			lbcat("/");
			lbcat(prefix);
			lbcat("/\"]");
			lbesc(seg);
			lbcat("/[e][e]");
			i = i + 1;
		}
		else
		{
			lbcat("[td]");
			lbesc(seg);
			lbcat("[e]");
		}
	}
	lbcat("[e][e]");
	lbflush();
}
 
static void
emit_dir(const char* user, const char* repo, const char* gitdir,
    const char* branch, const char* dir)
{
	char path[2048];
 
	strlcpy(path, WEB "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	strlcat(path, "/", sizeof(path));
	strlcat(path, repo, sizeof(path));
	strlcat(path, ".git/", sizeof(path));
	strlcat(path, branch, sizeof(path));
	strlcat(path, "/", sizeof(path));
	strlcat(path, dir, sizeof(path));
	mkpath(path);
	strlcat(path, "/index.btft", sizeof(path));
 
	oreset();
	emit_pathnav(user, repo, branch, dir);
	lbflush();
 
	lbset(dir);
	lbcat("/");
	lbflush();
 
	lbset("");
	lbflush();
	lbset("files:");
	lbflush();
	emit_filetree(user, repo, gitdir, branch, dir, 0);
 
	oflush(path);
}
 
static void
emit_log(const char* user, const char* repo, const char* branch, long record)
{
	char gitdir[1024];
	char rbuf[65536];
	char* f[4];
	struct commit* c;
	long nf;
	long t;
	long days;
	long y;
	long m;
	long d;
	long fd;
	long n;
	long i = 0;
	long end;
 
	strlcpy(gitdir, REPOS "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, "/", sizeof(gitdir));
	strlcat(gitdir, repo, sizeof(gitdir));
	strlcat(gitdir, ".git", sizeof(gitdir));
 
	fd = rungit(gitdir, "log", "-n", "15",
	    "--format=%h%x09%an%x09%ct%x09%s", branch, NULL);
	if(fd < 0)
	{
		return;
	}
	n = readall(fd, rbuf, sizeof(rbuf) - 1);
	close(fd);
	reap();
	rbuf[n] = '\0';
 
	while(i < n)
	{
		end = i;
		while(rbuf[end] != '\n' && rbuf[end] != '\0')
		{
			end = end + 1;
		}
		if(rbuf[end] == '\n')
		{
			rbuf[end] = '\0';
			end = end + 1;
		}
		f[0] = rbuf + i;
		f[1] = "";
		f[2] = "";
		f[3] = "";
		nf = splitfields(f[0], f, 4);
 
		t = atoi(f[2]);
		days = t / 86400;
		ymd(days, &y, &m, &d);
		lbset("  ");
		lbnum(y);
		lbcat("-");
		lbnump(m);
		lbcat("-");
		lbnump(d);
		lbcat(" ");
		lbesc(f[0]);
		lbcat(" ");
		lbesc(f[1]);
		lbcat(" ");
		lbesc(f[3]);
		lbflush();
 
		if(record && ncommits < 512)
		{
			c = &commits[ncommits];
			ncommits = ncommits + 1;
			c->time = t;
			strncpy(c->hash, f[0], 63);
			strncpy(c->author, f[1], 127);
			strncpy(c->subject, f[3], 511);
			strncpy(c->user, user, 127);
			strncpy(c->repo, repo, 127);
		}
		i = end;
	}
}
 
static void
emit_profile(const char* user)
{
	char path[1024];
	char gitdir[1024];
	char rbuf[65536];
	char dbuf[16384];
	char line[1024];
	char rname[256];
	char desc[512];
	unsigned short reclen;
	char type;
	char* name;
	long dd;
	long dn;
	long off;
	long fd;
	long n;
	long i;
	long j;
	long first;
 
	strlcpy(path, WEB "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	mkpath(path);
	strlcat(path, "/index.btft", sizeof(path));
 
	oreset();
 
	emit_nav_user(user);
	lbflush();
 
	lbset(user);
	lbflush();
 
	strlcpy(gitdir, REPOS "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, ".git", sizeof(gitdir));
 
	fd = rungit(gitdir, "show", "HEAD:readme.txt", NULL, NULL, NULL, NULL);
	if(fd >= 0)
	{
		n = readall(fd, rbuf, sizeof(rbuf) - 1);
		close(fd);
		reap();
		rbuf[n] = '\0';
		first = 1;
		i = 0;
		while(i < n)
		{
			j = 0;
			while(i < n && rbuf[i] != '\n')
			{
				if(j < 1022)
				{
					line[j] = rbuf[i];
					j = j + 1;
				}
				i = i + 1;
			}
			if(i < n)
			{
				i = i + 1;
			}
			line[j] = '\0';
			if(first != 0)
			{
				first = 0;
				if(line[0] == '{')
				{
					continue;
				}
			}
			lbset(line);
			lbflush();
		}
	}
 
	lbset("");
	lbflush();
	lbset("repos:");
	lbflush();
 
	strlcpy(line, REPOS "/", sizeof(line));
	strlcat(line, user, sizeof(line));
	dd = open(line, O_RDONLY, 0);
	if(dd >= 0)
	{
		dn = syscall6(SYS_getdents64, dd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
		close(dd);
		if(dn > 0)
		{
			off = 0;
			while(off < dn)
			{
				reclen = *(unsigned short*)(dbuf + off + 16);
				type = dbuf[off + 18];
				name = dbuf + off + 19;
				off = off + reclen;
				if(name[0] == '.')
				{
					continue;
				}
				if(isrepo(name) == 0)
				{
					continue;
				}
				if(type != DT_DIR)
				{
					continue;
				}
				strlcpy(rname, name, sizeof(rname));
				stripgit(rname);
				if(strcmp(rname, user) == 0)
				{
					continue;
				}
				readdesc(user, rname, desc, sizeof(desc));
				lbset("  [url=\"/");
				lbcat(user);
				lbcat("/");
				lbcat(rname);
				lbcat(".git\"]");
				lbesc(rname);
				lbcat("[e]");
				lbcat(" - ");
				lbesc(desc);
				lbflush();
			}
		}
	}
 
	oflush(path);
}
 
static void
emit_repo_page(const char* user, const char* repo, const char* branch,
    long nbranches, char branches[][128], const char* gitdir,
    const char* desc, const char* acl, const char* outpath, long record,
    long shallow)
{
	char line[1024];
	long i;
	long j;
 
	oreset();
 
	lbset(user);
	lbcat("/");
	lbcat(repo);
	lbflush();
 
	lbset(desc);
	lbflush();
 
	lbset("");
	lbflush();
	emit_branchnav(user, repo, branch, nbranches, branches);
	lbflush();
 
	lbset("");
	lbflush();
	lbset("clone:");
	lbflush();
	lbset("  [url=\"git://git.druid.rocks/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git\"]git://git.druid.rocks/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git[e]");
	lbflush();
	lbset("  [url=\"ssh://git@git.druid.rocks/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git\"]ssh://git@git.druid.rocks/");
	lbcat(user);
	lbcat("/");
	lbcat(repo);
	lbcat(".git[e]");
	lbflush();
 
	lbset("");
	lbflush();
	lbset("writers:");
	lbflush();
	lbset("  [url=\"/");
	lbcat(user);
	lbcat("\"]");
	lbesc(user);
	lbcat("[e]");
	lbflush();
	i = 0;
	while(acl[i] != '\0')
	{
		j = 0;
		while(acl[i] != '\0' && acl[i] != '\n')
		{
			if(j < 1022)
			{
				line[j] = acl[i];
				j = j + 1;
			}
			i = i + 1;
		}
		if(acl[i] != '\0')
		{
			i = i + 1;
		}
		line[j] = '\0';
		if(line[0] != '\0')
		{
			lbset("  [url=\"/");
			lbcat(line);
			lbcat("\"]");
			lbesc(line);
			lbcat("[e]");
			lbflush();
		}
	}
 
	lbset("");
	lbflush();
	lbset("files:");
	lbflush();
	emit_filetree(user, repo, gitdir, branch, "", shallow);
 
	lbset("");
	lbflush();
	lbset("commits:");
	lbflush();
	emit_log(user, repo, branch, record);
	oflush(outpath);
}
 
static void
emit_repo(const char* user, const char* repo)
{
	char path[1024];
	char outpath[1024];
	char gitdir[1024];
	char marker[1024];
	char desc[512];
	char acl[1024];
	char branches[32][128];
	long nbranches;
	long shallow;
	long fd;
	long b;
 
	strlcpy(path, WEB "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	strlcat(path, "/", sizeof(path));
	strlcat(path, repo, sizeof(path));
	strlcat(path, ".git", sizeof(path));
	mkpath(path);
	emit_repo_clean(user, repo);
 
	strlcpy(gitdir, REPOS "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, "/", sizeof(gitdir));
	strlcat(gitdir, repo, sizeof(gitdir));
	strlcat(gitdir, ".git", sizeof(gitdir));
 
	strlcpy(marker, gitdir, sizeof(marker));
	strlcat(marker, "/shallow", sizeof(marker));
	shallow = 0;
	fd = open(marker, O_RDONLY, 0);
	if(fd >= 0)
	{
		shallow = 1;
		close(fd);
	}
 
	nbranches = listbranches(gitdir, branches, 32);
	readdesc(user, repo, desc, sizeof(desc));
	readacl(user, repo, acl, sizeof(acl));
 
	b = 0;
	while(b < nbranches)
	{
		strlcpy(outpath, path, sizeof(outpath));
		strlcat(outpath, "/", sizeof(outpath));
		strlcat(outpath, branches[b], sizeof(outpath));
		mkpath(outpath);
		strlcat(outpath, "/index.btft", sizeof(outpath));
		emit_repo_page(user, repo, branches[b], nbranches, branches,
		    gitdir, desc, acl, outpath, b == 0, shallow);
		b = b + 1;
	}
 
	strlcpy(outpath, path, sizeof(outpath));
	strlcat(outpath, "/index.btft", sizeof(outpath));
	emit_repo_page(user, repo, branches[0], nbranches, branches,
	    gitdir, desc, acl, outpath, 0, shallow);
 
	if(shallow == 0)
	{
		emit_repo_files(user, repo);
		emit_repo_dirs(user, repo);
	}
}
 
static void
emit_file(const char* user, const char* repo, const char* branch,
    const char* path, const char* gitdir)
{
	char fpath[2048];
	char rbuf[65536];
	long n;
	long i = 0;
	long linestart;
 
	n = showfile(gitdir, branch, path, rbuf, sizeof(rbuf));
	if(n < 0)
	{
		return;
	}
	while(i < n)
	{
		if(rbuf[i] == '\0')
		{
			return;
		}
		i = i + 1;
	}
 
	strlcpy(fpath, WEB "/", sizeof(fpath));
	strlcat(fpath, user, sizeof(fpath));
	strlcat(fpath, "/", sizeof(fpath));
	strlcat(fpath, repo, sizeof(fpath));
	strlcat(fpath, ".git/", sizeof(fpath));
	strlcat(fpath, branch, sizeof(fpath));
	strlcat(fpath, "/", sizeof(fpath));
	strlcat(fpath, path, sizeof(fpath));
	strlcat(fpath, ".btft", sizeof(fpath));
	mkfilepath(fpath);
 
	oreset();
	emit_pathnav(user, repo, branch, path);
	lbflush();
 
	lbset(path);
	lbflush();
 
	lbset("");
	lbflush();
 
	oput("[code]\n");
	i = 0;
	linestart = 0;
	while(i < n)
	{
		if(rbuf[i] == '\n')
		{
			if(i == linestart)
			{
				oput(" ");
			}
			else
			{
				oesc(rbuf + linestart, i - linestart);
			}
			oput("\n");
			linestart = i + 1;
		}
		i = i + 1;
	}
	if(linestart < n)
	{
		oesc(rbuf + linestart, n - linestart);
		oput("\n");
	}
	oput("[e]\n");
	last_empty = 1;
 
	oflush(fpath);
}
 
static void
emit_repo_clean(const char* user, const char* repo)
{
	char rdir[2048];
	char bdir[2048];
	char dbuf[16384];
	unsigned short reclen;
	char type;
	char* name;
	long fd;
	long dn;
	long off;
 
	strlcpy(rdir, WEB "/", sizeof(rdir));
	strlcat(rdir, user, sizeof(rdir));
	strlcat(rdir, "/", sizeof(rdir));
	strlcat(rdir, repo, sizeof(rdir));
	strlcat(rdir, ".git", sizeof(rdir));
 
	fd = open(rdir, O_RDONLY, 0);
	if(fd < 0)
	{
		return;
	}
	dn = syscall6(SYS_getdents64, fd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
	close(fd);
	if(dn <= 0)
	{
		return;
	}
	off = 0;
	while(off < dn)
	{
		reclen = *(unsigned short*)(dbuf + off + 16);
		type = dbuf[off + 18];
		name = dbuf + off + 19;
		off = off + reclen;
		if(name[0] == '.' && name[1] == '\0')
		{
			continue;
		}
		if(name[0] == '.' && name[1] == '.' && name[2] == '\0')
		{
			continue;
		}
		if(type == DT_DIR)
		{
			strlcpy(bdir, rdir, sizeof(bdir));
			strlcat(bdir, "/", sizeof(bdir));
			strlcat(bdir, name, sizeof(bdir));
			rmtree(bdir);
			rmdir1(bdir);
		}
	}
}
 
static void
emit_repo_files(const char* user, const char* repo)
{
	char gitdir[1024];
	char rbuf[65536];
	char line[1024];
	char branches[32][128];
	long nbranches;
	long fd;
	long n;
	long i;
	long j;
	long b;
 
	strlcpy(gitdir, REPOS "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, "/", sizeof(gitdir));
	strlcat(gitdir, repo, sizeof(gitdir));
	strlcat(gitdir, ".git", sizeof(gitdir));
 
	nbranches = listbranches(gitdir, branches, 32);
	b = 0;
	while(b < nbranches)
	{
		fd = rungit(gitdir, "ls-tree", "-r", "--name-only",
		    branches[b], NULL, NULL);
		if(fd < 0)
		{
			b = b + 1;
			continue;
		}
		n = readall(fd, rbuf, sizeof(rbuf) - 1);
		close(fd);
		reap();
		rbuf[n] = '\0';
		i = 0;
		while(i < n)
		{
			j = 0;
			while(i < n && rbuf[i] != '\n')
			{
				if(j < 1022)
				{
					line[j] = rbuf[i];
					j = j + 1;
				}
				i = i + 1;
			}
			if(i < n)
			{
				i = i + 1;
			}
			line[j] = '\0';
			emit_file(user, repo, branches[b], line, gitdir);
		}
		b = b + 1;
	}
}
 
static void
emit_repo_dirs(const char* user, const char* repo)
{
	char gitdir[1024];
	char rbuf[65536];
	char line[1024];
	char branches[32][128];
	long nbranches;
	long fd;
	long n;
	long i;
	long j;
	long b;
 
	strlcpy(gitdir, REPOS "/", sizeof(gitdir));
	strlcat(gitdir, user, sizeof(gitdir));
	strlcat(gitdir, "/", sizeof(gitdir));
	strlcat(gitdir, repo, sizeof(gitdir));
	strlcat(gitdir, ".git", sizeof(gitdir));
 
	nbranches = listbranches(gitdir, branches, 32);
	b = 0;
	while(b < nbranches)
	{
		fd = rungit(gitdir, "ls-tree", "-r", "-d", "--name-only",
		    branches[b], NULL);
		if(fd < 0)
		{
			b = b + 1;
			continue;
		}
		n = readall(fd, rbuf, sizeof(rbuf) - 1);
		close(fd);
		reap();
		rbuf[n] = '\0';
		i = 0;
		while(i < n)
		{
			j = 0;
			while(i < n && rbuf[i] != '\n')
			{
				if(j < 1022)
				{
					line[j] = rbuf[i];
					j = j + 1;
				}
				i = i + 1;
			}
			if(i < n)
			{
				i = i + 1;
			}
			line[j] = '\0';
			emit_dir(user, repo, gitdir, branches[b], line);
		}
		b = b + 1;
	}
}
 
static void
emit_user_repos(const char* user)
{
	char path[1024];
	char dbuf[16384];
	char rname[256];
	unsigned short reclen;
	char type;
	char* name;
	long dd;
	long dn;
	long off;
 
	strlcpy(path, REPOS "/", sizeof(path));
	strlcat(path, user, sizeof(path));
	dd = open(path, O_RDONLY, 0);
	if(dd < 0)
	{
		return;
	}
	dn = syscall6(SYS_getdents64, dd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
	close(dd);
	if(dn < 0)
	{
		return;
	}
	off = 0;
	while(off < dn)
	{
		reclen = *(unsigned short*)(dbuf + off + 16);
		type = dbuf[off + 18];
		name = dbuf + off + 19;
		off = off + reclen;
		if(isrepo(name) == 0)
		{
			continue;
		}
		if(type != DT_DIR)
		{
			continue;
		}
		strlcpy(rname, name, sizeof(rname));
		stripgit(rname);
		if(strcmp(rname, user) == 0)
		{
			continue;
		}
		emit_repo(user, rname);
	}
}
 
static void
swap(struct commit* a, struct commit* b)
{
	struct commit t;
 
	memcpy(&t, a, sizeof(struct commit));
	memcpy(a, b, sizeof(struct commit));
	memcpy(b, &t, sizeof(struct commit));
}
 
static void
sortcommits(void)
{
	long i;
	long j;
 
	i = 1;
	while(i < ncommits)
	{
		j = i;
		while(j > 0 && commits[j - 1].time < commits[j].time)
		{
			swap(&commits[j - 1], &commits[j]);
			j = j - 1;
		}
		i = i + 1;
	}
}
 
static void
emit_rss(void)
{
	struct commit* c;
	long days;
	long y;
	long m;
	long d;
	long h;
	long mi;
	long s;
	long i = 0;
 
	oreset();
	oput("<?xml version=\"1.0\"?>\n");
	oput("<rss version=\"2.0\">\n<channel>\n");
	oput("<title>git.druid.rocks</title>\n");
	oput("<link>https://git.druid.rocks/</link>\n");
	oput("<description>recent commits</description>\n");
 
	sortcommits();
	while(i < ncommits && i < 30)
	{
		c = &commits[i];
		days = c->time / 86400;
		ymd(days, &y, &m, &d);
		h = (c->time % 86400) / 3600;
		mi = (c->time % 3600) / 60;
		s = c->time % 60;
 
		oput("<item>\n");
		oput("<title>");
		oput(c->user);
		oput("/");
		oput(c->repo);
		oput(": ");
		oput(c->subject);
		oput("</title>\n");
		oput("<link>https://git.druid.rocks/");
		oput(c->user);
		oput("/");
		oput(c->repo);
		oput(".git/</link>\n");
		oput("<guid>https://git.druid.rocks/");
		oput(c->user);
		oput("/");
		oput(c->repo);
		oput(".git/");
		oput(c->hash);
		oput("</guid>\n");
		oput("<pubDate>");
		oput(wdays[(days + 4) % 7]);
		oput(", ");
		onum2(d);
		oput(" ");
		oput(mons[m - 1]);
		oput(" ");
		onum(y);
		oput(" ");
		onum2(h);
		oput(":");
		onum2(mi);
		oput(":");
		onum2(s);
		oput(" +0000</pubDate>\n");
		oput("</item>\n");
		i = i + 1;
	}
	oput("</channel>\n</rss>\n");
	oflush(WEB "/rss.xml");
}
 
static void
emit_global(void)
{
	char path[1024];
	char dbuf[16384];
	char ibuf[16384];
	char rname[256];
	char desc[512];
	unsigned short reclen;
	char type;
	char* name;
	unsigned short ureclen;
	char utype;
	char* uname;
	long dd;
	long dn;
	long off;
	long ud;
	long udn;
	long uoff;
 
	oreset();
 
	lbset("[table class=\"topnav\"][tr][td class=\"logotab\"]git.druid.rocks[e][td][url=\"/\"]index[e][e][e][e]");
	lbflush();
 
	lbset("[h1]git.druid.rocks[e]");
	lbflush();
 
	dd = open(REPOS, O_RDONLY, 0);
	if(dd >= 0)
	{
		dn = syscall6(SYS_getdents64, dd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
		close(dd);
		if(dn > 0)
		{
			off = 0;
			while(off < dn)
			{
				reclen = *(unsigned short*)(dbuf + off + 16);
				type = dbuf[off + 18];
				name = dbuf + off + 19;
				off = off + reclen;
				if(name[0] == '.')
				{
					continue;
				}
				if(type != DT_DIR)
				{
					continue;
				}
 
				strlcpy(path, REPOS "/", sizeof(path));
				strlcat(path, name, sizeof(path));
				ud = open(path, O_RDONLY, 0);
				if(ud < 0)
				{
					continue;
				}
				lbset("[url=\"/");
				lbcat(name);
				lbcat("\"]");
				lbcat(name);
				lbcat("[e]");
				lbflush();
				udn = syscall6(SYS_getdents64, ud, (long)ibuf,
				    sizeof(ibuf), 0, 0, 0);
				close(ud);
				if(udn > 0)
				{
					uoff = 0;
					while(uoff < udn)
					{
						ureclen = *(unsigned short*)(ibuf + uoff + 16);
						utype = ibuf[uoff + 18];
						uname = ibuf + uoff + 19;
						uoff = uoff + ureclen;
						if(isrepo(uname) == 0)
						{
							continue;
						}
						if(utype != DT_DIR)
						{
							continue;
						}
						strlcpy(rname, uname, sizeof(rname));
						stripgit(rname);
						if(strcmp(rname, name) == 0)
						{
							continue;
						}
						readdesc(name, rname, desc, sizeof(desc));
						lbset("  [url=\"/");
						lbcat(name);
						lbcat("/");
						lbcat(rname);
						lbcat(".git\"]");
						lbesc(rname);
						lbcat("[e]");
						lbcat(" - ");
						lbesc(desc);
						lbflush();
					}
				}
				lbset("");
				lbflush();
			}
		}
	}
	lbset("rss: [url=\"https://git.druid.rocks/rss.xml\"]https://git.druid.rocks/rss.xml[e]");
	lbflush();
	oflush(WEB "/index.btft");
}
 
int
main(void)
{
	char dbuf[16384];
	unsigned short reclen;
	char type;
	char* name;
	long dd;
	long dn;
	long off;
 
	mkpath(WEB);
	oreset();
	ncommits = 0;
 
	dd = open(REPOS, O_RDONLY, 0);
	if(dd < 0)
	{
		return 1;
	}
	dn = syscall6(SYS_getdents64, dd, (long)dbuf, sizeof(dbuf), 0, 0, 0);
	close(dd);
	if(dn < 0)
	{
		return 1;
	}
	off = 0;
	while(off < dn)
	{
		reclen = *(unsigned short*)(dbuf + off + 16);
		type = dbuf[off + 18];
		name = dbuf + off + 19;
		off = off + reclen;
		if(name[0] == '.')
		{
			continue;
		}
		if(type != DT_DIR)
		{
			continue;
		}
		emit_profile(name);
		emit_user_repos(name);
	}
 
	emit_global();
	emit_rss();
	return 0;
}
powered by btf.