summaryrefslogtreecommitdiffabout
authorLars Hjemli <hjemli@gmail.com>2008-09-15 20:41:25 (UTC)
committer Lars Hjemli <hjemli@gmail.com>2008-09-15 20:41:25 (UTC)
commita30453a5d3f6db4d6e055da2112343e054e7e7d5 (patch) (unidiff)
treee8713318ee4c1d605eb02ae469cf22f0d1cc4df1
parent91fd1eca07f9e48109e8acebc0a92dc4b12ecb50 (diff)
parenta608ff7ba371c2dddf9274de3a438bf74e2560f7 (diff)
downloadcgit-a30453a5d3f6db4d6e055da2112343e054e7e7d5.zip
cgit-a30453a5d3f6db4d6e055da2112343e054e7e7d5.tar.gz
cgit-a30453a5d3f6db4d6e055da2112343e054e7e7d5.tar.bz2
Merge branch 'lh/parsing'
* lh/parsing: ui-tag: show the taggers email parsing.c: be prepared for unexpected content in commit/tag objects
Diffstat (more/less context) (ignore whitespace changes)
-rw-r--r--cgit.h2
-rw-r--r--parsing.c159
-rw-r--r--ui-tag.c4
3 files changed, 101 insertions, 64 deletions
diff --git a/cgit.h b/cgit.h
index 1615616..08fd95a 100644
--- a/cgit.h
+++ b/cgit.h
@@ -24,129 +24,129 @@
24 */ 24 */
25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)" 25#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
26#define FMT_SHORTDATE "%Y-%m-%d" 26#define FMT_SHORTDATE "%Y-%m-%d"
27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ" 27#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
28 28
29 29
30/* 30/*
31 * Limits used for relative dates 31 * Limits used for relative dates
32 */ 32 */
33#define TM_MIN 60 33#define TM_MIN 60
34#define TM_HOUR (TM_MIN * 60) 34#define TM_HOUR (TM_MIN * 60)
35#define TM_DAY (TM_HOUR * 24) 35#define TM_DAY (TM_HOUR * 24)
36#define TM_WEEK (TM_DAY * 7) 36#define TM_WEEK (TM_DAY * 7)
37#define TM_YEAR (TM_DAY * 365) 37#define TM_YEAR (TM_DAY * 365)
38#define TM_MONTH (TM_YEAR / 12.0) 38#define TM_MONTH (TM_YEAR / 12.0)
39 39
40 40
41/* 41/*
42 * Default encoding 42 * Default encoding
43 */ 43 */
44#define PAGE_ENCODING "UTF-8" 44#define PAGE_ENCODING "UTF-8"
45 45
46typedef void (*configfn)(const char *name, const char *value); 46typedef void (*configfn)(const char *name, const char *value);
47typedef void (*filepair_fn)(struct diff_filepair *pair); 47typedef void (*filepair_fn)(struct diff_filepair *pair);
48typedef void (*linediff_fn)(char *line, int len); 48typedef void (*linediff_fn)(char *line, int len);
49 49
50struct cgit_repo { 50struct cgit_repo {
51 char *url; 51 char *url;
52 char *name; 52 char *name;
53 char *path; 53 char *path;
54 char *desc; 54 char *desc;
55 char *owner; 55 char *owner;
56 char *defbranch; 56 char *defbranch;
57 char *group; 57 char *group;
58 char *module_link; 58 char *module_link;
59 char *readme; 59 char *readme;
60 char *clone_url; 60 char *clone_url;
61 int snapshots; 61 int snapshots;
62 int enable_log_filecount; 62 int enable_log_filecount;
63 int enable_log_linecount; 63 int enable_log_linecount;
64}; 64};
65 65
66struct cgit_repolist { 66struct cgit_repolist {
67 int length; 67 int length;
68 int count; 68 int count;
69 struct cgit_repo *repos; 69 struct cgit_repo *repos;
70}; 70};
71 71
72struct commitinfo { 72struct commitinfo {
73 struct commit *commit; 73 struct commit *commit;
74 char *author; 74 char *author;
75 char *author_email; 75 char *author_email;
76 unsigned long author_date; 76 unsigned long author_date;
77 char *committer; 77 char *committer;
78 char *committer_email; 78 char *committer_email;
79 unsigned long committer_date; 79 unsigned long committer_date;
80 char *subject; 80 char *subject;
81 char *msg; 81 char *msg;
82 char *msg_encoding; 82 char *msg_encoding;
83}; 83};
84 84
85struct taginfo { 85struct taginfo {
86 char *tagger; 86 char *tagger;
87 char *tagger_email; 87 char *tagger_email;
88 int tagger_date; 88 unsigned long tagger_date;
89 char *msg; 89 char *msg;
90}; 90};
91 91
92struct refinfo { 92struct refinfo {
93 const char *refname; 93 const char *refname;
94 struct object *object; 94 struct object *object;
95 union { 95 union {
96 struct taginfo *tag; 96 struct taginfo *tag;
97 struct commitinfo *commit; 97 struct commitinfo *commit;
98 }; 98 };
99}; 99};
100 100
101struct reflist { 101struct reflist {
102 struct refinfo **refs; 102 struct refinfo **refs;
103 int alloc; 103 int alloc;
104 int count; 104 int count;
105}; 105};
106 106
107struct cgit_query { 107struct cgit_query {
108 int has_symref; 108 int has_symref;
109 int has_sha1; 109 int has_sha1;
110 char *raw; 110 char *raw;
111 char *repo; 111 char *repo;
112 char *page; 112 char *page;
113 char *search; 113 char *search;
114 char *grep; 114 char *grep;
115 char *head; 115 char *head;
116 char *sha1; 116 char *sha1;
117 char *sha2; 117 char *sha2;
118 char *path; 118 char *path;
119 char *name; 119 char *name;
120 char *mimetype; 120 char *mimetype;
121 int ofs; 121 int ofs;
122}; 122};
123 123
124struct cgit_config { 124struct cgit_config {
125 char *agefile; 125 char *agefile;
126 char *cache_root; 126 char *cache_root;
127 char *clone_prefix; 127 char *clone_prefix;
128 char *css; 128 char *css;
129 char *favicon; 129 char *favicon;
130 char *footer; 130 char *footer;
131 char *index_header; 131 char *index_header;
132 char *index_info; 132 char *index_info;
133 char *logo; 133 char *logo;
134 char *logo_link; 134 char *logo_link;
135 char *module_link; 135 char *module_link;
136 char *repo_group; 136 char *repo_group;
137 char *robots; 137 char *robots;
138 char *root_title; 138 char *root_title;
139 char *root_desc; 139 char *root_desc;
140 char *root_readme; 140 char *root_readme;
141 char *script_name; 141 char *script_name;
142 char *virtual_root; 142 char *virtual_root;
143 int cache_size; 143 int cache_size;
144 int cache_dynamic_ttl; 144 int cache_dynamic_ttl;
145 int cache_max_create_time; 145 int cache_max_create_time;
146 int cache_repo_ttl; 146 int cache_repo_ttl;
147 int cache_root_ttl; 147 int cache_root_ttl;
148 int cache_static_ttl; 148 int cache_static_ttl;
149 int enable_index_links; 149 int enable_index_links;
150 int enable_log_filecount; 150 int enable_log_filecount;
151 int enable_log_linecount; 151 int enable_log_linecount;
152 int local_time; 152 int local_time;
diff --git a/parsing.c b/parsing.c
index 66e8b3d..c8f3048 100644
--- a/parsing.c
+++ b/parsing.c
@@ -1,208 +1,241 @@
1/* config.c: parsing of config files 1/* config.c: parsing of config files
2 * 2 *
3 * Copyright (C) 2006 Lars Hjemli 3 * Copyright (C) 2006 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10 10
11/* 11/*
12 * url syntax: [repo ['/' cmd [ '/' path]]] 12 * url syntax: [repo ['/' cmd [ '/' path]]]
13 * repo: any valid repo url, may contain '/' 13 * repo: any valid repo url, may contain '/'
14 * cmd: log | commit | diff | tree | view | blob | snapshot 14 * cmd: log | commit | diff | tree | view | blob | snapshot
15 * path: any valid path, may contain '/' 15 * path: any valid path, may contain '/'
16 * 16 *
17 */ 17 */
18void cgit_parse_url(const char *url) 18void cgit_parse_url(const char *url)
19{ 19{
20 char *cmd, *p; 20 char *cmd, *p;
21 21
22 ctx.repo = NULL; 22 ctx.repo = NULL;
23 if (!url || url[0] == '\0') 23 if (!url || url[0] == '\0')
24 return; 24 return;
25 25
26 ctx.repo = cgit_get_repoinfo(url); 26 ctx.repo = cgit_get_repoinfo(url);
27 if (ctx.repo) { 27 if (ctx.repo) {
28 ctx.qry.repo = ctx.repo->url; 28 ctx.qry.repo = ctx.repo->url;
29 return; 29 return;
30 } 30 }
31 31
32 cmd = strchr(url, '/'); 32 cmd = strchr(url, '/');
33 while (!ctx.repo && cmd) { 33 while (!ctx.repo && cmd) {
34 cmd[0] = '\0'; 34 cmd[0] = '\0';
35 ctx.repo = cgit_get_repoinfo(url); 35 ctx.repo = cgit_get_repoinfo(url);
36 if (ctx.repo == NULL) { 36 if (ctx.repo == NULL) {
37 cmd[0] = '/'; 37 cmd[0] = '/';
38 cmd = strchr(cmd + 1, '/'); 38 cmd = strchr(cmd + 1, '/');
39 continue; 39 continue;
40 } 40 }
41 41
42 ctx.qry.repo = ctx.repo->url; 42 ctx.qry.repo = ctx.repo->url;
43 p = strchr(cmd + 1, '/'); 43 p = strchr(cmd + 1, '/');
44 if (p) { 44 if (p) {
45 p[0] = '\0'; 45 p[0] = '\0';
46 if (p[1]) 46 if (p[1])
47 ctx.qry.path = trim_end(p + 1, '/'); 47 ctx.qry.path = trim_end(p + 1, '/');
48 } 48 }
49 if (cmd[1]) 49 if (cmd[1])
50 ctx.qry.page = xstrdup(cmd + 1); 50 ctx.qry.page = xstrdup(cmd + 1);
51 return; 51 return;
52 } 52 }
53} 53}
54 54
55char *substr(const char *head, const char *tail) 55char *substr(const char *head, const char *tail)
56{ 56{
57 char *buf; 57 char *buf;
58 58
59 buf = xmalloc(tail - head + 1); 59 buf = xmalloc(tail - head + 1);
60 strncpy(buf, head, tail - head); 60 strncpy(buf, head, tail - head);
61 buf[tail - head] = '\0'; 61 buf[tail - head] = '\0';
62 return buf; 62 return buf;
63} 63}
64 64
65char *parse_user(char *t, char **name, char **email, unsigned long *date)
66{
67 char *p = t;
68 int mode = 1;
69
70 while (p && *p) {
71 if (mode == 1 && *p == '<') {
72 *name = substr(t, p - 1);
73 t = p;
74 mode++;
75 } else if (mode == 1 && *p == '\n') {
76 *name = substr(t, p);
77 p++;
78 break;
79 } else if (mode == 2 && *p == '>') {
80 *email = substr(t, p + 1);
81 t = p;
82 mode++;
83 } else if (mode == 2 && *p == '\n') {
84 *email = substr(t, p);
85 p++;
86 break;
87 } else if (mode == 3 && isdigit(*p)) {
88 *date = atol(p);
89 mode++;
90 } else if (*p == '\n') {
91 p++;
92 break;
93 }
94 p++;
95 }
96 return p;
97}
98
99const char *reencode(char **txt, const char *src_enc, const char *dst_enc)
100{
101 char *tmp;
102
103 if (!txt || !*txt || !src_enc || !dst_enc)
104 return *txt;
105
106 tmp = reencode_string(*txt, src_enc, dst_enc);
107 if (tmp) {
108 free(*txt);
109 *txt = tmp;
110 }
111 return *txt;
112}
113
65struct commitinfo *cgit_parse_commit(struct commit *commit) 114struct commitinfo *cgit_parse_commit(struct commit *commit)
66{ 115{
67 struct commitinfo *ret; 116 struct commitinfo *ret;
68 char *p = commit->buffer, *t = commit->buffer; 117 char *p = commit->buffer, *t = commit->buffer;
69 118
70 ret = xmalloc(sizeof(*ret)); 119 ret = xmalloc(sizeof(*ret));
71 ret->commit = commit; 120 ret->commit = commit;
72 ret->author = NULL; 121 ret->author = NULL;
73 ret->author_email = NULL; 122 ret->author_email = NULL;
74 ret->committer = NULL; 123 ret->committer = NULL;
75 ret->committer_email = NULL; 124 ret->committer_email = NULL;
76 ret->subject = NULL; 125 ret->subject = NULL;
77 ret->msg = NULL; 126 ret->msg = NULL;
78 ret->msg_encoding = NULL; 127 ret->msg_encoding = NULL;
79 128
80 if (p == NULL) 129 if (p == NULL)
81 return ret; 130 return ret;
82 131
83 if (strncmp(p, "tree ", 5)) 132 if (strncmp(p, "tree ", 5))
84 die("Bad commit: %s", sha1_to_hex(commit->object.sha1)); 133 die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
85 else 134 else
86 p += 46; // "tree " + hex[40] + "\n" 135 p += 46; // "tree " + hex[40] + "\n"
87 136
88 while (!strncmp(p, "parent ", 7)) 137 while (!strncmp(p, "parent ", 7))
89 p += 48; // "parent " + hex[40] + "\n" 138 p += 48; // "parent " + hex[40] + "\n"
90 139
91 if (!strncmp(p, "author ", 7)) { 140 if (p && !strncmp(p, "author ", 7)) {
92 p += 7; 141 p = parse_user(p + 7, &ret->author, &ret->author_email,
93 t = strchr(p, '<') - 1; 142 &ret->author_date);
94 ret->author = substr(p, t);
95 p = t;
96 t = strchr(t, '>') + 1;
97 ret->author_email = substr(p, t);
98 ret->author_date = atol(t+1);
99 p = strchr(t, '\n') + 1;
100 } 143 }
101 144
102 if (!strncmp(p, "committer ", 9)) { 145 if (p && !strncmp(p, "committer ", 9)) {
103 p += 9; 146 p = parse_user(p + 9, &ret->committer, &ret->committer_email,
104 t = strchr(p, '<') - 1; 147 &ret->committer_date);
105 ret->committer = substr(p, t);
106 p = t;
107 t = strchr(t, '>') + 1;
108 ret->committer_email = substr(p, t);
109 ret->committer_date = atol(t+1);
110 p = strchr(t, '\n') + 1;
111 } 148 }
112 149
113 if (!strncmp(p, "encoding ", 9)) { 150 if (p && !strncmp(p, "encoding ", 9)) {
114 p += 9; 151 p += 9;
115 t = strchr(p, '\n') + 1; 152 t = strchr(p, '\n');
116 ret->msg_encoding = substr(p, t); 153 if (t) {
117 p = t; 154 ret->msg_encoding = substr(p, t + 1);
118 } else 155 p = t + 1;
119 ret->msg_encoding = xstrdup(PAGE_ENCODING); 156 }
157 }
120 158
121 while (*p && (*p != '\n')) 159 // skip unknown header fields
122 p = strchr(p, '\n') + 1; // skip unknown header fields 160 while (p && *p && (*p != '\n')) {
161 p = strchr(p, '\n');
162 if (p)
163 p++;
164 }
123 165
124 while (*p == '\n') 166 // skip empty lines between headers and message
125 p = strchr(p, '\n') + 1; 167 while (p && *p == '\n')
168 p++;
169
170 if (!p)
171 return ret;
126 172
127 t = strchr(p, '\n'); 173 t = strchr(p, '\n');
128 if (t) { 174 if (t) {
129 if (*t == '\0') 175 ret->subject = substr(p, t);
130 ret->subject = "** empty **";
131 else
132 ret->subject = substr(p, t);
133 p = t + 1; 176 p = t + 1;
134 177
135 while (*p == '\n') 178 while (p && *p == '\n') {
136 p = strchr(p, '\n') + 1; 179 p = strchr(p, '\n');
137 ret->msg = xstrdup(p); 180 if (p)
138 } else 181 p++;
139 ret->subject = substr(p, p+strlen(p));
140
141 if(strcmp(ret->msg_encoding, PAGE_ENCODING)) {
142 t = reencode_string(ret->subject, PAGE_ENCODING,
143 ret->msg_encoding);
144 if(t) {
145 free(ret->subject);
146 ret->subject = t;
147 } 182 }
183 if (p)
184 ret->msg = xstrdup(p);
185 } else
186 ret->subject = xstrdup(p);
148 187
149 t = reencode_string(ret->msg, PAGE_ENCODING, 188 if (ret->msg_encoding) {
150 ret->msg_encoding); 189 reencode(&ret->subject, PAGE_ENCODING, ret->msg_encoding);
151 if(t) { 190 reencode(&ret->msg, PAGE_ENCODING, ret->msg_encoding);
152 free(ret->msg);
153 ret->msg = t;
154 }
155 } 191 }
156 192
157 return ret; 193 return ret;
158} 194}
159 195
160 196
161struct taginfo *cgit_parse_tag(struct tag *tag) 197struct taginfo *cgit_parse_tag(struct tag *tag)
162{ 198{
163 void *data; 199 void *data;
164 enum object_type type; 200 enum object_type type;
165 unsigned long size; 201 unsigned long size;
166 char *p, *t; 202 char *p;
167 struct taginfo *ret; 203 struct taginfo *ret;
168 204
169 data = read_sha1_file(tag->object.sha1, &type, &size); 205 data = read_sha1_file(tag->object.sha1, &type, &size);
170 if (!data || type != OBJ_TAG) { 206 if (!data || type != OBJ_TAG) {
171 free(data); 207 free(data);
172 return 0; 208 return 0;
173 } 209 }
174 210
175 ret = xmalloc(sizeof(*ret)); 211 ret = xmalloc(sizeof(*ret));
176 ret->tagger = NULL; 212 ret->tagger = NULL;
177 ret->tagger_email = NULL; 213 ret->tagger_email = NULL;
178 ret->tagger_date = 0; 214 ret->tagger_date = 0;
179 ret->msg = NULL; 215 ret->msg = NULL;
180 216
181 p = data; 217 p = data;
182 218
183 while (p && *p) { 219 while (p && *p) {
184 if (*p == '\n') 220 if (*p == '\n')
185 break; 221 break;
186 222
187 if (!strncmp(p, "tagger ", 7)) { 223 if (!strncmp(p, "tagger ", 7)) {
188 p += 7; 224 p = parse_user(p + 7, &ret->tagger, &ret->tagger_email,
189 t = strchr(p, '<') - 1; 225 &ret->tagger_date);
190 ret->tagger = substr(p, t); 226 } else {
191 p = t; 227 p = strchr(p, '\n');
192 t = strchr(t, '>') + 1; 228 if (p)
193 ret->tagger_email = substr(p, t); 229 p++;
194 ret->tagger_date = atol(t+1);
195 } 230 }
196 p = strchr(p, '\n') + 1;
197 } 231 }
198 232
199 while (p && *p && (*p != '\n')) 233 // skip empty lines between headers and message
200 p = strchr(p, '\n') + 1; // skip unknown tag fields 234 while (p && *p == '\n')
235 p++;
201 236
202 while (p && (*p == '\n'))
203 p = strchr(p, '\n') + 1;
204 if (p && *p) 237 if (p && *p)
205 ret->msg = xstrdup(p); 238 ret->msg = xstrdup(p);
206 free(data); 239 free(data);
207 return ret; 240 return ret;
208} 241}
diff --git a/ui-tag.c b/ui-tag.c
index b4db32e..3aea87d 100644
--- a/ui-tag.c
+++ b/ui-tag.c
@@ -2,74 +2,78 @@
2 * 2 *
3 * Copyright (C) 2007 Lars Hjemli 3 * Copyright (C) 2007 Lars Hjemli
4 * 4 *
5 * Licensed under GNU General Public License v2 5 * Licensed under GNU General Public License v2
6 * (see COPYING for full license text) 6 * (see COPYING for full license text)
7 */ 7 */
8 8
9#include "cgit.h" 9#include "cgit.h"
10#include "html.h" 10#include "html.h"
11#include "ui-shared.h" 11#include "ui-shared.h"
12 12
13static void print_tag_content(char *buf) 13static void print_tag_content(char *buf)
14{ 14{
15 char *p; 15 char *p;
16 16
17 if (!buf) 17 if (!buf)
18 return; 18 return;
19 19
20 html("<div class='commit-subject'>"); 20 html("<div class='commit-subject'>");
21 p = strchr(buf, '\n'); 21 p = strchr(buf, '\n');
22 if (p) 22 if (p)
23 *p = '\0'; 23 *p = '\0';
24 html_txt(buf); 24 html_txt(buf);
25 html("</div>"); 25 html("</div>");
26 if (p) { 26 if (p) {
27 html("<div class='commit-msg'>"); 27 html("<div class='commit-msg'>");
28 html_txt(++p); 28 html_txt(++p);
29 html("</div>"); 29 html("</div>");
30 } 30 }
31} 31}
32 32
33void cgit_print_tag(char *revname) 33void cgit_print_tag(char *revname)
34{ 34{
35 unsigned char sha1[20]; 35 unsigned char sha1[20];
36 struct object *obj; 36 struct object *obj;
37 struct tag *tag; 37 struct tag *tag;
38 struct taginfo *info; 38 struct taginfo *info;
39 39
40 if (get_sha1(revname, sha1)) { 40 if (get_sha1(revname, sha1)) {
41 cgit_print_error(fmt("Bad tag reference: %s", revname)); 41 cgit_print_error(fmt("Bad tag reference: %s", revname));
42 return; 42 return;
43 } 43 }
44 obj = parse_object(sha1); 44 obj = parse_object(sha1);
45 if (!obj) { 45 if (!obj) {
46 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1))); 46 cgit_print_error(fmt("Bad object id: %s", sha1_to_hex(sha1)));
47 return; 47 return;
48 } 48 }
49 if (obj->type == OBJ_TAG) { 49 if (obj->type == OBJ_TAG) {
50 tag = lookup_tag(sha1); 50 tag = lookup_tag(sha1);
51 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) { 51 if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
52 cgit_print_error(fmt("Bad tag object: %s", revname)); 52 cgit_print_error(fmt("Bad tag object: %s", revname));
53 return; 53 return;
54 } 54 }
55 html("<table class='commit-info'>\n"); 55 html("<table class='commit-info'>\n");
56 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n", 56 htmlf("<tr><td>Tag name</td><td>%s (%s)</td></tr>\n",
57 revname, sha1_to_hex(sha1)); 57 revname, sha1_to_hex(sha1));
58 if (info->tagger_date > 0) { 58 if (info->tagger_date > 0) {
59 html("<tr><td>Tag date</td><td>"); 59 html("<tr><td>Tag date</td><td>");
60 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time); 60 cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time);
61 html("</td></tr>\n"); 61 html("</td></tr>\n");
62 } 62 }
63 if (info->tagger) { 63 if (info->tagger) {
64 html("<tr><td>Tagged by</td><td>"); 64 html("<tr><td>Tagged by</td><td>");
65 html_txt(info->tagger); 65 html_txt(info->tagger);
66 if (info->tagger_email) {
67 html(" ");
68 html_txt(info->tagger_email);
69 }
66 html("</td></tr>\n"); 70 html("</td></tr>\n");
67 } 71 }
68 html("<tr><td>Tagged object</td><td>"); 72 html("<tr><td>Tagged object</td><td>");
69 cgit_object_link(tag->tagged); 73 cgit_object_link(tag->tagged);
70 html("</td></tr>\n"); 74 html("</td></tr>\n");
71 html("</table>\n"); 75 html("</table>\n");
72 print_tag_content(info->msg); 76 print_tag_content(info->msg);
73 } 77 }
74 return; 78 return;
75} 79}