1 |
/* |
2 |
* table.C: Table rendering class. |
3 |
* Rights to this code are documented in doc/pod/license.pod. |
4 |
* |
5 |
* NOTE: This is a work in progress and will probably change considerably |
6 |
* later on. |
7 |
* |
8 |
* Copyright © 2005-2007 Atheme Project (http://www.atheme.org) |
9 |
*/ |
10 |
|
11 |
static char const rcsid[] = "$Id: table.C,v 1.4 2007-08-28 17:08:12 pippijn Exp $"; |
12 |
|
13 |
#include "atheme.h" |
14 |
|
15 |
static void |
16 |
table_destroy (void *obj) |
17 |
{ |
18 |
table_t *table = (table_t *) obj; |
19 |
node_t *n, *tn; |
20 |
|
21 |
return_if_fail (table != NULL); |
22 |
|
23 |
LIST_FOREACH_SAFE (n, tn, table->rows.head) |
24 |
{ |
25 |
table_row_t *r = (table_row_t *) n->data; |
26 |
node_t *n2, *tn2; |
27 |
|
28 |
return_if_fail (r != NULL); |
29 |
|
30 |
LIST_FOREACH_SAFE (n2, tn2, r->cells.head) |
31 |
{ |
32 |
table_cell_t *c = (table_cell_t *) n2->data; |
33 |
|
34 |
sfree (c->name); |
35 |
sfree (c->value); |
36 |
delete c; |
37 |
node_del (n2, &r->cells); |
38 |
node_free (n2); |
39 |
} |
40 |
|
41 |
delete r; |
42 |
|
43 |
node_del (n, &table->rows); |
44 |
node_free (n); |
45 |
} |
46 |
|
47 |
delete table; |
48 |
} |
49 |
|
50 |
/* |
51 |
* table_new(char const * const fmt, ...) |
52 |
* |
53 |
* Table constructor. |
54 |
* |
55 |
* Inputs: |
56 |
* - printf-style string to name the table with. |
57 |
* |
58 |
* Outputs: |
59 |
* - a table object. |
60 |
* |
61 |
* Side Effects: |
62 |
* - none |
63 |
*/ |
64 |
table_t * |
65 |
table_new (char const * const fmt, ...) |
66 |
{ |
67 |
va_list vl; |
68 |
char *buf; |
69 |
table_t *out; |
70 |
|
71 |
return_val_if_fail (fmt != NULL, NULL); |
72 |
|
73 |
va_start (vl, fmt); |
74 |
vasprintf (&buf, fmt, vl); |
75 |
va_end (vl); |
76 |
|
77 |
out = new table_t; |
78 |
object_init (&out->parent, buf, table_destroy); |
79 |
sfree (buf); |
80 |
|
81 |
return out; |
82 |
} |
83 |
|
84 |
/* |
85 |
* table_row_new |
86 |
* |
87 |
* Creates a table row. This isn't an object. |
88 |
* |
89 |
* Inputs: |
90 |
* - table to associate to. |
91 |
* |
92 |
* Outputs: |
93 |
* - a table row |
94 |
* |
95 |
* Side Effects: |
96 |
* - none |
97 |
*/ |
98 |
table_row_t * |
99 |
table_row_new (table_t * t) |
100 |
{ |
101 |
table_row_t *out; |
102 |
|
103 |
return_val_if_fail (t != NULL, NULL); |
104 |
|
105 |
out = new table_row_t; |
106 |
|
107 |
node_add (out, node_create (), &t->rows); |
108 |
|
109 |
return out; |
110 |
} |
111 |
|
112 |
/* |
113 |
* table_cell_associate |
114 |
* |
115 |
* Associates a cell with a row. |
116 |
* |
117 |
* Inputs: |
118 |
* - row to add cell to. |
119 |
* |
120 |
* Outputs: |
121 |
* - nothing |
122 |
* |
123 |
* Side Effects: |
124 |
* - none |
125 |
*/ |
126 |
void |
127 |
table_cell_associate (table_row_t * r, char const * const name, char const * const value) |
128 |
{ |
129 |
table_cell_t *c; |
130 |
|
131 |
return_if_fail (r != NULL); |
132 |
return_if_fail (name != NULL); |
133 |
return_if_fail (value != NULL); |
134 |
|
135 |
c = new table_cell_t; |
136 |
|
137 |
c->name = sstrdup (name); |
138 |
c->value = sstrdup (value); |
139 |
|
140 |
node_add (c, node_create (), &r->cells); |
141 |
} |
142 |
|
143 |
/* |
144 |
* table_render |
145 |
* |
146 |
* Renders a table. This is a two-pass operation, the first one to find out |
147 |
* the width of each cell, and then the second to render the data. |
148 |
* |
149 |
* Inputs: |
150 |
* - a table |
151 |
* - a callback function |
152 |
* - opaque data |
153 |
* |
154 |
* Outputs: |
155 |
* - none |
156 |
* |
157 |
* Side Effects: |
158 |
* - a callback function is called for each row of the table. |
159 |
*/ |
160 |
void |
161 |
table_render (table_t * t, void (*callback) (char const * const line, void *data), void *data) |
162 |
{ |
163 |
node_t *n; |
164 |
table_row_t *f; |
165 |
size_t bufsz = 0; |
166 |
char *buf = NULL; |
167 |
char *p; |
168 |
int i; |
169 |
|
170 |
return_if_fail (t != NULL); |
171 |
return_if_fail (callback != NULL); |
172 |
|
173 |
f = (table_row_t *) t->rows.head->data; |
174 |
|
175 |
LIST_FOREACH (n, t->rows.head) |
176 |
{ |
177 |
table_row_t *r = (table_row_t *) n->data; |
178 |
node_t *n2, *rn; |
179 |
|
180 |
/* we, uhh... we don't provide a macro for dealing with two lists at once ;) */ |
181 |
for (n2 = r->cells.head, rn = f->cells.head; n2 != NULL && rn != NULL; n2 = n2->next, rn = rn->next) |
182 |
{ |
183 |
table_cell_t *c, *rc; |
184 |
size_t sz; |
185 |
|
186 |
c = (table_cell_t *) n2->data; |
187 |
rc = (table_cell_t *) rn->data; |
188 |
|
189 |
if ((sz = strlen (c->name)) > (size_t) rc->width) |
190 |
rc->width = sz; |
191 |
|
192 |
if ((sz = strlen (c->value)) > (size_t) rc->width) |
193 |
rc->width = sz; |
194 |
} |
195 |
} |
196 |
|
197 |
/* now total up the result. */ |
198 |
LIST_FOREACH (n, f->cells.head) |
199 |
{ |
200 |
table_cell_t *c = (table_cell_t *) n->data; |
201 |
bufsz += c->width + 1; |
202 |
} |
203 |
|
204 |
buf = salloc<char> (bufsz); |
205 |
*buf = '\0'; |
206 |
|
207 |
/* start outputting the header. */ |
208 |
callback (asobject (t)->name, data); |
209 |
LIST_FOREACH (n, f->cells.head) |
210 |
{ |
211 |
table_cell_t *c = (table_cell_t *) n->data; |
212 |
char fmtbuf[12]; |
213 |
char buf2[1024]; |
214 |
|
215 |
/* dynamically generate the format string based on width. */ |
216 |
snprintf (fmtbuf, 12, n->next != NULL ? "%%-%ds " : "%%s", c->width); |
217 |
snprintf (buf2, 1024, fmtbuf, c->name); |
218 |
|
219 |
strlcat (buf, buf2, bufsz); |
220 |
} |
221 |
callback (buf, data); |
222 |
*buf = '\0'; |
223 |
|
224 |
/* separator line */ |
225 |
p = buf; |
226 |
LIST_FOREACH (n, f->cells.head) |
227 |
{ |
228 |
table_cell_t *c = (table_cell_t *) n->data; |
229 |
|
230 |
if (n->next != NULL) |
231 |
{ |
232 |
for (i = 0; i < c->width; i++) |
233 |
*p++ = '-'; |
234 |
*p++ = ' '; |
235 |
} |
236 |
else |
237 |
for (i = 0; i < (int) strlen (c->name); i++) |
238 |
*p++ = '-'; |
239 |
} |
240 |
*p = '\0'; |
241 |
callback (buf, data); |
242 |
*buf = '\0'; |
243 |
|
244 |
LIST_FOREACH (n, t->rows.head) |
245 |
{ |
246 |
table_row_t *r = (table_row_t *) n->data; |
247 |
node_t *n2, *rn; |
248 |
|
249 |
for (n2 = r->cells.head, rn = f->cells.head; n2 != NULL && rn != NULL; n2 = n2->next, rn = rn->next) |
250 |
{ |
251 |
table_cell_t *c, *rc; |
252 |
char fmtbuf[12]; |
253 |
char buf2[1024]; |
254 |
|
255 |
c = (table_cell_t *) n2->data; |
256 |
rc = (table_cell_t *) rn->data; |
257 |
|
258 |
/* dynamically generate the format string based on width. */ |
259 |
snprintf (fmtbuf, 12, n2->next != NULL ? "%%-%ds " : "%%s", rc->width); |
260 |
snprintf (buf2, 1024, fmtbuf, c->value); |
261 |
|
262 |
strlcat (buf, buf2, bufsz); |
263 |
} |
264 |
callback (buf, data); |
265 |
*buf = '\0'; |
266 |
} |
267 |
|
268 |
/* separator line */ |
269 |
p = buf; |
270 |
LIST_FOREACH (n, f->cells.head) |
271 |
{ |
272 |
table_cell_t *c = (table_cell_t *) n->data; |
273 |
|
274 |
if (n->next != NULL) |
275 |
{ |
276 |
for (i = 0; i < c->width; i++) |
277 |
*p++ = '-'; |
278 |
*p++ = ' '; |
279 |
} |
280 |
else |
281 |
for (i = 0; i < (int) strlen (c->name); i++) |
282 |
*p++ = '-'; |
283 |
} |
284 |
*p = '\0'; |
285 |
callback (buf, data); |
286 |
*buf = '\0'; |
287 |
} |
288 |
|
289 |
/* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs |
290 |
* vim:ts=8 |
291 |
* vim:sw=8 |
292 |
* vim:noexpandtab |
293 |
*/ |