mirror of
https://github.com/moshix/mvs.git
synced 2026-01-11 23:43:00 +00:00
2176 lines
66 KiB
C
Executable File
2176 lines
66 KiB
C
Executable File
// Things to do:
|
|
//
|
|
// 1) Add support for DSORG=PS, PDS/E, RECFM=V[B][S]
|
|
// 2) IB - blank out when ISPF stats present
|
|
// 4) Improve error handling/return, especially void functions & their calleds
|
|
// 5) Externalize translate table (or at least it's update) to accept input file
|
|
//
|
|
|
|
/*
|
|
This program reads the output of TSO TRANSMIT, which has been
|
|
(binary) file transferred from OS/390 to a PC, and produces
|
|
one file for each PDS member in the TRANSMIT dataset.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#define DIAG_GEN_FLOW
|
|
#define DIAG_GEN_MSG
|
|
#define DIAG_GEN_SNAP
|
|
|
|
// #undef DIAG_GEN_FLOW
|
|
// #undef DIAG_GEN_MSG
|
|
// #undef DIAG_GEN_SNAP
|
|
|
|
// handy macro definitions:
|
|
|
|
#define snapid " " __FILE__ " line " makestr(__LINE__) " "
|
|
#define makestr(x) whack(x)
|
|
#define whack(x) #x
|
|
|
|
#define snapset(x)
|
|
|
|
// Print string & decimal number in various formats:
|
|
|
|
#undef prtsd
|
|
#define prtsd(str, i) {char buf[256]; sprintf(buf, "%s: %d (signed), or %u (unsigned), or 0x%4.4X (hex) \n", str, i, i, i); diagmsg(buf);}
|
|
|
|
#undef diagflow
|
|
#ifndef DIAG_GEN_FLOW
|
|
#define diagflow(x)
|
|
#else
|
|
#define diagflow(x) printf(x "\n")
|
|
#endif
|
|
|
|
#undef diagmsg
|
|
#ifndef DIAG_GEN_MSG
|
|
#define diagmsg(x)
|
|
#else
|
|
#define diagmsg(x) printf(x "\n")
|
|
#endif
|
|
|
|
#undef snap
|
|
#ifndef DIAG_GEN_SNAP
|
|
#define snap(a,b,c)
|
|
#else
|
|
#define snap(a,b,c) snaplong(a,b,c)
|
|
#endif
|
|
|
|
int procdata();
|
|
int writemem();
|
|
int makerec(char *, int, int);
|
|
void openout();
|
|
int closeout(int);
|
|
char *assocmem(unsigned int);
|
|
void nextmem();
|
|
int procdir();
|
|
int cmpttr(const void *, const void *);
|
|
unsigned char*diralloc();
|
|
int parsedirblk(int);
|
|
int parsemem(int);
|
|
int printdirmem();
|
|
char *ispfdate(char *);
|
|
void juliangreg(int, int, int *, int *);
|
|
void unpackdate(unsigned char *, int *, int *);
|
|
int unpack(int);
|
|
void getblock(int, int);
|
|
void getseg(int);
|
|
int cmdline(int, char *[]);
|
|
int parseopt(char *, char *, char *);
|
|
void tranmap();
|
|
int aschex(char *);
|
|
void printhelp();
|
|
void cleanup();
|
|
int getvbin(char *, int);
|
|
void strxset(char *, int, char *, int);
|
|
void ebcdic2ascii(void *, int);
|
|
void snaplong(void *, int, char *);
|
|
int jmmsnap(void *, int, int, char *);
|
|
int halt(char *);
|
|
|
|
// ------- GLOBALS --------------------------------------------------
|
|
|
|
#define FIXED_ENTRY_LENGTH 16 // PDS member (8), ttr (3), slack - stored in "dir"
|
|
|
|
// Debugging
|
|
|
|
int ctlsnap = 0; // 1 = snap Control segments when read
|
|
int datasnap = 0; // 1 = snap Data segments when read
|
|
int snapdatablk = 0; // 1 = snap all data blocks
|
|
int getsegstats = 0; // 1 = show getseg stats ****
|
|
int prtoutrec = 0; // 1 = write records to stdout
|
|
int dbugshowclose = 0; // 1 = show FNout fclose
|
|
int dbugshowopen = 0; // 1 = show FNout fopen
|
|
int showsnapaddr = 1; // 1 = show snap addresses
|
|
int snapcr = 0; // 1 = begin snap with blank line
|
|
int snapshorthdr = 0; // 1 = snap header = title only
|
|
|
|
// Options
|
|
|
|
char opttran = '+'; // translate from EBCDIC to ASCII (makrec)
|
|
char optseq = '-'; // don't preserve sequence numbers
|
|
char opttrimblank = '+'; // trim trailing blanks
|
|
char optrdw = '-'; // no RDW
|
|
char optxmisum = '+'; // display TRANSMIT dataset summary
|
|
char optdsattr = '+'; // display dataset attributes
|
|
char optdir = '+'; // display PDS directory
|
|
char optwrite = '+'; // write output files
|
|
char opthalt = '+'; // halt for <press enter> msg in halt()
|
|
char optabout = '-'; // don't display copyright info
|
|
char opthelp = '-'; // don't display general help
|
|
char opthelptran = '-'; // don't display xlate help
|
|
char opthelprdw = '-'; // don't dispaly RDW help
|
|
char opthelpseq = '-'; // don't display SEQ help
|
|
char opthelpbug = '-'; // don't display debugging help
|
|
char optsyntax = '-'; // don't display syntax help
|
|
char optdirhex = '-'; // don't dump directory in hex
|
|
char optmap = '-'; // map translate table
|
|
|
|
// Debugging Options
|
|
|
|
char optlist = '-'; // list records on stdout
|
|
char optdumpdir = '-'; // dump IEBCOPY dir blocks
|
|
char optgetseg = '-'; // getseg segment info
|
|
char optsnapseg = '-'; // getseg snap
|
|
char optgetblock = '-'; // getblock block info
|
|
char optsnapblock = '-'; // getblock snap data block
|
|
char optsnaphalt = '-'; // snap <press enter> prompt
|
|
char optblock1 = '-'; // dump block 1
|
|
char optblock2 = '-'; // dump block 2
|
|
|
|
char *program = "recv390"; // program name
|
|
char *notprogram = " "; // blanks same length as program
|
|
int fatal = 0; // 1 = abandon execution
|
|
int unsupported = 0; // 1 = dataset not supported
|
|
int snapassumeascii = 0; // 1 = snap() won't xlate display to EBCDIC
|
|
|
|
FILE *fin = NULL;
|
|
FILE *fout = NULL;
|
|
char FNout[FILENAME_MAX] = "Dummy.txt";
|
|
char FNin[sizeof(FNout)] = "OS390.XMI";
|
|
char outext[40] = "TXT";
|
|
char dsn[80] = "";
|
|
char dsnmem[sizeof(dsn)];
|
|
char *pmem = NULL; // ptr to ASCII member name
|
|
char membername[8] = ""; // ASCII member name from assocmem
|
|
|
|
unsigned char line[255]; // EBCDIC data from TRANSMIT segment
|
|
unsigned char tranline[sizeof(line)]; // ASCII conversion of "line"
|
|
unsigned char *rec; // record buffer for makerec
|
|
unsigned char datestr[80]; // ispf date as char string from ispfdate()
|
|
|
|
int recpos = 0; // position in rec for makerec
|
|
unsigned char *block = 0; // instorage current IEBCOPY block
|
|
unsigned char *dir = 0; // instorage PDS directory storage
|
|
int dirpos = 0; // position in "dir" directory
|
|
int outdirpos = 0; // openout() dirpos for closeout()
|
|
int assocpos = 0; // assocmem() dirpos
|
|
int dirlen = 0; // length of "dir" used
|
|
int dirblkpos = 0; // position in 256 byte PDS dir block
|
|
int direntries = 0;
|
|
int ispfstats = 0; // ISPF stats available (procdir/parsemem)
|
|
|
|
int fileswritten = 0;
|
|
int databytesread = 0; // total bytes of data read
|
|
int databyteswritten = 0;
|
|
int datarecswritten = 0;
|
|
int outmembytes = 0;
|
|
int outrecbytes = 0; // # bytes output for record (writemem/makerec)
|
|
int membyteswritten = 0; // # bytes written for member
|
|
int memrecswritten = 0; // # records written for member
|
|
int warncounts = 0; // # members whose rec count didn't verify
|
|
|
|
int blocklen = 0; // IEBCOPY block length
|
|
int blocktrailer = 0; // set by getblock()
|
|
int seglen = 0; // length of TRANSMIT segment (0 - 253)
|
|
int segflag = 0; // segment flag
|
|
int segbytesread = 0;
|
|
int pos = 0; // position within block or line
|
|
int maxpos = 0; // position within block set by getblock()
|
|
int datablock = 0;
|
|
|
|
int dsorg = 0; // dataset dsorg
|
|
int recfm = 0; // dataset recfm
|
|
int blksize = 0; // dataset blksize
|
|
int lrecl = 0; // dataset lrecl
|
|
int vbfile = 0; // 1 = file is V[B] recfm
|
|
|
|
int msgfile = 0; // 1 = process msgfile
|
|
char lastmem[8]; // 8x'ff' End-of-Directory mem name
|
|
|
|
/* code page 37 below */
|
|
unsigned char trantab[256] = {
|
|
|
|
//-----------------------------------------------------------------------------------
|
|
// +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
|
|
//-----------------------------------------------------------------------------------
|
|
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 00
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 10
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 20
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 30
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5b,0x2e,0x3c,0x28,0x2b,0x7c,
|
|
// 40 -- -- -- -- -- -- --
|
|
// sp [ . < ( + |
|
|
//
|
|
0x26,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x21,0x24,0x2a,0x29,0x3b,0x5e,
|
|
// 50 -- -- -- -- -- -- --
|
|
// & ! $ * ) ; ^
|
|
//
|
|
0x2d,0x2f,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x7c,0x2c,0x25,0x5f,0x3e,0x3f,
|
|
// 60 -- -- -- -- -- -- -- --
|
|
// - / | , % _ > ?
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3a,0x23,0x40,0x27,0x3d,0x22,
|
|
// 70 -- -- -- -- -- --
|
|
// : # @ ' = "
|
|
//
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
|
|
//------------------------------------------------------------------------------------
|
|
|
|
0x20,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 80 -- -- -- -- -- -- -- -- --
|
|
// a b c d e f g h i
|
|
//
|
|
0x20,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,0x70,0x71,0x72,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// 90 -- -- -- -- -- -- -- -- --
|
|
// j k l m n o p q r
|
|
//
|
|
0x20,0x7e,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x20,0x20,0x20,0x5b,0x20,0x20,
|
|
// a0 -- -- -- -- -- -- -- -- -- --
|
|
// ~ s t u v w x y z [
|
|
//
|
|
0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5d,0x20,0x20,
|
|
// b0 --
|
|
// ]
|
|
//
|
|
0x7b,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// c0 -- -- -- -- -- -- -- -- -- --
|
|
// { A B C D E F G H I
|
|
//
|
|
0x7d,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// d0 -- -- -- -- -- -- -- -- -- --
|
|
// } J K L M N O P Q R
|
|
//
|
|
0x5c,0x01,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x20,0x20,0x20,0x20,0x20,0x20,
|
|
// e0 -- -- -- -- -- -- -- -- --
|
|
// \ S T U V W X Y Z
|
|
//
|
|
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x20,0x20,0x20,0x20,0x20,0x20};
|
|
// f0 -- -- -- -- -- -- -- -- -- --
|
|
// 0 1 2 3 4 5 6 7 8 9
|
|
//
|
|
|
|
//------------------------------------------------------------------------------------
|
|
// +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f
|
|
//------------------------------------------------------------------------------------
|
|
|
|
|
|
// ------- MAIN -----------------------------------------------------
|
|
|
|
int main(int argc, char *argv[]) {
|
|
|
|
int i, x, rc;
|
|
int segtype;
|
|
int blksize;
|
|
int templen;
|
|
int tempnum;
|
|
char orignode[9] = "?";
|
|
char origuser[9] = "?";
|
|
char targnode[9] = "?";
|
|
char targuser[9] = "?";
|
|
char origtime[20] = "?";
|
|
int numfiles = 0;
|
|
int rectype;
|
|
int filenum;
|
|
char utility[9] = "?";
|
|
|
|
rc = cmdline(argc, argv);
|
|
if ((opthelp == '+') || (rc) || (fatal)) {
|
|
cleanup();
|
|
return rc;
|
|
}
|
|
|
|
getseg(ctlsnap);
|
|
x = memcmp(tranline, "INMR01", 6);
|
|
if (x != 0) {
|
|
printf("Input file not a TSO TRANSMIT dataset\n");
|
|
cleanup();
|
|
return 4;
|
|
}
|
|
|
|
while (!feof(fin)) {
|
|
|
|
// Process the Data segments
|
|
|
|
while (! (segflag & 0x20) ) {
|
|
rc = procdata(); // process Data segments
|
|
if (rc) {
|
|
cleanup();
|
|
return fatal;
|
|
}
|
|
// snap(line, seglen, "Segment emitted from procdata");
|
|
}
|
|
|
|
// Process the Control segments
|
|
|
|
rectype = 0;
|
|
x = memcmp(tranline, "INMR0", 5);
|
|
if (x == 0) {
|
|
rectype = getvbin(&tranline[pos+5], 1); // what kind of INMR0?
|
|
// printf("INMR0 record %d\n", rectype);
|
|
pos = pos + 6;
|
|
datablock = 0; // reset data blk ctr
|
|
}
|
|
|
|
if (rectype == '2') {
|
|
if (optxmisum=='+') { // show info gathered from INMR01
|
|
optxmisum = '-'; // only show it once
|
|
printf("\n");
|
|
printf("TSO TRANSMIT dataset sent to user %s at node %s\n",
|
|
targuser, targnode);
|
|
printf("from user %s at node %s ", origuser, orignode);
|
|
printf("on %.2s/%.2s/%.4s at %.2s:%.2s:%.2s\n",
|
|
origtime+4, origtime+6, origtime+0,
|
|
origtime+8, origtime+10, origtime+12);
|
|
if (numfiles > 1) {
|
|
printf("contains %d files", numfiles);
|
|
printf(", the first of which is this message:\n");
|
|
} else {
|
|
printf("contains one file.\n");
|
|
}
|
|
}
|
|
filenum = getvbin(&line[pos], 4);
|
|
pos = pos + 4;
|
|
}
|
|
|
|
// Terminate when trailer found
|
|
|
|
if (rectype == '6') {
|
|
while (!(feof(fin))) {
|
|
fgetc(fin); // Read rest of input so stats look OK
|
|
segbytesread++;
|
|
}
|
|
segbytesread--; // went one too far
|
|
cleanup();
|
|
return 0;
|
|
}
|
|
|
|
// Deal with stuff in this Control segment
|
|
|
|
while (pos < seglen) {
|
|
segtype = getvbin(&line[pos], 2);
|
|
pos = pos + 2;
|
|
// if (rectype != '2') {
|
|
// printf("Current key %.2x\n", segtype);
|
|
// printf("Current position %d seglen %d\n", pos, seglen);
|
|
// snap(&line[pos], seglen, "current position");
|
|
// }
|
|
|
|
switch (segtype) {
|
|
case 0x1001: // target node name
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(targnode, sizeof(targnode), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x1002: // target userid
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(targuser, sizeof(targuser), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x1011: // origin node name
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(orignode, sizeof(orignode), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x1012: // origin userid
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(origuser, sizeof(origuser), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x1024: // origin time stamp
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(origtime, sizeof(origtime), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x102f: // number of files
|
|
templen = getvbin(&line[pos+2], 2);
|
|
numfiles = getvbin(&line[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x0042: // logical record length
|
|
templen = getvbin(&line[pos+2], 2);
|
|
lrecl = getvbin(&line[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x0030: // blocksize
|
|
templen = getvbin(&line[pos+2], 2);
|
|
blksize = getvbin(&line[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x1028: // utility name
|
|
templen = getvbin(&line[pos+2], 2);
|
|
strxset(utility, sizeof(utility), &tranline[pos+4], templen);
|
|
pos = pos + 4 + templen;
|
|
break;
|
|
case 0x0002: // dsname
|
|
tempnum = getvbin(&line[pos+0], 2);
|
|
pos = pos + 2;
|
|
memset( dsn, 0, sizeof(dsn));
|
|
for (i = 0; i < tempnum; i++) {
|
|
templen = getvbin(&line[pos], 2);
|
|
pos = pos + 2;
|
|
strncat(dsn, &tranline[pos], templen);
|
|
pos = pos + templen;
|
|
if (i + 1 != tempnum)
|
|
strncat(dsn, ".", 1);
|
|
}
|
|
break;
|
|
case 0x0028: // terminal allocation - flag only, count = 0
|
|
tempnum = getvbin(&line[pos+0], 2);
|
|
pos = pos +2;
|
|
msgfile = 1; // inline MESSAGE
|
|
break;
|
|
case 0x1023: // origin version number
|
|
case 0x1025: // destination time stamp
|
|
case 0x1026: // acknowledgement request
|
|
case 0x1027: // receive error code
|
|
case 0x1029: // user parameter string
|
|
case 0x102a: // transmitted record count
|
|
case 0x0001: // ddname
|
|
case 0x0003: // member name
|
|
case 0x000b: // secondary space quantity
|
|
case 0x000c: // dir blks
|
|
case 0x0022: // expiration date terminal
|
|
case 0x003c: // dsorg
|
|
case 0x0049: // recfm
|
|
case 0x1020: // last ref date
|
|
case 0x1021: // last chg date
|
|
case 0x1022: // create date
|
|
case 0x102c: // pri space qty
|
|
case 0x8012: // dataset type
|
|
tempnum = getvbin(&line[pos+0], 2);
|
|
pos = pos + 2;
|
|
if (tempnum != 1) {
|
|
for (i = 0; i < tempnum; i++) {
|
|
templen = getvbin(&line[pos], 2);
|
|
pos = pos + 2 + templen;
|
|
}
|
|
} else {
|
|
templen = getvbin(&line[pos], 2);
|
|
pos = pos + 2 + templen;
|
|
}
|
|
break;
|
|
default:
|
|
printf("unknown segment key %.2x\n", segtype);
|
|
printf("pos %d, seglen %d\n", pos, seglen);
|
|
snap(line, seglen, "line");
|
|
tempnum = getvbin(&line[pos+0], 2);
|
|
pos = pos + 2;
|
|
for (i = 0; i < tempnum; i++) {
|
|
templen = getvbin(&line[pos], 2);
|
|
pos = pos + 2 + templen; // just ignore it
|
|
}
|
|
break;
|
|
} // switch
|
|
} // pos < seglen
|
|
getseg(ctlsnap);
|
|
} // ! eof
|
|
cleanup();
|
|
return 0;
|
|
} /* main */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Process Data segments
|
|
|
|
// Upon entry, line & tranline contain the first segment of the
|
|
// first block from IEBCOPY, INMCOPY, AMSCIPHER or inline MESSAGE
|
|
// This code knows what to do for IEBCOPY & inline MESSAGE...
|
|
// Others: tough luck.
|
|
|
|
// Deciphering IEBCOPY data - Notes:
|
|
|
|
// TSO TRANSMIT doesn't give us the BDW & RDW from the IEBCOPY blocks.
|
|
// It does, however, set segflag x'80' & x'40' so that we can determine
|
|
// data block boundaries. MAIN resets blocknum for us whenever it sees an
|
|
// INMR0n record, so data block numbers are always relative to the most
|
|
// recently seen INMR0n segment. This is mildly useful when there's an
|
|
// imbedded MESSAGE from TSO TRANSMIT (at least during debugging).
|
|
|
|
// The TSO Customization doc for segflag says "record", but it means "block".
|
|
|
|
// Example 1: segflag x'c0' means segment contains entire block
|
|
// Example 2: segflag x'80' means segment is 1st in the block
|
|
// Example 3: segflag x'00' means segment is neither first nor last in block,
|
|
// ala VTAM middle-of-chain
|
|
// Example 4: segflag x'40' means segment is last in block
|
|
|
|
// Segflag x'20' means Control record (otherwise it's a Data record).
|
|
// Segflag x'10' says 'This is record number of next record', but I've never
|
|
// seen one.
|
|
|
|
// This code handles all the data for all members or one MESSAGE per call.
|
|
|
|
int procdata( ) {
|
|
char txtdsorg[5] = "";
|
|
char txtrecfm[4] = "";
|
|
const int eyepds = 0x00ca6d0f; // IEBCOPY PDS eyecatcher
|
|
/*
|
|
const int eyepdse = 0x01ca6d0f; // IEBCOPY PDSE eyecatcher (someday)
|
|
*/
|
|
int rc = 0;
|
|
|
|
if (msgfile) {
|
|
printf("\n");
|
|
while (! (segflag & 0x20) ) {
|
|
printf("%.*s\n", seglen, tranline);
|
|
getseg(0); // eat inline MESSAGE w/o snap
|
|
}
|
|
msgfile = 0; // only one MESSAGE per TRANSMIT
|
|
return 0;
|
|
}
|
|
|
|
getblock(1, 0); // get rest of 1st block, append line
|
|
|
|
// Contents of 1st IEBCOPY block (BDW & RDW are not present in TRANSMIT block):
|
|
//
|
|
// +00 (4) 0x00ca6d0f PDS eyecatcher
|
|
// +04 (2) dsorg
|
|
// +06 (2) blksize
|
|
// +08 (2) lrecl
|
|
// +10 (1) recfm and probably some other stuff
|
|
//
|
|
|
|
if (optblock1 == '+')
|
|
snap( block, blocklen, "Data Block One");
|
|
|
|
if (!(memcmp(block, &eyepds, 4))) {
|
|
printf("Fatal error; procdata could not verify IEBCOPY PDS eyecatcher\n");
|
|
snap(block, blocklen, "IEBCOPY header block");
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
|
|
dsorg = getvbin(&block[4], 2);
|
|
blksize = getvbin(&block[6], 2);
|
|
lrecl = getvbin(&block[8], 2); // lrecl from 1st IEBCOPY block
|
|
recfm = getvbin(&block[10], 1);
|
|
|
|
unsupported = 1; // assume unsupported dataset
|
|
if (dsorg & 0x8000) strcpy(txtdsorg, "ISAM");
|
|
if (dsorg & 0x4000) strcpy(txtdsorg, "PS");
|
|
if (dsorg & 0x2000) strcpy(txtdsorg, "DA");
|
|
if (dsorg & 0x0200) {
|
|
strcpy(txtdsorg, "PO");
|
|
unsupported = 0;
|
|
}
|
|
if (txtdsorg[0] == '\0')
|
|
strcpy(txtdsorg, "?");
|
|
if (dsorg & 0x01) strcat(txtdsorg, "U"); // unmovable
|
|
|
|
if (recfm & 0x80) strcpy(txtrecfm, "F");
|
|
if (recfm & 0x40) {
|
|
strcpy(txtrecfm, "V");
|
|
vbfile = 1;
|
|
}
|
|
if ((recfm & 0xc0) == 0xc0) {
|
|
strcpy(txtrecfm, "U");
|
|
optseq = '+'; // no RECFM=U sequence fields
|
|
}
|
|
if (recfm & 0x10) strcat(txtrecfm, "B");
|
|
if (recfm & 0x08) {
|
|
strcat(txtrecfm, "S");
|
|
if (!(memcmp(txtrecfm, "VBS", 3))) unsupported = 1;
|
|
}
|
|
if (recfm & 0x04) strcat(txtrecfm, "A");
|
|
if (recfm & 0x02) strcat(txtrecfm, "M");
|
|
|
|
if (optdsattr=='+') {
|
|
printf("\n");
|
|
printf("Dataset %s\n", dsn);
|
|
printf("Dsorg %s ", txtdsorg);
|
|
printf("recfm %s ", txtrecfm);
|
|
printf("blksize %d ", blksize);
|
|
printf("lrecl %d\n", lrecl);
|
|
}
|
|
|
|
if (unsupported) {
|
|
printf("\n");
|
|
printf("This dataset is not currently supported.\n\n");
|
|
rc = 1;
|
|
if (optdir=='+')
|
|
rc = halt("Press enter to display directory, enter 'x' to exit");
|
|
if (rc) {
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
}
|
|
|
|
getblock(0, 0); // get Data Block Two
|
|
if (optblock2=='+')
|
|
snap(block, blocklen, "Block 2");
|
|
|
|
// Contents of 2nd IEBCOPY block - secret (well ... _I_ don't know)
|
|
//
|
|
// Appears to have "dataset/member id" value that appears in blocks 4 - n
|
|
// So far, I haven't had to care. This block is mostly x'00' anyway.
|
|
|
|
getblock(0, 0); // get Data Block Three
|
|
|
|
// Contents of 3rd IEBCOPY block (assuming no BDW & RDW):
|
|
//
|
|
// +00 (8) 8x'00' directory eyecatcher
|
|
//
|
|
// FOLLOWING OCCURS FOR EACH PDS DIRECTORY BLOCK
|
|
//
|
|
// +08 (2) presumably PDS directory keylen = 8
|
|
// +10 (2) presumably pds directory blksize = 0x0100
|
|
// +12 (8) presumably high key value from CKD DASD key
|
|
//
|
|
// BEGINNING OF FIRST PDS DIRECTORY BLOCK
|
|
//
|
|
// +20 (2) length of bytes in PDS directory block used
|
|
// including space consumed by this halfword
|
|
// +22 (8) first member name begins here
|
|
//
|
|
// MORE PDS DIRECTORY BLOCKS MAY FOLLOW IN SAME IEBCOPY BLOCK
|
|
//
|
|
// Additional IEBCOPY PDS directory blocks may also follow
|
|
// when IEBCOPY decides they won't fit in current block
|
|
//
|
|
|
|
rc = procdir(); // parse & store PDS directory
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (unsupported) { // at least let user see dir
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
rc = writemem();
|
|
if (rc) {
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
return rc;
|
|
} /* procdata */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Write all the data for IEBCOPY unloaded PDS members
|
|
|
|
int writemem( ) {
|
|
int outlen;
|
|
int bytesleftinblock;
|
|
int bytesinblock = 0;
|
|
int bytes;
|
|
int rc = 0;
|
|
|
|
if (optwrite=='-') {
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
closeout(1); // print header line
|
|
dirpos = 0 - FIXED_ENTRY_LENGTH; // prepare for nextmem()
|
|
nextmem();
|
|
openout();
|
|
while (! (segflag & 0x20)) { // have Data segment
|
|
bytesinblock = getvbin(&block[9], 3); // # data bytes in IEBCOPY block
|
|
if (vbfile) {
|
|
pos += 4; // account for VB BDW
|
|
bytesinblock -= 4;
|
|
}
|
|
databytesread += bytesinblock;
|
|
if (bytesinblock < 1) { // member EOF block
|
|
rc = closeout(0);
|
|
if (rc) {
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
getblock(0, 0); // ignore empty block
|
|
continue; // check for INMR06
|
|
}
|
|
outmembytes = 0; // # bytes output for block
|
|
while (outmembytes < bytesinblock) {
|
|
makerec(NULL, 0, 0); // clear output record
|
|
outrecbytes = 0; // # bytes output for rec
|
|
if (vbfile) {
|
|
lrecl = getvbin(&block[pos], 2); // record length for V[B]
|
|
lrecl -= 4;
|
|
pos += 4;
|
|
if (pos >= maxpos)
|
|
break;
|
|
}
|
|
while ((outmembytes < bytesinblock) && (outrecbytes < lrecl)) {
|
|
if (prtoutrec) // add extra debug info
|
|
printf("%.4x %.4x %.4x ", blocklen, maxpos, pos);
|
|
if (pos >= maxpos) {
|
|
getblock(0,0);
|
|
} else {
|
|
outlen = lrecl - outrecbytes; // desired # rec output bytes
|
|
bytesleftinblock = maxpos - pos; // # bytes avail in block
|
|
if (outlen > bytesleftinblock)
|
|
outlen = bytesleftinblock; // all we can output for now
|
|
makerec(&block[pos], outlen, 0); // queue [partial] record
|
|
}
|
|
}
|
|
memrecswritten++;
|
|
bytes = makerec(rec,0,prtoutrec); // [list &] output rec
|
|
if (fatal)
|
|
return fatal;
|
|
membyteswritten += bytes; // accum bytes written for mem
|
|
}
|
|
if (blocktrailer) { // was there a blk trailer?
|
|
rc = closeout(0);
|
|
if (rc) {
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
}
|
|
getblock(0, 0); // get new data block
|
|
} // while Data
|
|
closeout(0);
|
|
return rc;
|
|
} /* writemem */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Make record, output "lrecl" bytes when complete
|
|
|
|
// Also updates outmembytes, outrecbytes, and pos based on "len" passed
|
|
// Trim trailing blanks
|
|
|
|
int makerec(char *ptxt, int len, int showrec ) {
|
|
// char showbuf[sizeof(line)];
|
|
int bytesout, i, validseq, x;
|
|
|
|
if (rec == NULL) {
|
|
rec = malloc(lrecl);
|
|
if (rec == NULL) {
|
|
printf("Fatal error; storage allocation failed for %d bytes\n", lrecl);
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
}
|
|
if (ptxt == NULL) {
|
|
memset(rec, 0, sizeof(rec)); // clear record buffer
|
|
recpos = 0;
|
|
return 0;
|
|
}
|
|
// ---------------------------------------------
|
|
if (len == 0) { // output record
|
|
if (opttran=='+')
|
|
ebcdic2ascii( rec, lrecl); // ASCII-ize buffer
|
|
|
|
bytesout = lrecl;
|
|
if ((optseq == '-') && (bytesout > 8)) { // remove sequence field
|
|
validseq = 1; // assume seq field valid
|
|
if (vbfile) {
|
|
for (i=0; i < 8; i++) {
|
|
x = rec[i];
|
|
if (!isdigit(x))
|
|
validseq = 0;
|
|
}
|
|
} else {
|
|
for (i=lrecl - 1; i >= lrecl - 8; i--) {
|
|
x = rec[i];
|
|
if (!isdigit(x))
|
|
validseq = 0;
|
|
}
|
|
}
|
|
if (validseq) {
|
|
if (vbfile) {
|
|
for (i = 0; i < lrecl - 8; i++) {
|
|
rec[i] = rec[i+8]; // shove VB line over 8 bytes
|
|
}
|
|
} else {
|
|
memset(&rec[lrecl-8], ' ', 8); // blank out sequence field
|
|
}
|
|
bytesout = bytesout - 8;
|
|
}
|
|
}
|
|
if (opttrimblank == '+') { // trim trailing blanks
|
|
for (i = bytesout - 1; i > 0; i--)
|
|
if (rec[i] == ' ') {
|
|
rec[i] = '\0';
|
|
bytesout--;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (bytesout < 0) {
|
|
bytesout = 1; // negative byte count bad
|
|
}
|
|
if (optrdw=='+') {
|
|
fprintf(fout, "%c%c%c%c",
|
|
((bytesout+4)/256), ((bytesout+4) - ((bytesout+4)/256)), 0, 0);
|
|
}
|
|
|
|
fprintf(fout, "%.*s\n", bytesout, rec); // output record
|
|
if (optlist=='+')
|
|
printf("%.*s\n", bytesout, rec); // list record
|
|
bytesout = bytesout + 2; // account for "\r"
|
|
databyteswritten += bytesout; // total data bytes written
|
|
datarecswritten++; // total data records written
|
|
recpos = 0;
|
|
return bytesout; // # bytes written
|
|
}
|
|
//----------------------------------------------
|
|
memcpy(&rec[recpos], ptxt, len); // add text to buffer
|
|
if (showrec) {
|
|
snap( rec, lrecl, "makerec fragmented rec");
|
|
}
|
|
recpos = recpos + len;
|
|
outmembytes = outmembytes + len;
|
|
outrecbytes = outrecbytes + len;
|
|
pos = pos + len;
|
|
return 0;
|
|
} /* makerec */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
// Close previous output file
|
|
// Build output file name, open it
|
|
|
|
void openout( ) {
|
|
unsigned int i;
|
|
int c;
|
|
char blank = ' ';
|
|
|
|
memset(dsnmem, 0, sizeof(dsnmem));
|
|
strcpy(dsnmem, dsn);
|
|
strcat(dsnmem, "(");
|
|
|
|
memset(FNout, 0, sizeof(FNout));
|
|
|
|
memcpy(FNout, membername, 8);
|
|
for (i = 7; i >= 0; i--) {
|
|
if (FNout[i] == blank)
|
|
FNout[i] = '\0';
|
|
else
|
|
break;
|
|
}
|
|
|
|
strcat(dsnmem, FNout);
|
|
strcat(dsnmem, ")");
|
|
strcat(FNout, ".");
|
|
strcat(FNout, outext);
|
|
for (i = 0; i < strlen(FNout); i++) {
|
|
c = tolower(FNout[i]);
|
|
FNout[i] = (char) c;
|
|
}
|
|
|
|
if (dbugshowopen)
|
|
printf("openout: opening %s\n", FNout);
|
|
fout = fopen(FNout, "w");
|
|
if (fout) {
|
|
outdirpos = assocpos; // save for closeout()
|
|
fileswritten++; // accumulate files written
|
|
} else {
|
|
outdirpos = 0xff; // bad open
|
|
printf("Error opening %s for output\n", FNout);
|
|
}
|
|
return;
|
|
} /* openout */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Close output file, list
|
|
|
|
int closeout(int prthdr) {
|
|
int len;
|
|
int dircurlines;
|
|
int rc = 0;
|
|
char work[FILENAME_MAX];
|
|
|
|
if (prthdr) {
|
|
len = 9 + strlen(outext);
|
|
printf("\n");
|
|
memset(work, ' ', sizeof(work));
|
|
memcpy(work, "output", 6);
|
|
work[len] = 0x00;
|
|
printf("%s bytes records\n", work);
|
|
memset(work, ' ', sizeof(work));
|
|
memcpy(work, "filename", 8);
|
|
work[len] = 0x00;
|
|
printf("%s written written\n", work);
|
|
memset(work, '-', len);
|
|
work[len] = 0x00;
|
|
printf("%s ---------- ----------\n", work);
|
|
// printf( "output bytes....records\n");
|
|
// printf( "filename written written\n");
|
|
// printf( "------------ ---------- ----------\n");
|
|
}
|
|
if (!fout)
|
|
return 0;
|
|
|
|
if (dbugshowclose)
|
|
printf("closeout: closing %s\n", FNout);
|
|
|
|
fclose(fout);
|
|
fout = NULL;
|
|
|
|
memset(work, ' ', sizeof(work));
|
|
memcpy(work, FNout, strlen(FNout));
|
|
work[9+strlen(outext)] = 0x00;
|
|
printf("%s %10d %10d", work, membyteswritten, memrecswritten);
|
|
|
|
if (outdirpos == 0xff)
|
|
printf(" open err\n");
|
|
else {
|
|
dircurlines = getvbin(&dir[outdirpos+14], 2); // # lines ISPF says in member
|
|
if (dircurlines == memrecswritten)
|
|
printf(" OK\n");
|
|
else
|
|
if (dircurlines == 0)
|
|
printf(" ? no ISPF statistics\n");
|
|
else {
|
|
printf(" error; ISPF statistics records: %d\n", dircurlines);
|
|
rc = halt(NULL);
|
|
warncounts++;
|
|
if (rc)
|
|
fatal = 1;
|
|
}
|
|
}
|
|
membyteswritten = 0;
|
|
memrecswritten = 0;
|
|
nextmem();
|
|
if (assocpos < dirlen)
|
|
openout();
|
|
return rc;
|
|
} /* closeout */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Associate member name with TTR
|
|
|
|
// IEBCOPY puts the record TTR in each data block header,
|
|
// so just need to find it in our list. If we didn't find it, it
|
|
// (likely) means we have another block for the same member.
|
|
|
|
char *assocmem(unsigned int memttr) {
|
|
char *p;
|
|
int scanpos;
|
|
unsigned int curttr;
|
|
|
|
assocpos = 0;
|
|
for (scanpos = 0; scanpos < dirlen; scanpos += FIXED_ENTRY_LENGTH) {
|
|
curttr = getvbin( &dir[scanpos+8], 3);
|
|
if (curttr == memttr) {
|
|
p = &dir[scanpos];
|
|
memcpy(membername, p, 8); // copy member name to return
|
|
ebcdic2ascii( membername, 8);
|
|
p[12] = (char)0xff; // show member used (debug)
|
|
assocpos = scanpos; // tell closeout()
|
|
return membername;
|
|
}
|
|
}
|
|
return 0;
|
|
} /* assocmem */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Next member
|
|
|
|
void nextmem() {
|
|
|
|
dirpos += FIXED_ENTRY_LENGTH;
|
|
memcpy(membername, &dir[dirpos], 8); // new member name
|
|
ebcdic2ascii(membername, 8); // convert to ASCII
|
|
assocpos = dirpos;
|
|
return;
|
|
} /* nextmem */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Parse PDS directory block'(s) entries in current IEBCOPY block
|
|
//* See procdata() for what we know about this IEBCOPY block
|
|
//*
|
|
//* This is first pass through directory. printdir actually creates "dir" list
|
|
//* using the entry count accumulated in direntries. We basically scan the
|
|
//* IEBCOPY block to get this count.
|
|
//
|
|
// IEBCOPY lumps all the directory blocks for a PDS
|
|
// into one block, with no apparent editing aside from slapping
|
|
// it's IEBCOPY block junk at the front of each 256 byte PDS directory block.
|
|
|
|
// Further testing reveals some TRANSMIT datasets have more than one IEBCOPY
|
|
// directory block containing PDS directory blocks. We cope with this added
|
|
// complication, and when we return the 1st PDS member's IEBCOPY block is in
|
|
// "block".
|
|
|
|
int procdir( ) {
|
|
int moredir = 1;
|
|
char work[8];
|
|
|
|
if (optdir=='+') {
|
|
printf("\n");
|
|
printf(" ----- LINES -----\n");
|
|
printf("MEMBER TTR IB VV.MM CREATED MODIFIED CUR INIT MOD USERID\n");
|
|
}
|
|
memset(work, 0, sizeof(work)); // IEBCOPY "dir blk" header
|
|
memset(lastmem, 0xff, sizeof(lastmem)); // end-of-dir mem name/highkey
|
|
while (moredir) {
|
|
pos = 0; // scan blocks, count
|
|
if (optdumpdir=='+')
|
|
snap(block, blocklen, "IEBCOPY directory block");
|
|
while (pos < blocklen) {
|
|
pos = pos + 20; // at 256 byte pds dir blk
|
|
if ((parsedirblk(0)) == 0) // count # members
|
|
break;
|
|
}
|
|
dir = diralloc(); // alloc new "dir"
|
|
pos = 0;
|
|
while (pos < blocklen) {
|
|
pos = pos + 20;
|
|
if ((parsedirblk(1)) == 0) // add member to "dir", list
|
|
break;
|
|
}
|
|
getblock(0, 0); // see if another IEBCOPY dir blk
|
|
if (memcmp(work, &block[0], 8)) // more dir blocks?
|
|
moredir = 0;
|
|
}
|
|
|
|
qsort(dir, dirlen / FIXED_ENTRY_LENGTH, // sort "dir" by TTR
|
|
FIXED_ENTRY_LENGTH,
|
|
cmpttr);
|
|
|
|
if (optdir=='+') {
|
|
printf("\n");
|
|
printf("There are %d member(s) in the PDS directory.\n",
|
|
dirlen / FIXED_ENTRY_LENGTH);
|
|
}
|
|
return 0;
|
|
} /* procdir */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Compare TTRs for qsort
|
|
|
|
int cmpttr(const void *entry1, const void *entry2) {
|
|
int result;
|
|
|
|
result = memcmp((char *)entry1+8, (char *)entry2+8, 3);
|
|
return result;
|
|
} /* cmpttr */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
unsigned char *diralloc( ) {
|
|
unsigned char *newdir;
|
|
int newdirlen;
|
|
|
|
newdirlen = direntries * FIXED_ENTRY_LENGTH;
|
|
newdir = malloc(newdirlen);
|
|
if (!newdir) {
|
|
printf("Fatal error allocating PDS directory storage\n");
|
|
fatal = 1;
|
|
return NULL;
|
|
}
|
|
memset(newdir, 0, newdirlen);
|
|
if (dir)
|
|
memcpy(newdir, dir, dirlen);
|
|
dir = newdir;
|
|
dirlen = newdirlen;
|
|
return newdir;
|
|
} /* allocdir */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Parse dir block ... block being the 256 byte PDS dir block
|
|
|
|
// Increments direntries during phase 0
|
|
|
|
int parsedirblk(int phase ) {
|
|
int usedbytes; // # bytes used in 256 byte block
|
|
int deadbytes; // # unused bytes at end of 256 byte blk
|
|
int entlen;
|
|
|
|
usedbytes = getvbin( &block[pos], 2); // how many bytes in block used
|
|
if (usedbytes > 254) {
|
|
printf("Fatal error; parsedirblk found invalid PDS directory block\n");
|
|
snap(&block[pos], 256, "invalid directory block");
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
deadbytes = 256 - usedbytes; // dead bytes @ end of dir blk
|
|
pos = pos + 2;
|
|
usedbytes = usedbytes - 2;
|
|
while (usedbytes > 0) {
|
|
if (phase == 1)
|
|
entlen = printdirmem(); // add to "dir", print
|
|
else
|
|
entlen = parsemem(phase); // just count member
|
|
if (entlen == 0)
|
|
return 0;
|
|
pos += entlen;
|
|
usedbytes -= entlen;
|
|
if (phase == 0)
|
|
direntries++;
|
|
}
|
|
pos = pos + deadbytes; // account for dead bytes
|
|
return 1; // probably more entries
|
|
} /* parsedirblk */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Parse member's PDS directory entry, return entry length
|
|
|
|
int parsemem(int phase ) {
|
|
int entlen;
|
|
const unsigned int ebcdicblanks =0x40404040;
|
|
int x;
|
|
|
|
x = memcmp( &block[pos], lastmem, 8); // x'ff..ff' member name?
|
|
if (x == 0)
|
|
return 0;
|
|
x = getvbin( &block[pos + 8 + 3], 1);
|
|
x = x & 0x1f; // isolate x'1f' bits
|
|
x = x * 2; // # bytes userdata in entry
|
|
entlen = x + 8 + 3 + 1; // user data length, member, ttr, indicator byte
|
|
|
|
if (entlen== 42) { // probably ISPF stats
|
|
if (!(memcmp(&block[pos+40],&ebcdicblanks,2))) {
|
|
ispfstats = 1; // close enough
|
|
}
|
|
}
|
|
return entlen;
|
|
} /* parsemem */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* print directory member entry, add it to "dir"
|
|
|
|
int printdirmem() {
|
|
int entlen;
|
|
char msg[101];
|
|
char work[sizeof(msg)];
|
|
int temp, i, scr;
|
|
int entispfstats;
|
|
|
|
entlen = parsemem(0); // determine how long entry is
|
|
if (entlen == 0) {
|
|
return 0;
|
|
}
|
|
|
|
memset( &dir[dirpos], 0, FIXED_ENTRY_LENGTH);
|
|
memcpy( &dir[dirpos], &block[pos], 11);
|
|
|
|
entispfstats = 1; // assume dir ent has ISPF stats
|
|
if (optdirhex == '+')
|
|
entispfstats = 0;
|
|
if (entlen != 42)
|
|
entispfstats = 0;
|
|
memset(work, 0x40, 3); // last 2 chars always EBCDIC blanks
|
|
if (memcmp(&block[pos+40],work, 2))
|
|
entispfstats = 0;
|
|
if (entispfstats)
|
|
memcpy(&dir[dirpos+14], &block[pos+26], 2); // save # lines in "dir" entry
|
|
|
|
if (optdir=='+') {
|
|
memset(work, 0, sizeof(work));
|
|
memcpy(work, &block[pos], 8); // MEMBER
|
|
ebcdic2ascii(work, 8); // convert to ascii
|
|
strcpy(msg, work);
|
|
temp = getvbin(&block[pos+8], 3); // TTR
|
|
sprintf(work, " %.6X", temp); // make display hex, use upper case letters
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+11], 1); // Indicator byte
|
|
sprintf(work, " %.2X", temp);
|
|
strcat(msg, work);
|
|
|
|
if (!entispfstats) { // not ISPF stats
|
|
temp = getvbin(&block[pos+11], 1);
|
|
strcat(msg, " ");
|
|
temp = temp & 0x1f; // mask out all but length bits
|
|
temp = temp * 2; // user data is counted in halfwords
|
|
for (i = 0; i < temp; i++) {
|
|
scr = getvbin(&block[pos+12+i], 1);
|
|
sprintf(work, "%.2X", scr); // list userdata
|
|
strcat(msg, work);
|
|
}
|
|
} else {
|
|
temp = getvbin(&block[pos+12], 1); // VERS
|
|
sprintf(work, " %.2d.", temp);
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+13], 1); // MOD
|
|
sprintf(work, "%.2d", temp);
|
|
strcat(msg, work);
|
|
|
|
strcat(msg, ispfdate(&block[pos])); // Creation & modification dates
|
|
|
|
temp = getvbin(&block[pos+24], 1); // modified HH time
|
|
sprintf(work, " %.2x", temp);
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+25], 1); // modified MM time
|
|
sprintf(work, ":%.2x", temp);
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+26], 2); // Current # lines
|
|
sprintf(work, " %5d", temp);
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+28], 2); // Initial # lines
|
|
sprintf(work, " %5d", temp);
|
|
strcat(msg, work);
|
|
temp = getvbin(&block[pos+30], 2); // Modified # lines
|
|
sprintf(work, " %5d ", temp);
|
|
strcat(msg, work);
|
|
memset(work, 0, sizeof(work));
|
|
memcpy(work, &block[pos+32], 8);
|
|
ebcdic2ascii(work, 8);
|
|
work[8] = 0x00;
|
|
strcat(msg, work);
|
|
}
|
|
printf("%s\n", msg);
|
|
}
|
|
dirpos += FIXED_ENTRY_LENGTH;
|
|
return entlen;
|
|
} /* printdirmem */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Convert ISPF date stored in user-data portion of PDS directory entry
|
|
//* into character string
|
|
|
|
// There are several date-related fields in an ISPF statistics directory entry
|
|
// +14 (2) currently unknown (checksum?)
|
|
// +16 (4) packed CCYYDDDF creation date
|
|
// +20 (4) packed CCYYDDDF modification date
|
|
// +24 (1) hex HH modified hours
|
|
// +25 (1) hex MM modified minutes
|
|
|
|
// We return a string with creation date and modification date only
|
|
// Our caller handles HH:MM, since it's so straight-forward
|
|
|
|
char *ispfdate(char *pdirent) {
|
|
int year, yearday; // year, Julian day of year (1 - 365)
|
|
int month, monthday; // month, day of month (1 - 31)
|
|
char work[12];
|
|
|
|
memset(datestr, 0, sizeof(datestr));
|
|
unpackdate(&block[pos+16], &year, &yearday);
|
|
juliangreg(year, yearday, &month, &monthday);
|
|
sprintf(datestr, " %.4d/%.2d/%.2d", year, month, monthday);
|
|
|
|
unpackdate(&block[pos+20], &year, &yearday);
|
|
juliangreg(year, yearday, &month, &monthday);
|
|
memset(work, 0, sizeof(work));
|
|
sprintf(work, " %.4d/%.2d/%.2d", year, month, monthday);
|
|
strcat(datestr, work);
|
|
return datestr;
|
|
} /* ispfdate */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Unpack ISPF date
|
|
|
|
// packed: x'ccyydddf' cc (century) relative 1900
|
|
|
|
void unpackdate(unsigned char *packed, int *xyear, int *xjulian) {
|
|
int century, year, day, temp, x, y, junk;
|
|
|
|
junk = getvbin(packed, 4); // debug aid
|
|
temp = packed[0]; century = (unpack(temp) * 100) + 1900;
|
|
|
|
temp = packed[1];
|
|
year = unpack(temp);
|
|
year += century;
|
|
|
|
temp = packed[2]; x = unpack(temp);
|
|
temp = packed[3]; y = unpack(temp);
|
|
day = (x * 10) + y;
|
|
|
|
*xyear = year;
|
|
*xjulian = day;
|
|
return;
|
|
} /* unpackdate */
|
|
|
|
//--------------------------------------------------------------------
|
|
int unpack(int packed) {
|
|
int result;
|
|
int hi, lo;
|
|
|
|
hi = packed / 16;
|
|
lo = packed - (hi * 16);
|
|
if (lo > 9) // x'.f' or x'.c'
|
|
result = hi;
|
|
else
|
|
result = (hi * 10) + lo;
|
|
return result;
|
|
}
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Date conversion routine ... month_day K&R 2nd Edition Chapter 5.7 pag 111
|
|
|
|
static char daytab[2][13] = {
|
|
{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
|
|
{0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} };
|
|
// J F M A M J J A S O N D
|
|
|
|
void juliangreg(int year, int yearday, int *pmonth, int *pday) {
|
|
int i, leap;
|
|
|
|
leap = ((year%4 == 0) && (year%100 != 0)) || (year%400 == 0) || (year%1000 == 0) ;
|
|
for (i = 1; yearday > daytab[leap][i]; i++)
|
|
yearday -= daytab[leap][i];
|
|
*pmonth = i;
|
|
*pday = yearday;
|
|
return;
|
|
} /* monthday */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Read whole IEBCOPY Data block into storage
|
|
|
|
// We set: block, blocklen, blocktrailer, pos, maxpos
|
|
// Note readseg resets pos
|
|
|
|
void getblock(int append, int snapblk) {
|
|
long zeros = 0x00;
|
|
int blockpos;
|
|
const int qsamblksize = 32 * 1024;
|
|
unsigned char *blockx;
|
|
|
|
blocktrailer = 0; // assume no block trailer present
|
|
datablock++;
|
|
if (block == 0) {
|
|
block = malloc(qsamblksize); // QSAM max blocksize
|
|
if (block == NULL) {
|
|
printf("Fatal error allocating buffer\n");
|
|
fatal = 1;
|
|
return;
|
|
}
|
|
}
|
|
memset( block, 0, qsamblksize);
|
|
if (append) {
|
|
if (segflag & 0x20) {
|
|
printf("Fatal error: getblock called with Control segflag %.2x\n", segflag);
|
|
fatal = 1;
|
|
return;
|
|
}
|
|
memcpy( block, line, seglen); // EBCDIC buffer w/o BDW & RDW space
|
|
} else {
|
|
seglen = 0;
|
|
segflag = 0x00;
|
|
}
|
|
blocklen = blockpos = seglen;
|
|
while (! (segflag & 0x40)) { // not last segment in block
|
|
getseg(0); // get data quietly
|
|
if (segflag & 0x20) // INMR06?
|
|
return; // yep
|
|
blockpos = blocklen + seglen;
|
|
if (blockpos > (qsamblksize)) {
|
|
printf("Fatal error; block exceeds buffer size!\n");
|
|
fatal = 1;
|
|
return;
|
|
}
|
|
memcpy( &block[blocklen], line, seglen);
|
|
blocklen = blockpos;
|
|
}
|
|
|
|
pos = 12; // 1st position in block for data
|
|
// (for VB, this is BDW)
|
|
|
|
// if ((memcmp(&block[blocklen-12], &zeros, 4) == 0) &&
|
|
if ((memcmp(&block[blocklen-12], &block[blocklen-12], 6) == 0) &&
|
|
(memcmp(&block[blocklen-3], &zeros, 3) == 0) ) { // block trailer?
|
|
blocktrailer = 1; // block trailer
|
|
maxpos = blocklen - 12;
|
|
} else {
|
|
maxpos = blocklen; // no block trailer
|
|
}
|
|
|
|
if (snapblk) {
|
|
printf("Block length %d\n", blocklen);
|
|
snap( block, blocklen, "getblock");
|
|
}
|
|
if ((optgetblock=='+') || (optsnapblock=='+')) {
|
|
printf("\n");
|
|
printf("Getblock: Data Block %.3d length %.6d (0x%.4x) ",
|
|
datablock, blocklen, blocklen);
|
|
if (blocktrailer)
|
|
printf("trailer\n");
|
|
else
|
|
printf("\n");
|
|
|
|
printf("Getblock: "
|
|
"%.2x%.2x%.2x%.2x "
|
|
"%.2x%.2x%.2x%.2x "
|
|
"%.2x%.2x%.2x%.2x ... ",
|
|
block[0], block[1], block[2], block[3],
|
|
block[4], block[5], block[6], block[7],
|
|
block[8], block[9], block[10], block[11]);
|
|
|
|
blockx = &block[blocklen-12];
|
|
printf(
|
|
"%.2x%.2x%.2x%.2x "
|
|
"%.2x%.2x%.2x%.2x "
|
|
"%.2x%.2x%.2x%.2x "
|
|
"\n",
|
|
blockx[0], blockx[1], blockx[2], blockx[3],
|
|
blockx[4], blockx[5], blockx[6], blockx[7],
|
|
blockx[8], blockx[9], blockx[10], blockx[11]);
|
|
}
|
|
if (optsnapblock=='+') {
|
|
snap(block, blocklen, "getblock: Block");
|
|
}
|
|
return;
|
|
} /* getblock */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Read in a new segment
|
|
|
|
// Seglen includes the length of it's own data
|
|
|
|
void getseg( int snapget) {
|
|
int temp;
|
|
int showstats = 0;
|
|
int i;
|
|
|
|
pos = 0;
|
|
memset( line, 0, sizeof(line));
|
|
seglen = fgetc(fin);
|
|
segbytesread += seglen; // accumulate segment bytes read
|
|
seglen = seglen - 2;
|
|
|
|
segflag = fgetc(fin);
|
|
|
|
if (snapget) showstats = 1;
|
|
if (getsegstats) showstats = 1;
|
|
if (optgetseg=='+') showstats = 1;
|
|
if (showstats) {
|
|
if (segflag & 0x20) {
|
|
printf("Control segment; seglen %.4d, segflag %.2x\n", seglen, segflag);
|
|
} else {
|
|
printf("Data segment; seglen %.4d, segflag %.2x\n", seglen, segflag);
|
|
}
|
|
}
|
|
for (i = 0; i < seglen; i++) {
|
|
temp = fgetc(fin);
|
|
line[i] = (char) temp;
|
|
}
|
|
if ((snapget == 1) || (optsnapseg=='+')) {
|
|
snap(line, seglen, "segment");
|
|
}
|
|
memcpy(tranline, line, sizeof(line));
|
|
ebcdic2ascii(tranline, seglen);
|
|
return;
|
|
} /* getseg */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Process command line: setup options, open input
|
|
|
|
int cmdline(int argc, char *argv[]) {
|
|
int argnum;
|
|
int lenarg;
|
|
int arghit;
|
|
int opthit;
|
|
char optchar;
|
|
char *arg;
|
|
|
|
opthit = 1; // assume we find + or -
|
|
for (argnum = 1; ((opthit) && (argnum < argc)); argnum++) {
|
|
arg = argv[argnum];
|
|
lenarg = strlen(argv[argnum]);
|
|
arghit = 0; // not 0 when we find option match
|
|
optchar = 0x00;
|
|
switch (argv[argnum][0]) {
|
|
case '+':
|
|
optchar = '+';
|
|
break;
|
|
case '-':
|
|
optchar = '-';
|
|
break;
|
|
default:
|
|
opthit = 0; // no + or -
|
|
argnum--; // we went one too far, back up
|
|
break;
|
|
}
|
|
if (optchar) {
|
|
arghit += parseopt(arg, "tran", &opttran);
|
|
arghit += parseopt(arg, "seq", &optseq);
|
|
arghit += parseopt(arg, "rdw", &optrdw);
|
|
arghit += parseopt(arg, "about", &optabout);
|
|
arghit += parseopt(arg, "help", &opthelp);
|
|
arghit += parseopt(arg, "syntax", &optsyntax);
|
|
arghit += parseopt(arg, "dirhex", &optdirhex);
|
|
arghit += parseopt(arg, "helptran", &opthelptran);
|
|
arghit += parseopt(arg, "helprdw", &opthelprdw);
|
|
arghit += parseopt(arg, "helpseq", &opthelpseq);
|
|
arghit += parseopt(arg, "helpbug", &opthelpbug);
|
|
arghit += parseopt(arg, "list", &optlist);
|
|
arghit += parseopt(arg, "dumpdir", &optdumpdir);
|
|
arghit += parseopt(arg, "getseg", &optgetseg);
|
|
arghit += parseopt(arg, "snapseg", &optsnapseg);
|
|
arghit += parseopt(arg, "getblock", &optgetblock);
|
|
arghit += parseopt(arg, "snapblock", &optsnapblock);
|
|
arghit += parseopt(arg, "snaphalt", &optsnaphalt);
|
|
arghit += parseopt(arg, "block1", &optblock1);
|
|
arghit += parseopt(arg, "block2", &optblock2);
|
|
arghit += parseopt(arg, "trim", &opttrimblank);
|
|
arghit += parseopt(arg, "dir", &optdir);
|
|
arghit += parseopt(arg, "xmisum", &optxmisum);
|
|
arghit += parseopt(arg, "dsattr", &optdsattr);
|
|
arghit += parseopt(arg, "halt", &opthalt);
|
|
arghit += parseopt(arg, "write", &optwrite);
|
|
arghit += parseopt(arg, "map", &optmap);
|
|
if (arghit == 0) {
|
|
printf("Option error: %s\n", argv[argnum]);
|
|
printhelp();
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((opthelp=='+')
|
|
|| (optsyntax=='+')
|
|
|| (optabout=='+')
|
|
|| (opthelptran=='+')
|
|
|| (opthelprdw=='+')
|
|
|| (opthelpseq=='+')
|
|
|| (opthelpbug=='+')) {
|
|
printhelp();
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
|
|
if (argc > argnum) { // argv[1] = fn[.ext]
|
|
memset(FNin, 0, sizeof(FNin));
|
|
strcpy(FNin, argv[argnum]);
|
|
argnum++;
|
|
fin = fopen(FNin, "rb");
|
|
if (!fin) {
|
|
strcat(FNin, ".XMI");
|
|
fin = fopen(FNin, "rb");
|
|
} else { }
|
|
} else {
|
|
fin = fopen(FNin, "rb");
|
|
}
|
|
|
|
if (argc > argnum) { // argv[2] = output ft
|
|
memset(outext, 0, sizeof(outext));
|
|
strcpy(outext, argv[argnum]);
|
|
argnum++;
|
|
}
|
|
|
|
if (fin == NULL) {
|
|
printf("Error opening input file %s\n", FNin);
|
|
printf("Execution will terminate following display of help.\n");
|
|
halt("Press enter to continue");
|
|
optsyntax = '+'; // turn on syntax display
|
|
printhelp();
|
|
fatal = 1;
|
|
return fatal;
|
|
}
|
|
|
|
tranmap(); // see if user wants trans updates
|
|
if (optmap=='+') {
|
|
snapassumeascii = 1;
|
|
snapshorthdr = 1;
|
|
snap(trantab, 256, "EBCDIC to ASCII translation");
|
|
}
|
|
if (fatal) {
|
|
printf("Execution terminated\n");
|
|
return fatal;
|
|
}
|
|
return 0;
|
|
} /* cmdline */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Parse option
|
|
|
|
int parseopt(char *parg, char *pvalue, char *flag) {
|
|
|
|
if ((strlen(parg) - 1) == strlen(pvalue)) {
|
|
if (!(memcmp(&parg[1], pvalue, strlen(pvalue)))) {
|
|
*flag = parg[0];
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
return 0;
|
|
} /* parseopt */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Update trantab from user specifications
|
|
|
|
void tranmap() {
|
|
FILE *fmap;
|
|
int ndx, value, x, y;
|
|
char buf[FILENAME_MAX + 8];
|
|
char condext[FILENAME_MAX];
|
|
char *p;
|
|
|
|
if ((fmap = fopen("RECV390.MAP", "r"))) {
|
|
while (!(feof(fmap))) {
|
|
memset(buf, 0, sizeof(buf));
|
|
p = fgets(buf, 3, fmap); // 1st 2 chars = hex index
|
|
if (p == NULL)
|
|
break;
|
|
ndx = aschex(buf);
|
|
if (ndx == 0x100)
|
|
break;
|
|
fgetc(fmap); // ignore space char
|
|
memset(buf, 0, sizeof(buf));
|
|
p = fgets(buf, 3, fmap); // next 2 chars = hex value
|
|
if (p == NULL)
|
|
break;
|
|
value = aschex(buf);
|
|
if (value == 0x100)
|
|
break;
|
|
memset(condext, 0, sizeof(condext));
|
|
x = fgetc(fmap); // space means extension follows
|
|
if (x == ' ') {
|
|
memset(buf, 0, sizeof(buf));
|
|
p = fgets(buf, sizeof(buf) - 1, fmap);
|
|
x = strlen(buf) - 1; // ignore \r after extension
|
|
y = memcmp(buf, outext, x);
|
|
if (y) // matches output extension?
|
|
continue; // no match; next record
|
|
strcpy(condext, outext);
|
|
}
|
|
trantab[ndx] = (char) value;
|
|
printf("EBCDIC %.2x = ASCII %.2x %s\n", ndx, value, condext);
|
|
}
|
|
fclose(fmap);
|
|
}
|
|
return;
|
|
} /* tranmap */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Convert ASCII "display hex" char string to hex
|
|
|
|
// Example: c'2b' -> x'2b'
|
|
|
|
int aschex(char *p) {
|
|
int len, value;
|
|
char x, y;
|
|
|
|
x = '0';
|
|
value = 0x100; // bad hex string value
|
|
len = strlen(p);
|
|
if ((len == 0) || (len > 2)) {
|
|
printf("Invalid hex value %s\n", p);
|
|
printf("Remainder of translation changes ignored\n");
|
|
fatal = 1;
|
|
return value;
|
|
}
|
|
y = p[0];
|
|
if (isxdigit(y)) {
|
|
if (len == 2) {
|
|
x = p[1];
|
|
if (isxdigit(x)) {
|
|
sscanf(p, "%2x", &value);
|
|
} else
|
|
printf("Invalid hex digit %c\n", x);
|
|
} else {
|
|
sscanf(p, "%1x", &value);
|
|
}
|
|
} else
|
|
printf("Invalid hex digit %c\n", y);
|
|
if (value == 0x100) {
|
|
fatal = 1;
|
|
printf("Remainder of translation changes ignored\n");
|
|
}
|
|
return value;
|
|
} /* aschex */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Print syntax help
|
|
|
|
void printhelp( ) {
|
|
if (opthelp=='+') printf("\n");
|
|
if ((optabout=='+') || (opthelp=='+')) {
|
|
printf("\n");
|
|
printf("Copyright 2000, 2001, Enhanced Software Services, Inc.\n");
|
|
printf("V1R1M4 - doc & license at http://ensose.com/recv390.html\n");
|
|
}
|
|
if (opthelp == '+') {
|
|
printf("\n"
|
|
"This program runs on Windows or OS/2 in console mode. "
|
|
"It reads the output of \n"
|
|
"the OS/390 TSO/E TRANSMIT command, "
|
|
"and produces one file per PDS member.\n"
|
|
"\n"
|
|
"Only PDSes are supported. "
|
|
"RECFM F, V, and U are supported; spanned\n"
|
|
"records (V[B]S) are not currently supported. When ISPF adds VBS PDS member \n"
|
|
"support, I'll seriously consider adding spanned record support.\n"
|
|
"\n"
|
|
"If an embedded MESSAGE is present, it will be displayed. This is sometimes\n"
|
|
"useful to keep track of the status of changes against the TRANSMITted data.\n"
|
|
"\n"
|
|
"If ISPF statistics are present in the PDS directory, they will be displayed\n"
|
|
"prior to creating the output files, and record written counts verified.\n"
|
|
"\n"
|
|
"Options are specified with either a + or - sign in front of the option name.\n"
|
|
"A + sign enables the option, a - sign disables the option; examples below.\n"
|
|
"\n"
|
|
"If any help option (below) is specified, no other action is taken after the\n"
|
|
"help information is displayed.\n"
|
|
"\n"
|
|
);
|
|
halt("Press enter to continue");
|
|
}
|
|
|
|
if ((optsyntax=='+') || (opthelp=='+')) {
|
|
printf("\n");
|
|
printf("Syntax: [options...] [[path]fn[.exti] [exto]]\n");
|
|
printf("\n");
|
|
printf(" path path name of input file (default=current directory)\n");
|
|
printf(" fn[.exti] input file name (default fn=OS390 ext=XMI)\n");
|
|
printf(" [exto] output file extension (default=TXT)\n");
|
|
printf(" output fn = PDS member name\n");
|
|
printf(" Help (default: not displayed\n");
|
|
printf("Default options on/yes(+) off/no(-) unless error occurs):\n");
|
|
printf("------------------------------------------- ------------------------------\n");
|
|
printf("+tran translate from EBCDIC to ASCII -about copyright & version\n");
|
|
printf("-seq remove sequence field -help general help\n");
|
|
printf("+trim remove trailing blanks -syntax syntax help\n");
|
|
printf("-rdw no OS/390 RDW -helpbug debug help\n");
|
|
printf("+dir display PDS directory -helptran translation help\n");
|
|
printf("+write write output file(s) -helprdw RDW help\n");
|
|
printf("+halt issue <press enter> prompt -helpseq sequence field help\n");
|
|
printf("+xmisum display TRANSMIT dataset summary\n");
|
|
printf("+dsattr display dataset attributes\n");
|
|
printf("-dirhex don't display directory entries'\n");
|
|
printf(" userdata in hex\n");
|
|
printf("-map don't show translation table\n");
|
|
printf("\n");
|
|
halt("Press enter to continue");
|
|
}
|
|
if (opthelp == '+') {
|
|
printf("\n");
|
|
printf("Syntax examples and their results\n");
|
|
printf("---------------------------------\n");
|
|
printf("%s +help General help, no other actions taken\n", program);
|
|
printf("%s +syntax Syntax help, no other actions taken\n", program);
|
|
printf("%s +about Copyright & version, no other actions taken\n", program);
|
|
printf("\n");
|
|
printf("%s ABC Input file ABC.XMI\n", program);
|
|
printf("%s Output files mem1.TXT, mem2.TXT ...\n", notprogram);
|
|
printf("%s Sequence field removed, trailing blanks removed\n", notprogram);
|
|
printf("%s ABC.XMI Exactly same as above example\n", program);
|
|
printf("%s ABC ASM Same input as above, output mem1.ASM, mem2.ASM ...\n", program);
|
|
printf("%s Sequence field removed, trailing blanks removed\n", notprogram);
|
|
printf("%s +seq ABC Preserve sequence field in each record\n", program);
|
|
printf("%s -trim ABC Sequence field removed, trailing blanks preserved\n", program);
|
|
printf("%s +seq -trim Sequence field preserved, trailing blanks preserved\n", program);
|
|
printf("%s Input file OS390.XMI\n", program);
|
|
printf("%s Sequence field removed, trailing blanks removed\n", notprogram);
|
|
printf("\n\n\n\n");
|
|
halt("Press enter to continue");
|
|
fatal = 1;
|
|
}
|
|
if (opthelptran=='+') {
|
|
printf("\n");
|
|
printf("Translation help:\n"
|
|
"\n"
|
|
"It is assumed the TRANSMITted dataset is encoded in the EBCDIC character set.\n"
|
|
"There is a default translation table used to map EBCDIC to ASCII, which may\n"
|
|
"be displayed with the +map option.\n"
|
|
"If the default translation is not suitable, the user may supply an update\n"
|
|
"specification in the RECV390.MAP file in the current directory.\n"
|
|
"When the RECV390.MAP file is present, the change specifications in it will\n"
|
|
"be applied before the +map option is processed, thus the user can determine\n"
|
|
"what values will be applied when the output file(s) are written.\n"
|
|
"The RECV390.MAP file must contain lines formatted exactly as below:\n"
|
|
"\n"
|
|
"First two characters: EBCDIC character (example: C1 = EBCDIC A)\n"
|
|
"Next character: space\n"
|
|
"Next two characters: ASCII value (example: 41 = ASCII A)\n"
|
|
"Next character: end-of-line or space\n"
|
|
"remaining characters: conditional change extension (example: asm)\n"
|
|
"\n"
|
|
"When extension is present on the change line, the specified extension\n"
|
|
"will be (case-sensitively) compared to the output extension (either\n"
|
|
"specified on the command line or defaulted); when equal, the change\n"
|
|
"will be applied to the table, otherwise the change line will be ignored.\n"
|
|
"\n"
|
|
);
|
|
halt("Press enter to continue");
|
|
}
|
|
if (opthelprdw=='+') {
|
|
printf("\n");
|
|
printf("RDW help:\n");
|
|
printf("\n"
|
|
"RDW stands for Record Descriptor Word. It is a four byte field which\n"
|
|
"prefixes each record in an OS/390 RECFM V[B] (variable length record) file.\n"
|
|
"RECV390 supports writing RDWs in front of any type of output record,\n"
|
|
"be it RECFM F (fixed), V (variable), or U (undefined).\n"
|
|
"\n"
|
|
"RECFM U PDSes are frequently used to store OS/390 load modules.\n"
|
|
"You may find it useful to use -tran +rdw for load modules, if you\n"
|
|
"wish to somehow manipulate them on your PC.\n"
|
|
"\n"
|
|
"The format of the RDW is:\n"
|
|
"\n"
|
|
" +0 length=2: 'length' of the following record, plus 4\n"
|
|
" +2 length=2: two bytes of hex zeroes\n"
|
|
" +4 length=n: record, of 'length' minus 4\n"
|
|
"\n"
|
|
"Example: x'0007',x'0000',c'ABC'\n"
|
|
"\n"
|
|
"Since the I/O model of a PC doesn't understand OS/390 'blocking', we don't\n"
|
|
"do anything about writing BDWs (block descriptor words).\n"
|
|
"\n"
|
|
);
|
|
halt("Press enter to continue");
|
|
fatal = 1;
|
|
}
|
|
if (opthelpseq=='+') {
|
|
printf("\n"
|
|
"Sequence field help:\n"
|
|
"\n"
|
|
"Sequence fields are assumed to begin in columns 73 - 80 (relative one)\n"
|
|
"for RECFM=F, columns 1 - 8 (relative one) for RECFM=V. RECFM=U files\n"
|
|
"are not expected to have sequence fields.\n"
|
|
"\n"
|
|
"For each record output, when the -seq option is in effect, the sequence\n"
|
|
"field is verified to be all numeric. Should any character in the sequence\n"
|
|
"area fail this test, the record is assumed to contain no sequence field,\n"
|
|
"and the area is preserved, hence ignoring the seq option.\n"
|
|
"\n\n\n\n\n\n\n\n\n\n\n\n"
|
|
);
|
|
halt("Press enter to continue");
|
|
}
|
|
if (opthelpbug=='+') {
|
|
printf("\n");
|
|
printf("Debugging options:\n");
|
|
printf("\n"
|
|
"These debugging options are mostly for my use; however, you are\n"
|
|
"more than welcome to play with them.\n"
|
|
"They are a good way to begin understanding both the TRANSMIT\n"
|
|
"wrapper data, as well as the IEBCOPY unload data formats.\n"
|
|
"If you don't understand the output, you'll know exactly how I felt\n"
|
|
"when I began developing RECV390.\n"
|
|
"\n"
|
|
"+list list output records on stdout\n"
|
|
"+block1 snap Data block one (IEBCOPY dataset attributes)\n"
|
|
"+block2 snap Data block two (IEBCOPY mystery block)\n"
|
|
"+dumpdir snap IEBCOPY directory block(s)\n"
|
|
"+getseg print getseg() summary information for each segment\n"
|
|
"+snapseg snap getseg() data (length and flag: use +getseg)\n"
|
|
"+getblock print getblock() summary information for each Data block\n"
|
|
"+snapblock snap IEBCOPY Data block contents\n"
|
|
"+snaphalt pause for <press enter> during snap of data\n"
|
|
" (snap data is presented 256 bytes at a time)\n"
|
|
"\n"
|
|
"P.S. If anyone figures out the contents of 'block1' and 'block2',\n"
|
|
"please let me know. Parts of them are still a mystery to me.\n"
|
|
"\n"
|
|
);
|
|
halt("Press enter to continue");
|
|
fatal = 1;
|
|
}
|
|
return;
|
|
} /* printhelp */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
//* Clean up prior to exiting
|
|
|
|
void cleanup( ) {
|
|
|
|
if (fin) fclose(fin);
|
|
if (fout) fclose(fout);
|
|
if (block) free(block);
|
|
if (dir) free(dir);
|
|
|
|
if (fatal == 0) {
|
|
printf("\n");
|
|
printf("Read %d bytes of %s\n", segbytesread, FNin);
|
|
printf("Read %d bytes of %s data\n", databytesread, dsn);
|
|
if ((fileswritten == 0) || (fileswritten > 1))
|
|
printf("Wrote %d files, %d bytes, %d records\n",
|
|
fileswritten, databyteswritten, datarecswritten);
|
|
else
|
|
printf("Wrote %d file, %d bytes, %d records\n",
|
|
fileswritten, databyteswritten, datarecswritten);
|
|
if ( (dirlen / FIXED_ENTRY_LENGTH) != fileswritten)
|
|
printf("Warning - # PDS members vs. # files written mismatch\n");
|
|
if (warncounts)
|
|
printf("Warning - %d PDS member(s) appear to have wrong number of records\n",
|
|
warncounts);
|
|
printf("Done\n");
|
|
}
|
|
return;
|
|
} /* cleanup */
|
|
|
|
//--------------------------------------------------------------------
|
|
// Various utility routines
|
|
//--------------------------------------------------------------------
|
|
|
|
// Get OS/390 binary field (Big Endian) of length "len"
|
|
|
|
int getvbin( char * ptr, int len) {
|
|
int binval, i;
|
|
unsigned char byte1;
|
|
|
|
binval = 0;
|
|
for (i = 0; i < len; i++) {
|
|
byte1 = * (unsigned char *) ptr;
|
|
binval = (binval * 256) + (int) byte1;
|
|
ptr++;
|
|
}
|
|
return binval;
|
|
} /* getvbin */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
// set string to nulls, copy lenin chars to out
|
|
|
|
void strxset( char * out, int lenout, char * in, int lenin) {
|
|
|
|
memset(out, 0, lenout);
|
|
memcpy(out, in, lenin);
|
|
return;
|
|
} /* strxset */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
// Convert EBCDIC to ASCII
|
|
|
|
void ebcdic2ascii( void *in, int len) {
|
|
char * ptr;
|
|
int i;
|
|
unsigned char oldchar;
|
|
unsigned char newchar;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
ptr = &((char *)in)[i];
|
|
oldchar = *ptr;
|
|
newchar = trantab[oldchar];
|
|
((unsigned char *)in)[i] = newchar;
|
|
}
|
|
return;
|
|
} /* ebcdic2ascii */
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//* Segmented snap so data doesn't roll off screen
|
|
|
|
void snaplong( void * p, int len, char * title) {
|
|
int snapseg = 256;
|
|
int snaplen;
|
|
int i, rc;
|
|
|
|
if (optsnaphalt=='+') {
|
|
for (i = 0; i < len; i = i + snapseg) {
|
|
if (len > snapseg)
|
|
printf("Segmented snap; total length %d, offset %d (0x%.2x)\n", len, i, i);
|
|
snaplen = len - i;
|
|
if (snaplen > snapseg)
|
|
snaplen = snapseg;
|
|
rc = jmmsnap(&((char *)p)[i], snaplen, i, title);
|
|
if (rc)
|
|
return;
|
|
}
|
|
} else
|
|
jmmsnap(p, len, 0, title);
|
|
|
|
return;
|
|
} /* snaplong */
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Snap - Version 1.1
|
|
// Copyright, 1993, 2000 James M. Morrison
|
|
//
|
|
// 22 March 2000 Intel-specific version - supports EBCDIC characters
|
|
//
|
|
// This code provides the caller with a 'display hexadecimal' dump
|
|
// of the supplied storage. The output looks like:
|
|
//
|
|
// <snap> 000290DC.29 (0x001D) title:
|
|
// 000290dc 0000 48656c6c 6f207468 65726520 616e6420 *Hello there and *
|
|
// 000290ec 0010 686f7720 61726520 796f753f 00 *how are you?.*
|
|
//
|
|
// Here's what the first line is about:
|
|
//
|
|
// <snap> Snap tag
|
|
//
|
|
// Looks pretty silly, until you have a (large) wrong length,
|
|
// then it makes scanning the snap output in your text editor
|
|
// quite a bit easier.
|
|
//
|
|
// 000290DC Address
|
|
//
|
|
// The address of the storage you have requested be snapped.
|
|
// You might not care what the address is, but if you blow
|
|
// up, it might prove handy to have.
|
|
//
|
|
// 29 Length of storage request in decimal
|
|
//
|
|
// (0x001D) Length of storage request in hexadecimal
|
|
//
|
|
// title: User supplied title
|
|
//
|
|
// Each line following consists of:
|
|
//
|
|
// 000290dc Beginning address of storage displayed on this line
|
|
//
|
|
// 0000 Offset from beginning of storage request, in hexadecimal
|
|
//
|
|
// 48656c6c Hexadecimal representation of storage
|
|
//
|
|
// Sixteen bytes per line, spaced every four bytes.
|
|
//
|
|
// *Hello there and * Character representation of storage
|
|
//
|
|
// Sixteen bytes per line, no extraneous spaces.
|
|
// The asterisks (*) delimit the characters, since
|
|
// the last line might possibly be shorter than the rest.
|
|
// Unprintable characters (ANSI C definition) are shown
|
|
// as periods (.) in case you want to send the output to
|
|
// a device (like a printer) which might take offense at
|
|
// raw hex data showing up in it's data stream.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
|
|
#define prt(x) printf(x) // use ANSI C printf
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Snap - display data area in hex and character format
|
|
//
|
|
// Arguments:
|
|
//
|
|
// pointer to storage area
|
|
//
|
|
// length of storage area
|
|
//
|
|
// offset to begin display at
|
|
//
|
|
// title (null terminated string)
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
int jmmsnap( void *ptr, int len, int offset, char *title)
|
|
{
|
|
int i, j, k, m, x, cd;
|
|
char workchar;
|
|
unsigned int workint;
|
|
char prtbuf[128];
|
|
char hdrbuf[128];
|
|
|
|
//--------------------------------------------------------------------------
|
|
// Display user-supplied title
|
|
//--------------------------------------------------------------------------
|
|
if (snapcr)
|
|
printf("\n");
|
|
if (snapshorthdr) {
|
|
prt(title);
|
|
prt("\n");
|
|
} else {
|
|
sprintf(hdrbuf, "<snap> %p.%d (0x%.4X) %s\n", ptr, len, len, title);
|
|
prt( hdrbuf );
|
|
}
|
|
snapshorthdr = 0;
|
|
prtbuf[0] = '\0';
|
|
|
|
for (i=0; i < len; i = i + 16) {
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Build hexadecimal display
|
|
//-----------------------------------------------------------------------
|
|
|
|
if (showsnapaddr)
|
|
x = sprintf(prtbuf, "%.8X %.4x ",
|
|
(int)(ptr + i), i + offset);
|
|
else
|
|
x = sprintf(prtbuf, "%.4x ", i + offset);
|
|
for (j = 0; j < 16; j = j + 4) {
|
|
for (k = 0; k < 4; k++) {
|
|
if (i+j+k < len) {
|
|
workchar = * ((char *)ptr+i+j+k);
|
|
workint = workchar;
|
|
// following line of code is Intel-specific
|
|
memset(((char *)&workint)+1, 0, sizeof(workint) - 1);
|
|
sprintf(&prtbuf[x], "%.2x", workint);
|
|
} else
|
|
memset(&prtbuf[x], ' ', 2);
|
|
x = x + 2;
|
|
}
|
|
prtbuf[x] = ' '; // space between 4 bytes of data
|
|
x = x + 1;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Build character display
|
|
//-----------------------------------------------------------------------
|
|
|
|
prtbuf[x] = '*';
|
|
x = x + 1;
|
|
for (m = 0,cd = 0; (m < 16) & (i+m < len); m++) {
|
|
workchar = * ((char *)ptr+i+m);
|
|
if (snapassumeascii == 0)
|
|
ebcdic2ascii(&workchar, 1);
|
|
if (isprint(workchar)) {
|
|
prtbuf[x+m+cd] = workchar;
|
|
} else {
|
|
workchar = * ((char *)ptr+i+m);
|
|
if (isprint(workchar))
|
|
prtbuf[x+m+cd] = workchar;
|
|
else
|
|
prtbuf[x+m+cd] = '.';
|
|
}
|
|
// prtbuf[x+m+cd] = isprint(*((char *)ptr+i+m)) ? *((char *)ptr+i+m) : '.';
|
|
if (prtbuf[x+m+cd] == '%') {
|
|
prtbuf[x+m+cd+1] = '%'; // double up % chars
|
|
cd++;
|
|
}
|
|
}
|
|
prtbuf[x+m+cd ] = '*';
|
|
prtbuf[x+m+cd+1] = '\0';
|
|
prt(prtbuf); // end of line
|
|
prt("\n");
|
|
|
|
}
|
|
snapassumeascii = 0; // reset for next call
|
|
if (optsnaphalt=='+') {
|
|
x = halt(NULL);
|
|
return x;
|
|
}
|
|
return 0;
|
|
} /* snap */
|
|
|
|
//--------------------------------------------------------------------
|
|
|
|
int halt(char *msg) {
|
|
char buf[4];
|
|
|
|
if (opthalt=='-')
|
|
return 0;
|
|
if (msg == NULL)
|
|
fprintf(stderr, "Press enter to continue, or type 'x' to exit\n");
|
|
else
|
|
fprintf(stderr, "%s\n", msg);
|
|
fgets(buf,sizeof(buf), stdin);
|
|
if (buf[0] == 'x')
|
|
return 1;
|
|
return 0; // continue
|
|
} /* halt */
|
|
|