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