]> gitweb.ps.run Git - ps-cgit/blob - html.c
ui-refs.c: Refactor print_tag()
[ps-cgit] / html.c
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!$()*,./:;@- */
19 static 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
44 static int htmlfd = STDOUT_FILENO;
45
46 char *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
66 void html_raw(const char *data, size_t size)
67 {
68         if (write(htmlfd, data, size) != size)
69                 fprintf(stderr, "[html.c] html output truncated.\n");
70 }
71
72 void html(const char *txt)
73 {
74         html_raw(txt, strlen(txt));
75 }
76
77 void htmlf(const char *format, ...)
78 {
79         static char buf[65536];
80         va_list args;
81
82         va_start(args, format);
83         vsnprintf(buf, sizeof(buf), format, args);
84         va_end(args);
85         html(buf);
86 }
87
88 void html_status(int code, const char *msg, int more_headers)
89 {
90         htmlf("Status: %d %s\n", code, msg);
91         if (!more_headers)
92                 html("\n");
93 }
94
95 void html_txt(const char *txt)
96 {
97         const char *t = txt;
98         while (t && *t) {
99                 int c = *t;
100                 if (c == '<' || c == '>' || c == '&') {
101                         html_raw(txt, t - txt);
102                         if (c == '>')
103                                 html("&gt;");
104                         else if (c == '<')
105                                 html("&lt;");
106                         else if (c == '&')
107                                 html("&amp;");
108                         txt = t + 1;
109                 }
110                 t++;
111         }
112         if (t != txt)
113                 html(txt);
114 }
115
116 void html_ntxt(int len, const char *txt)
117 {
118         const char *t = txt;
119         while (t && *t && len--) {
120                 int c = *t;
121                 if (c == '<' || c == '>' || c == '&') {
122                         html_raw(txt, t - txt);
123                         if (c == '>')
124                                 html("&gt;");
125                         else if (c == '<')
126                                 html("&lt;");
127                         else if (c == '&')
128                                 html("&amp;");
129                         txt = t + 1;
130                 }
131                 t++;
132         }
133         if (t != txt)
134                 html_raw(txt, t - txt);
135         if (len < 0)
136                 html("...");
137 }
138
139 void html_attr(const char *txt)
140 {
141         const char *t = txt;
142         while (t && *t) {
143                 int c = *t;
144                 if (c == '<' || c == '>' || c == '\'' || c == '\"' || c == '&') {
145                         html_raw(txt, t - txt);
146                         if (c == '>')
147                                 html("&gt;");
148                         else if (c == '<')
149                                 html("&lt;");
150                         else if (c == '\'')
151                                 html("&#x27;");
152                         else if (c == '"')
153                                 html("&quot;");
154                         else if (c == '&')
155                                 html("&amp;");
156                         txt = t + 1;
157                 }
158                 t++;
159         }
160         if (t != txt)
161                 html(txt);
162 }
163
164 void html_url_path(const char *txt)
165 {
166         const char *t = txt;
167         while (t && *t) {
168                 unsigned char c = *t;
169                 const char *e = url_escape_table[c];
170                 if (e && c != '+' && c != '&') {
171                         html_raw(txt, t - txt);
172                         html(e);
173                         txt = t + 1;
174                 }
175                 t++;
176         }
177         if (t != txt)
178                 html(txt);
179 }
180
181 void html_url_arg(const char *txt)
182 {
183         const char *t = txt;
184         while (t && *t) {
185                 unsigned char c = *t;
186                 const char *e = url_escape_table[c];
187                 if (c == ' ')
188                         e = "+";
189                 if (e) {
190                         html_raw(txt, t - txt);
191                         html(e);
192                         txt = t + 1;
193                 }
194                 t++;
195         }
196         if (t != txt)
197                 html(txt);
198 }
199
200 void html_hidden(const char *name, const char *value)
201 {
202         html("<input type='hidden' name='");
203         html_attr(name);
204         html("' value='");
205         html_attr(value);
206         html("'/>");
207 }
208
209 void html_option(const char *value, const char *text, const char *selected_value)
210 {
211         html("<option value='");
212         html_attr(value);
213         html("'");
214         if (selected_value && !strcmp(selected_value, value))
215                 html(" selected='selected'");
216         html(">");
217         html_txt(text);
218         html("</option>\n");
219 }
220
221 void html_intoption(int value, const char *text, int selected_value)
222 {
223         htmlf("<option value='%d'%s>", value,
224               value == selected_value ? " selected='selected'" : "");
225         html_txt(text);
226         html("</option>");
227 }
228
229 void html_link_open(const char *url, const char *title, const char *class)
230 {
231         html("<a href='");
232         html_attr(url);
233         if (title) {
234                 html("' title='");
235                 html_attr(title);
236         }
237         if (class) {
238                 html("' class='");
239                 html_attr(class);
240         }
241         html("'>");
242 }
243
244 void html_link_close(void)
245 {
246         html("</a>");
247 }
248
249 void html_fileperm(unsigned short mode)
250 {
251         htmlf("%c%c%c", (mode & 4 ? 'r' : '-'),
252               (mode & 2 ? 'w' : '-'), (mode & 1 ? 'x' : '-'));
253 }
254
255 int html_include(const char *filename)
256 {
257         FILE *f;
258         char buf[4096];
259         size_t len;
260
261         if (!(f = fopen(filename, "r"))) {
262                 fprintf(stderr, "[cgit] Failed to include file %s: %s (%d).\n",
263                         filename, strerror(errno), errno);
264                 return -1;
265         }
266         while ((len = fread(buf, 1, 4096, f)) > 0)
267                 html_raw(buf, len);
268         fclose(f);
269         return 0;
270 }
271
272 static int hextoint(char c)
273 {
274         if (c >= 'a' && c <= 'f')
275                 return 10 + c - 'a';
276         else if (c >= 'A' && c <= 'F')
277                 return 10 + c - 'A';
278         else if (c >= '0' && c <= '9')
279                 return c - '0';
280         else
281                 return -1;
282 }
283
284 static char *convert_query_hexchar(char *txt)
285 {
286         int d1, d2, n;
287         n = strlen(txt);
288         if (n < 3) {
289                 *txt = '\0';
290                 return txt-1;
291         }
292         d1 = hextoint(*(txt + 1));
293         d2 = hextoint(*(txt + 2));
294         if (d1 < 0 || d2 < 0) {
295                 memmove(txt, txt + 3, n - 2);
296                 return txt-1;
297         } else {
298                 *txt = d1 * 16 + d2;
299                 memmove(txt + 1, txt + 3, n - 2);
300                 return txt;
301         }
302 }
303
304 int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
305 {
306         char *o, *t, *txt, *value = NULL, c;
307
308         if (!txt_)
309                 return 0;
310
311         o = t = txt = xstrdup(txt_);
312         while ((c=*t) != '\0') {
313                 if (c == '=') {
314                         *t = '\0';
315                         value = t + 1;
316                 } else if (c == '+') {
317                         *t = ' ';
318                 } else if (c == '%') {
319                         t = convert_query_hexchar(t);
320                 } else if (c == '&') {
321                         *t = '\0';
322                         (*fn)(txt, value);
323                         txt = t + 1;
324                         value = NULL;
325                 }
326                 t++;
327         }
328         if (t != txt)
329                 (*fn)(txt, value);
330         free(o);
331         return 0;
332 }