X-Git-Url: https://git.armaanb.net/?a=blobdiff_plain;f=src%2Fstagit.c;h=24075f6081e2f54024c1babd5ab3e0fa4458d8ad;hb=d915e5e6d4b53355e512d76f5874be58827debc1;hp=aa99c7767eff014f68b0ec0477f6e1ac94b83a4c;hpb=70aaa5e01b2895d11b6d0dee8d600e822a199cfe;p=stagit.git diff --git a/src/stagit.c b/src/stagit.c index aa99c77..24075f6 100644 --- a/src/stagit.c +++ b/src/stagit.c @@ -8,14 +8,19 @@ #include #include #include +#include #include #include #include #include -#include "cp.h" +#ifdef HAS_CMARK +#include +#endif + #include "compat.h" +#include "cp.h" struct deltainfo { git_patch *patch; @@ -51,6 +56,7 @@ struct commitinfo { static git_repository *repo; +static const char *baseurl = ""; /* base URL to make absolute RSS/Atom URI */ static const char *relpath = ""; static const char *repodir; @@ -65,6 +71,9 @@ static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" }; static char *readme; static long long nlogcommits = -1; /* < 0 indicates not used */ +bool htmlized; /* true if markdoown converted to HTML */ +static char oldfilename[PATH_MAX]; /* filename of the last file */ + /* cache */ static git_oid lastoid; static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */ @@ -272,6 +281,25 @@ xmlencode(FILE *fp, const char *s, size_t len) } } +/* Escape characters below as HTML 2.0 / XML 1.0, ignore printing '\n', '\r' */ +void +xmlencodeline(FILE *fp, const char *s, size_t len) +{ + size_t i; + for (i = 0; *s && i < len; s++, i++) { + switch(*s) { + case '<': fputs("<", fp); break; + case '>': fputs(">", fp); break; + case '\'': fputs("'", fp); break; + case '&': fputs("&", fp); break; + case '"': fputs(""", fp); break; + case '\r': break; /* ignore CR */ + case '\n': break; /* ignore LF */ + default: putc(*s, fp); + } + } +} + int mkdirp(const char *path) { @@ -343,7 +371,7 @@ void writeheader(FILE *fp, const char *title) { fputs("\n" - "\n\n" + "\n\n" "\n" "\n" "", fp); @@ -357,24 +385,23 @@ writeheader(FILE *fp, const char *title) fprintf(fp, "\n\n", relpath); fprintf(fp, "\n", name, relpath); - fprintf(fp, "\n", relpath); + fprintf(fp, "\n", relpath); fprintf(fp, "\n", relpath); - fputs("\n\n", fp); + fputs("

", fp); if (cloneurl[0]) { - fputs("", fp); + fputs("

