/*
* od [-5abhrwx] [-f bn] [-l bn] file
*
*              od              File dumper
*
* This programme is a  replacement for DMP.  It does most things that are
* done by DMP, and does them in a more reasonable way; in particular, you
* can dump in more than one format at once, and you can get a nice header
* dump, with all the newest ODS-1 stuff in it. It'll dump anything.
*
* Its options are:
*              -a              Dump ASCII characters
*              -b              Dump octal bytes
*              -f #            Set first block, default is 0
*              -h              Dump the file header
*              -l #            Set last block, default is EOF
*              -r              Dump by record
*              -w              Dump octal words
*              -x              Dump hexadecimal bytes
*              -5              Dump radix 50 words.
*
* If no dump selection options are present, od defaults to -w.
*
* od only runs on the RSX library.
*
* Edited by Bob Denny. Bug fix (trapped), slight modification of
* "header" line, fix so 1 byte records will dump in "-w" mode,
* make VBN's long to support large files.
*
* Further edits by Bob Denny to add the full filespec to the
* block/record heading, add "-h" switch for dumping the file
* header, including multi-header files, with most of the (secret)
* attributes, etc.
*
* Do all block image dumps using QIO's rather than fget(), as the
* latter fails on "system" files such as INDEXF.SYS which have
* nothing at all in the H.UFAT area of the header. It runs faster
* this way too. Now frec() is used only on recordwise dumps.
*
*              ***************
*              **  N O T E  **
*              ***************
*
*  This program requires the RSX-11M/M+ executive
*  extensions library (CX.OLB) and the F11.H and QIOFUN.H
*  header files in LB:[1,1]. This stuff is in [5,7] of
*  the DECUS C kit.
*
* P.S.  I don't apologise for the lack of comments (!)
*/

/***** UNDEFINE THIS FOR SMALLER PROGRAM WITH NO HEADER DUMP *****/
#define hdump

#include <stdio.h>
#include <qiofun.h>                    /* I/O function codes */
#include <qioret.h>                    /* I/O return codes */
#include <f11.h>                       /* File header structure definition */

#define IS_SUC 1

#define HUGE    04000000l              /* Max blox on ODS-1 */
#define BSIZ    512                    /* Block size */

int     aflag = 0;                     /* Initialize these */
int     bflag = 0;
int     fflag = 0;
int     hflag = 0;
int     rflag = 0;
int     wflag = 0;
int     xflag = 0;

char    *fn;                           /* Command line filename */
FILE    *fp;                           /* IOV pointer */
char    fspec[40];                     /* Full filespec */
int     fl;                            /* LUN of file */
int     efl;                           /* LUN used for extension file */
int     efn;                           /* Extension file number */

long    fbn     = 0l;                  /* Start VBN/Record */
long    lbn     = HUGE;                /* Stop VBN/Record */

int     max_size;
char    buf[BSIZ];                     /* Gets header, blox, records */

char prot[20];
char prch[] = "RWED";

extern long ftell();

/*
 * Files-11 QIO stuff for reading file header and blocks
 */
int ratctl[3] = {0366, buf, 0};        /* Read att's control list */
int ratpar[6] = {0, ratctl, 0, 0, 0, 0};  /* IO.RAT parameters */

int blkmode = 0;                       /* 1 = block image dump w/QIO's */

struct pb  {                           /* Makes handling long VBN easier */
         char *bf;
         int size;
         int xxxx;
         long vbn;
         int yyyy;
         } rvbpar = {buf, BSIZ, 0, 0l, 0 };

int iosb[2];

