// selsort.cpp - SELECT AND SORT CUSTOMER FILE COLUMNS // // USAGE // selsort input-name field-list // // input-name is the name of the customer file. // field-list is the list of fields to be displayed. // // MODULE INDEX // NAME CONTENTS // LoadStr Load a string value // LoadCust Load the customer file // DecodeColList Decode column list // CmpCust Compare customer array rows by the sort key // CalcMaxLen Calculate maximum field lengths // PrintRep Print the report // main Main line // // MAINTENANCE HISTORY // DATE PROGRAMMER AND DETAILS // 08-11-02 JS Original // //----------------------------------------------------------------------------- #include // Standard input/output #include // String manipulation functions #include // Standard library #include // Error codes //----------------------------------------------------------------------------- // DEFINITIONS #define STRLEN 256 // Maximum string length #define MAXCUST 1024 // Maximum customers #define NUMCOL 5 // Number of columns //----------------------------------------------------------------------------- // TYPE DEFINITIONS typedef char StrTyp[STRLEN+1]; // String type //----------------------------------------------------------------------------- // DATA TABLE ROW struct Cust { char *custCol[NUMCOL]; // Column data }; //----------------------------------------------------------------------------- // FIELD TABLE ROW struct Field { char *fieldName; // Field name int fieldCol; // Field column }; //----------------------------------------------------------------------------- // COLUMN TABLE ROW struct Col { Field *colField; // Pointer to field table row int colMaxLen; // Maximum field length }; //----------------------------------------------------------------------------- // GLOBAL DATA Cust custArr[MAXCUST]; // Customer array int custCnt; // Customer count Col colArr[NUMCOL]; // Column array int colCnt; // Column count //----------------------------------------------------------------------------- // LOAD A STRING VALUE char * LoadStr ( FILE *fp) // Customer file pointer { StrTyp strBuf; // String buffer char *strPnt; // String pointer int ch; // Input character size_t len; // String length ch = getc(fp); while (ch == ' ') ch = getc(fp); if (ch != '"') { fprintf (stderr, "Error: invalid string in customer file\n"); exit (1); } ch = getc(fp); len = 0; while (ch != '"') { if (len >= STRLEN) { fprintf (stderr, "Error: string too long in customer file\n"); exit (1); } if (ch == EOF || ch == '\n') { fprintf (stderr, "Error: unterminated string in cust file\n"); exit (1); } if (ch == '\\') ch = getc(fp); strBuf[len++] = (char)ch; ch = getc(fp); } strBuf[len] = '\0'; if ((strPnt = (char*)malloc(len+1)) == NULL) { fprintf (stderr, "Error: allocate memory: %s\n", strerror(errno)); exit (1); } strcpy (strPnt, strBuf); return strPnt; } //----------------------------------------------------------------------------- // LOAD THE CUSTOMER FILE void LoadCust ( char *fileName) // Customer file name { FILE *fp; // Customer file pointer int ch; // Input character int i; // General purpose index if ((fp = fopen (fileName, "r")) == NULL) { fprintf (stderr, "Error: open %s: %s\n", fileName, strerror(errno)); exit (1); } custCnt = 0; ch = getc(fp); while (ch == ' ') ch = getc(fp); while (ch != EOF) { if (custCnt >= MAXCUST) { fprintf (stderr, "Error: too many customers\n"); exit (1); } ungetc (ch, fp); for (i = 0; i < NUMCOL; i++) custArr[custCnt].custCol[i] = LoadStr (fp); custCnt ++ ; ch = getc(fp); while (ch == ' ') ch = getc(fp); if (ch != '\n' && ch != EOF) { fprintf (stderr, "Error: extra data on line\n"); exit (1); } if (ch == '\n') { ch = getc(fp); while (ch == ' ') ch = getc(fp); } } fclose (fp); } //----------------------------------------------------------------------------- // FIELD TABLE Field fieldArr[] = { {"custId", 0}, {"name", 1}, {"typeCode", 2}, {"region", 3}, {"address", 4} }; #define FIELDCNT (sizeof(fieldArr)/sizeof(fieldArr[0])) //----------------------------------------------------------------------------- // DECODE COLUMN LIST void DecodeColList ( char *colList) // Column list { StrTyp colNameBuf; // Column name buffer int colNameLen; // Column name length char *p; // Parsing pointer int i; // General purpose index colCnt = 0; p = colList; for (;;) { // Decode the column name from the column list while (*p == ' ') p++; colNameLen = 0; while ( (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') ) { if (colNameLen >= STRLEN) { fprintf (stderr, "Error: column name too long\n"); exit (1); } colNameBuf[colNameLen++] = *p; p ++ ; } colNameBuf[colNameLen] = '\0'; // Look up the column name in the field table for ( i = 0; i < FIELDCNT && strcmp(fieldArr[i].fieldName, colNameBuf) != 0; i ++ ); if (i >= FIELDCNT) { fprintf (stderr, "Error: unrecognised field name\n"); exit (1); } // Add the column to the column table colArr[colCnt].colField = fieldArr + i; colArr[colCnt].colMaxLen = 0; colCnt ++ ; // Parse the field separator while (*p == ' ') p ++ ; if (*p == '\0') break; if (*p != ',') { fprintf (stderr, "Error: missing comma\n"); exit (1); } p ++ ; } } //----------------------------------------------------------------------------- // COMPARE CUSTOMER ARRAY ROWS BY THE SORT KEY int CmpCust ( const void *voidCust1, // First customer row const void *voidCust2) // Second customer row { const Cust *cust1; // First typed customer row const Cust *cust2; // Second typed customer row int i; // General purpose index int c; // Result of comparison cust1 = (const Cust*) voidCust1; cust2 = (const Cust*) voidCust2; for (i = 0, c = 0; i < colCnt && c == 0; i++) { c = strcmp ( cust1->custCol[ colArr[i].colField->fieldCol ], cust2->custCol[ colArr[i].colField->fieldCol ] ); } return c; } //----------------------------------------------------------------------------- // CALCULATE MAXIMUM FIELD LENGTHS void CalcMaxLen () { int i, j; // General purpose indices Cust *cust; // Customer row pointer Col *col; // Column pointer int len; // Field length for (i = 0; i < custCnt; i++) { cust = custArr + i; for (j = 0; j < colCnt; j++) { col = colArr + j; len = strlen (cust->custCol[col->colField->fieldCol]); if (len > col->colMaxLen) col->colMaxLen = len; } } } //----------------------------------------------------------------------------- // PRINT THE REPORT void PrintRep () { int i, j; // General purpose indices Cust *cust; // Customer row pointer Col *col; // Column pointer for (i = 0; i < custCnt; i++) { cust = custArr + i; for (j = 0; j < colCnt; j++) { col = colArr + j; printf ("%-*s", col->colMaxLen, cust->custCol[col->colField->fieldCol]); if (j != colCnt-1) putchar (' '); } putchar ('\n'); } } //----------------------------------------------------------------------------- // MAIN LINE int main ( int argc, // Argument count char *argv[]) // Argument value pointers { // Validate the argument count. if (argc != 3) { fprintf (stderr, "Usage: selsort input-name field-list\n"); exit (1); } // Decode the column name list DecodeColList (argv[2]); // Load the customer file data into the customer array. LoadCust (argv[1]); // Sort the customer table qsort (custArr, custCnt, sizeof(Cust), CmpCust); // Calculate maximum field lengths CalcMaxLen (); // Print the report PrintRep (); return 0; }