all repos — cgit @ 45555512ba63b823c6340875254563ea05737668

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