Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright (c) 2010, Christof Hanke, | |
3 | * RZG, Max-Planck-Institut f. Plasmaphysik. | |
4 | * All Rights Reserved. | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in | |
13 | * the documentation and/or other materials provided with the | |
14 | * distribution. | |
15 | * | |
16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, | |
17 | * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY | |
18 | * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
19 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
22 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
23 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |
24 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
26 | */ | |
27 | ||
28 | #include <afsconfig.h> | |
29 | #include <afs/param.h> | |
30 | ||
31 | #include <roken.h> | |
32 | ||
33 | #include <afs/afsutil.h> | |
34 | #include <afs/tabular_output.h> | |
35 | ||
36 | /* private structures */ | |
37 | ||
38 | struct util_TableRow { | |
39 | char **CellContents; | |
40 | }; | |
41 | ||
42 | struct util_Table { | |
43 | int Type; | |
44 | int numColumns,sortByColumn; | |
45 | int numRows, numAllocatedRows; | |
46 | int *ColumnWidths; | |
47 | char **ColumnHeaders; | |
48 | int *ColumnContentTypes; | |
49 | int RowLength; /* number of character per Row */ | |
50 | /* Basic subentities */ | |
51 | struct util_TableRow *Header; | |
52 | struct util_TableRow **Body; | |
53 | struct util_TableRow *Footer; | |
54 | /* output methods provided for this table */ | |
55 | int (*printHeader) (struct util_Table *Table); | |
56 | int (*printFooter) (struct util_Table *Table); | |
57 | int (*printBody) (struct util_Table *Table); | |
58 | int (*printRow) (struct util_Table *, struct util_TableRow *); | |
59 | }; | |
60 | ||
61 | /* private functions */ | |
62 | ||
63 | struct util_TableRow* newTableRow(struct util_Table *); | |
64 | int printTableFooter_CSV(struct util_Table* Table); | |
65 | int printTableHeader_CSV(struct util_Table* Table); | |
66 | int printTableRow_CSV(struct util_Table* Table, struct util_TableRow *aTableRow); | |
67 | int printTableFooter_ASCII(struct util_Table* Table); | |
68 | int printTableHeader_ASCII(struct util_Table* Table); | |
69 | int printTableRow_ASCII(struct util_Table* Table, struct util_TableRow *aTableRow); | |
70 | int printTableFooter_HTML(struct util_Table* Table); | |
71 | int printTableHeader_HTML(struct util_Table* Table); | |
72 | int printTableRow_HTML(struct util_Table* Table, struct util_TableRow *aTableRow); | |
73 | int findRowIndex(struct util_Table* Table, struct util_TableRow *aRow); | |
74 | int do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents); | |
75 | ||
76 | ||
77 | /* | |
78 | Public Interface | |
79 | */ | |
80 | ||
81 | int | |
82 | util_setTableBodyRow(struct util_Table *Table, int RowIndex, char **Contents) { | |
83 | struct util_TableRow *aRow; | |
84 | ||
85 | if (RowIndex >= Table->numRows) { | |
86 | return -1; | |
87 | } | |
88 | aRow=Table->Body[RowIndex]; | |
89 | return do_setTableRow(Table,aRow,Contents); | |
90 | } | |
91 | ||
92 | int util_setTableFooter(struct util_Table * Table, char ** Contents) { | |
93 | if (Table->Footer != NULL) | |
94 | Table->Footer = newTableRow(Table); | |
95 | return do_setTableRow(Table,Table->Footer,Contents); | |
96 | } | |
97 | ||
98 | ||
99 | int util_setTableHeader(struct util_Table *Table, char ** Contents) { | |
100 | return do_setTableRow(Table,Table->Header,Contents); | |
101 | } | |
102 | ||
103 | int | |
104 | util_addTableBodyRow(struct util_Table *Table, char **Contents) { | |
105 | struct util_TableRow *aRow; | |
106 | int indx,i,row,col; | |
107 | int thisRowLength=0; | |
108 | ||
109 | /* Allocate more Rows if required. */ | |
110 | if (Table->numRows >= Table->numAllocatedRows) { | |
111 | Table->numAllocatedRows += UTIL_T_NUMALLOC_ROW; | |
112 | Table->Body=realloc(Table->Body,\ | |
113 | Table->numAllocatedRows*sizeof(struct util_TableRow*)); | |
114 | for (i=0;i<UTIL_T_NUMALLOC_ROW;i++) { | |
115 | Table->Body[Table->numRows+i]=newTableRow(Table); | |
116 | } | |
117 | } | |
118 | aRow=newTableRow(Table); | |
119 | do_setTableRow(Table,aRow,Contents); | |
120 | if (Table->sortByColumn >= 0) { | |
121 | indx=findRowIndex(Table,aRow); | |
122 | for (row=Table->numRows;row>indx;row--) { | |
123 | for (col=0;col<Table->numColumns;col++) { | |
124 | strncpy(Table->Body[row]->CellContents[col], | |
125 | Table->Body[row-1]->CellContents[col], | |
126 | UTIL_T_MAX_CELLCONTENT_LEN); | |
127 | } | |
128 | } | |
129 | } else { | |
130 | indx=Table->numRows; | |
131 | } | |
132 | Table->numRows += 1; | |
133 | for (i=0;i<Table->numColumns;i++) { | |
134 | strncpy(Table->Body[indx]->CellContents[i],Contents[i],\ | |
135 | UTIL_T_MAX_CELLCONTENT_LEN); | |
136 | thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN); | |
137 | } | |
138 | if (thisRowLength > Table->RowLength) | |
139 | Table->RowLength = thisRowLength; | |
140 | return Table->numRows-1; | |
141 | } | |
142 | ||
143 | int | |
144 | util_printTableBody(struct util_Table *Table) { | |
145 | int i; | |
146 | ||
147 | for (i=0;i<Table->numRows;i++) | |
148 | Table->printRow(Table,Table->Body[i]); | |
149 | return 0; | |
150 | } | |
151 | ||
152 | int | |
153 | util_printTable(struct util_Table *Table) { | |
154 | Table->printHeader(Table); | |
155 | Table->printBody(Table); | |
156 | Table->printFooter(Table); | |
157 | return 0; | |
158 | } | |
159 | ||
160 | int | |
161 | util_printTableHeader(struct util_Table *Table) { | |
162 | Table->printHeader(Table); | |
163 | return 0; | |
164 | } | |
165 | ||
166 | int | |
167 | util_printTableFooter(struct util_Table *Table) { | |
168 | Table->printFooter(Table); | |
169 | return 0; | |
170 | } | |
171 | ||
172 | /* private functions */ | |
173 | ||
174 | int | |
175 | do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents) { | |
176 | int i; | |
177 | int thisRowLength=0; | |
178 | if ( Contents == NULL ) | |
179 | return -1; | |
180 | for (i=0;i<Table->numColumns;i++) { | |
181 | strcpy(aRow->CellContents[i],Contents[i]); | |
182 | thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN); | |
183 | } | |
184 | if (thisRowLength > Table->RowLength) | |
185 | Table->RowLength = thisRowLength; | |
186 | return 0; | |
187 | } | |
188 | ||
189 | ||
190 | /* ASCII output functions */ | |
191 | ||
192 | int | |
193 | printTableRow_ASCII(struct util_Table *Table, struct util_TableRow *aRow) { | |
194 | int i; | |
195 | ||
196 | if (!aRow) | |
197 | return 1; | |
198 | ||
199 | printf("%c",UTIL_T_CELLSEPARATOR); | |
200 | ||
201 | for (i=0;i< Table->numColumns-1;i++) { | |
202 | if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING) | |
203 | printf("%-*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\ | |
204 | UTIL_T_CELLSEPARATOR); | |
205 | else | |
206 | printf("%*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\ | |
207 | UTIL_T_CELLSEPARATOR); | |
208 | } | |
209 | if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING) | |
210 | printf("%-*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],\ | |
211 | UTIL_T_CELLSEPARATOR); | |
212 | else | |
213 | printf("%*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],UTIL_T_CELLSEPARATOR); | |
214 | return 0; | |
215 | } | |
216 | ||
217 | int | |
218 | printTableHeader_ASCII(struct util_Table *Table) { | |
219 | int i; | |
220 | ||
221 | printf("%c",UTIL_T_CELLSEPARATOR); | |
222 | for(i=0;i<Table->RowLength;i++) | |
223 | printf("%c",UTIL_T_ROWSEPARATOR); | |
224 | printf("%c\n",UTIL_T_CELLSEPARATOR); | |
225 | ||
226 | printTableRow_ASCII(Table,Table->Header); | |
227 | ||
228 | printf("%c",UTIL_T_CELLSEPARATOR); | |
229 | for(i=0;i<Table->RowLength;i++) | |
230 | printf("%c",UTIL_T_ROWSEPARATOR); | |
231 | printf("%c",UTIL_T_CELLSEPARATOR); | |
232 | printf("\n"); | |
233 | return 0; | |
234 | } | |
235 | ||
236 | ||
237 | int | |
238 | printTableFooter_ASCII(struct util_Table *Table) { | |
239 | int i; | |
240 | ||
241 | printf("%c",UTIL_T_CELLSEPARATOR); | |
242 | for(i=0;i<Table->RowLength;i++) | |
243 | printf("%c",UTIL_T_ROWSEPARATOR); | |
244 | printf("%c",UTIL_T_CELLSEPARATOR); | |
245 | printf( "\n"); | |
246 | if (Table->Footer) { | |
247 | printTableRow_ASCII(Table,Table->Footer); | |
248 | printf("%c",UTIL_T_CELLSEPARATOR); | |
249 | for(i=0;i<Table->RowLength;i++) | |
250 | printf("%c",UTIL_T_ROWSEPARATOR); | |
251 | printf("%c",UTIL_T_CELLSEPARATOR); | |
252 | printf( "\n"); | |
253 | } | |
254 | return 0; | |
255 | } | |
256 | ||
257 | /* HTML output functions */ | |
258 | ||
259 | int | |
260 | printTableRow_HTML(struct util_Table *Table, struct util_TableRow *aRow) { | |
261 | int i; | |
262 | ||
263 | if (!aRow) | |
264 | return 1; | |
265 | ||
266 | if (aRow == Table->Header) | |
267 | printf("\t\t<th>\n"); | |
268 | else | |
269 | printf("\t\t<tr>\n"); | |
270 | ||
271 | for (i=0;i< Table->numColumns;i++) { | |
272 | printf("\t\t<td>"); | |
273 | printf("%s",aRow->CellContents[i]); | |
274 | printf("\t\t</td>\n"); | |
275 | } | |
276 | if (aRow == Table->Header) | |
277 | printf("\t\t</th>\n"); | |
278 | else | |
279 | printf("\t\t</tr>\n"); | |
280 | printf("\n"); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | int | |
285 | printTableFooter_HTML(struct util_Table *Table) { | |
286 | ||
287 | printf("</tbody>\n"); | |
288 | if (Table->Footer) { | |
289 | printf("<tfooter>\n"); | |
290 | printTableRow_HTML(Table,Table->Footer); | |
291 | printf("</tfooter>\n"); | |
292 | } | |
293 | printf("</table>\n"); | |
294 | return 0; | |
295 | } | |
296 | ||
297 | int | |
298 | printTableHeader_HTML (struct util_Table *Table) { | |
299 | ||
300 | printf("<table>\n"); | |
301 | printf("<thead>\n"); | |
302 | printTableRow_HTML(Table,Table->Header); | |
303 | printf("</thead>\n"); | |
304 | printf("<tbody>\n"); | |
305 | return 0; | |
306 | } | |
307 | ||
308 | ||
309 | /* CSV output functions */ | |
310 | ||
311 | int | |
312 | printTableRow_CSV(struct util_Table *Table, struct util_TableRow *aRow) { | |
313 | int i; | |
314 | ||
315 | if (!aRow) | |
316 | return 1; | |
317 | for (i=0;i<Table->numColumns-1;i++) { | |
318 | printf("%s,",aRow->CellContents[i]); | |
319 | } | |
320 | printf("%s\n",aRow->CellContents[i]); | |
321 | return 0; | |
322 | } | |
323 | ||
324 | int | |
325 | printTableHeader_CSV (struct util_Table *Table) { | |
326 | return printTableRow_CSV(Table,Table->Header); | |
327 | } | |
328 | ||
329 | int | |
330 | printTableFooter_CSV (struct util_Table *Table) { | |
331 | return printTableRow_CSV(Table,Table->Footer); | |
332 | } | |
333 | ||
334 | ||
335 | /* Constructors */ | |
336 | ||
337 | char ** | |
338 | util_newCellContents(struct util_Table* Table) { | |
339 | char **CellContents=NULL; | |
340 | int i; | |
341 | ||
342 | if ( (CellContents=malloc( sizeof(char *) * Table->numColumns))== NULL ) { | |
343 | fprintf(stderr,"Internal Error. Cannot allocate memory for new CellContents-array.\n"); | |
344 | exit(EXIT_FAILURE); | |
345 | } | |
346 | for (i=0;i<Table->numColumns;i++) { | |
347 | if ( (CellContents[i]=malloc(UTIL_T_MAX_CELLCONTENT_LEN)) == NULL) { | |
348 | fprintf(stderr,\ | |
349 | "Internal Error. Cannot allocate memory for new CellContents-array.\n"); | |
350 | exit(EXIT_FAILURE); | |
351 | } | |
352 | CellContents[i][0]='\0'; | |
353 | } | |
354 | return CellContents; | |
355 | } | |
356 | ||
357 | ||
358 | struct util_Table* | |
359 | util_newTable(int Type, int numColumns, char **ColumnHeaders, int *ColumnContentTypes, int *ColumnWidths, int sortByColumn) { | |
360 | struct util_Table *Table=NULL; | |
361 | int i; | |
362 | ||
363 | if ( (Table=malloc(sizeof(struct util_Table))) == NULL) { | |
364 | fprintf(stderr,\ | |
365 | "Internal Error. Cannot allocate memory for new Table.\n"); | |
366 | exit(EXIT_FAILURE); | |
367 | } | |
368 | Table->Type=Type; | |
369 | Table->numColumns=numColumns; | |
370 | Table->numRows=0; | |
371 | Table->numAllocatedRows=0; | |
372 | if (sortByColumn < 0 || sortByColumn > numColumns) { | |
373 | fprintf(stderr,"Invalid Table Sortkey: %d.\n", sortByColumn); | |
374 | errno=EINVAL; | |
375 | free(Table); | |
376 | return NULL; | |
377 | } | |
378 | if (sortByColumn > 0 ) | |
379 | Table->sortByColumn=sortByColumn-1; /* externally, we treat the first | |
380 | column as 1, internally as 0 */ | |
381 | Table->ColumnHeaders=ColumnHeaders; | |
382 | Table->ColumnContentTypes=ColumnContentTypes; | |
383 | Table->ColumnWidths=ColumnWidths; | |
384 | Table->RowLength=0; | |
385 | for (i=0; i< numColumns;i++) | |
386 | Table->RowLength += ColumnWidths[i]+1; | |
387 | switch (Table->Type) { | |
388 | case UTIL_T_TYPE_ASCII : | |
389 | Table->printHeader=printTableHeader_ASCII; | |
390 | Table->printFooter=printTableFooter_ASCII; | |
391 | Table->printRow=printTableRow_ASCII; | |
392 | break; | |
393 | case UTIL_T_TYPE_CSV : | |
394 | Table->printHeader=printTableHeader_CSV; | |
395 | Table->printFooter=printTableFooter_CSV; | |
396 | Table->printRow=printTableRow_CSV; | |
397 | break; | |
398 | case UTIL_T_TYPE_HTML : | |
399 | Table->printHeader=printTableHeader_HTML; | |
400 | Table->printFooter=printTableFooter_HTML; | |
401 | Table->printRow=printTableRow_HTML; | |
402 | break; | |
403 | default : | |
404 | fprintf(stderr,"Error. Invalid TableType: %d.\n", Table->Type); | |
405 | free(Table); | |
406 | errno=EINVAL; | |
407 | return NULL; | |
408 | } | |
409 | Table->printBody=util_printTableBody; | |
410 | Table->Header=newTableRow(Table); | |
411 | do_setTableRow(Table,Table->Header,ColumnHeaders); | |
412 | Table->Body=NULL; | |
413 | Table->Footer=NULL; | |
414 | return Table; | |
415 | } | |
416 | ||
417 | ||
418 | /* private Constructors */ | |
419 | ||
420 | struct util_TableRow* | |
421 | newTableRow(struct util_Table* Table) { | |
422 | struct util_TableRow *aRow =NULL; | |
423 | ||
424 | if ( (aRow = malloc(sizeof(struct util_TableRow))) == NULL) { | |
425 | fprintf(stderr,\ | |
426 | "Internal Error. Cannot allocate memory for new TableRow.\n"); | |
427 | exit(EXIT_FAILURE); | |
428 | } | |
429 | aRow->CellContents=util_newCellContents(Table); | |
430 | return aRow; | |
431 | } | |
432 | ||
433 | int | |
434 | freeTableRow( struct util_Table* Table, struct util_TableRow *aRow) { | |
435 | int i; | |
436 | ||
437 | for (i=0;i<Table->numColumns;i++) { | |
438 | free(aRow->CellContents[i]); | |
439 | } | |
440 | free(aRow->CellContents); | |
441 | return 0; | |
442 | } | |
443 | ||
444 | int | |
445 | util_freeTable(struct util_Table *Table) { | |
446 | int i; | |
447 | ||
448 | freeTableRow(Table, Table->Header); | |
449 | freeTableRow(Table, Table->Footer); | |
450 | for(i=0;i<Table->numRows;i++) { | |
451 | freeTableRow(Table, Table->Body[i]); | |
452 | } | |
453 | free(Table); | |
454 | return 0; | |
455 | } | |
456 | ||
457 | ||
458 | afs_int64 | |
459 | compareBodyRow(struct util_Table *Table, int RowIndx, struct util_TableRow *aRow) { | |
460 | ||
461 | afs_int64 value1,value2; | |
462 | if (Table->ColumnContentTypes[Table->sortByColumn] == UTIL_T_CONTENTTYPE_STRING) { | |
463 | return strncmp(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\ | |
464 | aRow->CellContents[Table->sortByColumn],UTIL_T_MAX_CELLCONTENT_LEN); | |
465 | } else { | |
466 | util_GetInt64(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\ | |
467 | &value1); | |
468 | util_GetInt64(aRow->CellContents[Table->sortByColumn],&value2); | |
469 | return ( value1 - value2 ); | |
470 | } | |
471 | } | |
472 | ||
473 | /* find correct index for new row by bi-secting the table */ | |
474 | int | |
475 | findRowIndex(struct util_Table* Table, struct util_TableRow *aRow){ | |
476 | int cmp,lower,middle,upper; | |
477 | ||
478 | /* empty Table */ | |
479 | if (Table->numRows == 0) { | |
480 | return 0; | |
481 | } | |
482 | /* Entry smaller than smallest so far */ | |
483 | if (compareBodyRow(Table,0,aRow) > 0) { | |
484 | return 0; | |
485 | } | |
486 | /* Entry larger than largest so far */ | |
487 | if (compareBodyRow(Table,Table->numRows-1,aRow) < 0) { | |
488 | return Table->numRows; | |
489 | } | |
490 | ||
491 | lower = 0; | |
492 | upper= Table->numRows-1; | |
493 | do { | |
494 | middle=(upper-lower)/2+lower; | |
495 | cmp=compareBodyRow(Table,middle,aRow); | |
496 | if (cmp > 0) { | |
497 | upper=middle; | |
498 | } | |
499 | if (cmp < 0) { | |
500 | lower=middle; | |
501 | } | |
502 | if (cmp == 0) { | |
503 | return middle; | |
504 | } | |
505 | if (upper - lower < 2) { | |
506 | if ( compareBodyRow(Table,lower,aRow) < 0 ) | |
507 | return upper; | |
508 | else | |
509 | return lower; | |
510 | } | |
511 | } while(1); | |
512 | AFS_UNREACHED(return(0)); | |
513 | } |