Suppress statement not reached warnings under Solaris Studio
[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     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 }