From c1e874b4e2250438e0e6b0fe54ecd265356b3ee6 Mon Sep 17 00:00:00 2001 From: Christof Hanke Date: Sun, 17 Oct 2010 08:54:06 +0200 Subject: [PATCH] Add output-table to libafsutil This adds the complex structure Table to libafsutil. All binaries linking to libafsutil can use this to store their output in the table. This table can be sorted by a predefined column. The available output formats are ASCII, CSV and HTML. Change-Id: Id0a00d996a94fee08538226317c60e5bf5082051 Reviewed-on: http://gerrit.openafs.org/1970 Tested-by: BuildBot Reviewed-by: Derrick Brashear --- src/util/Makefile.in | 9 +- src/util/tabular_output.c | 515 ++++++++++++++++++++++++++++++++++++++ src/util/tabular_output.h | 41 +++ 3 files changed, 563 insertions(+), 2 deletions(-) create mode 100644 src/util/tabular_output.c create mode 100644 src/util/tabular_output.h diff --git a/src/util/Makefile.in b/src/util/Makefile.in index 8588eab42..80cb98c0a 100644 --- a/src/util/Makefile.in +++ b/src/util/Makefile.in @@ -21,7 +21,7 @@ objects = assert.o base64.o casestrcpy.o config_file.o ktime.o volparse.o \ hputil.o kreltime.o isathing.o get_krbrlm.o uuid.o serverLog.o \ dirpath.o fileutil.o netutils.o flipbase64.o fstab.o \ afs_atomlist.o afs_lhash.o snprintf.o pthread_glock.o \ - ${REGEX_OBJ} + tabular_output.o ${REGEX_OBJ} includes = \ ${TOP_INCDIR}/afs/dirpath.h \ @@ -43,7 +43,8 @@ includes = \ ${TOP_INCDIR}/afs/work_queue.h \ ${TOP_INCDIR}/afs/work_queue_types.h \ ${TOP_INCDIR}/afs/thread_pool.h \ - ${TOP_INCDIR}/afs/thread_pool_types.h + ${TOP_INCDIR}/afs/thread_pool_types.h \ + ${TOP_INCDIR}/afs/tabular_output.h all: ${includes} \ ${TOP_LIBDIR}/util.a \ @@ -113,6 +114,8 @@ ${TOP_INCDIR}/afs/thread_pool.h: ${srcdir}/thread_pool.h ${INSTALL_DATA} $? $@ ${TOP_INCDIR}/afs/thread_pool_types.h: ${srcdir}/thread_pool_types.h + +${TOP_INCDIR}/afs/tabular_output.h: ${srcdir}/tabular_output.h ${INSTALL_DATA} $? $@ ${TOP_LIBDIR}/util.a: util.a @@ -189,6 +192,7 @@ install: dirpath.h util.a sys ${INSTALL_DATA} ${srcdir}/work_queue_types.h ${DESTDIR}${includedir}/afs/work_queue_types.h ${INSTALL_DATA} ${srcdir}/thread_pool.h ${DESTDIR}${includedir}/afs/thread_pool.h ${INSTALL_DATA} ${srcdir}/thread_pool_types.h ${DESTDIR}${includedir}/afs/thread_pool_types.h + ${INSTALL_DATA} ${srcdir}/tabular_output.h ${DESTDIR}${includedir}/afs/tabular_output.h ${INSTALL_DATA} util.a ${DESTDIR}${libdir}/afs/util.a ${INSTALL_DATA} util.a ${DESTDIR}${libdir}/afs/libafsutil.a ${INSTALL_PROGRAM} sys ${DESTDIR}${bindir}/sys @@ -217,6 +221,7 @@ dest: dirpath.h util.a sys ${INSTALL_DATA} ${srcdir}/work_queue_types.h ${DEST}/include/afs/work_queue_types.h ${INSTALL_DATA} ${srcdir}/thread_pool.h ${DEST}/include/afs/thread_pool.h ${INSTALL_DATA} ${srcdir}/thread_pool_types.h ${DEST}/include/afs/thread_pool_types.h + ${INSTALL_DATA} ${srcdir}/tabular_output.h ${DEST}/include/afs/tabular_output.h ${INSTALL_DATA} util.a ${DEST}/lib/afs/util.a ${INSTALL_DATA} util.a ${DEST}/lib/afs/libafsutil.a ${INSTALL_PROGRAM} sys ${DEST}/bin/sys diff --git a/src/util/tabular_output.c b/src/util/tabular_output.c new file mode 100644 index 000000000..b879b8da8 --- /dev/null +++ b/src/util/tabular_output.c @@ -0,0 +1,515 @@ +/* + * Copyright (c) 2010, Christof Hanke, + * RZG, Max-Planck-Institut f. Plasmaphysik. + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* private structures */ + +struct util_TableRow { + char **CellContents; +}; + +struct util_Table { + int Type; + int numColumns,sortByColumn; + int numRows, numAllocatedRows; + int *ColumnWidths; + char **ColumnHeaders; + int *ColumnContentTypes; + int RowLength; /* number of character per Row */ + /* Basic subentities */ + struct util_TableRow *Header; + struct util_TableRow **Body; + struct util_TableRow *Footer; + /* output methods provided for this table */ + int (*printHeader) (struct util_Table *Table); + int (*printFooter) (struct util_Table *Table); + int (*printBody) (struct util_Table *Table); + int (*printRow) (struct util_Table *, struct util_TableRow *); +}; + +/* private functions */ + +struct util_TableRow* newTableRow(struct util_Table *); +int printTableFooter_CSV(struct util_Table* Table); +int printTableHeader_CSV(struct util_Table* Table); +int printTableRow_CSV(struct util_Table* Table, struct util_TableRow *aTableRow); +int printTableFooter_ASCII(struct util_Table* Table); +int printTableHeader_ASCII(struct util_Table* Table); +int printTableRow_ASCII(struct util_Table* Table, struct util_TableRow *aTableRow); +int printTableFooter_HTML(struct util_Table* Table); +int printTableHeader_HTML(struct util_Table* Table); +int printTableRow_HTML(struct util_Table* Table, struct util_TableRow *aTableRow); +int findRowIndex(struct util_Table* Table, struct util_TableRow *aRow); +int do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents); + + +/* +Public Interface +*/ + +int +util_setTableBodyRow(struct util_Table *Table, int RowIndex, char **Contents) { + struct util_TableRow *aRow; + + if (RowIndex >= Table->numRows) { + return -1; + } + aRow=Table->Body[RowIndex]; + return do_setTableRow(Table,aRow,Contents); +} + +int util_setTableFooter(struct util_Table * Table, char ** Contents) { + return do_setTableRow(Table,Table->Footer,Contents); +} + + +int util_setTableHeader(struct util_Table *Table, char ** Contents) { + return do_setTableRow(Table,Table->Header,Contents); +} + +int +util_addTableBodyRow(struct util_Table *Table, char **Contents) { + struct util_TableRow *aRow; + int indx,i,row,col; + int thisRowLength=0; + + /* Allocate more Rows if required. */ + if (Table->numRows >= Table->numAllocatedRows) { + Table->numAllocatedRows += UTIL_T_NUMALLOC_ROW; + Table->Body=realloc(Table->Body,\ + Table->numAllocatedRows*sizeof(struct util_TableRow*)); + for (i=0;iBody[Table->numRows+i]=newTableRow(Table); + } + } + aRow=newTableRow(Table); + do_setTableRow(Table,aRow,Contents); + if (Table->sortByColumn >= 0) { + indx=findRowIndex(Table,aRow); + for (row=Table->numRows;row>indx;row--) { + for (col=0;colnumColumns;col++) { + strncpy(Table->Body[row]->CellContents[col], + Table->Body[row-1]->CellContents[col], + UTIL_T_MAX_CELLCONTENT_LEN); + } + } + } else { + indx=Table->numRows; + } + Table->numRows += 1; + for (i=0;inumColumns;i++) { + strncpy(Table->Body[indx]->CellContents[i],Contents[i],\ + UTIL_T_MAX_CELLCONTENT_LEN); + thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN); + } + if (thisRowLength > Table->RowLength) + Table->RowLength = thisRowLength; + return Table->numRows-1; +} + +int +util_printTableBody(struct util_Table *Table) { + int i; + + for (i=0;inumRows;i++) + Table->printRow(Table,Table->Body[i]); + return 0; +} + +int +util_printTable(struct util_Table *Table) { + Table->printHeader(Table); + Table->printBody(Table); + Table->printFooter(Table); + return 0; +} + +int +util_printTableHeader(struct util_Table *Table) { + Table->printHeader(Table); + return 0; +} + +int +util_printTableFooter(struct util_Table *Table) { + Table->printFooter(Table); + return 0; +} + +/* private functions */ + +int +do_setTableRow(struct util_Table *Table, struct util_TableRow *aRow, char **Contents) { + int i; + int thisRowLength=0; + if ( Contents == NULL ) + return -1; + for (i=0;inumColumns;i++) { + strcpy(aRow->CellContents[i],Contents[i]); + thisRowLength += min(strlen(Contents[i]),UTIL_T_MAX_CELLCONTENT_LEN); + } + if (thisRowLength > Table->RowLength) + Table->RowLength = thisRowLength; + return 0; +} + + +/* ASCII output functions */ + +int +printTableRow_ASCII(struct util_Table *Table, struct util_TableRow *aRow) { + int i; + + if (!aRow) + return 1; + + printf("%c",UTIL_T_CELLSEPARATOR); + + for (i=0;i< Table->numColumns-1;i++) { + if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING) + printf("%-*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\ + UTIL_T_CELLSEPARATOR); + else + printf("%*s%c",Table->ColumnWidths[i],aRow->CellContents[i],\ + UTIL_T_CELLSEPARATOR); + } + if ( Table->ColumnContentTypes[i] == UTIL_T_CONTENTTYPE_STRING) + printf("%-*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],\ + UTIL_T_CELLSEPARATOR); + else + printf("%*s %c\n",Table->ColumnWidths[i],aRow->CellContents[i],UTIL_T_CELLSEPARATOR); + return 0; +} + +int +printTableHeader_ASCII(struct util_Table *Table) { + int i; + + printf("%c",UTIL_T_CELLSEPARATOR); + for(i=0;iRowLength;i++) + printf("%c",UTIL_T_ROWSEPARATOR); + printf("%c\n",UTIL_T_CELLSEPARATOR); + + printTableRow_ASCII(Table,Table->Header); + + printf("%c",UTIL_T_CELLSEPARATOR); + for(i=0;iRowLength;i++) + printf("%c",UTIL_T_ROWSEPARATOR); + printf("%c",UTIL_T_CELLSEPARATOR); + printf("\n"); + return 0; +} + + +int +printTableFooter_ASCII(struct util_Table *Table) { + int i; + + printf("%c",UTIL_T_CELLSEPARATOR); + for(i=0;iRowLength;i++) + printf("%c",UTIL_T_ROWSEPARATOR); + printf("%c",UTIL_T_CELLSEPARATOR); + printf( "\n"); + if (Table->Footer) { + printTableRow_ASCII(Table,Table->Footer); + printf("%c",UTIL_T_CELLSEPARATOR); + for(i=0;iRowLength;i++) + printf("%c",UTIL_T_ROWSEPARATOR); + printf("%c",UTIL_T_CELLSEPARATOR); + printf( "\n"); + } + return 0; +} + +/* HTML output functions */ + +int +printTableRow_HTML(struct util_Table *Table, struct util_TableRow *aRow) { + int i; + + if (!aRow) + return 1; + + if (aRow == Table->Header) + printf("\t\t\n"); + else + printf("\t\t\n"); + + for (i=0;i< Table->numColumns;i++) { + printf("\t\t"); + printf("%s",aRow->CellContents[i]); + printf("\t\t\n"); + } + if (aRow == Table->Header) + printf("\t\t\n"); + else + printf("\t\t\n"); + printf("\n"); + return 0; +} + +int +printTableFooter_HTML(struct util_Table *Table) { + + printf("\n"); + if (Table->Footer) { + printf("\n"); + printTableRow_HTML(Table,Table->Footer); + printf("\n"); + } + printf("\n"); + return 0; +} + +int +printTableHeader_HTML (struct util_Table *Table) { + + printf("\n"); + printf("\n"); + printTableRow_HTML(Table,Table->Header); + printf("\n"); + printf("\n"); + return 0; +} + + +/* CSV output functions */ + +int +printTableRow_CSV(struct util_Table *Table, struct util_TableRow *aRow) { + int i; + + if (!aRow) + return 1; + for (i=0;inumColumns-1;i++) { + printf("%s,",aRow->CellContents[i]); + } + printf("%s\n",aRow->CellContents[i]); + return 0; +} + +int +printTableHeader_CSV (struct util_Table *Table) { + return printTableRow_CSV(Table,Table->Header); +} + +int +printTableFooter_CSV (struct util_Table *Table) { + return printTableRow_CSV(Table,Table->Footer); +} + + +/* Constructors */ + +char ** +util_newCellContents(struct util_Table* Table) { + char **CellContents=NULL; + int i; + + if ( (CellContents=(char **) malloc( sizeof(char *) * Table->numColumns))\ + == NULL ) { + fprintf(stderr,"Internal Error. Cannot allocate memory for new CellContents-array.\n"); + exit(EXIT_FAILURE); + } + for (i=0;inumColumns;i++) { + if ( (CellContents[i]=(char *) malloc(UTIL_T_MAX_CELLCONTENT_LEN)) == NULL) { + fprintf(stderr,\ + "Internal Error. Cannot allocate memory for new CellContents-array.\n"); + exit(EXIT_FAILURE); + } + CellContents[i][0]='\0'; + } + return CellContents; +} + + +struct util_Table* +util_newTable(int Type, int numColumns, char **ColumnHeaders, int *ColumnContentTypes, int *ColumnWidths, int sortByColumn) { + struct util_Table *Table=NULL; + int i; + + if ( (Table=malloc(sizeof(struct util_Table))) == NULL) { + fprintf(stderr,\ + "Internal Error. Cannot allocate memory for new Table.\n"); + exit(EXIT_FAILURE); + } + Table->Type=Type; + Table->numColumns=numColumns; + Table->numRows=0; + if (sortByColumn < 0 || sortByColumn > numColumns) { + fprintf(stderr,"Invalid Table Sortkey: %d.\n", sortByColumn); + errno=EINVAL; + return NULL; + } + if (sortByColumn > 0 ) + Table->sortByColumn=sortByColumn-1; /* externally, we treat the first + column as 1, internally as 0 */ + Table->ColumnHeaders=ColumnHeaders; + Table->ColumnContentTypes=ColumnContentTypes; + Table->ColumnWidths=ColumnWidths; + Table->RowLength=0; + for (i=0; i< numColumns;i++) + Table->RowLength += ColumnWidths[i]+1; + switch (Table->Type) { + case UTIL_T_TYPE_ASCII : + Table->printHeader=printTableHeader_ASCII; + Table->printFooter=printTableFooter_ASCII; + Table->printRow=printTableRow_ASCII; + break; + case UTIL_T_TYPE_CSV : + Table->printHeader=printTableHeader_CSV; + Table->printFooter=printTableFooter_CSV; + Table->printRow=printTableRow_CSV; + break; + case UTIL_T_TYPE_HTML : + Table->printHeader=printTableHeader_HTML; + Table->printFooter=printTableFooter_HTML; + Table->printRow=printTableRow_HTML; + break; + default : + fprintf(stderr,"Error. Invalid TableType: %d.\n", Table->Type); + errno=EINVAL; + return NULL; + } + Table->printBody=util_printTableBody; + Table->Header=newTableRow(Table); + do_setTableRow(Table,Table->Header,ColumnHeaders); + Table->Body=NULL; + Table->Footer=NULL; + return Table; +} + + +/* private Constructors */ + +struct util_TableRow* +newTableRow(struct util_Table* Table) { + struct util_TableRow *aRow =NULL; + + if ( (aRow= (struct util_TableRow*) malloc(sizeof(struct util_TableRow))) == NULL) { + fprintf(stderr,\ + "Internal Error. Cannot allocate memory for new TableRow.\n"); + exit(EXIT_FAILURE); + } + aRow->CellContents=util_newCellContents(Table); + return aRow; +} + +int +freeTableRow( struct util_Table* Table, struct util_TableRow *aRow) { + int i; + + for (i=0;inumColumns;i++) { + free(aRow->CellContents[i]); + } + free(aRow->CellContents); + return 0; +} + +int +util_freeTable(struct util_Table *Table) { + int i; + + freeTableRow(Table, Table->Header); + freeTableRow(Table, Table->Footer); + for(i=0;inumRows;i++) { + freeTableRow(Table, Table->Body[i]); + } + free(Table); + return 0; +} + + +afs_int64 +compareBodyRow(struct util_Table *Table, int RowIndx, struct util_TableRow *aRow) { + + afs_int64 value1,value2; + if (Table->ColumnContentTypes[Table->sortByColumn] == UTIL_T_CONTENTTYPE_STRING) { + return strncmp(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\ + aRow->CellContents[Table->sortByColumn],UTIL_T_MAX_CELLCONTENT_LEN); + } else { + util_GetInt64(Table->Body[RowIndx]->CellContents[Table->sortByColumn],\ + &value1); + util_GetInt64(aRow->CellContents[Table->sortByColumn],&value2); + return ( value1 - value2 ); + } +} + +/* find correct index for new row by bi-secting the table */ +int +findRowIndex(struct util_Table* Table, struct util_TableRow *aRow){ + int cmp,best,lower,middle,upper; + + /* empty Table */ + if (Table->numRows == 0) { + return 0; + } + /* Entry smaller than smallest so far */ + if (compareBodyRow(Table,0,aRow) > 0) { + return 0; + } + /* Entry larger than largest so far */ + if (compareBodyRow(Table,Table->numRows-1,aRow) < 0) { + return Table->numRows; + } + + lower = 0; + upper= Table->numRows-1; + best=0; + do { + middle=(upper-lower)/2+lower; + cmp=compareBodyRow(Table,middle,aRow); + if (cmp > 0) { + upper=middle; + best = lower; + } + if (cmp < 0) { + lower=middle; + best = upper; + } + if (cmp == 0) { + return middle; + } + if (upper - lower < 2) { + if ( compareBodyRow(Table,lower,aRow) < 0 ) + return upper; + else + return lower; + } + } while(1); + return 0; +} diff --git a/src/util/tabular_output.h b/src/util/tabular_output.h new file mode 100644 index 000000000..8a2d70678 --- /dev/null +++ b/src/util/tabular_output.h @@ -0,0 +1,41 @@ +#ifndef _TABULAR_OUTPUT_H +#define _TABULAR_OUTPUT_H + +#define UTIL_T_MAX_CELLS 20 +#define UTIL_T_MAX_CELLCONTENT_LEN 30 +#define UTIL_T_TYPE_ASCII 0 +#define UTIL_T_TYPE_CSV 1 +#define UTIL_T_TYPE_HTML 2 +#define UTIL_T_TYPE_MAX 2 +#define UTIL_T_CELLSEPARATOR '|' +#define UTIL_T_ROWSEPARATOR '-' +#define UTIL_T_NUMALLOC_ROW 10 +#define UTIL_T_CONTENTTYPE_STRING 0 +#define UTIL_T_CONTENTTYPE_NUMERIC 1 + +/* private structures */ + +struct util_Table; + +/* public accessor functions */ + + +extern struct util_Table* util_newTable(int Type, int numColumns, + char **ColumnHeaders, int *ColumnContentTypes, + int *ColumnWidths, int sortByColumn); +extern char ** util_newCellContents(struct util_Table*); +extern int util_printTableHeader(struct util_Table *Table); +extern int util_printTableBody(struct util_Table *Table); +extern int util_printTableFooter(struct util_Table *Table); +extern int util_printTable(struct util_Table *Table); +extern int util_addTableBodyRow(struct util_Table *Table, char **Contents); +extern int util_setTableBodyRow(struct util_Table *Table, int RowIndex, + char **Contents); +extern int util_setTableHeader(struct util_Table *Table, char **Contents); +extern int util_setTableFooter(struct util_Table *Table, char **Contents); +extern int util_freeTable(struct util_Table* Table); + +#define UTIL_T_TYPE_HELP "output-format of table: 0: ASCII, 1: CSV, 2 : HTML" +#define UTIL_T_SORT_HELP "table-column to sort" + +#endif /* _TABULAR_OUTPUT_H */ -- 2.39.5