| git.druid.rocks | index | druid520 | wgit | src/ | 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;
}