/* * Program to read a COBOL FD copy book and produce a listing * of this FD with hex offsets and decimal column numbers for * each field and lengths. * Usage: reclay xxxx.FD >atextfile OR * reclay xxxx.FD | lp to print output * Lines per page may be specified as an optional 2nd parameter: * reclay xxxx.FD 82 |lp * By Warren Porter, CDP. Copyright 1998 * Others using this program or adapting this program for their own * use assume all risk. * As written the program does not support one digit level numbers or * code written in the first six columns. Usages have been added as * they have been encountered and certain usages are omitted. The program * supports group usage, but non numeric pictures under a COMP-x usage * may not be accurate. * If other usages are necessary, a search of how the field "found_comp" * is used currently should be a guide for adding other usages as the * need arises. */ #include #include #define COLS 4 /* #define bugging */ typedef struct { int level, line_no, beg, len, redef, mult; } line_rec; typedef struct { int line_no; char field[30]; } sort_type; /* Function Prototypes */ int read_line (FILE *fp, int rec_no, line_rec *arec); void write_line (FILE *fp, int rec_no, line_rec *arec); int back_level (FILE *fp, int rec_no, line_rec *arec, int level); int CompSortRec(sort_type *Rec1, sort_type *Rec2); void WriteSort(FILE *sortwork, sort_type *sort_rec,long int sort_count); void ReadSort(FILE *sortwork, sort_type *sort_rec,long int sort_count); void CombSort(FILE *sortwork,long int sort_count); void main(int argc, char **argv) { int line_cnt=0, i=0, doing_num=0, pic_width=0, build_length=0, curr_num=0, got_tab=0, sort_proc=0, found_end=0, found_redefine=0, found_sign=0, found_comp=0, group_usage=0, group_level=32767, //So can specify usage at group level eof=0, found_occurs=0, max_line=60, got_sep ; /* Next used for debugging only int argc=3; char *argv[3]= {"reclay","test.fd","20"}; */ long int sort_count=0, col_1, max_page, this_page, i_col, col_oth; char *ipbuff, *char_pnt, *buff, *occurs_lit=""; line_rec curr, other; sort_type sort_rec; FILE *fp, *wf, *sortwork; if (argc < 2) { fprintf(stderr,"The FD to be processed must be on command line\n"); fprintf(stderr,"Usage: reclay xxx.FD >mytextfile OR\n"); fprintf(stderr," reclay xxx.FD | lp to print output\n"); fprintf(stderr,"Lines / page can be changed with optional "); fprintf(stderr,"2nd parm: reclay xxx.FD 82, now %i\n", max_line); exit(1); } if ( (fp = fopen(argv[1],"r")) == NULL) { fprintf(stderr,"Unable to open \x1b[7m%s\x1b[0m as input\n", argv[1]); exit(1); } if (argc == 3) { max_line = atoi(argv[2]); if (max_line < 10 || max_line > 99) { fprintf(stderr,"Line count %i out of range or invalid\a\n", max_line); exit(1); } } wf=tmpfile(); sortwork = tmpfile(); curr.level = curr.line_no = curr.beg = curr.len = 0; curr.mult = 1; other.level = other.line_no = other.beg = other.len = 0; other.mult = 1; sort_proc = 0; buff=(char *)malloc(1024); ipbuff=(char *)malloc(250); while (1) { /* Main processing loop */ buff[0] = found_end = got_sep = 0; found_redefine = found_sign = found_comp = sort_proc = 0; do { /* Concatenates into buff until trailing period found */ if ( (fgets(ipbuff,99,fp)) == NULL) { eof=found_end=1; break; /* To prepare to list results */ } line_cnt++; if (ipbuff[6] == '*') /* Found a comment */ continue; if (ipbuff[6] == '$') /* Found XFD statement */ continue; for (i=0; ipbuff[i] ; i++) /* Find any tabs */ if (ipbuff[i] == '\t') { ipbuff[i] = ' '; got_tab = 1; } for (i=0; ipbuff[i] >= ' '; i++) /* Find EOL */ ipbuff[i] = toupper(ipbuff[i]); if (i < 12) /* Ignore short lines */ continue; ipbuff[i] = 0; /* Null terminate */ while (ipbuff[--i] == ' ') ipbuff[i] = 0; /* Delete trailing spaces */ if (ipbuff[i] == '.') { /* Last char was a period */ ipbuff[i] = 0; found_end = 1; } if (buff[0] == 0) /* First read on this line */ curr.line_no = line_cnt; strcat(buff,&ipbuff[buff[0]?10:7]); } while (found_end == 0); curr.len = pic_width = curr.redef = 0; curr.mult = 1; if (eof) break; /* Next statement to get past leading spaces */ for (char_pnt = buff;*char_pnt == ' ';char_pnt++); if (!*char_pnt) continue; if (isdigit(*char_pnt) && isdigit(*(char_pnt + 1))) { /* Checks for two digit level number */ curr.level = (*(char_pnt++) - '0') * 10; curr.level += *(char_pnt++) - '0'; char_pnt++; } else continue; /* Didn't start with a level number */ if (curr.level > 50) continue; /* Ignore 66, 88, 77 levels, etc */ if (curr.level == 1) /* Implicit redefinition at 01 level */ curr.beg = 0; while (*char_pnt) { /* Process all record clauses*/ while (*char_pnt == ' ') char_pnt++; /* Ignore spaces between operators */ if (!strncmp(char_pnt,"REDEFINES ",10)) { found_redefine = curr.redef = 1; char_pnt += 10; continue; } if (!strncmp(char_pnt,"COMP-3",6) || !strncmp(char_pnt,"COMP-6",6)) { found_comp = 3; char_pnt += 6; continue; } if (!strncmp(char_pnt,"COMP-2",6)) { found_comp = 2; char_pnt += 6; continue; } if (!strncmp(char_pnt,"SEPARATE",8)) { got_sep = 1; char_pnt += 8; continue; } if (!strncmp(char_pnt,"PIC ",4) || !strncmp(char_pnt,"PICTURE ",8)) { /* next only if PICTURE spelled out */ for (char_pnt+=3 ; *char_pnt != ' '; char_pnt++); for ( ; *char_pnt == ' '; char_pnt++); /*Get past space*/ doing_num = pic_width = build_length = 0; for ( ; *char_pnt > ' '; char_pnt++) { if (*char_pnt == 'S') { found_sign = 1; continue; } if (*char_pnt == 'V') char_pnt++; if (doing_num == 0) { if (*char_pnt == '(') doing_num = 1; else pic_width++; } else { if (*char_pnt == ')') { doing_num = 0; pic_width += build_length - 1; build_length = 0; } else build_length = build_length * 10 + *char_pnt - '0'; } } /* End picture clause processing */ continue; } /* End all processing for PIC ..... */ if (!strncmp(char_pnt,"OCCURS ",7)) { curr.mult = 0; char_pnt += 7; while (*char_pnt == ' ') /* To skip extra spaces */ char_pnt++; for ( ; isdigit(*char_pnt); char_pnt++) curr.mult = 10 * curr.mult + *char_pnt - '0'; found_occurs = 1; continue; } if (!strncmp(char_pnt,"FILLER ",7)) { sort_proc = 1; /* So won't try to sort it */ char_pnt += 7; continue; } if (sort_proc == 0) { sort_proc = 1; for (i=0; (*char_pnt > ' ') && (i < 29); char_pnt++,i++) sort_rec.field[i] = *char_pnt; sort_rec.field[i] = 0; /* Null terminate */ sort_rec.line_no = curr.line_no; WriteSort(sortwork,&sort_rec,sort_count++); } while (*char_pnt > ' ') /* To avoid keywords in data names */ char_pnt++; } /* Completed processing of all clauses */ /* New code to check for group usage */ if (pic_width == 0) { /*Just processed a group definition */ if (found_comp && curr.level <= group_level) { group_usage = found_comp; group_level = curr.level; } else if (curr.level <= group_level) { group_usage = 0; group_level=32767; } } else if (curr.level > group_level) found_comp = group_usage; /*Override usage at elementary lev */ switch (found_comp) { case 3: { curr.len = (pic_width + found_sign) / 2 + /* Divide by 2 */ (pic_width + found_sign) % 2; /* and round up if ness*/ break; } case 2: { curr.len = (pic_width < 5) ? 2 : 4; break; } default: curr.len = pic_width + got_sep * found_sign ; /* May still be zero */ } curr.len *= curr.mult; curr_num++; if (curr_num > 1) { /* Reads previous line */ read_line(wf, curr_num - 1, &other); if (curr.level > other.level) curr.beg = other.beg; else { int max_col=0; if (curr.level < other.level) max_col=back_level(wf, curr_num - 1, &other, curr.level); else max_col= other.beg + other.len; curr.beg = (found_redefine)?other.beg : max_col; } } if (curr.level == 1) /* Implicit redefinition, start all 01 */ curr.beg=0; /* levels at col 1 (offset 0) */ write_line(wf, curr_num, &curr); } /* End of file */ back_level(wf, curr_num, &other, 1); /* Update whole file */ rewind(fp); /* Prep to reread */ curr_num = 1; line_cnt = 0; curr.line_no = -1; if (found_occurs) occurs_lit=" Occurs"; while (1) { /* Final output loop, match FD against work file */ if ( (fgets(buff,99,fp)) == NULL) break; /* End of file */ if (!(line_cnt % (max_line - 2))) { if (line_cnt) printf("\f"); printf("%-83sHEX OFFSETS DEC COLUMNS\n",argv[1]); for (i = 0; i < 82; i++) fputc((int)' ',stdout); printf("Beg Length End Beg Length End%s\n",occurs_lit); } line_cnt++; while (line_cnt > curr.line_no) { if ( (read_line(wf, curr_num++,&curr)) == 0) curr.line_no = 32767; /* End of work file */ } for (i=0; buff[i] ; i++) /* Find any tabs */ if (buff[i] == '\t') buff[i] = ' '; for (i = 0; buff[i] >= ' '; i++); buff[(i > 72)? 72 : i] = 0; /* Null terminate col 73*/ if (line_cnt == curr.line_no) { printf("%3i %-72s %4X %4X %4X %4i %4i %4i", line_cnt, buff, curr.beg, curr.len / curr.mult, curr.beg + curr.len - 1, curr.beg + 1, curr.len / curr.mult, curr.beg + curr.len); if (curr.mult > 1) printf(" %3i\n",curr.mult); else printf("\n"); } else printf("%3i %s\n", line_cnt, buff); } fclose(fp); fclose(wf); fprintf(stderr,"Finished with %s\n",argv[1]); if (got_tab) fprintf(stderr,"Tab stops were found in file\n"); CombSort(sortwork,sort_count); line_cnt = 0; max_page = max_line * COLS; for (this_page = 0; this_page < sort_count; this_page+=max_page) { for (col_1 = this_page, line_cnt = 0; line_cnt < max_line ; col_1++) { char *printf_cmd="%4i %-27s "; if (!(line_cnt)) printf("\f"); else printf("\n"); if (col_1 >= sort_count) break; ReadSort(sortwork, &sort_rec, col_1); printf(printf_cmd, sort_rec.line_no,sort_rec.field); for (i_col = 1; i_col < COLS; i_col++) { col_oth = col_1 + i_col * max_line; if (col_oth >= sort_count) break; ReadSort(sortwork, &sort_rec, col_oth); printf(printf_cmd, sort_rec.line_no,sort_rec.field); } line_cnt++; } } fclose(sortwork); free(buff); free(ipbuff); } void write_line (FILE *fp, int rec_no, line_rec *arec) { fpos_t woffset; woffset = (rec_no - 1) * sizeof(line_rec); fsetpos(fp,&woffset); fwrite(arec,sizeof(line_rec),1,fp); } int read_line (FILE *fp, int rec_no, line_rec *arec) { long roffset; roffset = (rec_no - 1) * sizeof(line_rec); if (fseek(fp,roffset,SEEK_SET)) return 0; return fread(arec,sizeof(line_rec),1,fp); } int back_level (FILE *fp, int rec_no, line_rec *arec, int level) /* * Function fills in the length and ending columns of the previous * group item which was deferred until all elementary items under * that group had been processed. When run at end of file it * processes all unfinished groups including the initial 01. */ { int i, max_end = 0, curr_end = 0; for (i = rec_no ; i > 0; i--) { read_line(fp, i, arec); if (arec->level < level) break; if (arec->len) { curr_end = arec->beg + arec->len - 1; if (curr_end > max_end) max_end = curr_end; } else { arec->len = (max_end + 1 - arec->beg) * arec->mult ; curr_end = arec->beg + arec->len - 1; if (curr_end > max_end) max_end = curr_end; write_line(fp, i, arec); } if (arec->level == level && !arec->redef) break; } return max_end + 1; } int CompSortRec(sort_type *Rec1, sort_type *Rec2) { int result; result = strcmp(Rec1->field, Rec2->field); if (result == 0) result = Rec1->line_no - Rec2->line_no; return result; } void WriteSort(FILE *sortwork, sort_type *sort_rec,long int sort_count) { long int offset; offset = (long int) sort_count * sizeof(sort_type); fseek(sortwork,offset,SEEK_SET); fwrite(sort_rec,sizeof(sort_type),1,sortwork); } void ReadSort(FILE *sortwork, sort_type *sort_rec,long int sort_count) { long int offset; offset = (long int) sort_count * sizeof(sort_type); fseek(sortwork,offset,SEEK_SET); fread(sort_rec,sizeof(sort_type),1,sortwork); } void CombSort(FILE *sortwork,long int sort_count) { long int i, j; long int gap = sort_count, size = sort_count; int inOrder, result; sort_type strI, strJ; do { gap = (long int) (8 * gap) / 11; if (gap < 1) gap = 1; /* printf("gap = %5i\n",gap); */ inOrder = 1; for (i = 0, j = gap; i < ( size - gap); i++, j++) { ReadSort(sortwork, &strI, i); ReadSort(sortwork, &strJ, j); if ( (result = CompSortRec(&strI, &strJ)) > 0) { inOrder = 0; /* Need to swap records */ WriteSort(sortwork, &strI, j); WriteSort(sortwork, &strJ, i); } else if (result == 0) printf("Warning duplicate strings: %s %li %s %li\n", strI,i,strJ,j); } } while (!(gap == 1 && inOrder)); }