diff --git a/LICENSE b/LICENSE index afbaf25..1b58029 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT/X Consortium License -(c) 2015-2021 Hiltjo Posthuma +(c) 2015-2022 Hiltjo Posthuma Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/Makefile b/Makefile index 92f9128..3558552 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ .POSIX: NAME = stagit -VERSION = 0.9.6 +VERSION = 1.0 # paths PREFIX = /usr/local diff --git a/stagit-index.c b/stagit-index.c index 84785a9..7c1f76d 100644 --- a/stagit-index.c +++ b/stagit-index.c @@ -28,6 +28,28 @@ joinpath(char *buf, size_t bufsiz, const char *path, const char *path2) path, path[0] && path[strlen(path) - 1] != '/' ? "/" : "", path2); } +/* Percent-encode, see RFC3986 section 2.1. */ +void +percentencode(FILE *fp, const char *s, size_t len) +{ + static char tab[] = "0123456789ABCDEF"; + unsigned char uc; + size_t i; + + for (i = 0; *s && i < len; s++, i++) { + uc = *s; + /* NOTE: do not encode '/' for paths or ",-." */ + if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') || + uc == '[' || uc == ']') { + putc('%', fp); + putc(tab[(uc >> 4) & 0x0f], fp); + putc(tab[uc & 0x0f], fp); + } else { + putc(uc, fp); + } + } +} + /* Escape characters below as HTML 2.0 / XML 1.0. */ void xmlencode(FILE *fp, const char *s, size_t len) @@ -118,7 +140,7 @@ writelog(FILE *fp) *p = '\0'; fputs("", fp); xmlencode(fp, stripped_name, strlen(stripped_name)); fputs("", fp); @@ -151,7 +173,11 @@ main(int argc, char *argv[]) return 1; } + /* do not search outside the git repository: + GIT_CONFIG_LEVEL_APP is the highest level currently */ git_libgit2_init(); + for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++) + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, ""); #ifdef __OpenBSD__ if (pledge("stdio rpath", NULL) == -1) diff --git a/stagit.c b/stagit.c index ebdebd5..8fd1fbc 100644 --- a/stagit.c +++ b/stagit.c @@ -71,7 +71,7 @@ static char *licensefiles[] = { "HEAD:LICENSE", "HEAD:LICENSE.md", "HEAD:COPYING static char *license; static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" }; static char *readme; -static long long nlogcommits = -1; /* < 0 indicates not used */ +static long long nlogcommits = -1; /* -1 indicates not used */ /* cache */ static git_oid lastoid; @@ -359,6 +359,28 @@ efopen(const char *filename, const char *flags) return fp; } +/* Percent-encode, see RFC3986 section 2.1. */ +void +percentencode(FILE *fp, const char *s, size_t len) +{ + static char tab[] = "0123456789ABCDEF"; + unsigned char uc; + size_t i; + + for (i = 0; *s && i < len; s++, i++) { + uc = *s; + /* NOTE: do not encode '/' for paths or ",-." */ + if (uc < ',' || uc >= 127 || (uc >= ':' && uc <= '@') || + uc == '[' || uc == ']') { + putc('%', fp); + putc(tab[(uc >> 4) & 0x0f], fp); + putc(tab[uc & 0x0f], fp); + } else { + putc(uc, fp); + } + } +} + /* Escape characters below as HTML 2.0 / XML 1.0. */ void xmlencode(FILE *fp, const char *s, size_t len) @@ -480,10 +502,12 @@ writeheader(FILE *fp, const char *title) fputs(" - ", fp); xmlencode(fp, description, strlen(description)); fprintf(fp, "\n\n", relpath); - fprintf(fp, "\n", - name, relpath); - fprintf(fp, "\n", - name, relpath); + fputs("\n", relpath); + fputs("\n", relpath); fprintf(fp, "\n", relpath); fputs("\n\n", fp); if (cloneurl[0]) { fputs("", fp); @@ -538,14 +562,15 @@ writeblobhtml(FILE *fp, const git_blob *blob) continue; n++; fprintf(fp, nfmt, n, n, n); - xmlencode(fp, &s[prev], i - prev + 1); + xmlencodeline(fp, &s[prev], i - prev + 1); + putc('\n', fp); prev = i + 1; } /* trailing data */ if ((len - prev) > 0) { n++; fprintf(fp, nfmt, n, n, n); - xmlencode(fp, &s[prev], len - prev); + xmlencodeline(fp, &s[prev], len - prev); } } @@ -568,7 +593,7 @@ printcommit(FILE *fp, struct commitinfo *ci) fputs("Author: ", fp); xmlencode(fp, ci->author->name, strlen(ci->author->name)); fputs(" <author->email, strlen(ci->author->email)); + xmlencode(fp, ci->author->email, strlen(ci->author->email)); /* not percent-encoded */ fputs("\">", fp); xmlencode(fp, ci->author->email, strlen(ci->author->email)); fputs(">\nDate: ", fp); @@ -663,11 +688,11 @@ printshowfile(FILE *fp, struct commitinfo *ci) patch = ci->deltas[i]->patch; delta = git_patch_get_delta(patch); fprintf(fp, "diff --git a/old_file.path, strlen(delta->old_file.path)); + percentencode(fp, delta->old_file.path, strlen(delta->old_file.path)); fputs(".html\">", fp); xmlencode(fp, delta->old_file.path, strlen(delta->old_file.path)); fprintf(fp, " b/new_file.path, strlen(delta->new_file.path)); + percentencode(fp, delta->new_file.path, strlen(delta->new_file.path)); fprintf(fp, ".html\">"); xmlencode(fp, delta->new_file.path, strlen(delta->new_file.path)); fprintf(fp, "\n"); @@ -739,6 +764,7 @@ writelog(FILE *fp, const git_oid *oid) git_oid id; char path[PATH_MAX], oidstr[GIT_OID_HEXSZ + 1]; FILE *fpfile; + size_t remcommits = 0; int r; git_revwalk_new(&w, repo); @@ -758,8 +784,11 @@ writelog(FILE *fp, const git_oid *oid) /* optimization: if there are no log lines to write and the commit file already exists: skip the diffstat */ - if (!nlogcommits && !r) - continue; + if (!nlogcommits) { + remcommits++; + if (!r) + continue; + } if (!(ci = commitinfo_getbyoid(&id))) break; @@ -767,15 +796,10 @@ writelog(FILE *fp, const git_oid *oid) if (commitinfo_getstats(ci) == -1) goto err; - if (nlogcommits < 0) { + if (nlogcommits != 0) { writelogline(fp, ci); - } else if (nlogcommits > 0) { - writelogline(fp, ci); - nlogcommits--; - if (!nlogcommits && ci->parentoid[0]) - fputs("" - "\n", fp); + if (nlogcommits > 0) + nlogcommits--; } if (cachefile) @@ -797,6 +821,12 @@ err: } git_revwalk_free(w); + if (nlogcommits == 0 && remcommits != 0) { + fprintf(fp, "\n", remcommits); + } + relpath = ""; return 0; @@ -1035,7 +1065,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) fputs("
", fp); fprintf(fp, "\"\"", @@ -495,7 +519,7 @@ writeheader(FILE *fp, const char *title) fputs("
git clone ", fp); xmlencode(fp, cloneurl, strlen(cloneurl)); fputs("
" - "More commits remaining [...]
" + "%zu more commits remaining, fetch the repository" + "
", fp); fputs(filemode(git_tree_entry_filemode(entry)), fp); fprintf(fp, "", fp); xmlencode(fp, entrypath, strlen(entrypath)); fputs("", fp); @@ -1190,7 +1220,11 @@ main(int argc, char *argv[]) if (!realpath(repodir, repodirabs)) err(1, "realpath"); + /* do not search outside the git repository: + GIT_CONFIG_LEVEL_APP is the highest level currently */ git_libgit2_init(); + for (i = 1; i <= GIT_CONFIG_LEVEL_APP; i++) + git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, ""); #ifdef __OpenBSD__ if (unveil(repodir, "r") == -1)