all repos — cgit @ a6317505ead198c23925c93abbb0926f70e02861

a hyperfast web frontend for git written in c

ui-plain.c (view raw)

  1/* ui-plain.c: functions for output of plain blobs by path
  2 *
  3 * Copyright (C) 2008 Lars Hjemli
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include <stdio.h>
 10#include "cgit.h"
 11#include "html.h"
 12#include "ui-shared.h"
 13
 14int match_baselen;
 15int match;
 16
 17static char *get_mimetype_from_file(const char *filename, const char *ext)
 18{
 19	static const char *delimiters;
 20	char *result;
 21	FILE *fd;
 22	char line[1024];
 23	char *mimetype;
 24	char *token;
 25
 26	if (!filename)
 27		return NULL;
 28
 29	fd = fopen(filename, "r");
 30	if (!fd)
 31		return NULL;
 32
 33	delimiters = " \t\r\n";
 34	result = NULL;
 35
 36	/* loop over all lines in the file */
 37	while (!result && fgets(line, sizeof(line), fd)) {
 38		mimetype = strtok(line, delimiters);
 39
 40		/* skip empty lines and comment lines */
 41		if (!mimetype || (mimetype[0] == '#'))
 42			continue;
 43
 44		/* loop over all extensions of mimetype */
 45		while ((token = strtok(NULL, delimiters))) {
 46			if (!strcasecmp(ext, token)) {
 47				result = xstrdup(mimetype);
 48				break;
 49			}
 50		}
 51	}
 52	fclose(fd);
 53
 54	return result;
 55}
 56
 57static int print_object(const unsigned char *sha1, const char *path)
 58{
 59	enum object_type type;
 60	char *buf, *ext;
 61	unsigned long size;
 62	struct string_list_item *mime;
 63	int freemime;
 64
 65	type = sha1_object_info(sha1, &size);
 66	if (type == OBJ_BAD) {
 67		html_status(404, "Not found", 0);
 68		return 0;
 69	}
 70
 71	buf = read_sha1_file(sha1, &type, &size);
 72	if (!buf) {
 73		html_status(404, "Not found", 0);
 74		return 0;
 75	}
 76	ctx.page.mimetype = NULL;
 77	ext = strrchr(path, '.');
 78	freemime = 0;
 79	if (ext && *(++ext)) {
 80		mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
 81		if (mime) {
 82			ctx.page.mimetype = (char *)mime->util;
 83		} else {
 84			ctx.page.mimetype = get_mimetype_from_file(ctx.cfg.mimetype_file, ext);
 85			if (ctx.page.mimetype)
 86				freemime = 1;
 87		}
 88	}
 89	if (!ctx.page.mimetype) {
 90		if (buffer_is_binary(buf, size))
 91			ctx.page.mimetype = "application/octet-stream";
 92		else
 93			ctx.page.mimetype = "text/plain";
 94	}
 95	ctx.page.filename = fmt("%s", path);
 96	ctx.page.size = size;
 97	ctx.page.etag = sha1_to_hex(sha1);
 98	cgit_print_http_headers(&ctx);
 99	html_raw(buf, size);
100	if (freemime)
101		free(ctx.page.mimetype);
102	return 1;
103}
104
105static char *buildpath(const char *base, int baselen, const char *path)
106{
107	if (path[0])
108		return fmt("%.*s%s/", baselen, base, path);
109	else
110		return fmt("%.*s/", baselen, base);
111}
112
113static void print_dir(const unsigned char *sha1, const char *base,
114		      int baselen, const char *path)
115{
116	char *fullpath, *slash;
117	size_t len;
118
119	fullpath = buildpath(base, baselen, path);
120	slash = (fullpath[0] == '/' ? "" : "/");
121	ctx.page.etag = sha1_to_hex(sha1);
122	cgit_print_http_headers(&ctx);
123	htmlf("<html><head><title>%s", slash);
124	html_txt(fullpath);
125	htmlf("</title></head>\n<body>\n<h2>%s", slash);
126	html_txt(fullpath);
127	html("</h2>\n<ul>\n");
128	len = strlen(fullpath);
129	if (len > 1) {
130		fullpath[len - 1] = 0;
131		slash = strrchr(fullpath, '/');
132		if (slash)
133			*(slash + 1) = 0;
134		else
135			fullpath = NULL;
136		html("<li>");
137		cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
138				fullpath);
139		html("</li>\n");
140	}
141}
142
143static void print_dir_entry(const unsigned char *sha1, const char *base,
144			    int baselen, const char *path, unsigned mode)
145{
146	char *fullpath;
147
148	fullpath = buildpath(base, baselen, path);
149	if (!S_ISDIR(mode) && !S_ISGITLINK(mode))
150		fullpath[strlen(fullpath) - 1] = 0;
151	html("  <li>");
152	if (S_ISGITLINK(mode)) {
153		cgit_submodule_link(NULL, fullpath, sha1_to_hex(sha1));
154	} else
155		cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
156				fullpath);
157	html("</li>\n");
158}
159
160static void print_dir_tail(void)
161{
162	html(" </ul>\n</body></html>\n");
163}
164
165static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
166		     const char *pathname, unsigned mode, int stage,
167		     void *cbdata)
168{
169	if (baselen == match_baselen) {
170		if (S_ISREG(mode)) {
171			if (print_object(sha1, pathname))
172				match = 1;
173		} else if (S_ISDIR(mode)) {
174			print_dir(sha1, base, baselen, pathname);
175			match = 2;
176			return READ_TREE_RECURSIVE;
177		}
178	} else if (baselen > match_baselen) {
179		print_dir_entry(sha1, base, baselen, pathname, mode);
180		match = 2;
181	} else if (S_ISDIR(mode)) {
182		return READ_TREE_RECURSIVE;
183	}
184
185	return 0;
186}
187
188static int basedir_len(const char *path)
189{
190	char *p = strrchr(path, '/');
191	if (p)
192		return p - path + 1;
193	return 0;
194}
195
196void cgit_print_plain(struct cgit_context *ctx)
197{
198	const char *rev = ctx->qry.sha1;
199	unsigned char sha1[20];
200	struct commit *commit;
201	struct pathspec_item path_items = {
202		.match = ctx->qry.path,
203		.len = ctx->qry.path ? strlen(ctx->qry.path) : 0
204	};
205	struct pathspec paths = {
206		.nr = 1,
207		.items = &path_items
208	};
209
210	if (!rev)
211		rev = ctx->qry.head;
212
213	if (get_sha1(rev, sha1)) {
214		html_status(404, "Not found", 0);
215		return;
216	}
217	commit = lookup_commit_reference(sha1);
218	if (!commit || parse_commit(commit)) {
219		html_status(404, "Not found", 0);
220		return;
221	}
222	if (!path_items.match) {
223		path_items.match = "";
224		match_baselen = -1;
225		print_dir(commit->tree->object.sha1, "", 0, "");
226		match = 2;
227	}
228	else
229		match_baselen = basedir_len(path_items.match);
230	read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, NULL);
231	if (!match)
232		html_status(404, "Not found", 0);
233	else if (match == 2)
234		print_dir_tail();
235}