Add output-table to libafsutil
[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 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #include <time.h>
35 #include <afs/afsutil.h>
36 #include <afs/tabular_output.h>
37 #include <errno.h>
38
39 /* private structures */
40
41 struct util_TableRow {
42     char **CellContents;
43 };
44
45 struct util_Table {
46     int Type;
47     int numColumns,sortByColumn;
48     int numRows, numAllocatedRows;
49     int *ColumnWidths;
50     char **ColumnHeaders;
51     int  *ColumnContentTypes;
52     int RowLength; /* number of character per Row */
53     /* Basic subentities */
54     struct util_TableRow *Header;
55     struct util_TableRow **Body;
56     struct util_TableRow *Footer;
57     /* output methods provided for this table */
58     int (*printHeader) (struct util_Table *Table);
59     int (*printFooter) (struct util_Table *Table);
60     int (*printBody) (struct util_Table *Table);
61     int (*printRow) (struct util_Table *, struct util_TableRow *);
62 };
63
64 /* private functions */
65
66 struct util_TableRow* newTableRow(struct util_Table *);
67 int printTableFooter_CSV(struct util_Table* Table);
68 int printTableHeader_CSV(struct util_Table* Table);
69 int printTableRow_CSV(struct util_Table* Table, struct util_TableRow *aTableRow);
70 int printTableFooter_ASCII(struct util_Table* Table);
71 int printTableHeader_ASCII(struct util_Table* Table);
72 int printTableRow_ASCII(struct util_Table* Table, struct util_TableRow *aTableRow);
73 int printTableFooter_HTML(struct util_Table* Table);
74 int printTableHeader_HTML(struct util_Table* Table);
75 int printTableRow_HTML(struct util_Table* Table, struct util_TableRow *aTableRow);
76 int findRowIndex(struct util_Table* Table, struct util_TableRow *aRow);
77 int do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents);
78
79
80 /*
81 Public Interface
82 */
83
84 int
85 util_setTableBodyRow(struct util_Table *Table, int RowIndex, char **Contents) {
86     struct util_TableRow *aRow;
87
88     if (RowIndex >= Table->numRows) {
89         return -1;
90     }
91     aRow=Table->Body[RowIndex];
92     return do_setTableRow(Table,aRow,Contents);
93 }
94
95 int util_setTableFooter(struct util_Table * Table, char ** Contents) {
96     return do_setTableRow(Table,Table->Footer,Contents);
97 }
98
99
100 int util_setTableHeader(struct util_Table *Table, char ** Contents) {
101     return do_setTableRow(Table,Table->Header,Contents);
102 }
103
104 int
105 util_addTableBodyRow(struct util_Table *Table, char **Contents) {
106     struct util_TableRow *aRow;
107     int indx,i,row,col;
108     int thisRowLength=0;
109
110     /* Allocate more Rows if required. */
111     if (Table->numRows >= Table->numAllocatedRows) {
112         Table->numAllocatedRows += UTIL_T_NUMALLOC_ROW;
113         Table->Body=realloc(Table->Body,\
114                     Table->numAllocatedRows*sizeof(struct util_TableRow*));
115         for (i=0;i<UTIL_T_NUMALLOC_ROW;i++) {
116             Table->Body[Table->numRows+i]=newTableRow(Table);
117         }
118     }
119     aRow=newTableRow(Table);
120     do_setTableRow(Table,aRow,Contents);
121     if (Table->sortByColumn >= 0)  {
122         indx=findRowIndex(Table,aRow);
123         for (row=Table->numRows;row>indx;row--) {
124             for (col=0;col<Table->numColumns;col++) {
125                  strncpy(Table->Body[row]->CellContents[col],
126                          Table->Body[row-1]->CellContents[col],
127                          UTIL_T_MAX_CELLCONTENT_LEN);
128             }
129         }
130     } else {
131       indx=Table->numRows;
132     }
133     Table->numRows += 1;
134     for (i=0;i<Table->numColumns;i++) {
135         strncpy(Table->Body[indx]->CellContents[i],Contents[i],\
136                 UTIL_T_MAX_CELLCONTENT_LEN);
137         thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN);
138     }
139     if (thisRowLength > Table->RowLength)
140         Table->RowLength = thisRowLength;
141     return Table->numRows-1;
142 }
143
144 int
145 util_printTableBody(struct util_Table *Table) {
146     int i;
147
148     for (i=0;i<Table->numRows;i++)
149         Table->printRow(Table,Table->Body[i]);
150     return 0;
151 }
152
153 int
154 util_printTable(struct util_Table *Table) {
155     Table->printHeader(Table);
156     Table->printBody(Table);
157     Table->printFooter(Table);
158     return 0;
159 }
160
161 int
162 util_printTableHeader(struct util_Table *Table) {
163     Table->printHeader(Table);
164     return 0;
165 }
166
167 int
168 util_printTableFooter(struct util_Table *Table) {
169     Table->printFooter(Table);
170     return 0;
171 }
172
173 /* private functions */
174
175 int
176 do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents) {
177     int i;
178     int thisRowLength=0;
179     if ( Contents == NULL )
180         return -1;
181     for (i=0;i<Table->numColumns;i++) {
182         strcpy(aRow->CellContents[i],Contents[i]);
183         thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN);
184     }
185     if (thisRowLength > Table->RowLength)
186         Table->RowLength = thisRowLength;
187     return 0;
188 }
189
190
191 /* ASCII output functions */
192
193 int
194 printTableRow_ASCII(struct util_Table *Table, struct util_TableRow *aRow) {
195     int i;
196
197     if (!aRow)
198         return 1;
199
200     printf("%c",UTIL_T_CELLSEPARATOR);
201
202     for (i=0;i< Table->numColumns-1;i++) {
203         if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING)
204             printf("%-*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\
205                    UTIL_T_CELLSEPARATOR);
206         else
207             printf("%*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\
208                    UTIL_T_CELLSEPARATOR);
209     }
210     if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING)
211         printf("%-*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],\
212                UTIL_T_CELLSEPARATOR);
213     else
214         printf("%*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],UTIL_T_CELLSEPARATOR);
215     return 0;
216 }
217
218 int
219 printTableHeader_ASCII(struct util_Table *Table) {
220     int i;
221
222     printf("%c",UTIL_T_CELLSEPARATOR);
223     for(i=0;i<Table->RowLength;i++)
224         printf("%c",UTIL_T_ROWSEPARATOR);
225     printf("%c\n",UTIL_T_CELLSEPARATOR);
226
227     printTableRow_ASCII(Table,Table->Header);
228
229     printf("%c",UTIL_T_CELLSEPARATOR);
230     for(i=0;i<Table->RowLength;i++)
231         printf("%c",UTIL_T_ROWSEPARATOR);
232     printf("%c",UTIL_T_CELLSEPARATOR);
233     printf("\n");
234     return 0;
235 }
236
237
238 int
239 printTableFooter_ASCII(struct util_Table *Table) {
240     int i;
241
242     printf("%c",UTIL_T_CELLSEPARATOR);
243     for(i=0;i<Table->RowLength;i++)
244         printf("%c",UTIL_T_ROWSEPARATOR);
245     printf("%c",UTIL_T_CELLSEPARATOR);
246     printf( "\n");
247     if (Table->Footer) {
248         printTableRow_ASCII(Table,Table->Footer);
249         printf("%c",UTIL_T_CELLSEPARATOR);
250         for(i=0;i<Table->RowLength;i++)
251             printf("%c",UTIL_T_ROWSEPARATOR);
252         printf("%c",UTIL_T_CELLSEPARATOR);
253         printf( "\n");
254     }
255     return 0;
256 }
257
258 /* HTML output functions */
259
260 int
261 printTableRow_HTML(struct util_Table *Table, struct util_TableRow *aRow) {
262     int i;
263
264     if (!aRow)
265         return 1;
266
267     if (aRow == Table->Header)
268         printf("\t\t<th>\n");
269     else
270         printf("\t\t<tr>\n");
271
272     for (i=0;i< Table->numColumns;i++) {
273         printf("\t\t<td>");
274         printf("%s",aRow->CellContents[i]);
275         printf("\t\t</td>\n");
276     }
277     if (aRow == Table->Header)
278         printf("\t\t</th>\n");
279     else
280         printf("\t\t</tr>\n");
281     printf("\n");
282     return 0;
283 }
284
285 int
286 printTableFooter_HTML(struct util_Table *Table) {
287
288     printf("</tbody>\n");
289     if (Table->Footer) {
290         printf("<tfooter>\n");
291         printTableRow_HTML(Table,Table->Footer);
292         printf("</tfooter>\n");
293     }
294     printf("</table>\n");
295     return 0;
296 }
297
298 int
299 printTableHeader_HTML (struct util_Table *Table) {
300
301     printf("<table>\n");
302     printf("<thead>\n");
303     printTableRow_HTML(Table,Table->Header);
304     printf("</thead>\n");
305     printf("<tbody>\n");
306     return 0;
307 }
308
309
310 /* CSV output functions */
311
312 int
313 printTableRow_CSV(struct util_Table *Table, struct util_TableRow *aRow) {
314     int i;
315
316     if (!aRow)
317         return 1;
318     for (i=0;i<Table->numColumns-1;i++) {
319         printf("%s,",aRow->CellContents[i]);
320     }
321     printf("%s\n",aRow->CellContents[i]);
322     return 0;
323 }
324
325 int
326 printTableHeader_CSV (struct util_Table *Table) {
327     return printTableRow_CSV(Table,Table->Header);
328 }
329
330 int
331 printTableFooter_CSV (struct util_Table *Table) {
332     return printTableRow_CSV(Table,Table->Footer);
333 }
334
335
336 /* Constructors */
337
338 char **
339 util_newCellContents(struct util_Table* Table) {
340     char **CellContents=NULL;
341     int i;
342
343     if ( (CellContents=(char **) malloc( sizeof(char *) * Table->numColumns))\
344           == NULL ) {
345         fprintf(stderr,"Internal Error. Cannot allocate memory for new CellContents-array.\n");
346         exit(EXIT_FAILURE);
347     }
348     for (i=0;i<Table->numColumns;i++) {
349         if ( (CellContents[i]=(char *) malloc(UTIL_T_MAX_CELLCONTENT_LEN)) == NULL)  {
350             fprintf(stderr,\
351                     "Internal Error. Cannot allocate memory for new CellContents-array.\n");
352             exit(EXIT_FAILURE);
353         }
354         CellContents[i][0]='\0';
355     }
356     return CellContents;
357 }
358
359
360 struct util_Table*
361 util_newTable(int Type, int numColumns, char **ColumnHeaders, int *ColumnContentTypes, int *ColumnWidths, int sortByColumn) {
362     struct util_Table *Table=NULL;
363     int i;
364
365     if ( (Table=malloc(sizeof(struct util_Table))) == NULL) {
366           fprintf(stderr,\
367                   "Internal Error. Cannot allocate memory for new Table.\n");
368           exit(EXIT_FAILURE);
369     }
370     Table->Type=Type;
371     Table->numColumns=numColumns;
372     Table->numRows=0;
373     if (sortByColumn < 0 || sortByColumn > numColumns) {
374         fprintf(stderr,"Invalid Table Sortkey: %d.\n", sortByColumn);
375         errno=EINVAL;
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                 errno=EINVAL;
406                 return NULL;
407     }
408     Table->printBody=util_printTableBody;
409     Table->Header=newTableRow(Table);
410     do_setTableRow(Table,Table->Header,ColumnHeaders);
411     Table->Body=NULL;
412     Table->Footer=NULL;
413     return Table;
414 }
415
416
417 /* private Constructors */
418
419 struct util_TableRow*
420 newTableRow(struct util_Table* Table) {
421     struct util_TableRow *aRow =NULL;
422
423     if ( (aRow= (struct util_TableRow*) malloc(sizeof(struct util_TableRow))) == NULL) {
424         fprintf(stderr,\
425                 "Internal Error. Cannot allocate memory for new TableRow.\n");
426         exit(EXIT_FAILURE);
427     }
428     aRow->CellContents=util_newCellContents(Table);
429     return aRow;
430 }
431
432 int
433 freeTableRow( struct util_Table* Table, struct util_TableRow *aRow) {
434     int i;
435
436     for (i=0;i<Table->numColumns;i++) {
437         free(aRow->CellContents[i]);
438     }
439     free(aRow->CellContents);
440     return 0;
441 }
442
443 int
444 util_freeTable(struct util_Table *Table) {
445     int i;
446
447     freeTableRow(Table, Table->Header);
448     freeTableRow(Table, Table->Footer);
449     for(i=0;i<Table->numRows;i++) {
450         freeTableRow(Table, Table->Body[i]);
451     }
452     free(Table);
453     return 0;
454 }
455
456
457 afs_int64
458 compareBodyRow(struct util_Table *Table, int RowIndx, struct util_TableRow *aRow) {
459
460     afs_int64 value1,value2;
461     if (Table->ColumnContentTypes[Table->sortByColumn] == UTIL_T_CONTENTTYPE_STRING) {
462         return strncmp(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\
463                aRow->CellContents[Table->sortByColumn],UTIL_T_MAX_CELLCONTENT_LEN);
464     } else {
465         util_GetInt64(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\
466                &value1);
467         util_GetInt64(aRow->CellContents[Table->sortByColumn],&value2);
468         return ( value1 - value2 );
469     }
470 }
471
472 /* find correct index for new row by bi-secting the table */
473 int
474 findRowIndex(struct util_Table* Table, struct util_TableRow *aRow){
475     int cmp,best,lower,middle,upper;
476
477     /* empty Table */
478     if (Table->numRows == 0)  {
479         return 0;
480     }
481     /* Entry smaller than smallest so far */
482     if (compareBodyRow(Table,0,aRow) > 0)  {
483         return 0;
484     }
485     /* Entry larger than largest so far */
486     if (compareBodyRow(Table,Table->numRows-1,aRow) < 0)  {
487         return Table->numRows;
488     }
489
490     lower =  0;
491     upper= Table->numRows-1;
492     best=0;
493     do {
494         middle=(upper-lower)/2+lower;
495         cmp=compareBodyRow(Table,middle,aRow);
496         if (cmp > 0)  {
497             upper=middle;
498             best = lower;
499         }
500         if (cmp < 0)  {
501             lower=middle;
502             best = upper;
503         }
504         if (cmp == 0) {
505             return middle;
506         }
507         if (upper - lower < 2) {
508             if ( compareBodyRow(Table,lower,aRow) < 0 )
509                 return upper;
510             else
511                 return lower;
512         }
513     }  while(1);
514     return 0;
515 }