backport to buster
[hcoop/debian/openafs.git] / src / util / tabular_output.c
CommitLineData
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
38struct util_TableRow {
39 char **CellContents;
40};
41
42struct 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
63struct util_TableRow* newTableRow(struct util_Table *);
64int printTableFooter_CSV(struct util_Table* Table);
65int printTableHeader_CSV(struct util_Table* Table);
66int printTableRow_CSV(struct util_Table* Table, struct util_TableRow *aTableRow);
67int printTableFooter_ASCII(struct util_Table* Table);
68int printTableHeader_ASCII(struct util_Table* Table);
69int printTableRow_ASCII(struct util_Table* Table, struct util_TableRow *aTableRow);
70int printTableFooter_HTML(struct util_Table* Table);
71int printTableHeader_HTML(struct util_Table* Table);
72int printTableRow_HTML(struct util_Table* Table, struct util_TableRow *aTableRow);
73int findRowIndex(struct util_Table* Table, struct util_TableRow *aRow);
74int do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents);
75
76
77/*
78Public Interface
79*/
80
81int
82util_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
92int 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
99int util_setTableHeader(struct util_Table *Table, char ** Contents) {
100 return do_setTableRow(Table,Table->Header,Contents);
101}
102
103int
104util_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
143int
144util_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
152int
153util_printTable(struct util_Table *Table) {
154 Table->printHeader(Table);
155 Table->printBody(Table);
156 Table->printFooter(Table);
157 return 0;
158}
159
160int
161util_printTableHeader(struct util_Table *Table) {
162 Table->printHeader(Table);
163 return 0;
164}
165
166int
167util_printTableFooter(struct util_Table *Table) {
168 Table->printFooter(Table);
169 return 0;
170}
171
172/* private functions */
173
174int
175do_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
192int
193printTableRow_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
217int
218printTableHeader_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
237int
238printTableFooter_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
259int
260printTableRow_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
284int
285printTableFooter_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
297int
298printTableHeader_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
311int
312printTableRow_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
324int
325printTableHeader_CSV (struct util_Table *Table) {
326 return printTableRow_CSV(Table,Table->Header);
327}
328
329int
330printTableFooter_CSV (struct util_Table *Table) {
331 return printTableRow_CSV(Table,Table->Footer);
332}
333
334
335/* Constructors */
336
337char **
338util_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
358struct util_Table*
359util_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
420struct util_TableRow*
421newTableRow(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
433int
434freeTableRow( 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
444int
445util_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
458afs_int64
459compareBodyRow(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 */
474int
475findRowIndex(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}