1 /* DASDISUP.C   (c) Copyright Roger Bowler, 1999-2009                */
2 /*              Hercules DASD Utilities: IEHIOSUP                    */
3 
4 /*-------------------------------------------------------------------*/
5 /* This program performs the IEHIOSUP function of OS/360.            */
6 /* It adjusts the TTRs in the XCTL tables in each of the             */
7 /* Open/Close/EOV modules in SYS1.SVCLIB.                            */
8 /*                                                                   */
9 /* The command format is:                                            */
10 /*      dasdisup ckdfile                                             */
11 /* where: ckdfile is the name of the CKD image file                  */
12 /*-------------------------------------------------------------------*/
13 
14 #include "hstdinc.h"
15 
16 #include "hercules.h"
17 #include "dasdblks.h"
18 
19 /*-------------------------------------------------------------------*/
20 /* Internal macro definitions                                        */
21 /*-------------------------------------------------------------------*/
22 #define HEX00           ((BYTE)'\x00')  /* EBCDIC low value          */
23 #define HEX40           ((BYTE)'\x40')  /* EBCDIC space              */
24 #define HEXFF           ((BYTE)'\xFF')  /* EBCDIC high value         */
25 #define OVERPUNCH_ZERO  ((BYTE)'\xC0')  /* EBCDIC 12-0 card punch    */
26 
27 /*-------------------------------------------------------------------*/
28 /* Definition of member information array entry                      */
29 /*-------------------------------------------------------------------*/
30 typedef struct _MEMINFO {
31         BYTE    memname[8];             /* Member name (EBCDIC)      */
32         BYTE    ttrtext[3];             /* TTR of first text record  */
33         BYTE    dwdsize;                /* Text size in doublewords  */
34         BYTE    alias;                  /* 1=This is an alias        */
35         BYTE    notable;                /* 1=Member has no XCTL table*/
36         BYTE    multitxt;               /* 1=Member has >1 text rcd  */
37     } MEMINFO;
38 
39 #define MAX_MEMBERS             1000    /* Size of member info array */
40 
41 /*-------------------------------------------------------------------*/
42 /* Static data areas                                                 */
43 /*-------------------------------------------------------------------*/
44 
45 /* List of first loads for Open/Close/EOV routines */
46 static char *firstload[] = {
47         "IGC0001I",                     /* Open (SVC19)              */
48         "IGC0002{",                     /* Close (SVC20)             */
49         "IGC0002A",                     /* Stow (SVC21)              */
50         "IGC0002B",                     /* OpenJ (SVC22)             */
51         "IGC0002C",                     /* TClose (SVC23)            */
52         "IGC0002I",                     /* Scratch (SVC29)           */
53         "IGC0003A",                     /* FEOV (SVC31)              */
54         "IGC0003B",                     /* Allocate (SVC32)          */
55         "IGC0005E",                     /* EOV (SVC55)               */
56         "IGC0008A",                     /* Setprt (SVC81)            */
57         "IGC0008F",                     /* Atlas (SVC86)             */
58         "IGC0009C",                     /* TSO (SVC93)               */
59         "IGC0009D",                     /* TSO (SVC94)               */
60         NULL };                         /* End of list               */
61 
62 /* List of second loads for Open/Close/EOV routines */
63 static char *secondload[] = {
64         "IFG019", "IGG019",             /* Open (SVC19)              */
65         "IFG020", "IGG020",             /* Close (SVC20)             */
66         "IGG021",                       /* Stow (SVC21)              */
67         "IFG023", "IGG023",             /* TClose (SVC23)            */
68         "IGG029",                       /* Scratch (SVC29)           */
69         "IGG032",                       /* Allocate (SVC32)          */
70         "IFG055", "IGG055",             /* EOV (SVC55)               */
71         "IGG081",                       /* Setprt (SVC81)            */
72         "IGG086",                       /* Atlas (SVC86)             */
73         "IGG093",                       /* TSO (SVC93)               */
74         "IGG094",                       /* TSO (SVC94)               */
75         NULL };                         /* End of list               */
76 
77 static  BYTE eighthexFF[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
78 
79 #if 0
80 /*-------------------------------------------------------------------*/
81 /* Subroutine to process a member                                    */
82 /* Input:                                                            */
83 /*      cif     -> CKD image file descriptor structure               */
84 /*      noext   Number of extents in dataset                         */
85 /*      extent  Dataset extent array                                 */
86 /*      memname Member name (ASCIIZ)                                 */
87 /*      ttr     Member TTR                                           */
88 /*                                                                   */
89 /* Return value is 0 if successful, or -1 if error                   */
90 /*-------------------------------------------------------------------*/
91 static int
92 process_member (CIFBLK *cif, int noext, DSXTENT extent[],
93                 BYTE *memname, BYTE *ttr)
94 {
95 int             rc;                     /* Return code               */
96 int             len;                    /* Record length             */
97 int             trk;                    /* Relative track number     */
98 int             cyl;                    /* Cylinder number           */
99 int             head;                   /* Head number               */
100 int             rec;                    /* Record number             */
101 BYTE           *buf;                    /* -> Data block             */
102 FILE           *ofp;                    /* Output file pointer       */
103 BYTE            ofname[256];            /* Output file name          */
104 int             offset;                 /* Offset of record in buffer*/
105 BYTE            card[81];               /* Logical record (ASCIIZ)   */
106 char            pathname[MAX_PATH];     /* ofname in host path format*/
107 
108     /* Build the output file name */
109     memset (ofname, 0, sizeof(ofname));
110     strncpy (ofname, memname, 8);
111     string_to_lower (ofname);
112     strcat (ofname, ".mac");
113 
114     /* Open the output file */
115     hostpath(pathname, ofname, sizeof(pathname));
116     ofp = fopen (pathname, "w");
117     if (ofp == NULL)
118     {
119         fprintf (stderr,
120                 "Cannot open %s: %s\n",
121                 ofname, strerror(errno));
122         return -1;
123     }
124 
125     /* Point to the start of the member */
126     trk = (ttr[0] << 8) | ttr[1];
127     rec = ttr[2];
128 
129     fprintf (stdout,
130             "Member %s TTR=%4.4X%2.2X\n",
131             memname, trk, rec);
132 
133     /* Read the member */
134     while (1)
135     {
136         /* Convert relative track to cylinder and head */
137         rc = convert_tt (trk, noext, extent, cif->heads, &cyl, &head);
138         if (rc < 0) return -1;
139 
140 //      fprintf (stdout,
141 //              "CCHHR=%4.4X%4.4X%2.2X\n",
142 //              cyl, head, rec);
143 
144         /* Read a data block */
145         rc = read_block (cif, cyl, head, rec, NULL, NULL, &buf, &len);
146         if (rc < 0) return -1;
147 
148         /* Move to next track if record not found */
149         if (rc > 0)
150         {
151             trk++;
152             rec = 1;
153             continue;
154         }
155 
156         /* Exit at end of member */
157         if (len == 0) break;
158 
159         /* Check length of data block */
160         if (len % 80 != 0)
161         {
162             fprintf (stdout,
163                     "Bad block length %d at cyl %d head %d rec %d\n",
164                     len, cyl, head, rec);
165             return -1;
166         }
167 
168         /* Process each record in the data block */
169         for (offset = 0; offset < len; offset += 80)
170         {
171             if (asciiflag)
172             {
173                 make_asciiz (card, sizeof(card), buf + offset, 72);
174                 fprintf (ofp, "%s\n", card);
175             }
176             else
177             {
178                 fwrite (buf+offset, 80, 1, ofp);
179             }
180 
181             if (ferror(ofp))
182             {
183                 fprintf (stdout,
184                         "Error writing %s: %s\n",
185                         ofname, strerror(errno));
186                 return -1;
187             }
188         } /* end for(offset) */
189 
190         /* Point to the next data block */
191         rec++;
192 
193     } /* end while */
194 
195     /* Close the output file and exit */
196     fclose (ofp);
197     return 0;
198 
199 } /* end function process_member */
200 #endif
201 
202 /*-------------------------------------------------------------------*/
203 /* Subroutine to process a directory block                           */
204 /* Input:                                                            */
205 /*      cif     -> CKD image file descriptor structure               */
206 /*      noext   Number of extents in dataset                         */
207 /*      extent  Dataset extent array                                 */
208 /*      dirblk  Pointer to directory block                           */
209 /* Input/output:                                                     */
210 /*      memtab  Member information array                             */
211 /*      nmem    Number of entries in member information array        */
212 /*                                                                   */
213 /* Directory information for each member is extracted from the       */
214 /* directory block and saved in the member information array.        */
215 /*                                                                   */
216 /* Return value is 0 if OK, +1 if end of directory, or -1 if error   */
217 /*-------------------------------------------------------------------*/
218 static int
process_dirblk(CIFBLK * cif,int noext,DSXTENT extent[],BYTE * dirblk,MEMINFO memtab[],int * nmem)219 process_dirblk (CIFBLK *cif, int noext, DSXTENT extent[],
220                 BYTE *dirblk, MEMINFO memtab[], int *nmem)
221 {
222 int             n;                      /* Member array subscript    */
223 int             i;                      /* Array subscript           */
224 int             totlen;                 /* Total module length       */
225 int             txtlen;                 /* Length of 1st text block  */
226 int             size;                   /* Size of directory entry   */
227 int             k;                      /* Userdata halfword count   */
228 BYTE           *dirptr;                 /* -> Next byte within block */
229 int             dirrem;                 /* Number of bytes remaining */
230 PDSDIR         *dirent;                 /* -> Directory entry        */
231 char            memnama[9];             /* Member name (ASCIIZ)      */
232 
233     UNREFERENCED(cif);
234     UNREFERENCED(noext);
235     UNREFERENCED(extent);
236 
237     /* Load number of bytes in directory block */
238     dirptr = dirblk;
239     dirrem = (dirptr[0] << 8) | dirptr[1];
240     if (dirrem < 2 || dirrem > 256)
241     {
242         fprintf (stdout, _("HHCDS003E Directory block byte count is invalid\n"));
243         return -1;
244     }
245 
246     /* Point to first directory entry */
247     dirptr += 2;
248     dirrem -= 2;
249 
250     /* Process each directory entry */
251     for (n = *nmem; dirrem > 0; )
252     {
253         /* Point to next directory entry */
254         dirent = (PDSDIR*)dirptr;
255 
256         /* Test for end of directory */
257         if (memcmp(dirent->pds2name, eighthexFF, 8) == 0)
258             return +1;
259 
260         /* Load the user data halfword count */
261         k = dirent->pds2indc & PDS2INDC_LUSR;
262 
263         /* Point to next directory entry */
264         size = 12 + k*2;
265         dirptr += size;
266         dirrem -= size;
267 
268         /* Extract the member name */
269         make_asciiz (memnama, sizeof(memnama), dirent->pds2name, 8);
270         if (dirent->pds2name[7] == OVERPUNCH_ZERO)
271             memnama[7] = '{';
272 
273         /* Find member in first load table */
274         for (i = 0; firstload[i] != NULL; i++)
275             if (strcmp(memnama, firstload[i]) == 0) break;
276 
277         /* If not in first table, find in second table */
278         if (firstload[i] == NULL)
279         {
280             for (i = 0; secondload[i] != NULL; i++)
281                 if (memcmp(memnama, secondload[i], 6) == 0) break;
282 
283             /* If not in second table then skip member */
284             if (secondload[i] == NULL)
285             {
286                 fprintf (stdout,
287                         _("HHCDS018I %s %s skipped\n"),
288                         memnama,
289                         ((dirent->pds2indc & PDS2INDC_ALIAS) ?
290                         "Alias" : "Member"));
291                 continue;
292             }
293 
294         } /* end if(firstload[i]==NULL) */
295 
296         /* Check that member information array is not full */
297         if (n >= MAX_MEMBERS)
298         {
299             fprintf (stdout,
300                     _("HHCDS004E Number of members exceeds MAX_MEMBERS\n"));
301             return -1;
302         }
303 
304         /* Check that user data contains at least 1 TTR */
305         if (((dirent->pds2indc & PDS2INDC_NTTR) >> PDS2INDC_NTTR_SHIFT)
306                 < 1)
307         {
308             fprintf (stdout,
309                     _("HHCDS005E Member %s TTR count is zero\n"), memnama);
310             return -1;
311         }
312 
313         /* Extract the total module length */
314         totlen = (dirent->pds2usrd[10] << 16)
315                 | (dirent->pds2usrd[11] << 8)
316                 | dirent->pds2usrd[12];
317 
318         /* Extract the length of the first text block */
319         txtlen = (dirent->pds2usrd[13] << 8)
320                 | dirent->pds2usrd[14];
321 
322         /* Save member information in the array */
323         memcpy (memtab[n].memname, dirent->pds2name, 8);
324         memcpy (memtab[n].ttrtext, dirent->pds2usrd + 0, 3);
325         memtab[n].dwdsize = totlen / 8;
326 
327         /* Flag the member if it is an alias */
328         memtab[n].alias = (dirent->pds2indc & PDS2INDC_ALIAS) ? 1 : 0;
329 
330         /* Flag member if 7th character of name is non-numeric */
331         memtab[n].notable = (memnama[6] < '0' || memnama[6] > '9') ?
332                                                                 1 : 0;
333 
334         /* Check that the member has a single text record */
335         if ((dirent->pds2usrd[8] & 0x01) == 0 || totlen != txtlen)
336         {
337             fprintf (stdout,
338                     _("HHCDS006W Member %s is not single text record\n"),
339                     memnama);
340             memtab[n].multitxt = 1;
341         } else {
342             memtab[n].multitxt = 0;
343         }
344 
345         /* Check that the total module length does not exceed X'7F8' */
346         if (totlen > 255*8)
347         {
348             fprintf (stdout,
349                     _("HHCDS007W Member %s size %4.4X "
350                     "exceeds X\'7F8\' bytes\n"),
351                     memnama, totlen);
352         }
353 
354         /* Check that the total module length is a multiple of 8 */
355         if (totlen & 0x7)
356         {
357             fprintf (stdout,
358                     _("HHCDS008W Member %s size %4.4X "
359                     "is not a multiple of 8\n"),
360                     memnama, totlen);
361         }
362 
363         /* Increment number of entries in table */
364         *nmem = ++n;
365 
366     } /* end for */
367 
368     return 0;
369 
370 } /* end function process_dirblk */
371 
372 /*-------------------------------------------------------------------*/
373 /* Subroutine to resolve TTRs embedded within a member               */
374 /* Input:                                                            */
375 /*      cif     -> CKD image file descriptor structure               */
376 /*      noext   Number of extents in dataset                         */
377 /*      extent  Dataset extent array                                 */
378 /*      memp    Array entry for member whose TTRs are to be resolved */
379 /*      memtab  Member information array                             */
380 /*      nmem    Number of entries in member information array        */
381 /*                                                                   */
382 /* Return value is 0 if OK, -1 if error                              */
383 /*-------------------------------------------------------------------*/
384 static int
resolve_xctltab(CIFBLK * cif,int noext,DSXTENT extent[],MEMINFO * memp,MEMINFO memtab[],int nmem)385 resolve_xctltab (CIFBLK *cif, int noext, DSXTENT extent[],
386                 MEMINFO *memp, MEMINFO memtab[], int nmem)
387 {
388 int             rc;                     /* Return code               */
389 int             i;                      /* Array subscript           */
390 int             len;                    /* Record length             */
391 int             cyl;                    /* Cylinder number           */
392 int             head;                   /* Head number               */
393 int             rec;                    /* Record number             */
394 int             trk;                    /* Relative track number     */
395 int             xctloff;                /* Offset to XCTL table      */
396 int             warn;                   /* 1=Flag TTRL difference    */
397 BYTE           *blkptr;                 /* -> Text record data       */
398 char            memnama[9];             /* Member name (ASCIIZ)      */
399 BYTE            svcnum[3];              /* SVC number (EBCDIC)       */
400 BYTE            prefix[3];              /* IGG/IFG prefix (EBCDIC)   */
401 BYTE            refname[8];             /* Referred name (EBCDIC)    */
402 char            refnama[9];             /* Referred name (ASCIIZ)    */
403 
404     /* Extract the member name */
405     make_asciiz (memnama, sizeof(memnama), memp->memname, 8);
406     if (memp->memname[7] == OVERPUNCH_ZERO)
407         memnama[7] = '{';
408 
409     /* Skip the member if it is an alias */
410     if (memp->alias)
411     {
412         fprintf (stdout, _("HHCDS009I Alias %s skipped\n"), memnama);
413         return 0;
414     }
415 
416     /* Skip the member if it has no XCTL table */
417     if (memp->notable)
418     {
419         fprintf (stdout, _("HHCDS010I Member %s skipped\n"), memnama);
420         return 0;
421     }
422 
423     /* Error if member is not a single text record */
424     if (memp->multitxt)
425     {
426         fprintf (stdout,
427                 _("HHCDS011E Member %s has multiple text records\n"),
428                 memnama);
429         return -1;
430     }
431 
432     /* Convert relative track to cylinder and head */
433     trk = (memp->ttrtext[0] << 8) | memp->ttrtext[1];
434     rec = memp->ttrtext[2];
435     rc = convert_tt (trk, noext, extent, cif->heads, &cyl, &head);
436     if (rc < 0)
437     {
438         fprintf (stdout,
439                 _("HHCDS012E Member %s has invalid TTR %4.4X%2.2X\n"),
440                 memnama, trk, rec);
441         return -1;
442     }
443 
444     fprintf (stdout,
445             _("HHCDS013I Processing member %s text record TTR=%4.4X%2.2X "
446             "CCHHR=%4.4X%4.4X%2.2X\n"),
447             memnama, trk, rec, cyl, head, rec);
448 
449     /* Read the text record */
450     rc = read_block (cif, cyl, head, rec,
451                     NULL, NULL, &blkptr, &len);
452     if (rc != 0)
453     {
454         fprintf (stdout,
455                 _("HHCDS014E Member %s error reading TTR %4.4X%2.2X\n"),
456                 memnama, trk, rec);
457         return -1;
458     }
459 
460     /* Check for incorrect length record */
461     if (len < 8 || len > 1024 || (len & 0x7))
462     {
463         fprintf (stdout,
464                 _("HHCDS015E Member %s TTR %4.4X%2.2X "
465                 "text record length %4.4X is not valid\n"),
466                 memnama, trk, rec, len);
467         return -1;
468     }
469 
470     /* Check that text record length matches directory entry */
471     if (len != memp->dwdsize * 8)
472     {
473         fprintf (stdout,
474                 _("HHCDS016E Member %s TTR %4.4X%2.2X "
475                 "text record length %4.4X does not match "
476                 "length %4.4X in directory\n"),
477                 memnama, trk, rec, len, memp->dwdsize * 8);
478         return -1;
479     }
480 
481     /* Extract the SVC number and the XCTL table offset
482        from the last 4 bytes of the text record */
483     memcpy (svcnum, blkptr + len - 4, sizeof(svcnum));
484     xctloff = blkptr[len-1] * 8;
485 
486     /* For the first load of SVC 19, 20, 23, and 55, and for
487        IFG modules, the table is in two parts.  The parts are
488        separated by a X'FFFF' delimiter.  The first part refers
489        to IFG modules, the second part refers to IGG modules */
490     if ((memcmp(memnama, "IGC", 3) == 0
491          && (memcmp(svcnum, "\xF0\xF1\xF9", 3) == 0
492             || memcmp(svcnum, "\xF0\xF2\xF0", 3) == 0
493             || memcmp(svcnum, "\xF0\xF2\xF3", 3) == 0
494             || memcmp(svcnum, "\xF0\xF5\xF5", 3) == 0))
495         || memcmp(memnama, "IFG", 3) == 0)
496         convert_to_ebcdic (prefix, sizeof(prefix), "IFG");
497     else
498         convert_to_ebcdic (prefix, sizeof(prefix), "IGG");
499 
500     /* Process each entry in the XCTL table */
501     while (1)
502     {
503         /* Exit at end of XCTL table */
504         if (blkptr[xctloff] == HEX00 && blkptr[xctloff+1] == HEX00)
505             break;
506 
507         /* Switch prefix at end of first part of table */
508         if (blkptr[xctloff] == HEXFF && blkptr[xctloff+1] == HEXFF)
509         {
510             xctloff += 2;
511             convert_to_ebcdic (prefix, sizeof(prefix), "IGG");
512             continue;
513         }
514 
515         /* Error if XCTL table overflows text record */
516         if (xctloff >= len - 10)
517         {
518             fprintf (stdout,
519                     _("HHCDS017E Member %s TTR %4.4X%2.2X "
520                     "XCTL table improperly terminated\n"),
521                     memnama, trk, rec);
522             return -1;
523         }
524 
525         /* Skip this entry if the suffix is blank */
526         if (blkptr[xctloff] == HEX40
527             && blkptr[xctloff+1] == HEX40)
528         {
529             xctloff += 6;
530             continue;
531         }
532 
533         /* Build the name of the member referred to */
534         memcpy (refname, prefix, 3);
535         memcpy (refname + 3, svcnum, 3);
536         memcpy (refname + 6, blkptr+xctloff, 2);
537         make_asciiz (refnama, sizeof(refnama), refname, 8);
538 
539         /* Display XCTL table entry */
540         fprintf (stdout,
541                 _("HHCDS019I In member %s: %s TTRL=%2.2X%2.2X%2.2X%2.2X"),
542                 memnama, refnama,
543                 blkptr[xctloff+2], blkptr[xctloff+3],
544                 blkptr[xctloff+4], blkptr[xctloff+5]);
545 
546         /* Find the referred member in the member array */
547         for (i = 0; i < nmem; i++)
548         {
549             if (memcmp(memtab[i].memname, refname, 8) == 0)
550                 break;
551         } /* end for(i) */
552 
553         /* Loop if member not found */
554         if (i == nmem)
555         {
556             fprintf (stdout, " ** Member %s not found **\n", refnama);
557             xctloff += 6;
558             continue;
559         }
560 
561         /* Loop if TTRL in the XCTL table matches actual TTRL */
562         if (memcmp(blkptr+xctloff+2, memtab[i].ttrtext, 3) == 0
563             && blkptr[xctloff+5] == memtab[i].dwdsize)
564         {
565             fprintf (stdout, "\n");
566             xctloff += 6;
567             continue;
568         }
569 
570         /* Flag entries whose L differs */
571         if (blkptr[xctloff+5] != memtab[i].dwdsize)
572             warn = 1;
573         else
574             warn = 0;
575 
576         /* Replace TTRL in the XCTL table by the actual TTRL */
577         memcpy (blkptr+xctloff+2, memtab[i].ttrtext, 3);
578         blkptr[xctloff+5] = memtab[i].dwdsize;
579 
580         fprintf (stdout,
581                 " replaced by TTRL=%2.2X%2.2X%2.2X%2.2X %s\n",
582                 blkptr[xctloff+2], blkptr[xctloff+3],
583                 blkptr[xctloff+4], blkptr[xctloff+5],
584                 (warn ? "****" : ""));
585 
586         /* Flag the track as modified to force rewrite */
587         cif->trkmodif = 1;
588 
589         /* Point to next entry in XCTL table */
590         xctloff += 6;
591 
592     } /* end while */
593 
594     return 0;
595 } /* end function resolve_xctltab */
596 
597 /*-------------------------------------------------------------------*/
598 /* DASDISUP main entry point                                         */
599 /*-------------------------------------------------------------------*/
main(int argc,char * argv[])600 int main (int argc, char *argv[])
601 {
602 int             rc;                     /* Return code               */
603 int             i;                      /* Array subscript           */
604 int             len;                    /* Record length             */
605 int             cyl;                    /* Cylinder number           */
606 int             head;                   /* Head number               */
607 int             rec;                    /* Record number             */
608 int             trk;                    /* Relative track number     */
609 char           *fname;                  /* -> CKD image file name    */
610 char           *sfname;                 /* -> CKD shadow file name   */
611 int             noext;                  /* Number of extents         */
612 DSXTENT         extent[16];             /* Extent descriptor array   */
613 BYTE           *blkptr;                 /* -> PDS directory block    */
614 CIFBLK         *cif;                    /* CKD image file descriptor */
615 MEMINFO        *memtab;                 /* -> Member info array      */
616 int             nmem = 0;               /* Number of array entries   */
617 
618     INITIALIZE_UTILITY("dasdisup");
619 
620     /* Display the program identification message */
621     display_version (stderr,
622                      "Hercules IEHIOSUP program ", FALSE);
623 
624     /* Check the number of arguments */
625     if (argc < 2 || argc > 3)
626     {
627         fprintf (stdout,
628                 "Usage: %s ckdfile [sf=shadow-file-name]\n",
629                 argv[0]);
630         return -1;
631     }
632 
633     /* The first argument is the name of the CKD image file */
634     fname = argv[1];
635 
636     /* The next argument, if there, is the name of the shadow file */
637     if (argc > 2) sfname = argv[2];
638     else sfname = NULL;
639 
640     /* Obtain storage for the member information array */
641     memtab = (MEMINFO*) malloc (sizeof(MEMINFO) * MAX_MEMBERS);
642     if (memtab == NULL)
643     {
644         fprintf (stdout,
645                 _("HHCDS001E Cannot obtain storage for member array: %s\n"),
646                 strerror(errno));
647         return -1;
648     }
649 
650     /* Open the CKD image file */
651     cif = open_ckd_image (fname, sfname, O_RDWR|O_BINARY, 0);
652     if (cif == NULL) return -1;
653 
654     /* Build the extent array for the SVCLIB dataset */
655     rc = build_extent_array (cif, "SYS1.SVCLIB", extent, &noext);
656     if (rc < 0) return -1;
657 
658     /* Point to the start of the directory */
659     trk = 0;
660     rec = 1;
661 
662     /* Read the directory */
663     while (1)
664     {
665         /* Convert relative track to cylinder and head */
666         rc = convert_tt (trk, noext, extent, cif->heads, &cyl, &head);
667         if (rc < 0) return -1;
668 
669         /* Read a directory block */
670         rc = read_block (cif, cyl, head, rec,
671                         NULL, NULL, &blkptr, &len);
672         if (rc < 0) return -1;
673 
674         /* Move to next track if block not found */
675         if (rc > 0)
676         {
677             trk++;
678             rec = 1;
679             continue;
680         }
681 
682         /* Exit at end of directory */
683         if (len == 0) break;
684 
685         /* Extract information for each member in directory block */
686         rc = process_dirblk (cif, noext, extent, blkptr,
687                             memtab, &nmem);
688         if (rc < 0) return -1;
689         if (rc > 0) break;
690 
691         /* Point to the next directory block */
692         rec++;
693 
694     } /* end while */
695 
696     fprintf (stdout,
697             _("HHCDS002I End of directory: %d members selected\n"),
698             nmem);
699 
700 #ifdef EXTERNALGUI
701     if (extgui) fprintf (stderr,"NMEM=%d\n",nmem);
702 #endif /*EXTERNALGUI*/
703 
704     /* Read each member and resolve the embedded TTRs */
705     for (i = 0; i < nmem; i++)
706     {
707 #ifdef EXTERNALGUI
708         if (extgui) fprintf (stderr,"MEM=%d\n",i);
709 #endif /*EXTERNALGUI*/
710         rc = resolve_xctltab (cif, noext, extent, memtab+i,
711                                 memtab, nmem);
712 
713     } /* end for(i) */
714 
715     /* Close the CKD image file and exit */
716     rc = close_ckd_image (cif);
717     free (memtab);
718     return rc;
719 
720 } /* end function main */
721