all repos — cgit @ 23debef62104c70600be2b745ec3957538eeac6e

a hyperfast web frontend for git written in c

html.c (view raw)

  1/* html.c: helper functions for html output
  2 *
  3 * Copyright (C) 2006 Lars Hjemli
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10#include "html.h"
 11#include <unistd.h>
 12#include <stdio.h>
 13#include <stdlib.h>
 14#include <stdarg.h>
 15#include <string.h>
 16#include <errno.h>
 17
 18/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
 19static const char* url_escape_table[256] = {
 20	"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
 21	"%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13",
 22	"%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d",
 23	"%1e", "%1f", "%20", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0,
 24	"%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d",
 25	"%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 26	0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0,
 27	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%7b",
 28	"%7c", "%7d", 0, "%7f", "%80", "%81", "%82", "%83", "%84", "%85",
 29	"%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
 30	"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
 31	"%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3",
 32	"%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad",
 33	"%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
 34	"%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1",
 35	"%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb",
 36	"%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5",
 37	"%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
 38	"%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9",
 39	"%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3",
 40	"%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%fa", "%fb", "%fc", "%fd",
 41	"%fe", "%ff"
 42};
 43
 44static int htmlfd = STDOUT_FILENO;
 45
 46char *fmt(const char *format, ...)
 47{
 48	static char buf[8][1024];
 49	static int bufidx;
 50	int len;
 51	va_list args;
 52
 53	bufidx++;
 54	bufidx &= 7;
 55
 56	va_start(args, format);
 57	len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
 58	va_end(args);
 59	if (len > sizeof(buf[bufidx])) {
 60		fprintf(stderr, "[html.c] string truncated: %s\n", format);
 61		exit(1);
 62	}
 63	return buf[bufidx];
 64}
 65
 66char *fmtalloc(const char *format, ...)
 67{
 68	struct strbuf sb = STRBUF_INIT;
 69	va_list args;
 70
 71	va_start(args, format);
 72	strbuf_vaddf(&sb, format, args);
 73	va_end(args);
 74
 75	return strbuf_detach(&sb, NULL);
 76}
 77
 78void html_raw(const char *data, size_t size)
 79{
 80	if (write(htmlfd, data, size) != size)
 81		die_errno("write error on html output");
 82}
 83
 84void html(const char *txt)
 85{
 86	html_raw(txt, strlen(txt));
 87}
 88
 89void htmlf(const char *format, ...)
 90{
 91	va_list args;
 92	struct strbuf buf = STRBUF_INIT;
 93
 94	va_start(args, format);
 95	strbuf_vaddf(&buf, format, args);
 96	va_end(args);
 97	html(buf.buf);
 98	strbuf_release(&buf);
 99}
100
101void html_txtf(const char *format, ...)
102{
103	va_list args;
104
105	va_start(args, format);
106	html_vtxtf(format, args);
107	va_end(args);
108}
109
110void html_vtxtf(const char *format, va_list ap)
111{
112	va_list cp;
113	struct strbuf buf = STRBUF_INIT;
114
115	va_copy(cp, ap);
116	strbuf_vaddf(&buf, format, cp);
117	va_end(cp);
118	html_txt(buf.buf);
119	strbuf_release(&buf);
120}
121
122void html_status(int code, const char *msg, int more_headers)
123{
124	htmlf("Status: %d %s\n", code, msg);
125	if (!more_headers)
126		html("\n");
127}
128
129void html_txt(const char *txt)
130{
131	const char *t = txt;
132	while (t && *t) {
133		int c = *t;
134		if (c == '<' || c == '>' || c == '&') {
135			html_raw(txt, t - txt);
136			if (c == '>')
137				html("&gt;");
138			else if (c == '<')
139				html("&lt;");
140			else if (c == '&')
141				html("&amp;");
142			txt = t + 1;
143		}
144		t++;
145	}
146	if (t != txt)
147		html(txt);
148}
149
150void html_ntxt(int len, const char *txt)
151{
152	const char *t = txt;
153	while (t && *t && len--) {
154		int c = *t;
155		if (c == '<' || c == '>' || c == '&') {
156			html_raw(txt, t - txt);
157			if (c == '>')
158				html("&gt;");
159			else if (c == '<')
160				html("&lt;");
161			else if (c == '&')
162				html("&amp;");
163			txt = t + 1;
164		}
165		t++;
166	}
167	if (t != txt)
168		html_raw(txt, t - txt);
169	if (len < 0)
170		html("...");
171}
172
173void html_attrf(const char *fmt, ...)
174{
175	va_list ap;
176	struct strbuf sb = STRBUF_INIT;
177
178	va_start(ap, fmt);
179	strbuf_vaddf(&sb, fmt, ap);
180	va_end(ap);
181
182	html_attr(sb.buf);
183	strbuf_release(&sb);
184}
185
186void html_attr(const char *txt)
187{
188	const char *t = txt;
189	while (t && *t) {
190		int c = *t;
191		if (c == '<' || c == '>' || c == '\'' || c == '\"' || c == '&') {
192			html_raw(txt, t - txt);
193			if (c == '>')
194				html("&gt;");
195			else if (c == '<')
196				html("&lt;");
197			else if (c == '\'')
198				html("&#x27;");
199			else if (c == '"')
200				html("&quot;");
201			else if (c == '&')
202				html("&amp;");
203			txt = t + 1;
204		}
205		t++;
206	}
207	if (t != txt)
208		html(txt);
209}
210
211void html_url_path(const char *txt)
212{
213	const char *t = txt;
214	while (t && *t) {
215		unsigned char c = *t;
216		const char *e = url_escape_table[c];
217		if (e && c != '+' && c != '&') {
218			html_raw(txt, t - txt);
219			html(e);
220			txt = t + 1;
221		}
222		t++;
223	}
224	if (t != txt)
225		html(txt);
226}
227
228void html_url_arg(const char *txt)
229{
230	const char *t = txt;
231	while (t && *t) {
232		unsigned char c = *t;
233		const char *e = url_escape_table[c];
234		if (c == ' ')
235			e = "+";
236		if (e) {
237			html_raw(txt, t - txt);
238			html(e);
239			txt = t + 1;
240		}
241		t++;
242	}
243	if (t != txt)
244		html(txt);
245}
246
247void html_hidden(const char *name, const char *value)
248{
249	html("<input type='hidden' name='");
250	html_attr(name);
251	html("' value='");
252	html_attr(value);
253	html("'/>");
254}
255
256void html_option(const char *value, const char *text, const char *selected_value)
257{
258	html("<option value='");
259	html_attr(value);
260	html("'");
261	if (selected_value && !strcmp(selected_value, value))
262		html(" selected='selected'");
263	html(">");
264	html_txt(text);
265	html("</option>\n");
266}
267
268void html_intoption(int value, const char *text, int selected_value)
269{
270	htmlf("<option value='%d'%s>", value,
271	      value == selected_value ? " selected='selected'" : "");
272	html_txt(text);
273	html("</option>");
274}
275
276void html_link_open(const char *url, const char *title, const char *class)
277{
278	html("<a href='");
279	html_attr(url);
280	if (title) {
281		html("' title='");
282		html_attr(title);
283	}
284	if (class) {
285		html("' class='");
286		html_attr(class);
287	}
288	html("'>");
289}
290
291void html_link_close(void)
292{
293	html("</a>");
294}
295
296void html_fileperm(unsigned short mode)
297{
298	htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
299	      (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
300}
301
302int html_include(const char *filename)
303{
304	FILE *f;
305	char buf[4096];
306	size_t len;
307
308	if (!(f = fopen(filename, "r"))) {
309		fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
310			filename, strerror(errno), errno);
311		return -1;
312	}
313	while ((len = fread(buf, 1, 4096, f)) > 0)
314		html_raw(buf, len);
315	fclose(f);
316	return 0;
317}
318
319static int hextoint(char c)
320{
321	if (c >= 'a' && c <= 'f')
322		return 10 + c - 'a';
323	else if (c >= 'A' && c <= 'F')
324		return 10 + c - 'A';
325	else if (c >= '0' && c <= '9')
326		return c - '0';
327	else
328		return -1;
329}
330
331static char *convert_query_hexchar(char *txt)
332{
333	int d1, d2, n;
334	n = strlen(txt);
335	if (n < 3) {
336		*txt = '\0';
337		return txt-1;
338	}
339	d1 = hextoint(*(txt + 1));
340	d2 = hextoint(*(txt + 2));
341	if (d1 < 0 || d2 < 0) {
342		memmove(txt, txt + 3, n - 2);
343		return txt-1;
344	} else {
345		*txt = d1 * 16 + d2;
346		memmove(txt + 1, txt + 3, n - 2);
347		return txt;
348	}
349}
350
351int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
352{
353	char *o, *t, *txt, *value = NULL, c;
354
355	if (!txt_)
356		return 0;
357
358	o = t = txt = xstrdup(txt_);
359	while ((c=*t) != '\0') {
360		if (c == '=') {
361			*t = '\0';
362			value = t + 1;
363		} else if (c == '+') {
364			*t = ' ';
365		} else if (c == '%') {
366			t = convert_query_hexchar(t);
367		} else if (c == '&') {
368			*t = '\0';
369			(*fn)(txt, value);
370			txt = t + 1;
371			value = NULL;
372		}
373		t++;
374	}
375	if (t != txt)
376		(*fn)(txt, value);
377	free(o);
378	return 0;
379}