/******************************************** * LEGACY FILE READER -- CLFR -- C version * * Copyright (c) 2001, Salford Systems * All rights reserved worldwide * Questions? Bug reports? Please send * them to plc@salford-systems.com, and * be sure to attach the legacy dataset * that is causing the problem. * * Conditional compilation symbol WIN32 is * a proxy for Intel x86 platforms running * Windows. Other * * v1.0, August 2001 * * This program is offered as a convenience * and is unsupported. ********************************************/ #include /* fread(), fopen(), etc. */ #include /* malloc(), etc. */ #include /* memcpy() */ typedef unsigned char UCHAR; typedef unsigned int UINT; #ifdef WIN32 typedef unsigned short HEADERINT; /* ints in first two records are 2-byte */ typedef unsigned char PADINT; #else typedef unsigned int HEADERINT; /* ints in first two records are 4-byte */ typedef unsigned int PADINT; #endif /*********************************************** * Various constants * Use actual types where possible in C so as to * enable compiler to perform type-checking, * otherwise resort to #defines ***********************************************/ #ifdef WIN32 static const UINT N_PAD_BYTES = 1; /* 1 byte integer lead/trails each segment */ static const UINT N_SEG_BYTES = 128; /* records are segmented into chunks of 128 */ /* bytes or (for the final chunk) less */ #else static const UINT N_PAD_BYTES = 4; /* 4 byte integer lead/trails each record */ static const UINT N_SEG_BYTES = 0; /* records are not broken into chunks */ #endif static const UINT MAX_N_VARS = 8192; /* max n vars in a legacy SYSTAT set */ static const UINT COMMENT_LEN = 72; /* length of a comment record in the header */ static const UINT VARNAM_LEN = 12; /* max var name length in legacy SYSTAT set */ static const UINT SHORTS_LEN = 3 * sizeof(HEADERINT); static const UINT SINGLE_PRECISION = 1; static const UINT DOUBLE_PRECISION = 2; static const UINT RECTANGULAR = 1; /* only file type supported by this code */ static const UINT NUMERIC_VARIABLE = 1; static const UINT CHARACTER_VARIABLE = 2; static const float RMIS = -1.0e36F; static const double DMIS = -1.0e36; #define CHARDATA_LEN 12 /* length of character data point */ #ifdef WIN32 #define MAX_HDR_REC_LEN 74 /* comment length plus lead/trail 1-byte ints */ #else #define MAX_HDR_REC_LEN 80 /* comment length plus lead/trail 4-byte ints */ #endif /*************************************************************************** * MAIN() * Expects at least one command line parameter (name of input legacy SYSTAT * dataset) and optionally a second one (name of output text file) ***************************************************************************/ int main(int argc, char * argv[]) { FILE * pInfile = NULL, * pOutfile = stdout; UCHAR header[MAX_HDR_REC_LEN], * label, * letter, * record; UCHAR cdata[CHARDATA_LEN + 1]; HEADERINT * header_shorts; PADINT * pad_int; UINT version, release, mod; UINT nvar, nnvar, ncvar, filetype, precision, var, rec_len, real_size, pos, seg, n_segs, n_recs; UINT * var_type, * rec_pos; int i; /* must not be UINT for use in for loop */ double data; float fdata; if (argc < 2) return (EXIT_FAILURE); /* REQ: first command line argument is the name of the input legacy dataset */ pInfile = fopen(argv[1], "rb"); if (!pInfile) { fprintf(stderr, "Unable to open %s for binary reading.\n", argv[1]); return (EXIT_FAILURE); } /* OPT: second command line argument is the name of the output text file */ if (argc > 2 && strcmp(argv[1], argv[2]) != 0) { pOutfile = fopen(argv[2], "wt"); if (!pOutfile) { fprintf(stderr, "Unable to open %s for text writing.\n", argv[2]); return (EXIT_FAILURE); } } /* first byte in a WIN32 Fortran file is always 75 */ #ifdef WIN32 if (fread(header, sizeof(UCHAR), 1, pInfile) != 1) return (EXIT_FAILURE); if (*header != 75) return (EXIT_FAILURE); #endif /* * first record w/ three 2-byte integers, plus leading/trailing bytes */ if (fread(header, 3 * sizeof(HEADERINT) + 2 * N_PAD_BYTES, 1, pInfile) != 1) return (EXIT_FAILURE); if (N_PAD_BYTES > 0) { /* verify leading and trailing bytes */ pad_int = (PADINT *) header; if (*pad_int != SHORTS_LEN) return (EXIT_FAILURE); pad_int = (PADINT *) (header + SHORTS_LEN + N_PAD_BYTES); if (*pad_int != SHORTS_LEN) return (EXIT_FAILURE); } header_shorts = (HEADERINT *) (header + N_PAD_BYTES); /* skip leading byte */ version = *header_shorts; /* version of program that created this dataset */ release = *(header_shorts + 1); /* release ... */ mod = *(header_shorts + 2); /* modification ... */ /* * comment records, each of which is 72 bytes plus lead/trail bytes */ for (;;) { if (fread(header, COMMENT_LEN + 2 * N_PAD_BYTES, 1, pInfile) != 1) return (EXIT_FAILURE); if (N_PAD_BYTES > 0) { /* verify leading and trailing bytes */ pad_int = (PADINT *) header; if (*pad_int != COMMENT_LEN) return (EXIT_FAILURE); pad_int = (PADINT *) (header + COMMENT_LEN + N_PAD_BYTES); if (*pad_int != COMMENT_LEN) return (EXIT_FAILURE); } if (header[N_PAD_BYTES] == '$') break; /* final comment record is a row of dollar signs */ header[COMMENT_LEN + N_PAD_BYTES] = '\0'; printf((char *) header + N_PAD_BYTES); } /* * second record w/ three 2-byte integers, plus leading/trailing bytes */ if (fread(header, 3 * sizeof(HEADERINT) + 2 * N_PAD_BYTES, 1, pInfile) != 1) return (EXIT_FAILURE); if (N_PAD_BYTES) { /* verify leading and trailing bytes */ pad_int = (PADINT *) header; if (*pad_int != SHORTS_LEN) return (EXIT_FAILURE); pad_int = (PADINT *) (header + SHORTS_LEN + N_PAD_BYTES); if (*pad_int != SHORTS_LEN) return (EXIT_FAILURE); } header_shorts = (HEADERINT *) (header + N_PAD_BYTES); /* skip leading byte */ nvar = *header_shorts; /* n variables on the dataset */ filetype = *(header_shorts + 1); /* dataset type (rectangular, etc.) */ precision = *(header_shorts + 2); /* storage precision (single/double) */ if (precision != SINGLE_PRECISION && precision != DOUBLE_PRECISION) return (EXIT_FAILURE); if (nvar < 1 || nvar > MAX_N_VARS) return (EXIT_FAILURE); if (filetype != RECTANGULAR) return (EXIT_FAILURE); real_size = precision * sizeof(float); var_type = (UINT *) malloc(nvar * sizeof(UINT)); rec_pos = (UINT *) malloc(nvar * sizeof(UINT)); if (!var_type || !rec_pos) return (EXIT_FAILURE); /* * variable names, each on a separate record, VARNAM_LEN bytes long */ for (var = 0, nnvar = 0, ncvar = 0; var < nvar; var++) { if (fread(header, VARNAM_LEN + 2 * N_PAD_BYTES, 1, pInfile) != 1) return (EXIT_FAILURE); if (N_PAD_BYTES) { /* verify leading and trailing bytes */ pad_int = (PADINT *) header; if (*pad_int != VARNAM_LEN) return (EXIT_FAILURE); pad_int = (PADINT *) (header + VARNAM_LEN + N_PAD_BYTES); if (*pad_int != VARNAM_LEN) return (EXIT_FAILURE); } /* ensure label begins with a letter */ for (label = header + N_PAD_BYTES; label < header + VARNAM_LEN + N_PAD_BYTES; ++label) { if ((*label >= 'A' && *label <= 'Z') || (*label >= 'a' && *label <= 'z')) break; } if (label == header + VARNAM_LEN) return (EXIT_FAILURE); letter = label + 1; var_type[var] = NUMERIC_VARIABLE; while (letter < header + VARNAM_LEN + N_PAD_BYTES) { /* $ indicates character variable and must be final character in name */ if (*letter == '$') { var_type[var] = CHARACTER_VARIABLE; *++letter = '\0'; break; } else if ((*letter >= 'A' && *letter <= 'Z') || (*letter >= 'a' && *letter <= 'z') || (*letter >= '0' && *letter <= '9') || *letter == '_' || *letter == '(' || *letter == ')') { ++letter; } else { *letter = '\0'; break; } } if (letter == header + VARNAM_LEN + 1) *letter = '\0'; fprintf(pOutfile, (char *) label); if (var == nvar - 1) fprintf(pOutfile, "\n"); else fprintf(pOutfile, ","); if (var_type[var] == NUMERIC_VARIABLE) ++nnvar; else ++ncvar; } /* * length of a data record, including lead/trail padding */ rec_len = nnvar * real_size + ncvar * CHARDATA_LEN; if (N_SEG_BYTES > 0) { div_t div_result = div(rec_len, N_SEG_BYTES); n_segs = div_result.quot; if (div_result.rem > 0) ++n_segs; } else n_segs = 1; rec_len += n_segs * 2 * N_PAD_BYTES; /* * positions for each variable within the record. * numeric data written first, followed by character data. */ pos = N_PAD_BYTES; seg = 0; for (var = 0; var < nvar; var++) { if (var_type[var] == NUMERIC_VARIABLE) { rec_pos[var] = pos; pos += real_size; seg += real_size; if (N_SEG_BYTES > 0 && seg >= N_SEG_BYTES) { pos += 2 * N_PAD_BYTES; seg -= N_SEG_BYTES; } } } for (var = 0; var < nvar; var++) { if (var_type[var] == CHARACTER_VARIABLE) { rec_pos[var] = pos; pos += CHARDATA_LEN; seg += CHARDATA_LEN; if (N_SEG_BYTES > 0 && seg >= N_SEG_BYTES) { pos += 2 * N_PAD_BYTES; seg -= N_SEG_BYTES; } } } if (seg != 0) pos += N_PAD_BYTES; else pos -= N_PAD_BYTES; if (pos != rec_len) return (EXIT_FAILURE); /* consistency check */ record = (UCHAR *) malloc((rec_len + 1) * sizeof(UCHAR)); if (!record) return (EXIT_FAILURE); /* * read data and echo to console. */ n_recs = 0; cdata[CHARDATA_LEN] = '\0'; while(fread(record, sizeof(UCHAR), rec_len, pInfile) == rec_len) { pad_int = (PADINT *) record; if (N_SEG_BYTES > 0) { /* lead/trail bytes for all but final segment have */ /* value 129 just check the first one, however */ #ifdef WIN32 if (*pad_int != 129) return (EXIT_FAILURE); #endif } else { /* consistency check for platforms which write record */ /* without segmenting */ if (*pad_int + 2 * N_PAD_BYTES != rec_len) return (EXIT_FAILURE); } for (var = 0; var < nvar; var++) { if (var_type[var] == NUMERIC_VARIABLE) { if (precision == SINGLE_PRECISION) { fdata = * (float *) (record + rec_pos[var]); if (fdata == RMIS) data = DMIS; else data = fdata; } else data = * (double *) (record + rec_pos[var]); if (data == DMIS) fprintf(pOutfile, "."); else fprintf(pOutfile, "%g", data); } else { memcpy(cdata, record + rec_pos[var], CHARDATA_LEN * sizeof(UCHAR)); for (i = CHARDATA_LEN - 1; i >= 0; i--) { /* strip trailing spaces */ if (cdata[i] == ' ') cdata[i] = '\0'; else break; } fprintf(pOutfile, "%c%s%c", '"', cdata, '"'); } if (var == nvar - 1) fprintf(pOutfile, "\n"); else fprintf(pOutfile, ","); } ++n_recs; } fclose(pInfile); if (pOutfile != stdout) { fclose(pOutfile); printf( "Reading from %s\n" "Writing to %s\n" "%d records\n" "%d numeric and %d character variables.\n", argv[1], argv[2], n_recs, nnvar, ncvar); } free(var_type); free(rec_pos); free(record); return (EXIT_SUCCESS); }