7e55e8b785f1a466e7ddc2ea5eddb083fa379a9c
[openafs.git] / src / util / tabular_output.c
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     return do_setTableRow(Table,Table->Footer,Contents);
94 }
95
96
97 int util_setTableHeader(struct util_Table *Table, char ** Contents) {
98     return do_setTableRow(Table,Table->Header,Contents);
99 }
100
101 int
102 util_addTableBodyRow(struct util_Table *Table, char **Contents) {
103     struct util_TableRow *aRow;
104     int indx,i,row,col;
105     int thisRowLength=0;
106
107     /* Allocate more Rows if required. */
108     if (Table->numRows >= Table->numAllocatedRows) {
109         Table->numAllocatedRows += UTIL_T_NUMALLOC_ROW;
110         Table->Body=realloc(Table->Body,\
111                     Table->numAllocatedRows*sizeof(struct util_TableRow*));
112         for (i=0;i<UTIL_T_NUMALLOC_ROW;i++) {
113             Table->Body[Table->numRows+i]=newTableRow(Table);
114         }
115     }
116     aRow=newTableRow(Table);
117     do_setTableRow(Table,aRow,Contents);
118     if (Table->sortByColumn >= 0)  {
119         indx=findRowIndex(Table,aRow);
120         for (row=Table->numRows;row>indx;row--) {
121             for (col=0;col<Table->numColumns;col++) {
122                  strncpy(Table->Body[row]->CellContents[col],
123                          Table->Body[row-1]->CellContents[col],
124                          UTIL_T_MAX_CELLCONTENT_LEN);
125             }
126         }
127     } else {
128       indx=Table->numRows;
129     }
130     Table->numRows += 1;
131     for (i=0;i<Table->numColumns;i++) {
132         strncpy(Table->Body[indx]->CellContents[i],Contents[i],\
133                 UTIL_T_MAX_CELLCONTENT_LEN);
134         thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN);
135     }
136     if (thisRowLength > Table->RowLength)
137         Table->RowLength = thisRowLength;
138     return Table->numRows-1;
139 }
140
141 int
142 util_printTableBody(struct util_Table *Table) {
143     int i;
144
145     for (i=0;i<Table->numRows;i++)
146         Table->printRow(Table,Table->Body[i]);
147     return 0;
148 }
149
150 int
151 util_printTable(struct util_Table *Table) {
152     Table->printHeader(Table);
153     Table->printBody(Table);
154     Table->printFooter(Table);
155     return 0;
156 }
157
158 int
159 util_printTableHeader(struct util_Table *Table) {
160     Table->printHeader(Table);
161     return 0;
162 }
163
164 int
165 util_printTableFooter(struct util_Table *Table) {
166     Table->printFooter(Table);
167     return 0;
168 }
169
170 /* private functions */
171
172 int
173 do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents) {
174     int i;
175     int thisRowLength=0;
176     if ( Contents == NULL )
177         return -1;
178     for (i=0;i<Table->numColumns;i++) {
179         strcpy(aRow->CellContents[i],Contents[i]);
180         thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN);
181     }
182     if (thisRowLength > Table->RowLength)
183         Table->RowLength = thisRowLength;
184     return 0;
185 }
186
187
188 /* ASCII output functions */
189
190 int
191 printTableRow_ASCII(struct util_Table *Table, struct util_TableRow *aRow) {
192     int i;
193
194     if (!aRow)
195         return 1;
196
197     printf("%c",UTIL_T_CELLSEPARATOR);
198
199     for (i=0;i< Table->numColumns-1;i++) {
200         if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING)
201             printf("%-*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\
202                    UTIL_T_CELLSEPARATOR);
203         else
204             printf("%*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\
205                    UTIL_T_CELLSEPARATOR);
206     }
207     if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING)
208         printf("%-*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],\
209                UTIL_T_CELLSEPARATOR);
210     else
211         printf("%*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],UTIL_T_CELLSEPARATOR);
212     return 0;
213 }
214
215 int
216 printTableHeader_ASCII(struct util_Table *Table) {
217     int i;
218
219     printf("%c",UTIL_T_CELLSEPARATOR);
220     for(i=0;i<Table->RowLength;i++)
221         printf("%c",UTIL_T_ROWSEPARATOR);
222     printf("%c\n",UTIL_T_CELLSEPARATOR);
223
224     printTableRow_ASCII(Table,Table->Header);
225
226     printf("%c",UTIL_T_CELLSEPARATOR);
227     for(i=0;i<Table->RowLength;i++)
228         printf("%c",UTIL_T_ROWSEPARATOR);
229     printf("%c",UTIL_T_CELLSEPARATOR);
230     printf("\n");
231     return 0;
232 }
233
234
235 int
236 printTableFooter_ASCII(struct util_Table *Table) {
237     int i;
238
239     printf("%c",UTIL_T_CELLSEPARATOR);
240     for(i=0;i<Table->RowLength;i++)
241         printf("%c",UTIL_T_ROWSEPARATOR);
242     printf("%c",UTIL_T_CELLSEPARATOR);
243     printf( "\n");
244     if (Table->Footer) {
245         printTableRow_ASCII(Table,Table->Footer);
246         printf("%c",UTIL_T_CELLSEPARATOR);
247         for(i=0;i<Table->RowLength;i++)
248             printf("%c",UTIL_T_ROWSEPARATOR);
249         printf("%c",UTIL_T_CELLSEPARATOR);
250         printf( "\n");
251     }
252     return 0;
253 }
254
255 /* HTML output functions */
256
257 int
258 printTableRow_HTML(struct util_Table *Table, struct util_TableRow *aRow) {
259     int i;
260
261     if (!aRow)
262         return 1;
263
264     if (aRow == Table->Header)
265         printf("\t\t<th>\n");
266     else
267         printf("\t\t<tr>\n");
268
269     for (i=0;i< Table->numColumns;i++) {
270         printf("\t\t<td>");
271         printf("%s",aRow->CellContents[i]);
272         printf("\t\t</td>\n");
273     }
274     if (aRow == Table->Header)
275         printf("\t\t</th>\n");
276     else
277         printf("\t\t</tr>\n");
278     printf("\n");
279     return 0;
280 }
281
282 int
283 printTableFooter_HTML(struct util_Table *Table) {
284
285     printf("</tbody>\n");
286     if (Table->Footer) {
287         printf("<tfooter>\n");
288         printTableRow_HTML(Table,Table->Footer);
289         printf("</tfooter>\n");
290     }
291     printf("</table>\n");
292     return 0;
293 }
294
295 int
296 printTableHeader_HTML (struct util_Table *Table) {
297
298     printf("<table>\n");
299     printf("<thead>\n");
300     printTableRow_HTML(Table,Table->Header);
301     printf("</thead>\n");
302     printf("<tbody>\n");
303     return 0;
304 }
305
306
307 /* CSV output functions */
308
309 int
310 printTableRow_CSV(struct util_Table *Table, struct util_TableRow *aRow) {
311     int i;
312
313     if (!aRow)
314         return 1;
315     for (i=0;i<Table->numColumns-1;i++) {
316         printf("%s,",aRow->CellContents[i]);
317     }
318     printf("%s\n",aRow->CellContents[i]);
319     return 0;
320 }
321
322 int
323 printTableHeader_CSV (struct util_Table *Table) {
324     return printTableRow_CSV(Table,Table->Header);
325 }
326
327 int
328 printTableFooter_CSV (struct util_Table *Table) {
329     return printTableRow_CSV(Table,Table->Footer);
330 }
331
332
333 /* Constructors */
334
335 char **
336 util_newCellContents(struct util_Table* Table) {
337     char **CellContents=NULL;
338     int i;
339
340     if ( (CellContents=(char **) malloc( sizeof(char *) * Table->numColumns))\
341           == NULL ) {
342         fprintf(stderr,"Internal Error. Cannot allocate memory for new CellContents-array.\n");
343         exit(EXIT_FAILURE);
344     }
345     for (i=0;i<Table->numColumns;i++) {
346         if ( (CellContents[i]=(char *) malloc(UTIL_T_MAX_CELLCONTENT_LEN)) == NULL)  {
347             fprintf(stderr,\
348                     "Internal Error. Cannot allocate memory for new CellContents-array.\n");
349             exit(EXIT_FAILURE);
350         }
351         CellContents[i][0]='\0';
352     }
353     return CellContents;
354 }
355
356
357 struct util_Table*
358 util_newTable(int Type, int numColumns, char **ColumnHeaders, int *ColumnContentTypes, int *ColumnWidths, int sortByColumn) {
359     struct util_Table *Table=NULL;
360     int i;
361
362     if ( (Table=malloc(sizeof(struct util_Table))) == NULL) {
363           fprintf(stderr,\
364                   "Internal Error. Cannot allocate memory for new Table.\n");
365           exit(EXIT_FAILURE);
366     }
367     Table->Type=Type;
368     Table->numColumns=numColumns;
369     Table->numRows=0;
370     if (sortByColumn < 0 || sortByColumn > numColumns) {
371         fprintf(stderr,"Invalid Table Sortkey: %d.\n", sortByColumn);
372         errno=EINVAL;
373         return NULL;
374     }
375     if (sortByColumn > 0 )
376         Table->sortByColumn=sortByColumn-1; /* externally, we treat the first
377                                              column as 1, internally as 0 */
378     Table->ColumnHeaders=ColumnHeaders;
379     Table->ColumnContentTypes=ColumnContentTypes;
380     Table->ColumnWidths=ColumnWidths;
381     Table->RowLength=0;
382     for (i=0; i< numColumns;i++)
383         Table->RowLength += ColumnWidths[i]+1;
384     switch (Table->Type) {
385         case UTIL_T_TYPE_ASCII :
386                 Table->printHeader=printTableHeader_ASCII;
387                 Table->printFooter=printTableFooter_ASCII;
388                 Table->printRow=printTableRow_ASCII;
389                 break;
390         case UTIL_T_TYPE_CSV :
391                 Table->printHeader=printTableHeader_CSV;
392                 Table->printFooter=printTableFooter_CSV;
393                 Table->printRow=printTableRow_CSV;
394                 break;
395         case UTIL_T_TYPE_HTML :
396                 Table->printHeader=printTableHeader_HTML;
397                 Table->printFooter=printTableFooter_HTML;
398                 Table->printRow=printTableRow_HTML;
399                 break;
400         default :
401                 fprintf(stderr,"Error. Invalid TableType: %d.\n", Table->Type);
402                 errno=EINVAL;
403                 return NULL;
404     }
405     Table->printBody=util_printTableBody;
406     Table->Header=newTableRow(Table);
407     do_setTableRow(Table,Table->Header,ColumnHeaders);
408     Table->Body=NULL;
409     Table->Footer=NULL;
410     return Table;
411 }
412
413
414 /* private Constructors */
415
416 struct util_TableRow*
417 newTableRow(struct util_Table* Table) {
418     struct util_TableRow *aRow =NULL;
419
420     if ( (aRow= (struct util_TableRow*) malloc(sizeof(struct util_TableRow))) == NULL) {
421         fprintf(stderr,\
422                 "Internal Error. Cannot allocate memory for new TableRow.\n");
423         exit(EXIT_FAILURE);
424     }
425     aRow->CellContents=util_newCellContents(Table);
426     return aRow;
427 }
428
429 int
430 freeTableRow( struct util_Table* Table, struct util_TableRow *aRow) {
431     int i;
432
433     for (i=0;i<Table->numColumns;i++) {
434         free(aRow->CellContents[i]);
435     }
436     free(aRow->CellContents);
437     return 0;
438 }
439
440 int
441 util_freeTable(struct util_Table *Table) {
442     int i;
443
444     freeTableRow(Table, Table->Header);
445     freeTableRow(Table, Table->Footer);
446     for(i=0;i<Table->numRows;i++) {
447         freeTableRow(Table, Table->Body[i]);
448     }
449     free(Table);
450     return 0;
451 }
452
453
454 afs_int64
455 compareBodyRow(struct util_Table *Table, int RowIndx, struct util_TableRow *aRow) {
456
457     afs_int64 value1,value2;
458     if (Table->ColumnContentTypes[Table->sortByColumn] == UTIL_T_CONTENTTYPE_STRING) {
459         return strncmp(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\
460                aRow->CellContents[Table->sortByColumn],UTIL_T_MAX_CELLCONTENT_LEN);
461     } else {
462         util_GetInt64(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\
463                &value1);
464         util_GetInt64(aRow->CellContents[Table->sortByColumn],&value2);
465         return ( value1 - value2 );
466     }
467 }
468
469 /* find correct index for new row by bi-secting the table */
470 int
471 findRowIndex(struct util_Table* Table, struct util_TableRow *aRow){
472     int cmp,best,lower,middle,upper;
473
474     /* empty Table */
475     if (Table->numRows == 0)  {
476         return 0;
477     }
478     /* Entry smaller than smallest so far */
479     if (compareBodyRow(Table,0,aRow) > 0)  {
480         return 0;
481     }
482     /* Entry larger than largest so far */
483     if (compareBodyRow(Table,Table->numRows-1,aRow) < 0)  {
484         return Table->numRows;
485     }
486
487     lower =  0;
488     upper= Table->numRows-1;
489     best=0;
490     do {
491         middle=(upper-lower)/2+lower;
492         cmp=compareBodyRow(Table,middle,aRow);
493         if (cmp > 0)  {
494             upper=middle;
495             best = lower;
496         }
497         if (cmp < 0)  {
498             lower=middle;
499             best = upper;
500         }
501         if (cmp == 0) {
502             return middle;
503         }
504         if (upper - lower < 2) {
505             if ( compareBodyRow(Table,lower,aRow) < 0 )
506                 return upper;
507             else
508                 return lower;
509         }
510     }  while(1);
511     return 0;
512 }