main(argc, argv)
char *argv[];
   {
   /*register*/ int i;
   /*register*/ struct header *hdr;        /* File header pointer */
   /*register*/ char *p;
   int k;
   char *q;
   char c;
   long j;
   char *bp;
   struct ufat *ufp;

   fn = NULL;
   hdr = buf;                          /* buf used for header too */
   for(i=1; i<argc; ++i)
      {
      p = argv[i];
      if(*p == '-')
         {
         ++p;
         while(c = *p++)
            switch(c)
               {

            case '5':
               ++fflag;
               break;

            case 'a':
            case 'A':
               ++aflag;
               break;

            case 'b':
            case 'B':
               ++bflag;
               break;

            case 'h':
            case 'H':
               ++hflag;
               break;

            case 'r':
            case 'R':
               ++rflag;
               break;

            case 'w':
            case 'W':
               ++wflag;
               break;

            case 'x':
            case 'X':
               ++xflag;
               break;

            case 'f':
            case 'F':
               if(++i >= argc)
                  usage();
               fbn = atol(argv[i]);
               break;

            case 'l':
            case 'L':
               if(++i >= argc)
                  usage();
               lbn = atol(argv[i]);
               break;

            default:
               usage();
               }
         }
       else if(fn != NULL)
         usage();
      else
         fn = p;
      }
   if(fn == NULL)
      usage();
   if(lbn < fbn)
      usage();
   if(!aflag && !bflag && !fflag && !hflag && !wflag && !xflag)
      ++wflag;

   /*
   * The following is an RSX-specific hack to control the format
   * in which the file is dumped. In particular, if the file is record
   * oriented, but the user did not set the "-r" switch, the attributes
   * in the FDB are fudged to make the FCS routines think it is an R.FIX
   * file with R.SIZE = 512. and no carriage control attributes. This
   * makes it default to dump a record file in block format, so you
   * can see all the RCW's etc.
   *
   * First, open the file in "u" mode to enable "user" record buffering.
   * Get the full filespec and LUN.
   */
   if((fp=fopen(fn, "ru")) == NULL)
      cant(fn);
   iovtoa(fp, fspec);                  /* Get full filespec */
   fl = flun(fp);                      /* Get LUN for later QIO's */

   /*
   * Now, if it's not a "record oriented device" (CR, MT etc.), and
   * the "-r" switch wasn't set, dump in block image.
   */
   max_size = fp->io_rbsz;
   bp = "** Record";
   if(!rflag && !frec(fp))
      {
      bp = "** Block";                 /* Banner */
      blkmode++;                       /* Mode = block image */
      }

   /*
   * Read the file header. Point ufp to the user att's.
   */
   qiow(IO_RAT,fl,15,0,0,ratpar);      /* Read file header */
   ufp = &(hdr->h_ufat);


#ifdef hdump
   /*
   * If the "-h" switch set, dump the file header prettily
   * (this takes a ton of ugly code)
   */
   efn = 0;                            /* Start with primary file */

hdrdump:                               /* Branch back for multi-header files */
   if(hflag && !frec(fp))              /* Record devices have no header */
      {
      printf("\n\n** DUMP OF %s\n", fspec);
      if(efn == 0)
         printf("** Primary File Header:");
      else
         printf("** Extension File %d Header:", efn);
      printf("\n\nHeader area:\n\n");
      printf("H.IDOF  %03o    H.MPOF  %03o\n", hdr->h_idof, hdr->h_mpof);
      printf("H.FNUM,H.FSEQ,H.FLEV = (%06o,%06o,%06o)\n",
                                    hdr->h_fnum, hdr->h_fseq, hdr->h_flev);
      printf("H.PROJ  %03o    H.PROG  %03o\n", hdr->h_proj, hdr->h_prog);
      p = prot;
      q = prch;
      k = hdr->h_fpro;
      for(i=0; i<16; i++)              /* Check this out */
         {
         if(*q == '\0')
            {
            q = prch;
            *p++ = ',';
            }
         if((k & 1) == 0)
            *p++ = *q++;
         else
            q++;
         k >>= 1;
         }
      *p = '\0';
      printf("H.FPRO  %06o = [%s]\n", hdr->h_fpro, prot);
      i = hdr->h_ucha&0377;
      printf("H.UCHA  %03o = ", i);
      if(i & UC_DLK) printf("UC.DLK = Deaccess locked  ");
      if(i & UC_CON) printf("UC.CON = Contiguous    ");
      if(i & UC_NID) printf("UC.NID = No incr. dump  ");
      if(i & UC_WBC) printf("UC.WBC = Write-back cache  ");
      if(i & UC_RCK) printf("UC.RCK = Read check  ");
      if(i & UC_WCK) printf("UC.WCK = Write check  ");
      if(i & UC_CNB) printf("UC.CNB = Contig. best effort  ");
      printf("\n");
      i = hdr->h_scha&0377;
      printf("H.SCHA  %03o = ", i);
      if(i & SC_MDL) printf("SC.MDL = Marked for delete  ");
      if(i & SC_BAD) printf("SC.BAD = Bad blocks within  ");
      if(i & SC_DIR) printf("SC.DIR = Directory file  ");
      if(i & SC_SPL) printf("SC.SPL = Spool int. file  ");
      printf("\n");
      printf("H.UCHA: User defined attributes (FCS format)\n");
      i = ufp->f_rtyp&0377;
      printf("\tF.RTYP  %03o = ", i);
      if(i == 0) printf("UNDEF");
      if(i == R_FIX) printf("R.FIX");
      if(i == R_VAR) printf("R.VAR");
      if(i == R_SEQ) printf("R.SEQ");
      i = ufp->f_ratt&0377;
      printf("    F.RATT  %03o = ", i);
      if(i == 0) printf("NONE");
      if(i & FD_FTN) printf("FD.FTN");
      if(i & FD_CR) printf("FD.CR");
      if(i & FD_PRN) printf("FD.PRN");
      if(i & FD_BLK) printf(",FD.BLK");
      printf("\n");
      i = ufp->f_rsiz;
      printf("\tF.RSIZ  %06o = %d.\n", i, i);
      j = ufp->f_hibk;
      printf("\tF.HIBK  %07lo  [ H:%03o  L:%06o ] = %ld.\n", j, j, j);
      j = ufp->f_efbk;
      printf("\tF.EFBK  %07lo  [ H:%03o  L:%06o ] = %ld.\n", j, j, j);
      i = ufp->f_ffby;
      printf("\tF.FFBY  %03o = %d.\n", i, i);
      printf("\tRest of user attributes:\n\t");
      for(i=0; i<18;)
         {
         printf("%03o ", ufp->f_crap[i++]);
         if(i%8 == 0) printf("\n\t");
         }
      printf("\n\nIdent area:\n");
      r50toa(fn, hdr->i_fnam, 3);
      fn[9] = '\0';
      printf("I.FNAM: %s\n", fn);
      r50toa(fn, hdr->i_ftyp, 1);
      fn[3] = '\0';
      printf("I.FTYP: %s\n", fn);
      printf("I.FVER = %o (octal)\n", hdr->i_fver);
      printf("I.RVNO = %o (octal)\n", hdr->i_rvno);
      printf("I.RVDT: ");
      p = hdr->i_rvdt;
      for(i=0; i<7; i++) putchar(*p++);
      printf("    I.RVTI: ");
      p = hdr->i_rvti;
      for(i=0; i<6; i++) putchar(*p++);
      printf("\nI.CRDT: ");
      p = hdr->i_crdt;
      for(i=0; i<7; i++) putchar(*p++);
      printf("    I.CRTI: ");
      p = hdr->i_crti;
      for(i=0; i<6; i++) putchar(*p++);
      printf("\nI.EXDT: ");
      p = hdr->i_exdt;
      for(i=0; i<7; i++) putchar(*p++);
      printf("\n\nMap Area:\n");
      printf("M.ESQN  %03o    M.ERVN  %03o\n",
         hdr->m_esqn&0377, hdr->m_ervn&0377);
      printf("M.EFNU,M.EFSQ = (%06o,%06o)\n", hdr->m_efnu, hdr->m_efsq);
      printf("M.CTSZ  %03o    M.LBSZ  %03o\n",
         hdr->m_ctsz&0377, hdr->m_lbsz&0377);
      i = hdr->m_max&0377;
      k = hdr->m_use&0377;
      printf("M.USE   %03o    M.MAX   %03o\n", k, i);
      printf("M.RTRV: Retrieval Pointers (%d maximum)\n", i/2);
      for(i=0; i<k; i+=2)
         {
         /* Hack for compiler bug unsigned -> long extends sign ) */
         j = ((hdr->m_map[i]&0377l)<<16) + (hdr->m_map[i+1]&0177777l);
         printf
            (
            "Segment %3d:  Size = %3d.  LBN %07lo  [ H:%03o L:%06o ] = %ld.\n",
            i/2+1, ((hdr->m_map[i]>>8)&0377)+1, j, j, j
            );
         }
      printf("\nH.CKSM = %06o\n", hdr->h_cksm);
      }
      /*
      * Here, we handle multi-header files. If there is at least 1 extension
      * file, we assign LUN fn+1 to the same device as the primary file, do
      * a read attributes to the extension file ID, and loop back through the
      * header dump code. We keep this up till there is no extension file
      * indicated. We use the M.ESQN value in efn. Sorry about the hacking
      * in the FDB, referring to it as an array, rather than a struct.
      */
      if(hdr->m_efnu != 0)             /* Is there an(other) ext. file? */
         {
         if(efn == 0)                  /* Assign LUN only once */
            {
            efl = fl+1;
            alun(efl, fp->io_fdb[46], fp->io_fdb[47]);
            }
         hdr->m_ctsz = hdr->m_lbsz = 1;   /* Fake a full FID for ext. file */
         ratpar[0] = &(hdr->m_efnu);      /* Point to the extension FID */
         qiow(IO_RAT,efl,15,0,0,ratpar);  /* Read the extension header */
         efn = (hdr->m_esqn ? hdr->m_esqn : 1); /* Avoid loop on QIO fail */
         goto hdrdump;                    /* Dump out the extension header */
         }
#endif
   if(aflag || bflag || fflag || wflag || xflag) dump(bp);
   }

