-rw-r--r-- | cgit.c | 3 | ||||
-rw-r--r-- | cgit.css | 6 | ||||
-rw-r--r-- | cgit.h | 1 | ||||
-rw-r--r-- | cgitrc.5.txt | 4 | ||||
-rw-r--r-- | ui-tree.c | 25 |
5 files changed, 27 insertions, 12 deletions
@@ -41,96 +41,98 @@ struct cgit_filter *new_filter(const char *cmd, int extra_args) } void config_cb(const char *name, const char *value) { if (!strcmp(name, "root-title")) ctx.cfg.root_title = xstrdup(value); else if (!strcmp(name, "root-desc")) ctx.cfg.root_desc = xstrdup(value); else if (!strcmp(name, "root-readme")) ctx.cfg.root_readme = xstrdup(value); else if (!strcmp(name, "css")) ctx.cfg.css = xstrdup(value); else if (!strcmp(name, "favicon")) ctx.cfg.favicon = xstrdup(value); else if (!strcmp(name, "footer")) ctx.cfg.footer = xstrdup(value); else if (!strcmp(name, "head-include")) ctx.cfg.head_include = xstrdup(value); else if (!strcmp(name, "header")) ctx.cfg.header = xstrdup(value); else if (!strcmp(name, "logo")) ctx.cfg.logo = xstrdup(value); else if (!strcmp(name, "index-header")) ctx.cfg.index_header = xstrdup(value); else if (!strcmp(name, "index-info")) ctx.cfg.index_info = xstrdup(value); else if (!strcmp(name, "logo-link")) ctx.cfg.logo_link = xstrdup(value); else if (!strcmp(name, "module-link")) ctx.cfg.module_link = xstrdup(value); else if (!strcmp(name, "virtual-root")) { ctx.cfg.virtual_root = trim_end(value, '/'); if (!ctx.cfg.virtual_root && (!strcmp(value, "/"))) ctx.cfg.virtual_root = ""; } else if (!strcmp(name, "nocache")) ctx.cfg.nocache = atoi(value); else if (!strcmp(name, "noplainemail")) ctx.cfg.noplainemail = atoi(value); else if (!strcmp(name, "noheader")) ctx.cfg.noheader = atoi(value); else if (!strcmp(name, "snapshots")) ctx.cfg.snapshots = cgit_parse_snapshots_mask(value); else if (!strcmp(name, "enable-index-links")) ctx.cfg.enable_index_links = atoi(value); else if (!strcmp(name, "enable-log-filecount")) ctx.cfg.enable_log_filecount = atoi(value); else if (!strcmp(name, "enable-log-linecount")) ctx.cfg.enable_log_linecount = atoi(value); + else if (!strcmp(name, "enable-tree-linenumbers")) + ctx.cfg.enable_tree_linenumbers = atoi(value); else if (!strcmp(name, "max-stats")) ctx.cfg.max_stats = cgit_find_stats_period(value, NULL); else if (!strcmp(name, "cache-size")) ctx.cfg.cache_size = atoi(value); else if (!strcmp(name, "cache-root")) ctx.cfg.cache_root = xstrdup(value); else if (!strcmp(name, "cache-root-ttl")) ctx.cfg.cache_root_ttl = atoi(value); else if (!strcmp(name, "cache-repo-ttl")) ctx.cfg.cache_repo_ttl = atoi(value); else if (!strcmp(name, "cache-static-ttl")) ctx.cfg.cache_static_ttl = atoi(value); else if (!strcmp(name, "cache-dynamic-ttl")) ctx.cfg.cache_dynamic_ttl = atoi(value); else if (!strcmp(name, "about-filter")) ctx.cfg.about_filter = new_filter(value, 0); else if (!strcmp(name, "commit-filter")) ctx.cfg.commit_filter = new_filter(value, 0); else if (!strcmp(name, "embedded")) ctx.cfg.embedded = atoi(value); else if (!strcmp(name, "max-message-length")) ctx.cfg.max_msg_len = atoi(value); else if (!strcmp(name, "max-repodesc-length")) ctx.cfg.max_repodesc_len = atoi(value); else if (!strcmp(name, "max-repo-count")) ctx.cfg.max_repo_count = atoi(value); else if (!strcmp(name, "max-commit-count")) ctx.cfg.max_commit_count = atoi(value); else if (!strcmp(name, "source-filter")) ctx.cfg.source_filter = new_filter(value, 1); else if (!strcmp(name, "summary-log")) ctx.cfg.summary_log = atoi(value); else if (!strcmp(name, "summary-branches")) ctx.cfg.summary_branches = atoi(value); else if (!strcmp(name, "summary-tags")) ctx.cfg.summary_tags = atoi(value); else if (!strcmp(name, "agefile")) ctx.cfg.agefile = xstrdup(value); else if (!strcmp(name, "renamelimit")) ctx.cfg.renamelimit = atoi(value); else if (!strcmp(name, "robots")) ctx.cfg.robots = xstrdup(value); else if (!strcmp(name, "clone-prefix")) ctx.cfg.clone_prefix = xstrdup(value); else if (!strcmp(name, "local-time")) ctx.cfg.local_time = atoi(value); else if (!prefixcmp(name, "mimetype.")) add_mimetype(name + 9, value); @@ -193,96 +195,97 @@ static void querystring_cb(const char *name, const char *value) } else if (!strcmp(name, "q")) { ctx.qry.search = xstrdup(value); } else if (!strcmp(name, "h")) { ctx.qry.head = xstrdup(value); ctx.qry.has_symref = 1; } else if (!strcmp(name, "id")) { ctx.qry.sha1 = xstrdup(value); ctx.qry.has_sha1 = 1; } else if (!strcmp(name, "id2")) { ctx.qry.sha2 = xstrdup(value); ctx.qry.has_sha1 = 1; } else if (!strcmp(name, "ofs")) { ctx.qry.ofs = atoi(value); } else if (!strcmp(name, "path")) { ctx.qry.path = trim_end(value, '/'); } else if (!strcmp(name, "name")) { ctx.qry.name = xstrdup(value); } else if (!strcmp(name, "mimetype")) { ctx.qry.mimetype = xstrdup(value); } else if (!strcmp(name, "s")){ ctx.qry.sort = xstrdup(value); } else if (!strcmp(name, "showmsg")) { ctx.qry.showmsg = atoi(value); } else if (!strcmp(name, "period")) { ctx.qry.period = xstrdup(value); } } char *xstrdupn(const char *str) { return (str ? xstrdup(str) : NULL); } static void prepare_context(struct cgit_context *ctx) { memset(ctx, 0, sizeof(ctx)); ctx->cfg.agefile = "info/web/last-modified"; ctx->cfg.nocache = 0; ctx->cfg.cache_size = 0; ctx->cfg.cache_dynamic_ttl = 5; ctx->cfg.cache_max_create_time = 5; ctx->cfg.cache_repo_ttl = 5; ctx->cfg.cache_root = CGIT_CACHE_ROOT; ctx->cfg.cache_root_ttl = 5; ctx->cfg.cache_static_ttl = -1; ctx->cfg.css = "/cgit.css"; ctx->cfg.logo = "/cgit.png"; ctx->cfg.local_time = 0; + ctx->cfg.enable_tree_linenumbers = 1; ctx->cfg.max_repo_count = 50; ctx->cfg.max_commit_count = 50; ctx->cfg.max_lock_attempts = 5; ctx->cfg.max_msg_len = 80; ctx->cfg.max_repodesc_len = 80; ctx->cfg.max_stats = 0; ctx->cfg.module_link = "./?repo=%s&page=commit&id=%s"; ctx->cfg.renamelimit = -1; ctx->cfg.robots = "index, nofollow"; ctx->cfg.root_title = "Git repository browser"; ctx->cfg.root_desc = "a fast webinterface for the git dscm"; ctx->cfg.script_name = CGIT_SCRIPT_NAME; ctx->cfg.summary_branches = 10; ctx->cfg.summary_log = 10; ctx->cfg.summary_tags = 10; ctx->env.cgit_config = xstrdupn(getenv("CGIT_CONFIG")); ctx->env.http_host = xstrdupn(getenv("HTTP_HOST")); ctx->env.https = xstrdupn(getenv("HTTPS")); ctx->env.no_http = xstrdupn(getenv("NO_HTTP")); ctx->env.path_info = xstrdupn(getenv("PATH_INFO")); ctx->env.query_string = xstrdupn(getenv("QUERY_STRING")); ctx->env.request_method = xstrdupn(getenv("REQUEST_METHOD")); ctx->env.script_name = xstrdupn(getenv("SCRIPT_NAME")); ctx->env.server_name = xstrdupn(getenv("SERVER_NAME")); ctx->env.server_port = xstrdupn(getenv("SERVER_PORT")); ctx->page.mimetype = "text/html"; ctx->page.charset = PAGE_ENCODING; ctx->page.filename = NULL; ctx->page.size = 0; ctx->page.modified = time(NULL); ctx->page.expires = ctx->page.modified; ctx->page.etag = NULL; memset(&ctx->cfg.mimetypes, 0, sizeof(struct string_list)); if (ctx->env.script_name) ctx->cfg.script_name = ctx->env.script_name; if (ctx->env.query_string) ctx->qry.raw = ctx->env.query_string; if (!ctx->env.cgit_config) ctx->env.cgit_config = CGIT_CONFIG; } struct refmatch { char *req_ref; char *first_ref; int match; }; int find_current_ref(const char *refname, const unsigned char *sha1, @@ -192,106 +192,106 @@ td#content { div#summary { vertical-align: top; margin-bottom: 1em; } table#downloads { float: right; border-collapse: collapse; border: solid 1px #777; margin-left: 0.5em; margin-bottom: 0.5em; } table#downloads th { background-color: #ccc; } div#blob { border: solid 1px black; } div.error { color: red; font-weight: bold; margin: 1em 2em; } a.ls-blob, a.ls-dir, a.ls-mod { font-family: monospace; } td.ls-size { text-align: right; font-family: monospace; width: 10em; } td.ls-mode { font-family: monospace; width: 10em; } table.blob { margin-top: 0.5em; border-top: solid 1px black; } table.blob td.lines { - margin: 0; padding: 0; + margin: 0; padding: 0 0 0 0.5em; vertical-align: top; color: black; } table.blob td.linenumbers { - margin: 0; padding: 0; + margin: 0; padding: 0 0.5em 0 0.5em; vertical-align: top; + text-align: right; border-right: 1px solid gray; - background-color: #eee; } table.blob pre { padding: 0; margin: 0; } table.blob a.no { color: gray; text-align: right; text-decoration: none; } table.blob a.no a:hover { color: black; } table.bin-blob { margin-top: 0.5em; border: solid 1px black; } table.bin-blob th { font-family: monospace; white-space: pre; border: solid 1px #777; padding: 0.5em 1em; } table.bin-blob td { font-family: monospace; white-space: pre; border-left: solid 1px #777; padding: 0em 1em; } table.nowrap td { white-space: nowrap; } table.commit-info { border-collapse: collapse; margin-top: 1.5em; } table.commit-info th { text-align: left; font-weight: normal; padding: 0.1em 1em 0.1em 0.1em; @@ -128,96 +128,97 @@ struct cgit_query { char *page; char *search; char *grep; char *head; char *sha1; char *sha2; char *path; char *name; char *mimetype; char *url; char *period; int ofs; int nohead; char *sort; int showmsg; }; struct cgit_config { char *agefile; char *cache_root; char *clone_prefix; char *css; char *favicon; char *footer; char *head_include; char *header; char *index_header; char *index_info; char *logo; char *logo_link; char *module_link; char *repo_group; char *robots; char *root_title; char *root_desc; char *root_readme; char *script_name; char *virtual_root; int cache_size; int cache_dynamic_ttl; int cache_max_create_time; int cache_repo_ttl; int cache_root_ttl; int cache_static_ttl; int embedded; int enable_index_links; int enable_log_filecount; int enable_log_linecount; + int enable_tree_linenumbers; int local_time; int max_repo_count; int max_commit_count; int max_lock_attempts; int max_msg_len; int max_repodesc_len; int max_stats; int nocache; int noplainemail; int noheader; int renamelimit; int snapshots; int summary_branches; int summary_log; int summary_tags; struct string_list mimetypes; struct cgit_filter *about_filter; struct cgit_filter *commit_filter; struct cgit_filter *source_filter; }; struct cgit_page { time_t modified; time_t expires; size_t size; char *mimetype; char *charset; char *filename; char *etag; char *title; int status; char *statusmsg; }; struct cgit_environment { char *cgit_config; char *http_host; char *https; char *no_http; char *path_info; char *query_string; char *request_method; char *script_name; char *server_name; char *server_port; }; struct cgit_context { diff --git a/cgitrc.5.txt b/cgitrc.5.txt index 0d18290..3b16db9 100644 --- a/cgitrc.5.txt +++ b/cgitrc.5.txt @@ -54,96 +54,100 @@ cache-root-ttl:: Number which specifies the time-to-live, in minutes, for the cached version of the repository index page. Default value: "5". cache-size:: The maximum number of entries in the cgit cache. Default value: "0" (i.e. caching is disabled). cache-static-ttl:: Number which specifies the time-to-live, in minutes, for the cached version of repository pages accessed with a fixed SHA1. Default value: "5". clone-prefix:: Space-separated list of common prefixes which, when combined with a repository url, generates valid clone urls for the repository. This setting is only used if `repo.clone-url` is unspecified. Default value: none. commit-filter:: Specifies a command which will be invoked to format commit messages. The command will get the message on its STDIN, and the STDOUT from the command will be included verbatim as the commit message, i.e. this can be used to implement bugtracker integration. Default value: none. css:: Url which specifies the css document to include in all cgit pages. Default value: "/cgit.css". embedded:: Flag which, when set to "1", will make cgit generate a html fragment suitable for embedding in other html pages. Default value: none. See also: "noheader". enable-index-links:: Flag which, when set to "1", will make cgit generate extra links for each repo in the repository index (specifically, to the "summary", "commit" and "tree" pages). Default value: "0". enable-log-filecount:: Flag which, when set to "1", will make cgit print the number of modified files for each commit on the repository log page. Default value: "0". enable-log-linecount:: Flag which, when set to "1", will make cgit print the number of added and removed lines for each commit on the repository log page. Default value: "0". +enable-tree-linenumbers:: + Flag which, when set to "1", will make cgit generate linenumber links + for plaintext blobs printed in the tree view. Default value: "1". + favicon:: Url used as link to a shortcut icon for cgit. If specified, it is suggested to use the value "/favicon.ico" since certain browsers will ignore other values. Default value: none. footer:: The content of the file specified with this option will be included verbatim at the bottom of all pages (i.e. it replaces the standard "generated by..." message. Default value: none. head-include:: The content of the file specified with this option will be included verbatim in the html HEAD section on all pages. Default value: none. header:: The content of the file specified with this option will be included verbatim at the top of all pages. Default value: none. include:: Name of a configfile to include before the rest of the current config- file is parsed. Default value: none. index-header:: The content of the file specified with this option will be included verbatim above the repository index. This setting is deprecated, and will not be supported by cgit-1.0 (use root-readme instead). Default value: none. index-info:: The content of the file specified with this option will be included verbatim below the heading on the repository index page. This setting is deprecated, and will not be supported by cgit-1.0 (use root-desc instead). Default value: none. local-time:: Flag which, if set to "1", makes cgit print commit and tag times in the servers timezone. Default value: "0". logo:: Url which specifies the source of an image which will be used as a logo on all cgit pages. Default value: "/cgit.png". logo-link:: Url loaded when clicking on the cgit logo image. If unspecified the calculated url of the repository index page will be used. Default value: none. max-commit-count:: @@ -1,95 +1,102 @@ /* ui-tree.c: functions for tree output * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include <ctype.h> #include "cgit.h" #include "html.h" #include "ui-shared.h" char *curr_rev; char *match_path; int header = 0; static void print_text_buffer(const char *name, char *buf, unsigned long size) { unsigned long lineno, idx; const char *numberfmt = "<a class='no' id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a>\n"; html("<table summary='blob content' class='blob'>\n"); - if (ctx.repo->source_filter) { - html("<tr><td class='lines'><pre><code>"); - ctx.repo->source_filter->argv[1] = xstrdup(name); - cgit_open_filter(ctx.repo->source_filter); - write(STDOUT_FILENO, buf, size); - cgit_close_filter(ctx.repo->source_filter); - html("</code></pre></td></tr></table>\n"); - return; - } + if (ctx.cfg.enable_tree_linenumbers) { html("<tr><td class='linenumbers'><pre>"); idx = 0; lineno = 0; if (size) { htmlf(numberfmt, ++lineno); while(idx < size - 1) { // skip absolute last newline if (buf[idx] == '\n') htmlf(numberfmt, ++lineno); idx++; } } html("</pre></td>\n"); + } + else { + html("<tr>\n"); + } + + if (ctx.repo->source_filter) { + html("<td class='lines'><pre><code>"); + ctx.repo->source_filter->argv[1] = xstrdup(name); + cgit_open_filter(ctx.repo->source_filter); + write(STDOUT_FILENO, buf, size); + cgit_close_filter(ctx.repo->source_filter); + html("</code></pre></td></tr></table>\n"); + return; + } + html("<td class='lines'><pre><code>"); html_txt(buf); html("</code></pre></td></tr></table>\n"); } #define ROWLEN 32 static void print_binary_buffer(char *buf, unsigned long size) { unsigned long ofs, idx; static char ascii[ROWLEN + 1]; html("<table summary='blob content' class='bin-blob'>\n"); html("<tr><th>ofs</th><th>hex dump</th><th>ascii</th></tr>"); for (ofs = 0; ofs < size; ofs += ROWLEN, buf += ROWLEN) { htmlf("<tr><td class='right'>%04x</td><td class='hex'>", ofs); for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) htmlf("%*s%02x", idx == 16 ? 4 : 1, "", buf[idx] & 0xff); html(" </td><td class='hex'>"); for (idx = 0; idx < ROWLEN && ofs + idx < size; idx++) ascii[idx] = isgraph(buf[idx]) ? buf[idx] : '.'; ascii[idx] = '\0'; html_txt(ascii); html("</td></tr>\n"); } html("</table>\n"); } static void print_object(const unsigned char *sha1, char *path, const char *basename) { enum object_type type; char *buf; unsigned long size; type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { cgit_print_error(fmt("Bad object name: %s", sha1_to_hex(sha1))); return; } buf = read_sha1_file(sha1, &type, &size); if (!buf) { cgit_print_error(fmt("Error reading object %s", sha1_to_hex(sha1))); return; |