", fp); } - fputs("
", fp); - fprintf(fp, "\"\"", + fputs("\n\n", fp); + fprintf(fp, "\"\"", relpath, relpath); - fputs("

", fp); + fputs("

", fp); xmlencode(fp, strippedname, strlen(strippedname)); - fputs("

", fp); + fputs("

", fp); xmlencode(fp, description, strlen(description)); - fputs("

git clone git clone ", fp); xmlencode(fp, cloneurl, strlen(cloneurl)); - fputs("
\n", fp); fprintf(fp, "Log | ", relpath); fprintf(fp, "Files | ", relpath); fprintf(fp, "Refs", relpath); @@ -387,7 +414,9 @@ writeheader(FILE *fp, const char *title) if (license) fprintf(fp, " | LICENSE", relpath, license); - fputs("
\n
\n
\n", fp); + fprintf(fp, " | Download", + relpath, strippedname); + fputs("
\n
\n", fp); } void @@ -396,62 +425,84 @@ writefooter(FILE *fp) fputs("
\n\n\n", fp); } -int -call_py(const char *filename, FILE *fp, const char *s, size_t len) +const char * +get_ext(const char *filename) +{ + const char *dot = strrchr(filename, '.'); + if(!dot || dot == filename) return ""; + return dot + 1; +} + +void +call_chroma(const char *filename, FILE *fp, const char *s, size_t len) { + htmlized = false; + char *html = ""; // Flush HTML-file fflush(fp); - // Copy STDOUT - int stdout_copy = dup(1); - // Redirect STDOUT - dup2(fileno(fp), 1); - - // Python Pygments script for syntax highlighting. - FILE *child = popen("/usr/local/share/stagit/highlight.py", "w"); - if (child == NULL) { - printf("child is null: %s", strerror(errno)); - exit(1); - } - // Give filename through STDIN: - fprintf(child, "%s\n", filename); - // Give code to highlight through STDIN: - int lc; - size_t i; - for (i = 0; *s && i < len; s++, i++) { - if (*s == '\n') lc++; - fprintf(child, "%c", *s); - } - pclose(child); - fflush(stdout); - // Give back STDOUT. - dup2(stdout_copy, 1); - return lc; +#ifdef HAS_CMARK + html = cmark_markdown_to_html(s, len, CMARK_OPT_DEFAULT); + if (strcmp(get_ext(filename), "md") == 0) htmlized = true; +#endif + +#ifdef HAS_CHROMA + if (!htmlized) { + // Copy STDOUT + int stdout_copy = dup(1); + + // Redirect STDOUT + dup2(fileno(fp), 1); + + char cmd[255] = "chroma --html --html-only --html-lines --html-lines-table --filename "; + strncat(cmd, filename, strlen(filename) + 1); + FILE *child = popen(cmd, "w"); + if (child == NULL) { + printf("child is null: %s", strerror(errno)); + exit(1); + } + + // Give code to highlight through STDIN: + size_t i; + for (i = 0; *s && i < len; s++, i++) { + fprintf(child, "%c", *s); + } + + pclose(child); + fflush(stdout); + + // Give back STDOUT. + dup2(stdout_copy, 1); + + } else { + fprintf(fp, "%s", html); + } +#else + fprintf(fp, "
%s
", s); +#endif + free(html); } -int +void writeblobhtml(const char *filename, FILE *fp, const git_blob *blob) { - int lc = 0; const char *s = git_blob_rawcontent(blob); git_off_t len = git_blob_rawsize(blob); if (len > 0) { - lc = call_py(filename, fp, s, len); + call_chroma(filename, fp, s, len); } - - return lc; } void printcommit(FILE *fp, struct commitinfo *ci) { fprintf(fp, "commit %s\n", - relpath, ci->oid, ci->oid); + relpath, ci->oid, ci->oid); if (ci->parentoid[0]) fprintf(fp, "parent %s\n", - relpath, ci->parentoid, ci->parentoid); + relpath, ci->parentoid, ci->parentoid); if (ci->author) { fputs("Author: ", fp); @@ -462,12 +513,12 @@ printcommit(FILE *fp, struct commitinfo *ci) xmlencode(fp, ci->author->email, strlen(ci->author->email)); fputs(">\nDate: ", fp); printtime(fp, &(ci->author->when)); - fputc('\n', fp); + putc('\n', fp); } if (ci->msg) { - fputc('\n', fp); + putc('\n', fp); xmlencode(fp, ci->msg, strlen(ci->msg)); - fputc('\n', fp); + putc('\n', fp); } } @@ -586,8 +637,9 @@ printshowfile(FILE *fp, struct commitinfo *ci) fprintf(fp, "-", i, j, k, i, j, k); else - fputc(' ', fp); - xmlencode(fp, line->content, line->content_len); + putc(' ', fp); + xmlencodeline(fp, line->content, line->content_len); + putc('\n', fp); if (line->old_lineno == -1 || line->new_lineno == -1) fputs("", fp); } @@ -631,7 +683,6 @@ writelog(FILE *fp, const git_oid *oid) git_revwalk_new(&w, repo); git_revwalk_push(w, oid); - git_revwalk_simplify_first_parent(w); while (!git_revwalk_next(&id, w)) { relpath = ""; @@ -712,8 +763,8 @@ printcommitatom(FILE *fp, struct commitinfo *ci) xmlencode(fp, ci->summary, strlen(ci->summary)); fputs("\n", fp); } - fprintf(fp, "\n", - ci->oid); + fprintf(fp, "\n", + baseurl, ci->oid); if (ci->author) { fputs("\n", fp); @@ -734,10 +785,10 @@ printcommitatom(FILE *fp, struct commitinfo *ci) xmlencode(fp, ci->author->email, strlen(ci->author->email)); fputs(">\nDate: ", fp); printtime(fp, &(ci->author->when)); - fputc('\n', fp); + putc('\n', fp); } if (ci->msg) { - fputc('\n', fp); + putc('\n', fp); xmlencode(fp, ci->msg, strlen(ci->msg)); } fputs("\n\n\n", fp); @@ -760,7 +811,6 @@ writeatom(FILE *fp) git_revwalk_new(&w, repo); git_revwalk_push_head(w); - git_revwalk_simplify_first_parent(w); for (i = 0; i < m && !git_revwalk_next(&id, w); i++) { if (!(ci = commitinfo_getbyoid(&id))) @@ -775,20 +825,36 @@ writeatom(FILE *fp) return 0; } -int +float +rounder(float var) +{ + int value = var * 10 + .5; + return value / 10.0; +} + +const char * +convertbytes(int bytes) +{ + bytes = (float)bytes; + static char outp[25]; + if (bytes < 1024) sprintf(outp, "%u %s", bytes, "B"); + else if (bytes < 1048576) sprintf(outp, "%0.1f %s", rounder(bytes/1024.0), "K"); + else sprintf(outp, "%0.1f %s", rounder(bytes/1048576.0), "M"); + return outp; +} + +void writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t filesize) { char tmp[PATH_MAX] = "", *d; const char *p; - int lc = 0; FILE *fp; if (strlcpy(tmp, fpath, sizeof(tmp)) >= sizeof(tmp)) errx(1, "path truncated: '%s'", fpath); if (!(d = dirname(tmp))) err(1, "dirname"); - if (mkdirp(d)) - return -1; + mkdirp(d); for (p = fpath, tmp[0] = '\0'; *p; p++) { if (*p == '/' && strlcat(tmp, "../", sizeof(tmp)) >= sizeof(tmp)) @@ -800,22 +866,41 @@ writeblob(git_object *obj, const char *fpath, const char *filename, git_off_t fi writeheader(fp, filename); fputs("

", fp); xmlencode(fp, filename, strlen(filename)); - fprintf(fp, " (%juB)", (uintmax_t)filesize); - fputs("


", fp); + fprintf(fp, " (%s)", convertbytes((int)filesize)); + +#ifdef HAS_CMARK + char newfpath[PATH_MAX]; + char newfilename[PATH_MAX]; + if (strcmp(get_ext(filename), "md") == 0) { + fprintf(fp, " View raw", filename); + strcpy(newfpath, fpath); + strcat(newfpath, "-raw"); + + strcpy(newfilename, filename); + strcat(newfilename, "-raw"); + strcpy(oldfilename, filename); + + /* NOTE: recurses */ + writeblob(obj, newfpath, newfilename, filesize); + } else if (strcmp(get_ext(filename), "md-raw" ) == 0) { + fprintf(fp, " View rendered", oldfilename); + } +#endif + + fputs(".


", fp); if (git_blob_is_binary((git_blob *)obj)) { fputs("

Binary file.

\n", fp); } else { - lc = writeblobhtml(filename, fp, (git_blob *)obj); + writeblobhtml(filename, fp, (git_blob *)obj); if (ferror(fp)) err(1, "fwrite"); } + writefooter(fp); fclose(fp); relpath = ""; - - return lc; } const char * @@ -870,7 +955,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) const char *entryname; char filepath[PATH_MAX], entrypath[PATH_MAX]; size_t count, i; - int lc, r, ret; + int r, ret; count = git_tree_entrycount(tree); for (i = 0; i < count; i++) { @@ -902,7 +987,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) } filesize = git_blob_rawsize((git_blob *)obj); - lc = writeblob(obj, filepath, entryname, filesize); + writeblob(obj, filepath, entryname, filesize); fputs("", fp); fputs(filemode(git_tree_entry_filemode(entry)), fp); @@ -911,10 +996,7 @@ writefilestree(FILE *fp, git_tree *tree, const char *path) fputs("\">", fp); xmlencode(fp, entrypath, strlen(entrypath)); fputs("", fp); - if (lc > 0) - fprintf(fp, "%dL", lc); - else - fprintf(fp, "%juB", (uintmax_t)filesize); + fprintf(fp, "%s", convertbytes((int)filesize)); fputs("\n", fp); git_object_free(obj); } else if (!git_submodule_lookup(&module, repo, entryname)) { @@ -1070,7 +1152,8 @@ err: void usage(char *argv0) { - fprintf(stderr, "%s [-c cachefile | -l commits] repodir\n", argv0); + fprintf(stderr, "%s [-c cachefile | -l commits] " + "[-u baseurl] repodir\n", argv0); exit(1); } @@ -1103,6 +1186,10 @@ main(int argc, char *argv[]) if (argv[i][0] == '\0' || *p != '\0' || nlogcommits <= 0 || errno) usage(argv[0]); + } else if (argv[i][1] == 'u') { + if (i + 1 >= argc) + usage(argv[0]); + baseurl = argv[++i]; } } if (!repodir) @@ -1152,7 +1239,7 @@ main(int argc, char *argv[]) strcpy(cwd, getcwd(cwd, sizeof(cwd))); cp("/usr/local/share/stagit/syntax.css", strcat(cwd, "/syntax.css")); strcpy(cwd, getcwd(cwd, sizeof(cwd))); - cp("/usr/local/share/stagit/style.min.css", strcat(cwd, "/style.min.css")); + cp("/usr/local/share/stagit/style.css", strcat(cwd, "/style.css")); /* strip .git suffix */ if (!(strippedname = strdup(name))) @@ -1207,6 +1294,12 @@ main(int argc, char *argv[]) submodules = ".gitmodules"; git_object_free(obj); + /* Generate tarball */ + char tarball[255]; + sprintf(tarball, "tar -zcf %s.tar.gz --ignore-failed-read --exclude='.git' %s", + strippedname, repodir); + system(tarball); + /* log for HEAD */ fp = efopen("log.html", "w"); relpath = ""; @@ -1268,6 +1361,8 @@ main(int argc, char *argv[]) writefooter(fp); fclose(fp); + cp("files.html", "index.html"); + /* summary page with branches and tags */ fp = efopen("refs.html", "w"); writeheader(fp, "Refs"); @@ -1293,6 +1388,7 @@ main(int argc, char *argv[]) /* cleanup */ git_repository_free(repo); git_libgit2_shutdown(); + free(strippedname); return 0; }