/*
* Do the dump.
* `bp' is the banner string
* (either "block" or "record").
*/
dump(bp)
char *bp;
   {
   register int n;
   long rn;
   long            place;
   long            ftell();

   rn = 1l;                            /* VBN's start with 1 */

   if(!blkmode)                       /* Advance record file to first rec. */
      {
      while(rn < fbn)
         {
         fget(buf, max_size, fp);
         if(feof(fp))
            return;
         rn++;
         }
      }

   while(rn <= lbn)
      {
      if(blkmode)
         {
         rvbpar.vbn = rn;
         qiow(IO_RVB,fl,15,iosb,0,&rvbpar);
         if(iosb[0] != IS_SUC)
            return;
         n = iosb[1];
         }
      else
         {
         place = ftell(fp) + 1l;
         n = fget(buf, max_size, fp);
         if(feof(fp))
            return;
         }

      /*
      * The format has been modified, taking into
      * account the current ODS-1 LBN limit of 1,048,576 (4,000,000 octal),
      * and the filespec has been added.  Also, it varies between device
      * types and formats.
      * (RBD)
      */
      printf("\n\n** DUMP OF %s\n", fspec);
      if(blkmode)
         printf("%s %ld. [ H:%03o L:%06o ]    Length = %d. bytes\n\n",
                bp, rn, rn, n);
      else if(frec(fp))
         printf("%s %ld.  Length = %d. bytes\n\n",
                bp, rn, n);
      else
         printf(
      "%s %ld.  Length = %d. bytes  VBN %ld. [ H:%03o L:%07o ]  Byte %03lo\n\n",
                bp, rn, n, place>>9, place>>9, (place&0777l)+1l);
      format(buf, n);
      rn++;
      }
   }

