stagit

My personal fork of stagit https://codemadness.org/stagit.html
git clone https://git.grace.moe/stagit
Log | Files | Refs | README | LICENSE

commit eabec8e6a5d4972d0135e39b4730551b6399473a
parent 96cbcdf6e6f2e5e572c8b01a6990151462cb4b00
Author: gracefu <81774659+gracefuu@users.noreply.github.com>
Date:   Sat,  3 May 2025 00:47:18 +0800

Allow overriding the style file

Diffstat:
Mstagit-index.c | 361++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Mstagit.c | 342++++++++++++++++++++++++++++++++++++++++++-------------------------------------
2 files changed, 386 insertions(+), 317 deletions(-)

diff --git a/stagit-index.c b/stagit-index.c @@ -16,6 +16,165 @@ static char description[255] = "Repositories"; static char *name = ""; static char owner[255]; +static char * const default_style = + "body {\n" + " color: #000;\n" + " background-color: #fff;\n" + " font-family: monospace;\n" + "}\n" + "\n" + "h1, h2, h3, h4, h5, h6 {\n" + " font-size: 1em;\n" + " margin: 0;\n" + "}\n" + "\n" + "img, svg, h1, h2 {\n" + " vertical-align: middle;\n" + "}\n" + "\n" + "img {\n" + " border: 0;\n" + "}\n" + "\n" + "a:target {\n" + " background-color: #ccc;\n" + "}\n" + "\n" + "a.d,\n" + "a.h,\n" + "a.i,\n" + "a.line {\n" + " text-decoration: none;\n" + "}\n" + "\n" + "#blob a {\n" + " color: #555;\n" + "}\n" + " \n" + "#blob a:hover {\n" + " color: blue;\n" + " text-decoration: none;\n" + "}\n" + " \n" + "table thead td {\n" + " font-weight: bold;\n" + "}\n" + "\n" + "table td {\n" + " padding: 0 0.4em;\n" + "}\n" + "\n" + "#content table td {\n" + " vertical-align: top;\n" + " white-space: nowrap;\n" + "}\n" + " \n" + "#branches tr:hover td,\n" + "#tags tr:hover td,\n" + "#index tr:hover td,\n" + "#log tr:hover td,\n" + "#files tr:hover td {\n" + " background-color: #eee;\n" + "}\n" + " \n" + "#index tr td:nth-child(2),\n" + "#tags tr td:nth-child(3),\n" + "#branches tr td:nth-child(3),\n" + "#log tr td:nth-child(2) {\n" + " white-space: normal;\n" + "}\n" + " \n" + "td.num {\n" + " text-align: right;\n" + "}\n" + " \n" + ".desc {\n" + " color: #555;\n" + "}\n" + "\n" + "hr {\n" + " border: 0;\n" + " border-top: 1px solid #555;\n" + " height: 1px;\n" + "}\n" + "\n" + "pre {\n" + " font-family: monospace;\n" + "}\n" + "\n" + "pre a.h {\n" + " color: #00a;\n" + "}\n" + "\n" + ".A,\n" + "span.i,\n" + "pre a.i {\n" + " color: #070;\n" + "}\n" + "\n" + ".D,\n" + "span.d,\n" + "pre a.d {\n" + " color: #e00;\n" + "}\n" + "\n" + "pre a.h:hover,\n" + "pre a.i:hover,\n" + "pre a.d:hover {\n" + " text-decoration: none;\n" + "}\n" + "\n" + "@media (prefers-color-scheme: dark) {\n" + " body {\n" + " background-color: #000;\n" + " color: #bdbdbd;\n" + " }\n" + " hr {\n" + " border-color: #222;\n" + " }\n" + " a {\n" + " color: #56c8ff;\n" + " }\n" + " a:target {\n" + " background-color: #222;\n" + " }\n" + " .desc {\n" + " color: #aaa;\n" + " }\n" + " #blob a {\n" + " color: #555;\n" + " }\n" + " #blob a:target {\n" + " color: #eee;\n" + " }\n" + " #blob a:hover {\n" + " color: #56c8ff;\n" + " }\n" + " pre a.h {\n" + " color: #00cdcd;\n" + " }\n" + " .A,\n" + " span.i,\n" + " pre a.i {\n" + " color: #00cd00;\n" + " }\n" + " .D,\n" + " span.d,\n" + " pre a.d {\n" + " color: #cd0000;\n" + " }\n" + " #branches tr:hover td,\n" + " #tags tr:hover td,\n" + " #index tr:hover td,\n" + " #log tr:hover td,\n" + " #files tr:hover td {\n" + " background-color: #111;\n" + " }\n" + "}\n"; + +static char *stylefile; +static char *style = default_style; + /* Handle read or write errors for a FILE * stream */ void checkfileerror(FILE *fp, const char *name, int mode) @@ -112,163 +271,9 @@ writeheader(FILE *fp) "<meta name=\"msapplication-TileColor\" content=\"#603cba\">\n" "<meta name=\"msapplication-config\" content=\"/icons/browserconfig.xml\">\n" "<meta name=\"theme-color\" content=\"#ffffff\">\n"); - fprintf(fp, - "<style>\n" - "body {\n" - " color: #000;\n" - " background-color: #fff;\n" - " font-family: monospace;\n" - "}\n" - "\n" - "h1, h2, h3, h4, h5, h6 {\n" - " font-size: 1em;\n" - " margin: 0;\n" - "}\n" - "\n" - "img, svg, h1, h2 {\n" - " vertical-align: middle;\n" - "}\n" - "\n" - "img {\n" - " border: 0;\n" - "}\n" - "\n" - "a:target {\n" - " background-color: #ccc;\n" - "}\n" - "\n" - "a.d,\n" - "a.h,\n" - "a.i,\n" - "a.line {\n" - " text-decoration: none;\n" - "}\n" - "\n" - "#blob a {\n" - " color: #555;\n" - "}\n" - " \n" - "#blob a:hover {\n" - " color: blue;\n" - " text-decoration: none;\n" - "}\n" - " \n" - "table thead td {\n" - " font-weight: bold;\n" - "}\n" - "\n" - "table td {\n" - " padding: 0 0.4em;\n" - "}\n" - "\n" - "#content table td {\n" - " vertical-align: top;\n" - " white-space: nowrap;\n" - "}\n" - " \n" - "#branches tr:hover td,\n" - "#tags tr:hover td,\n" - "#index tr:hover td,\n" - "#log tr:hover td,\n" - "#files tr:hover td {\n" - " background-color: #eee;\n" - "}\n" - " \n" - "#index tr td:nth-child(2),\n" - "#tags tr td:nth-child(3),\n" - "#branches tr td:nth-child(3),\n" - "#log tr td:nth-child(2) {\n" - " white-space: normal;\n" - "}\n" - " \n" - "td.num {\n" - " text-align: right;\n" - "}\n" - " \n" - ".desc {\n" - " color: #555;\n" - "}\n" - "\n" - "hr {\n" - " border: 0;\n" - " border-top: 1px solid #555;\n" - " height: 1px;\n" - "}\n" - "\n" - "pre {\n" - " font-family: monospace;\n" - "}\n" - "\n" - "pre a.h {\n" - " color: #00a;\n" - "}\n" - "\n" - ".A,\n" - "span.i,\n" - "pre a.i {\n" - " color: #070;\n" - "}\n" - "\n" - ".D,\n" - "span.d,\n" - "pre a.d {\n" - " color: #e00;\n" - "}\n" - "\n" - "pre a.h:hover,\n" - "pre a.i:hover,\n" - "pre a.d:hover {\n" - " text-decoration: none;\n" - "}\n" - "\n" - "@media (prefers-color-scheme: dark) {\n" - " body {\n" - " background-color: #000;\n" - " color: #bdbdbd;\n" - " }\n" - " hr {\n" - " border-color: #222;\n" - " }\n" - " a {\n" - " color: #56c8ff;\n" - " }\n" - " a:target {\n" - " background-color: #222;\n" - " }\n" - " .desc {\n" - " color: #aaa;\n" - " }\n" - " #blob a {\n" - " color: #555;\n" - " }\n" - " #blob a:target {\n" - " color: #eee;\n" - " }\n" - " #blob a:hover {\n" - " color: #56c8ff;\n" - " }\n" - " pre a.h {\n" - " color: #00cdcd;\n" - " }\n" - " .A,\n" - " span.i,\n" - " pre a.i {\n" - " color: #00cd00;\n" - " }\n" - " .D,\n" - " span.d,\n" - " pre a.d {\n" - " color: #cd0000;\n" - " }\n" - " #branches tr:hover td,\n" - " #tags tr:hover td,\n" - " #index tr:hover td,\n" - " #log tr:hover td,\n" - " #files tr:hover td {\n" - " background-color: #111;\n" - " }\n" - "}\n" - "</style>\n"); + fputs("<style>\n", fp); + fputs(style, fp); + fputs("</style>\n", fp); fputs("</head>\n<body>\n", fp); fprintf(fp, "<table>\n<tr><td><a href=\"../\"><svg height=\"32px\" width=\"32px\" xmlns=\"http://www.w3.org/2000/svg\"><circle r=\"16\" cx=\"16\" cy=\"16\" fill=\"lightpink\"></circle></svg></a></td>\n" "<td><span class=\"desc\">"); @@ -339,13 +344,13 @@ err: int main(int argc, char *argv[]) { - FILE *fp; + FILE *fp, *fpread; char path[PATH_MAX], repodirabs[PATH_MAX + 1]; const char *repodir; int i, ret = 0; if (argc < 2) { - fprintf(stderr, "usage: %s [repodir...]\n", argv[0]); + fprintf(stderr, "usage: %s [-s stylefile] [repodir...]\n", argv[0]); return 1; } @@ -362,9 +367,47 @@ main(int argc, char *argv[]) err(1, "pledge"); #endif + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + continue; + } + if (argv[i][1] == 's') { + if (i + 1 >= argc) + fprintf(stderr, "usage: %s [-s stylefile] [repodir...]\n", argv[0]); + stylefile = argv[++i]; + continue; + } + err(1, "usage: %s [-s stylefile] [repodir...]\n", argv[0]); + } + + if (stylefile) { + if (!(fpread = fopen(stylefile, "r"))) + err(1, "fopen: '%s'", stylefile); + + if (fpread) { + fseek(fpread, 0, SEEK_END); + long int len = ftell(fpread); + rewind(fpread); + if (len >= 0) { + style = calloc(1, (size_t)len + 1); + if (!fread(style, (size_t)len, 1, fpread)) { + free(style); + style = default_style; + } + } + checkfileerror(fpread, stylefile, 'r'); + fclose(fpread); + } + } + writeheader(stdout); for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + ++i; + continue; + } + repodir = argv[i]; if (!realpath(repodir, repodirabs)) err(1, "realpath"); diff --git a/stagit.c b/stagit.c @@ -73,6 +73,164 @@ static char *readmefiles[] = { "HEAD:README", "HEAD:README.md" }; static char *readme; static long long nlogcommits = -1; /* -1 indicates not used */ +static char * const default_style = + "body {\n" + " color: #000;\n" + " background-color: #fff;\n" + " font-family: monospace;\n" + "}\n" + "\n" + "h1, h2, h3, h4, h5, h6 {\n" + " font-size: 1em;\n" + " margin: 0;\n" + "}\n" + "\n" + "img, svg, h1, h2 {\n" + " vertical-align: middle;\n" + "}\n" + "\n" + "img {\n" + " border: 0;\n" + "}\n" + "\n" + "a:target {\n" + " background-color: #ccc;\n" + "}\n" + "\n" + "a.d,\n" + "a.h,\n" + "a.i,\n" + "a.line {\n" + " text-decoration: none;\n" + "}\n" + "\n" + "#blob a {\n" + " color: #555;\n" + "}\n" + " \n" + "#blob a:hover {\n" + " color: blue;\n" + " text-decoration: none;\n" + "}\n" + " \n" + "table thead td {\n" + " font-weight: bold;\n" + "}\n" + "\n" + "table td {\n" + " padding: 0 0.4em;\n" + "}\n" + "\n" + "#content table td {\n" + " vertical-align: top;\n" + " white-space: nowrap;\n" + "}\n" + " \n" + "#branches tr:hover td,\n" + "#tags tr:hover td,\n" + "#index tr:hover td,\n" + "#log tr:hover td,\n" + "#files tr:hover td {\n" + " background-color: #eee;\n" + "}\n" + " \n" + "#index tr td:nth-child(2),\n" + "#tags tr td:nth-child(3),\n" + "#branches tr td:nth-child(3),\n" + "#log tr td:nth-child(2) {\n" + " white-space: normal;\n" + "}\n" + " \n" + "td.num {\n" + " text-align: right;\n" + "}\n" + " \n" + ".desc {\n" + " color: #555;\n" + "}\n" + "\n" + "hr {\n" + " border: 0;\n" + " border-top: 1px solid #555;\n" + " height: 1px;\n" + "}\n" + "\n" + "pre {\n" + " font-family: monospace;\n" + "}\n" + "\n" + "pre a.h {\n" + " color: #00a;\n" + "}\n" + "\n" + ".A,\n" + "span.i,\n" + "pre a.i {\n" + " color: #070;\n" + "}\n" + "\n" + ".D,\n" + "span.d,\n" + "pre a.d {\n" + " color: #e00;\n" + "}\n" + "\n" + "pre a.h:hover,\n" + "pre a.i:hover,\n" + "pre a.d:hover {\n" + " text-decoration: none;\n" + "}\n" + "\n" + "@media (prefers-color-scheme: dark) {\n" + " body {\n" + " background-color: #000;\n" + " color: #bdbdbd;\n" + " }\n" + " hr {\n" + " border-color: #222;\n" + " }\n" + " a {\n" + " color: #56c8ff;\n" + " }\n" + " a:target {\n" + " background-color: #222;\n" + " }\n" + " .desc {\n" + " color: #aaa;\n" + " }\n" + " #blob a {\n" + " color: #555;\n" + " }\n" + " #blob a:target {\n" + " color: #eee;\n" + " }\n" + " #blob a:hover {\n" + " color: #56c8ff;\n" + " }\n" + " pre a.h {\n" + " color: #00cdcd;\n" + " }\n" + " .A,\n" + " span.i,\n" + " pre a.i {\n" + " color: #00cd00;\n" + " }\n" + " .D,\n" + " span.d,\n" + " pre a.d {\n" + " color: #cd0000;\n" + " }\n" + " #branches tr:hover td,\n" + " #tags tr:hover td,\n" + " #index tr:hover td,\n" + " #log tr:hover td,\n" + " #files tr:hover td {\n" + " background-color: #111;\n" + " }\n" + "}\n"; +static char *stylefile; +static char *style = default_style; + /* cache */ static git_oid lastoid; static char lastoidstr[GIT_OID_HEXSZ + 2]; /* id + newline + NUL byte */ @@ -528,163 +686,9 @@ writeheader(FILE *fp, const char *title) fputs("<link rel=\"alternate\" type=\"application/atom+xml\" title=\"", fp); xmlencode(fp, name, strlen(name)); fprintf(fp, " Atom Feed (tags)\" href=\"%stags.xml\" />\n", relpath); - fprintf(fp, - "<style>\n" - "body {\n" - " color: #000;\n" - " background-color: #fff;\n" - " font-family: monospace;\n" - "}\n" - "\n" - "h1, h2, h3, h4, h5, h6 {\n" - " font-size: 1em;\n" - " margin: 0;\n" - "}\n" - "\n" - "img, svg, h1, h2 {\n" - " vertical-align: middle;\n" - "}\n" - "\n" - "img {\n" - " border: 0;\n" - "}\n" - "\n" - "a:target {\n" - " background-color: #ccc;\n" - "}\n" - "\n" - "a.d,\n" - "a.h,\n" - "a.i,\n" - "a.line {\n" - " text-decoration: none;\n" - "}\n" - "\n" - "#blob a {\n" - " color: #555;\n" - "}\n" - " \n" - "#blob a:hover {\n" - " color: blue;\n" - " text-decoration: none;\n" - "}\n" - " \n" - "table thead td {\n" - " font-weight: bold;\n" - "}\n" - "\n" - "table td {\n" - " padding: 0 0.4em;\n" - "}\n" - "\n" - "#content table td {\n" - " vertical-align: top;\n" - " white-space: nowrap;\n" - "}\n" - " \n" - "#branches tr:hover td,\n" - "#tags tr:hover td,\n" - "#index tr:hover td,\n" - "#log tr:hover td,\n" - "#files tr:hover td {\n" - " background-color: #eee;\n" - "}\n" - " \n" - "#index tr td:nth-child(2),\n" - "#tags tr td:nth-child(3),\n" - "#branches tr td:nth-child(3),\n" - "#log tr td:nth-child(2) {\n" - " white-space: normal;\n" - "}\n" - " \n" - "td.num {\n" - " text-align: right;\n" - "}\n" - " \n" - ".desc {\n" - " color: #555;\n" - "}\n" - "\n" - "hr {\n" - " border: 0;\n" - " border-top: 1px solid #555;\n" - " height: 1px;\n" - "}\n" - "\n" - "pre {\n" - " font-family: monospace;\n" - "}\n" - "\n" - "pre a.h {\n" - " color: #00a;\n" - "}\n" - "\n" - ".A,\n" - "span.i,\n" - "pre a.i {\n" - " color: #070;\n" - "}\n" - "\n" - ".D,\n" - "span.d,\n" - "pre a.d {\n" - " color: #e00;\n" - "}\n" - "\n" - "pre a.h:hover,\n" - "pre a.i:hover,\n" - "pre a.d:hover {\n" - " text-decoration: none;\n" - "}\n" - "\n" - "@media (prefers-color-scheme: dark) {\n" - " body {\n" - " background-color: #000;\n" - " color: #bdbdbd;\n" - " }\n" - " hr {\n" - " border-color: #222;\n" - " }\n" - " a {\n" - " color: #56c8ff;\n" - " }\n" - " a:target {\n" - " background-color: #222;\n" - " }\n" - " .desc {\n" - " color: #aaa;\n" - " }\n" - " #blob a {\n" - " color: #555;\n" - " }\n" - " #blob a:target {\n" - " color: #eee;\n" - " }\n" - " #blob a:hover {\n" - " color: #56c8ff;\n" - " }\n" - " pre a.h {\n" - " color: #00cdcd;\n" - " }\n" - " .A,\n" - " span.i,\n" - " pre a.i {\n" - " color: #00cd00;\n" - " }\n" - " .D,\n" - " span.d,\n" - " pre a.d {\n" - " color: #cd0000;\n" - " }\n" - " #branches tr:hover td,\n" - " #tags tr:hover td,\n" - " #index tr:hover td,\n" - " #log tr:hover td,\n" - " #files tr:hover td {\n" - " background-color: #111;\n" - " }\n" - "}\n" - "</style>\n"); + fputs("<style>\n", fp); + fputs(style, fp); + fputs("</style>\n", fp); fputs("</head>\n<body>\n<table><tr><td>", fp); fprintf(fp, "<a href=\"../%s\"><svg height=\"32px\" width=\"32px\" xmlns=\"http://www.w3.org/2000/svg\"><circle r=\"16\" cx=\"16\" cy=\"16\" fill=\"lightpink\"></circle></svg></a>", relpath); fputs("</td><td><h1>", fp); @@ -1350,7 +1354,7 @@ void usage(char *argv0) { fprintf(stderr, "usage: %s [-c cachefile | -l commits] " - "[-u baseurl] repodir\n", argv0); + "[-u baseurl] [-s stylefile] repodir\n", argv0); exit(1); } @@ -1387,6 +1391,10 @@ main(int argc, char *argv[]) if (i + 1 >= argc) usage(argv[0]); baseurl = argv[++i]; + } else if (argv[i][1] == 's') { + if (i + 1 >= argc) + usage(argv[0]); + stylefile = argv[++i]; } } if (!repodir) @@ -1395,6 +1403,24 @@ main(int argc, char *argv[]) if (!realpath(repodir, repodirabs)) err(1, "realpath"); + if (stylefile) { + fpread = efopen(stylefile, "r"); + if (fpread) { + fseek(fpread, 0, SEEK_END); + long int len = ftell(fpread); + rewind(fpread); + if (len >= 0) { + style = calloc(1, (size_t)len + 1); + if (!fread(style, (size_t)len, 1, fpread)) { + free(style); + style = default_style; + } + } + checkfileerror(fpread, stylefile, 'r'); + fclose(fpread); + } + } + /* do not search outside the git repository: GIT_CONFIG_LEVEL_APP is the highest level currently */ git_libgit2_init();