all repos — cgit @ d01c600c179593a53162a9d4e3040ecfc5078fdc

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 void 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;
 69	}
 70
 71	buf = read_sha1_file(sha1, &type, &size);
 72	if (!buf) {
 73		html_status(404, "Not found", 0);
 74		return;
 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	match = 1;
101	if (freemime)
102		free(ctx.page.mimetype);
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	match = 2;
142}
143
144static void print_dir_entry(const unsigned char *sha1, const char *base,
145			    int baselen, const char *path, unsigned mode)
146{
147	char *fullpath;
148
149	fullpath = buildpath(base, baselen, path);
150	if (!S_ISDIR(mode))
151		fullpath[strlen(fullpath) - 1] = 0;
152	html("  <li>");
153	cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
154			fullpath);
155	html("</li>\n");
156	match = 2;
157}
158
159static void print_dir_tail(void)
160{
161	html(" </ul>\n</body></html>\n");
162}
163
164static int walk_tree(const unsigned char *sha1, const char *base, int baselen,
165		     const char *pathname, unsigned mode, int stage,
166		     void *cbdata)
167{
168	if (baselen == match_baselen) {
169		if (S_ISREG(mode))
170			print_object(sha1, pathname);
171		else if (S_ISDIR(mode)) {
172			print_dir(sha1, base, baselen, pathname);
173			return READ_TREE_RECURSIVE;
174		}
175	}
176	else if (baselen > match_baselen)
177		print_dir_entry(sha1, base, baselen, pathname, mode);
178	else if (S_ISDIR(mode))
179		return READ_TREE_RECURSIVE;
180
181	return 0;
182}
183
184static int basedir_len(const char *path)
185{
186	char *p = strrchr(path, '/');
187	if (p)
188		return p - path + 1;
189	return 0;
190}
191
192void cgit_print_plain(struct cgit_context *ctx)
193{
194	const char *rev = ctx->qry.sha1;
195	unsigned char sha1[20];
196	struct commit *commit;
197	const char *paths[] = {ctx->qry.path, NULL};
198
199	if (!rev)
200		rev = ctx->qry.head;
201
202	if (get_sha1(rev, sha1)) {
203		html_status(404, "Not found", 0);
204		return;
205	}
206	commit = lookup_commit_reference(sha1);
207	if (!commit || parse_commit(commit)) {
208		html_status(404, "Not found", 0);
209		return;
210	}
211	if (!paths[0]) {
212		paths[0] = "";
213		match_baselen = -1;
214		print_dir(commit->tree->object.sha1, "", 0, "");
215	}
216	else
217		match_baselen = basedir_len(paths[0]);
218	read_tree_recursive(commit->tree, "", 0, 0, paths, walk_tree, NULL);
219	if (!match)
220		html_status(404, "Not found", 0);
221	else if (match == 2)
222		print_dir_tail();
223}