/*
* Dump out an `n' byte
* item according to the flags
* set by the user.
*/
format(ap, n)
char *ap;
   {
   register int  *wp;
   register char *bp;
   register int  i;
   int o, of, nb, c;
   char ab[3], *fmt;

   if(!bflag && !wflag && xflag)
      fmt = "%04x  ";
   else
      fmt = "%06o";
   o = 0;
   while(n)
      {
      nb = min(16, n);
      of = 1;
      if(wflag)
         {
         printf(fmt, o);
         of = 0;
         printf(" w");
         wp = ap;
         i = ((nb+1)&017776)/2;        /* FIX for odd size rec's */
         while(i--)
            printf("  %06o", *wp++);
         putchar('\n');
         }
      if(aflag)
         {
         if(of)
            {
            printf(fmt, o);
            of = 0;
            }
          else
            printf("      ");
         printf(" a");
         bp = ap;
         i = nb;
         while(i--)
            {
            c = *bp++ & 0377;
            if(c<' ' || c>'~')
               printf(" %03o", c);
            else
               printf("   %c", c);
            }
         putchar('\n');
         }
      if(bflag)
         {
         if(of)
            {
            printf(fmt, o);
            of = 0;
            }
          else
            printf("      ");
         printf(" b");
         bp = ap;
         i = nb;
         while(i--)
            printf(" %03o", *bp++ & 0377);
         putchar('\n');
         }
      if(fflag)
         {
         if(of)
            {
            printf(fmt, o);
            of = 0;
            }
          else
            printf("      ");
         printf(" 5");
         wp = ap;
         i = nb/2;
         while(i--)
            {
            r50toa(ab, wp++, 1);
            printf("     %.3s", ab);
            }
         putchar('\n');
         }
      if(xflag)
         {
         if(of)
            printf(fmt, o);
         else
            printf("      ");
         printf(" x");
         bp = ap;
         i = nb;
         while(i--)
            printf("  %02x", *bp++ & 0377);
         putchar('\n');
         }
      o  += 16;
      ap += 16;
      n  -= nb;
      }
   }

/*
* Compute the minimum of
* two numbers.
* Should be in the library.
*/
min(a, b)
   {
   if(a < b)
      return(a);
   return(b);
   }

/*
* Cannot open diagnostic.
* Just exit.
*/
cant(p)
char *p;
   {
   fprintf(stderr, "%s: cannot open\n", p);
   exit(1);
   }

/*
* Usage diagnostic.
* Just exit.
*/
usage()
   {
   fprintf(stderr, "Usage: dump [-5abrwx] [-f bn] [-l bn] file\n");
   exit(1);
   }
