stagit

Personal Stagit Fork
commit 27a03de9bfae4946f394fe27ab5a7e87b87c0fac
parent 465050f2ef885a2bc9ee66635c9eafbd3c66c148
Author: maydayv7 <maydayv7@gmail.com>
Date:   Fri,  2 Jan 2026 01:50:52 +0530

feat: Add Markdown Render and Syntax Highlight

Also add Nix Flake

Diffstat:
Aflake.lock | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aflake.nix | 44++++++++++++++++++++++++++++++++++++++++++++
Mstagit.c | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
3 files changed, 184 insertions(+), 19 deletions(-)

diff --git a/flake.lock b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1767116409, + "narHash": "sha256-5vKw92l1GyTnjoLzEagJy5V5mDFck72LiQWZSOnSicw=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "cad22e7d996aea55ecab064e84834289143e44a0", + "type": "github" + }, + "original": { + "owner": "nixos", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "utils": "utils" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix @@ -0,0 +1,44 @@ +{ + description = "Personal Stagit Fork"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs"; + utils.url = "github:numtide/flake-utils"; + }; + + outputs = + { nixpkgs, utils, ... }: + utils.lib.eachDefaultSystem ( + system: with nixpkgs.legacyPackages.${system}; { + packages.default = stdenv.mkDerivation { + pname = "stagit"; + version = "1.2-custom"; + src = ./.; + + buildInputs = [ + libgit2 + md4c + ]; + + nativeBuildInputs = [ + pkg-config + makeWrapper + ]; + + buildPhase = '' + make PREFIX=$out \ + STAGIT_CFLAGS="-I${libgit2}/include -I${md4c}/include" \ + STAGIT_LDFLAGS="-L${libgit2}/lib -lgit2 -L${md4c}/lib -lmd4c -lmd4c-html" + ''; + + installPhase = '' + mkdir -p $out/bin + mkdir -p $out/share/doc/stagit + cp stagit $out/bin/ + cp stagit-index $out/bin/ + cp style.css logo.png favicon.png $out/share/doc/stagit/ + wrapProgram $out/bin/stagit --prefix PATH : ${lib.makeBinPath [ chroma ]} + ''; + }; + } + ); +} diff --git a/stagit.c b/stagit.c @@ -16,6 +16,9 @@ #include "compat.h" +#include <md4c-html.h> +#define CMD_BUFSIZE 255 + #define LEN(s) (sizeof(s)/sizeof(*s)) struct deltainfo { @@ -556,37 +559,86 @@ writefooter(FILE *fp) fputs("</div>\n</body>\n</html>\n", fp); } +const char * +get_ext(const char *filename) +{ + const char *dot = strrchr(filename, '.'); + if (!dot || dot == filename) return ""; + return dot + 1; +} + +void +processmd(const char* output, unsigned int len, void *fp) +{ + fprintf((FILE *)fp, "%.*s", len, output); +} + size_t -writeblobhtml(FILE *fp, const git_blob *blob) +writeblobmd(FILE *fp, const git_blob *blob) { - size_t n = 0, i, len, prev; - const char *nfmt = "<a href=\"#l%zu\" class=\"line\" id=\"l%zu\">%7zu</a> "; + size_t n = 0, i, len, prev, ret; const char *s = git_blob_rawcontent(blob); - len = git_blob_rawsize(blob); - fputs("<pre id=\"blob\">\n", fp); + fputs("<div id=\"md\">\n", fp); if (len > 0) { for (i = 0, prev = 0; i < len; i++) { - if (s[i] != '\n') - continue; + if (s[i] != '\n') continue; n++; - fprintf(fp, nfmt, n, n, n); - 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); - xmlencodeline(fp, &s[prev], len - prev); - } + if ((len - prev) > 0) n++; + + ret = md_html(s, len, processmd, fp, MD_FLAG_TABLES | MD_FLAG_TASKLISTS | + MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS, 0); } + fputs("</div>\n", fp); + return n; +} - fputs("</pre>\n", fp); +size_t +syntax_highlight(const char *filename, FILE *fp, const char *s, size_t len) +{ + size_t lc = 0; + fflush(fp); - return n; + int stdout_copy = dup(1); + dup2(fileno(fp), 1); + + char cmd[CMD_BUFSIZE]; + snprintf(cmd, sizeof(cmd), "chroma --html --html-only --html-lines " + "--html-lines-table --filename %s", filename); + + FILE *child = popen(cmd, "w"); + if (child == NULL) { + dup2(stdout_copy, 1); + fprintf(stderr, "stagit: chroma failed\n"); + return 0; + } + + size_t i; + for (i = 0; *s && i < len; s++, i++) { + if (*s == '\n') lc++; + fprintf(child, "%c", *s); + } + + pclose(child); + fflush(stdout); + dup2(stdout_copy, 1); + return lc; +} + +size_t +writeblobhtml(const char *filename, FILE *fp, const git_blob *blob) +{ + size_t lc = 0; + const char *s = git_blob_rawcontent(blob); + size_t len = git_blob_rawsize(blob); + + if (len > 0) { + lc = syntax_highlight(filename, fp, s, len); + } + return lc; } void @@ -946,6 +998,13 @@ writeatom(FILE *fp, int all) return 0; } +int +git_blob_is_markdown_file(const char *filename) +{ + const char *ext = strrchr(filename, '.'); + return ((ext != NULL && (!strcmp (ext+1, "md")))); +} + size_t writeblob(git_object *obj, const char *fpath, const char *filename, size_t filesize) { @@ -976,8 +1035,10 @@ writeblob(git_object *obj, const char *fpath, const char *filename, size_t files if (git_blob_is_binary((git_blob *)obj)) fputs("<p>Binary file.</p>\n", fp); + else if (git_blob_is_markdown_file(filename)) + lc = writeblobmd(fp, (git_blob *)obj); else - lc = writeblobhtml(fp, (git_blob *)obj); + lc = writeblobhtml(filename, fp, (git_blob *)obj); writefooter(fp); checkfileerror(fp, fpath, 'w');