mirror of
https://git.zx2c4.com/cgit
synced 2024-11-22 16:38:42 +00:00
fb3655df3b
Use "struct strbuf" from Git to remove the limit on file path length. Notes on scan-tree: This is slightly involved since I decided to pass the strbuf into add_repo() and modify if whenever a new file name is required, which should avoid any extra allocations within that function. The pattern there is to append the filename, use it and then reset the buffer to its original length (retaining a trailing '/'). Notes on ui-snapshot: Since write_archive modifies the argv array passed to it we copy the argv_array values into a new array of char* and then free the original argv_array structure and the new array without worrying about what the values now look like. Signed-off-by: John Keeping <john@keeping.me.uk>
247 lines
5.5 KiB
C
247 lines
5.5 KiB
C
/* ui-plain.c: functions for output of plain blobs by path
|
|
*
|
|
* Copyright (C) 2008 Lars Hjemli
|
|
*
|
|
* Licensed under GNU General Public License v2
|
|
* (see COPYING for full license text)
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include "cgit.h"
|
|
#include "ui-plain.h"
|
|
#include "html.h"
|
|
#include "ui-shared.h"
|
|
|
|
struct walk_tree_context {
|
|
int match_baselen;
|
|
int match;
|
|
};
|
|
|
|
static char *get_mimetype_from_file(const char *filename, const char *ext)
|
|
{
|
|
static const char *delimiters;
|
|
char *result;
|
|
FILE *fd;
|
|
char line[1024];
|
|
char *mimetype;
|
|
char *token;
|
|
|
|
if (!filename)
|
|
return NULL;
|
|
|
|
fd = fopen(filename, "r");
|
|
if (!fd)
|
|
return NULL;
|
|
|
|
delimiters = " \t\r\n";
|
|
result = NULL;
|
|
|
|
/* loop over all lines in the file */
|
|
while (!result && fgets(line, sizeof(line), fd)) {
|
|
mimetype = strtok(line, delimiters);
|
|
|
|
/* skip empty lines and comment lines */
|
|
if (!mimetype || (mimetype[0] == '#'))
|
|
continue;
|
|
|
|
/* loop over all extensions of mimetype */
|
|
while ((token = strtok(NULL, delimiters))) {
|
|
if (!strcasecmp(ext, token)) {
|
|
result = xstrdup(mimetype);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fclose(fd);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int print_object(const unsigned char *sha1, const char *path)
|
|
{
|
|
enum object_type type;
|
|
char *buf, *ext;
|
|
unsigned long size;
|
|
struct string_list_item *mime;
|
|
int freemime;
|
|
|
|
type = sha1_object_info(sha1, &size);
|
|
if (type == OBJ_BAD) {
|
|
html_status(404, "Not found", 0);
|
|
return 0;
|
|
}
|
|
|
|
buf = read_sha1_file(sha1, &type, &size);
|
|
if (!buf) {
|
|
html_status(404, "Not found", 0);
|
|
return 0;
|
|
}
|
|
ctx.page.mimetype = NULL;
|
|
ext = strrchr(path, '.');
|
|
freemime = 0;
|
|
if (ext && *(++ext)) {
|
|
mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
|
|
if (mime) {
|
|
ctx.page.mimetype = (char *)mime->util;
|
|
} else {
|
|
ctx.page.mimetype = get_mimetype_from_file(ctx.cfg.mimetype_file, ext);
|
|
if (ctx.page.mimetype)
|
|
freemime = 1;
|
|
}
|
|
}
|
|
if (!ctx.page.mimetype) {
|
|
if (buffer_is_binary(buf, size))
|
|
ctx.page.mimetype = "application/octet-stream";
|
|
else
|
|
ctx.page.mimetype = "text/plain";
|
|
}
|
|
ctx.page.filename = path;
|
|
ctx.page.size = size;
|
|
ctx.page.etag = sha1_to_hex(sha1);
|
|
cgit_print_http_headers(&ctx);
|
|
html_raw(buf, size);
|
|
/* If we allocated this, then casting away const is safe. */
|
|
if (freemime)
|
|
free((char*) ctx.page.mimetype);
|
|
return 1;
|
|
}
|
|
|
|
static char *buildpath(const char *base, int baselen, const char *path)
|
|
{
|
|
if (path[0])
|
|
return fmtalloc("%.*s%s/", baselen, base, path);
|
|
else
|
|
return fmtalloc("%.*s/", baselen, base);
|
|
}
|
|
|
|
static void print_dir(const unsigned char *sha1, const char *base,
|
|
int baselen, const char *path)
|
|
{
|
|
char *fullpath, *slash;
|
|
size_t len;
|
|
|
|
fullpath = buildpath(base, baselen, path);
|
|
slash = (fullpath[0] == '/' ? "" : "/");
|
|
ctx.page.etag = sha1_to_hex(sha1);
|
|
cgit_print_http_headers(&ctx);
|
|
htmlf("<html><head><title>%s", slash);
|
|
html_txt(fullpath);
|
|
htmlf("</title></head>\n<body>\n<h2>%s", slash);
|
|
html_txt(fullpath);
|
|
html("</h2>\n<ul>\n");
|
|
len = strlen(fullpath);
|
|
if (len > 1) {
|
|
fullpath[len - 1] = 0;
|
|
slash = strrchr(fullpath, '/');
|
|
if (slash)
|
|
*(slash + 1) = 0;
|
|
else
|
|
fullpath = NULL;
|
|
html("<li>");
|
|
cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
|
fullpath);
|
|
html("</li>\n");
|
|
}
|
|
free(fullpath);
|
|
}
|
|
|
|
static void print_dir_entry(const unsigned char *sha1, const char *base,
|
|
int baselen, const char *path, unsigned mode)
|
|
{
|
|
char *fullpath;
|
|
|
|
fullpath = buildpath(base, baselen, path);
|
|
if (!S_ISDIR(mode) && !S_ISGITLINK(mode))
|
|
fullpath[strlen(fullpath) - 1] = 0;
|
|
html(" <li>");
|
|
if (S_ISGITLINK(mode)) {
|
|
cgit_submodule_link(NULL, fullpath, sha1_to_hex(sha1));
|
|
} else
|
|
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
|
fullpath);
|
|
html("</li>\n");
|
|
free(fullpath);
|
|
}
|
|
|
|
static void print_dir_tail(void)
|
|
{
|
|
html(" </ul>\n</body></html>\n");
|
|
}
|
|
|
|
static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
|
|
const char *pathname, unsigned mode, int stage,
|
|
void *cbdata)
|
|
{
|
|
struct walk_tree_context *walk_tree_ctx = cbdata;
|
|
|
|
if (baselen == walk_tree_ctx->match_baselen) {
|
|
if (S_ISREG(mode)) {
|
|
if (print_object(sha1, pathname))
|
|
walk_tree_ctx->match = 1;
|
|
} else if (S_ISDIR(mode)) {
|
|
print_dir(sha1, base, baselen, pathname);
|
|
walk_tree_ctx->match = 2;
|
|
return READ_TREE_RECURSIVE;
|
|
}
|
|
} else if (baselen > walk_tree_ctx->match_baselen) {
|
|
print_dir_entry(sha1, base, baselen, pathname, mode);
|
|
walk_tree_ctx->match = 2;
|
|
} else if (S_ISDIR(mode)) {
|
|
return READ_TREE_RECURSIVE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int basedir_len(const char *path)
|
|
{
|
|
char *p = strrchr(path, '/');
|
|
if (p)
|
|
return p - path + 1;
|
|
return 0;
|
|
}
|
|
|
|
void cgit_print_plain(struct cgit_context *ctx)
|
|
{
|
|
const char *rev = ctx->qry.sha1;
|
|
unsigned char sha1[20];
|
|
struct commit *commit;
|
|
struct pathspec_item path_items = {
|
|
.match = ctx->qry.path,
|
|
.len = ctx->qry.path ? strlen(ctx->qry.path) : 0
|
|
};
|
|
struct pathspec paths = {
|
|
.nr = 1,
|
|
.items = &path_items
|
|
};
|
|
struct walk_tree_context walk_tree_ctx = {
|
|
.match = 0
|
|
};
|
|
|
|
if (!rev)
|
|
rev = ctx->qry.head;
|
|
|
|
if (get_sha1(rev, sha1)) {
|
|
html_status(404, "Not found", 0);
|
|
return;
|
|
}
|
|
commit = lookup_commit_reference(sha1);
|
|
if (!commit || parse_commit(commit)) {
|
|
html_status(404, "Not found", 0);
|
|
return;
|
|
}
|
|
if (!path_items.match) {
|
|
path_items.match = "";
|
|
walk_tree_ctx.match_baselen = -1;
|
|
print_dir(commit->tree->object.sha1, "", 0, "");
|
|
walk_tree_ctx.match = 2;
|
|
}
|
|
else
|
|
walk_tree_ctx.match_baselen = basedir_len(path_items.match);
|
|
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
|
if (!walk_tree_ctx.match)
|
|
html_status(404, "Not found", 0);
|
|
else if (walk_tree_ctx.match == 2)
|
|
print_dir_tail();
|
|
}
|