-rw-r--r-- | ui-shared.c | 6 | ||||
-rw-r--r-- | ui-shared.h | 2 | ||||
-rw-r--r-- | ui-tree.c | 8 |
3 files changed, 12 insertions, 4 deletions
diff --git a/ui-shared.c b/ui-shared.c index 4408969..a2e0dd2 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1,501 +1,507 @@ /* ui-shared.c: common web output functions * * Copyright (C) 2006 Lars Hjemli * * Licensed under GNU General Public License v2 * (see COPYING for full license text) */ #include "cgit.h" #include "cmd.h" #include "html.h" const char cgit_doctype[] = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n" " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n"; static char *http_date(time_t t) { static char day[][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static char month[][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Now", "Dec"}; struct tm *tm = gmtime(&t); return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday], tm->tm_mday, month[tm->tm_mon], 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec); } void cgit_print_error(char *msg) { html("<div class='error'>"); html_txt(msg); html("</div>\n"); } char *cgit_rooturl() { if (ctx.cfg.virtual_root) return fmt("%s/", ctx.cfg.virtual_root); else return ctx.cfg.script_name; } char *cgit_repourl(const char *reponame) { if (ctx.cfg.virtual_root) { return fmt("%s/%s/", ctx.cfg.virtual_root, reponame); } else { return fmt("?r=%s", reponame); } } char *cgit_fileurl(const char *reponame, const char *pagename, const char *filename, const char *query) { char *tmp; char *delim; if (ctx.cfg.virtual_root) { tmp = fmt("%s/%s/%s/%s", ctx.cfg.virtual_root, reponame, pagename, (filename ? filename:"")); delim = "?"; } else { tmp = fmt("?url=%s/%s/%s", reponame, pagename, (filename ? filename : "")); delim = "&"; } if (query) tmp = fmt("%s%s%s", tmp, delim, query); return tmp; } char *cgit_pageurl(const char *reponame, const char *pagename, const char *query) { return cgit_fileurl(reponame,pagename,0,query); } const char *cgit_repobasename(const char *reponame) { /* I assume we don't need to store more than one repo basename */ static char rvbuf[1024]; int p; const char *rv; strncpy(rvbuf,reponame,sizeof(rvbuf)); if(rvbuf[sizeof(rvbuf)-1]) die("cgit_repobasename: truncated repository name '%s'", reponame); p = strlen(rvbuf)-1; /* strip trailing slashes */ while(p && rvbuf[p]=='/') rvbuf[p--]=0; /* strip trailing .git */ if(p>=3 && !strncmp(&rvbuf[p-3],".git",4)) { p -= 3; rvbuf[p--] = 0; } /* strip more trailing slashes if any */ while( p && rvbuf[p]=='/') rvbuf[p--]=0; /* find last slash in the remaining string */ rv = strrchr(rvbuf,'/'); if(rv) return ++rv; return rvbuf; } char *cgit_currurl() { if (!ctx.cfg.virtual_root) return ctx.cfg.script_name; else if (ctx.qry.page) return fmt("%s/%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo, ctx.qry.page); else if (ctx.qry.repo) return fmt("%s/%s/", ctx.cfg.virtual_root, ctx.qry.repo); else return fmt("%s/", ctx.cfg.virtual_root); } static void site_url(char *page, char *search, int ofs) { char *delim = "?"; if (ctx.cfg.virtual_root) { html_attr(ctx.cfg.virtual_root); if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') html("/"); } else html(ctx.cfg.script_name); if (page) { htmlf("?p=%s", page); delim = "&"; } if (search) { html(delim); html("q="); html_attr(search); delim = "&"; } if (ofs) { html(delim); htmlf("ofs=%d", ofs); } } static void site_link(char *page, char *name, char *title, char *class, char *search, int ofs) { html("<a"); if (title) { html(" title='"); html_attr(title); html("'"); } if (class) { html(" class='"); html_attr(class); html("'"); } html(" href='"); site_url(page, search, ofs); html("'>"); html_txt(name); html("</a>"); } void cgit_index_link(char *name, char *title, char *class, char *pattern, int ofs) { site_link(NULL, name, title, class, pattern, ofs); } static char *repolink(char *title, char *class, char *page, char *head, char *path) { char *delim = "?"; html("<a"); if (title) { html(" title='"); html_attr(title); html("'"); } if (class) { html(" class='"); html_attr(class); html("'"); } html(" href='"); if (ctx.cfg.virtual_root) { html_attr(ctx.cfg.virtual_root); if (ctx.cfg.virtual_root[strlen(ctx.cfg.virtual_root) - 1] != '/') html("/"); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } } else { html(ctx.cfg.script_name); html("?url="); html_attr(ctx.repo->url); if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/') html("/"); if (page) { html(page); html("/"); if (path) html_attr(path); } delim = "&"; } if (head && strcmp(head, ctx.repo->defbranch)) { html(delim); html("h="); html_attr(head); delim = "&"; } return fmt("%s", delim); } static void reporevlink(char *page, char *name, char *title, char *class, char *head, char *rev, char *path) { char *delim; delim = repolink(title, class, page, head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_tree_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("tree", name, title, class, head, rev, path); } +void cgit_plain_link(char *name, char *title, char *class, char *head, + char *rev, char *path) +{ + reporevlink("plain", name, title, class, head, rev, path); +} + void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern) { char *delim; delim = repolink(title, class, "log", head, path); if (rev && strcmp(rev, ctx.qry.head)) { html(delim); html("id="); html_attr(rev); delim = "&"; } if (grep && pattern) { html(delim); html("qt="); html_attr(grep); delim = "&"; html(delim); html("q="); html_attr(pattern); } if (ofs > 0) { html(delim); html("ofs="); htmlf("%d", ofs); } html("'>"); html_txt(name); html("</a>"); } void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev) { if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { name[ctx.cfg.max_msg_len] = '\0'; name[ctx.cfg.max_msg_len - 1] = '.'; name[ctx.cfg.max_msg_len - 2] = '.'; name[ctx.cfg.max_msg_len - 3] = '.'; } reporevlink("commit", name, title, class, head, rev, NULL); } void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path) { reporevlink("refs", name, title, class, head, rev, path); } void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename) { reporevlink("snapshot", name, title, class, head, rev, archivename); } void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path) { char *delim; delim = repolink(title, class, "diff", head, path); if (new_rev && strcmp(new_rev, ctx.qry.head)) { html(delim); html("id="); html_attr(new_rev); delim = "&"; } if (old_rev) { html(delim); html("id2="); html_attr(old_rev); } html("'>"); html_txt(name); html("</a>"); } void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev) { reporevlink("patch", name, title, class, head, rev, NULL); } void cgit_object_link(struct object *obj) { char *page, *arg, *url; if (obj->type == OBJ_COMMIT) { cgit_commit_link(fmt("commit %s", sha1_to_hex(obj->sha1)), NULL, NULL, ctx.qry.head, sha1_to_hex(obj->sha1)); return; } else if (obj->type == OBJ_TREE) { page = "tree"; arg = "id"; } else if (obj->type == OBJ_TAG) { page = "tag"; arg = "id"; } else { page = "blob"; arg = "id"; } url = cgit_pageurl(ctx.qry.repo, page, fmt("%s=%s", arg, sha1_to_hex(obj->sha1))); html_link_open(url, NULL, NULL); htmlf("%s %s", typename(obj->type), sha1_to_hex(obj->sha1)); html_link_close(); } void cgit_print_date(time_t secs, char *format, int local_time) { char buf[64]; struct tm *time; if (!secs) return; if(local_time) time = localtime(&secs); else time = gmtime(&secs); strftime(buf, sizeof(buf)-1, format, time); html_txt(buf); } void cgit_print_age(time_t t, time_t max_relative, char *format) { time_t now, secs; if (!t) return; time(&now); secs = now - t; if (secs > max_relative && max_relative >= 0) { cgit_print_date(t, format, ctx.cfg.local_time); return; } if (secs < TM_HOUR * 2) { htmlf("<span class='age-mins'>%.0f min.</span>", secs * 1.0 / TM_MIN); return; } if (secs < TM_DAY * 2) { htmlf("<span class='age-hours'>%.0f hours</span>", secs * 1.0 / TM_HOUR); return; } if (secs < TM_WEEK * 2) { htmlf("<span class='age-days'>%.0f days</span>", secs * 1.0 / TM_DAY); return; } if (secs < TM_MONTH * 2) { htmlf("<span class='age-weeks'>%.0f weeks</span>", secs * 1.0 / TM_WEEK); return; } if (secs < TM_YEAR * 2) { htmlf("<span class='age-months'>%.0f months</span>", secs * 1.0 / TM_MONTH); return; } htmlf("<span class='age-years'>%.0f years</span>", secs * 1.0 / TM_YEAR); } void cgit_print_http_headers(struct cgit_context *ctx) { if (ctx->page.mimetype && ctx->page.charset) htmlf("Content-Type: %s; charset=%s\n", ctx->page.mimetype, ctx->page.charset); else if (ctx->page.mimetype) htmlf("Content-Type: %s\n", ctx->page.mimetype); if (ctx->page.size) htmlf("Content-Length: %ld\n", ctx->page.size); if (ctx->page.filename) htmlf("Content-Disposition: inline; filename=\"%s\"\n", ctx->page.filename); htmlf("Last-Modified: %s\n", http_date(ctx->page.modified)); htmlf("Expires: %s\n", http_date(ctx->page.expires)); html("\n"); } void cgit_print_docstart(struct cgit_context *ctx) { html(cgit_doctype); html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n"); html("<head>\n"); html("<title>"); html_txt(ctx->page.title); html("</title>\n"); htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version); if (ctx->cfg.robots && *ctx->cfg.robots) htmlf("<meta name='robots' content='%s'/>\n", ctx->cfg.robots); html("<link rel='stylesheet' type='text/css' href='"); html_attr(ctx->cfg.css); html("'/>\n"); if (ctx->cfg.favicon) { html("<link rel='shortcut icon' href='"); html_attr(ctx->cfg.favicon); html("'/>\n"); } html("</head>\n"); html("<body>\n"); } void cgit_print_docend() { html("</div>"); if (ctx.cfg.footer) html_include(ctx.cfg.footer); else { html("<div class='footer'>generated "); cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time); htmlf(" by cgit %s", cgit_version); html("</div>\n"); } html("</body>\n</html>\n"); } int print_branch_option(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { char *name = (char *)refname; html_option(name, name, ctx.qry.head); return 0; } int print_archive_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data) { struct tag *tag; struct taginfo *info; struct object *obj; char buf[256], *url; unsigned char fileid[20]; int *header = (int *)cb_data; if (prefixcmp(refname, "refs/archives")) return 0; strncpy(buf, refname+14, sizeof(buf)); obj = parse_object(sha1); if (!obj) return 1; if (obj->type == OBJ_TAG) { tag = lookup_tag(sha1); if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) return 0; hashcpy(fileid, tag->tagged->sha1); } else if (obj->type != OBJ_BLOB) { return 0; } else { hashcpy(fileid, sha1); } diff --git a/ui-shared.h b/ui-shared.h index 07da4b4..c5ce056 100644 --- a/ui-shared.h +++ b/ui-shared.h @@ -1,40 +1,42 @@ #ifndef UI_SHARED_H #define UI_SHARED_H extern char *cgit_repourl(const char *reponame); extern char *cgit_fileurl(const char *reponame, const char *pagename, const char *filename, const char *query); extern char *cgit_pageurl(const char *reponame, const char *pagename, const char *query); extern void cgit_index_link(char *name, char *title, char *class, char *pattern, int ofs); extern void cgit_tree_link(char *name, char *title, char *class, char *head, char *rev, char *path); +extern void cgit_plain_link(char *name, char *title, char *class, char *head, + char *rev, char *path); extern void cgit_log_link(char *name, char *title, char *class, char *head, char *rev, char *path, int ofs, char *grep, char *pattern); extern void cgit_commit_link(char *name, char *title, char *class, char *head, char *rev); extern void cgit_patch_link(char *name, char *title, char *class, char *head, char *rev); extern void cgit_refs_link(char *name, char *title, char *class, char *head, char *rev, char *path); extern void cgit_snapshot_link(char *name, char *title, char *class, char *head, char *rev, char *archivename); extern void cgit_diff_link(char *name, char *title, char *class, char *head, char *new_rev, char *old_rev, char *path); extern void cgit_object_link(struct object *obj); extern void cgit_print_error(char *msg); extern void cgit_print_date(time_t secs, char *format, int local_time); extern void cgit_print_age(time_t t, time_t max_relative, char *format); extern void cgit_print_http_headers(struct cgit_context *ctx); extern void cgit_print_docstart(struct cgit_context *ctx); extern void cgit_print_docend(); extern void cgit_print_pageheader(struct cgit_context *ctx); extern void cgit_print_filemode(unsigned short mode); extern void cgit_print_snapshot_links(const char *repo, const char *head, const char *hex, int snapshots); #endif /* UI_SHARED_H */ @@ -1,223 +1,223 @@ /* 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 "cgit.h" #include "html.h" #include "ui-shared.h" char *curr_rev; char *match_path; int header = 0; static void print_object(const unsigned char *sha1, char *path) { enum object_type type; char *buf; unsigned long size, lineno, start, idx; const char *linefmt = "<tr><td class='no'><a id='n%1$d' name='n%1$d' href='#n%1$d'>%1$d</a></td><td class='txt'>"; 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; } - html(" blob: <a href='"); - html_attr(cgit_pageurl(ctx.qry.repo, "blob", - fmt("id=%s&path=%s", sha1_to_hex(sha1), path))); - htmlf("'>%s</a>",sha1_to_hex(sha1)); + html(" ("); + cgit_plain_link("plain", NULL, NULL, ctx.qry.head, + curr_rev, path); + htmlf(")<br/>blob: %s", sha1_to_hex(sha1)); html("<table summary='blob content' class='blob'>\n"); idx = 0; start = 0; lineno = 0; while(idx < size) { if (buf[idx] == '\n') { buf[idx] = '\0'; htmlf(linefmt, ++lineno); html_txt(buf + start); html("</td></tr>\n"); start = idx + 1; } idx++; } htmlf(linefmt, ++lineno); html_txt(buf + start); html("</td></tr>\n"); html("</table>\n"); } static int ls_item(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned int mode, int stage, void *cbdata) { char *name; char *fullpath; enum object_type type; unsigned long size = 0; name = xstrdup(pathname); fullpath = fmt("%s%s%s", ctx.qry.path ? ctx.qry.path : "", ctx.qry.path ? "/" : "", name); if (!S_ISGITLINK(mode)) { type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>", name, sha1_to_hex(sha1)); return 0; } } html("<tr><td class='ls-mode'>"); cgit_print_filemode(mode); html("</td><td>"); if (S_ISGITLINK(mode)) { htmlf("<a class='ls-mod' href='"); html_attr(fmt(ctx.repo->module_link, name, sha1_to_hex(sha1))); html("'>"); html_txt(name); html("</a>"); } else if (S_ISDIR(mode)) { cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, curr_rev, fullpath); } else { cgit_tree_link(name, NULL, "ls-blob", ctx.qry.head, curr_rev, fullpath); } htmlf("</td><td class='ls-size'>%li</td>", size); html("<td>"); cgit_log_link("log", NULL, "button", ctx.qry.head, curr_rev, fullpath, 0, NULL, NULL); html("</td></tr>\n"); free(name); return 0; } static void ls_head() { html("<table summary='tree listing' class='list'>\n"); html("<tr class='nohover'>"); html("<th class='left'>Mode</th>"); html("<th class='left'>Name</th>"); html("<th class='right'>Size</th>"); html("<th/>"); html("</tr>\n"); header = 1; } static void ls_tail() { if (!header) return; html("</table>\n"); header = 0; } static void ls_tree(const unsigned char *sha1, char *path) { struct tree *tree; tree = parse_tree_indirect(sha1); if (!tree) { cgit_print_error(fmt("Not a tree object: %s", sha1_to_hex(sha1))); return; } ls_head(); read_tree_recursive(tree, "", 0, 1, NULL, ls_item, NULL); ls_tail(); } static int walk_tree(const unsigned char *sha1, const char *base, int baselen, const char *pathname, unsigned mode, int stage, void *cbdata) { static int state; static char buffer[PATH_MAX]; char *url; if (state == 0) { memcpy(buffer, base, baselen); strcpy(buffer+baselen, pathname); url = cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s&path=%s", curr_rev, buffer)); html("/"); cgit_tree_link(xstrdup(pathname), NULL, NULL, ctx.qry.head, curr_rev, buffer); if (strcmp(match_path, buffer)) return READ_TREE_RECURSIVE; if (S_ISDIR(mode)) { state = 1; ls_head(); return READ_TREE_RECURSIVE; } else { print_object(sha1, buffer); return 0; } } ls_item(sha1, base, baselen, pathname, mode, stage, NULL); return 0; } /* * Show a tree or a blob * rev: the commit pointing at the root tree object * path: path to tree or blob */ void cgit_print_tree(const char *rev, char *path) { unsigned char sha1[20]; struct commit *commit; const char *paths[] = {path, NULL}; if (!rev) rev = ctx.qry.head; curr_rev = xstrdup(rev); if (get_sha1(rev, sha1)) { cgit_print_error(fmt("Invalid revision name: %s", rev)); return; } commit = lookup_commit_reference(sha1); if (!commit || parse_commit(commit)) { cgit_print_error(fmt("Invalid commit reference: %s", rev)); return; } html("path: <a href='"); html_attr(cgit_pageurl(ctx.qry.repo, "tree", fmt("h=%s", rev))); html("'>root</a>"); if (path == NULL) { ls_tree(commit->tree->object.sha1, NULL); return; } match_path = path; read_tree_recursive(commit->tree, NULL, 0, 0, paths, walk_tree, NULL); ls_tail(); } |