/* Updated on 03-May-89 at 5:54 PM by Creed A. Erickson; edit time: 1:06:08 */
/*************************************************************************
**                                                                       *
**          ERZ.C - Erstaz definitions display/edit utility.             *
**                                                                       *
**************************************************************************
**
**                               NOTICE
**
**     COPYRIGHT (C) 1989 Professional Applied Computer Engineering, Inc.
**     UNPUBLISHED -- ALL RIGHTS RESERVED.
**
** THIS  PROGRAM IS  CONFIDENTIAL AND TRADE  SECRET OF  PROFESSIONAL APPLIED
** COMPUTER ENGINEERING, INC. THE RECEIPT OR POSSESSION OF THIS PROGRAM DOES
** NOT  CONVEY  ANY  RIGHTS  TO  REPRODUCE OR DISCLOSE  ITS  CONTENTS, OR TO
** MANUFACTURE, USE, OR SELL ANYTHING THAT IT MAY  DESCRIBE, IN  WHOLE OR IN
** PART,  WITHOUT  THE  SPECIFIC  WRITTEN  CONSENT  OF  PROFESSIONAL APPLIED
** COMPUTER ENGINEEERING, INC.
**
** Edit History:
**
**[101] 05/03/89 Minor changes for release as ESP programming example.  /CAE
**[100] 04/14/89 Designed and implemented by Creed A. Erickson.
**
**
** What it is and what it does:
**
**  ERZ is a utility for displaying and/or editing ersatz definitions on
**  the system. The changes to the ersatz device table are made "real time"
**  without having to reboot the system. Several ersatz devices may be
**  changed at the same time. The program DOES NOT update the system ersatz
**  initialization file(s) which define ersatz devices upon a system boot.
**
** What you need to run it:
**
**  - ESP (run time or development pkg) or DART (which includes the former).
**  - ERZ.LIT program.
**  - ERZ.SCR screen file.
**
** If you want to manipulate the source code you need:
**
**  - AlphaC compiler package (version 1.0B or later).
**  - TBXC.H and TBXC.LIB ESP/TOOLBOX interface.
**  - As written, source code is intended to compile using /NOINIT and
**    the CNINIT.OBJ start-up code (I despise those silly .RTI files).
**
** How to work it:
**
**  - From AMOS command level enter the command ERZ[ret].
**  - To page back and forth (assuming > 1 screen of ersatz devices),
**    simply press NEXT-SCREEN or PREV-SCREEN.
**  - To edit the current screen full of ersatz device definitions, press
**    EDIT (F1 on most terminals).
**  - If your user security has been defined to ESP as 254, you may also
**    change the device names, as well as the definitions.
**  - Once in edit mode, the changes made will take effect as soon as
**    EXECUTE is pressed.
**  - In edit mode, changes may be "abandoned" by pressing MENU.
**  - The program is exited by pressing MENU from display mode.
**
** If you have problems:
**
**  This utility is NOT downward compatible with earlier versions of ESP.
**  If your version of ESP is 1.1(126) or less (as judged by the version of
**  DSK0:SEDIT.LIT[1,4]) then you may need to upgrade before this utility
**  will work.
**
** If you have any questions you may contact:
**
**              Creed Erickson
**              PACE, Inc.
**              Post Office Box 278
**              Pleasanton, CA.  94566
**              (415) 462-2300
*/

version "1.0(101),-1,PV$RSM!PV$RSM,PH$REE!PH$REU";

#include <STDIO.H>                      /* Standard AlphaC definitions. */
#include <TBXC.H>                       /* Standard ESP definitions.    */

/*************************************************************************
**               AMOS ersatz structures and typedefs.                    *
**************************************************************************
*/
typedef struct                          /* Ersatz table entry typedef.  */
   {
   rad50l nam;                         /*  Ersatz device name.         */
   unsigned cpu;                       /*  CPU id.                     */
   rad50 dev;                          /*  Device.                     */
   unsigned short unt;                 /*  Unit (or drive).            */
   rad50l fil;                         /*  File name.                  */
   rad50 ext;                          /*  Extension.                  */
   unsigned short ppn;                 /*  PPN.                        */
   } ERSATZ;                           /* Typedef named "ERSATZ".      */

typedef ERSATZ *ERZPTR;                 /* Pointer to an ersatz entry.  */

/*************************************************************************
**           "#defined"'d constants and macro definitions.               *
**************************************************************************
*/
#define LSTFLD 34                       /* Last ersatz display field.   */
#define DSPEDT 37                       /* Display/Edit indicator fld.  */
#define PAGNBR 38                       /* "Page number" field.         */
#define TOTPAG 39                       /* Total # of pages field.      */
#define EDTPRM 40                       /* Edit mode prompt field.      */
#define SCRMAX 17                       /* Max # devices per page.      */
#define ERSATZ_TABLE (syscom->ersatz)   /* Where ersatz table starts.   */

/*************************************************************************
**                  Global variable declarations.                        *
**************************************************************************
*/
ERZPTR ez;                              /* Pointer into  ersatz table.  */
pointer screen;                         /* Pointer to display/edit scr. */
int erzcnt;                             /* Number of ersatz devices.    */
int scrcnt;                             /* Number of screens used.      */
int curscr;                             /* Currently displayed screen # */
int keyhit;                             /* Display mode key pressed.    */
int edtchr;                             /* Edit mode screen edit char.  */
int edtfld;                             /* Edit mode screen field.      */
ERZPTR where[SCRMAX];                   /* Screen line to table xref.   */
char erznam[10];                        /* Ersatz name (ASCII).         */
char defnam[70];                        /* Ersatz definition (ASCII).   */
char *msg[4];                           /* Bottom line message pointers.*/
int msgidx;                             /* Idx to active bot. line msg. */


/***************************
**        main()           *
****************************
** Program's main-line logic.
**
*/
void main()
   {
   /* Make sure that there are ersatz devices defined.
   */
   if (nil == (ez = ERSATZ_TABLE))     /* If no ersatz devices...      */
       {
       printf("No ersatz defininition.\n"); /* Say none there.         */
       exit();                         /* All done.                    */
       }

   /* Count the number of ersatz devices.
   */
   erzcnt = 0;                         /* Initialize count to none.    */
   while (ez->nam)                     /* While not at end of table.   */
       {
       erzcnt += 1;                    /* Increment the count.         */
       ez += 1;                        /* Increment the table pointer. */
       }
   scrcnt = (erzcnt+SCRMAX-1)/SCRMAX;  /* Calculate # of scrs to use.  */

   /* Miscellaneous initialization.
   */
   msg[0] = "Press F1 to edit or MENU to exit";
   msg[1] = "Press NEXT-SCREEN, PREV-SCREEN, F1 to edit, or MENU to exit ";
   msg[2] = "Press PREV-SCREEN, F1 to edit, or MENU to exit ";
   msg[3] = "Press NEXT-SCREEN, F1 to edit, or MENU to exit ";

   /* Fetch the display/edit screen form the disk.
   */
   initrm("Ersatz Display/Edit Utility","Copyright (c) 1989 - P.A.C.E., Inc.", 0);
   screen = load_screen("DMSESP:ERZ.SCR");/* Get the screen file.      */
   opnscr(screen);                     /* Open screen for processing.  */
   hidfld(screen, EDTPRM);
   skpfld(screen, PAGNBR);

   /* Load up the fields for the first display.
   */
   curscr = 1;                         /* Set the current screen.      */
   load_fields(curscr);                /* Load up the fields.          */

   /* Get the fields displayed.
   */
   while (keyhit != ESP_MENU)          /* Until a MENU is pressed.     */
       {
       dspscr(screen);                 /* Display the screen.          */

       /* Setup the line 24 message index.
       */
       if (scrcnt == 1)                /* If only one screen...        */
           {
           msgidx = 0;                 /*  Index F1 & MENU msg.        */
           }
       else
           {
           if ((curscr > 1) && (curscr < scrcnt)) /* If not 1st or last */
               {
               msgidx = 1;             /* Index NEXT, PREV, F1, MENU   */
               }
           else
               {
               if (curscr > 1)         /* If not first screen.         */
                   {
                   msgidx = 2;         /* Index PREV, F1, MENU msg.    */
                   }
               else
                   {
                   msgidx = 3;         /* Index NEXT, F1, MENU msg.    */
                   }
               }
           }

       /* Display the indexed message. No ding, no acknowlegement, no blink.
       */
       errdsp(msg[msgidx], ERRDSP_NDG|ERRDSP_NAK|ERRDSP_NBL);
       CUROFF;                         /* Turn the cursor back off.    */
       keyhit = gtchr();               /* Wait for user to press key.  */

       /* Perform action based on the key which was pressed.
       */
       switch (keyhit)
           {

           /* If NEXT SCREEN pressed, go to the next display screen full.
           ** If no next screen is available then ding.
           */
           case ESP_NXTSCREEN :    {
                                   if (curscr < scrcnt)
                                       {
                                       load_fields(curscr += 1);
                                       }
                                   else
                                       {
                                       DING;
                                       }
                                   break;
                                   }   /* End of case.                 */

           /* If PREV SCREEN pressed, go to the previous display screen full.
           ** If no previous screen is available then ding.
           */
           case ESP_PRVSCREEN :    {
                                   if (curscr > 1)
                                       {
                                       load_fields(curscr -= 1);
                                       }
                                   else
                                       {
                                       DING;
                                       }
                                   break;
                                   }   /*   End of case.               */

           /* If F1 key pressed then go to edit mode.
           */
           case ESP_F1         :   {
                                   edit_ersatz();
                                   }

           }                           /*  End switch.                 */
       }                               /* End of while loop.           */

   /* While loop has been exited, user must have pressed MENU.
   */
   clsscr(screen);                     /* Close the screen.            */
   initrm(nil, nil, 0);                /* Clear the display.           */
   CURON;                              /* Turn the cursor on.          */
   }

/* --- end main() --- */


/***************************
**     load_fields()       *
****************************
** Function to load the fields of the display/edit screen for a specified
**  display page.
**
** Arguments/Dependencies:
**
**  scrnbr  := The display page (screen number) wich is to be loaded.
**
** Returned/Side effects:
**
**  The display/edit screen is altered.
**  The global array where[] is adjusted to reflect the ersatz table entries
**   for each of the displayed ersatz devices.
**
*/
void load_fields(scrnbr)
int scrnbr;                             /* Display page to load.        */
   {
   int i;                              /* Loop indexing variable.      */
   ERZPTR erz;                         /* Temp. ersatz table pointer.  */

   setbnm(screen, PAGNBR, scrnbr);     /* Set page #                   */
   setbnm(screen, TOTPAG, scrcnt);     /* Set total pages count.       */

   clrfls(screen, 1, LSTFLD);          /* Clear the screen fields.     */
   erz = ERSATZ_TABLE;                 /* Index start of ersatz table. */
   scrnbr -= 1;                        /* Adjust page # to base 0.     */

   /* Index the proper point in the ersatz table for this display page.
   ** REMEMBER: Incrementing or decrementing a pointer always causes it
   ** to be adjusted according to the size of the object it points to.
   */
   while (scrnbr)                      /* While not to page 0...       */
       {
       scrnbr -= 1;                    /* Decrement the page counter.  */
       erz += SCRMAX;                  /* Advance ersatz pointer.      */
       }

   /* Load the screen fields and adjust the where[] global array.
   */
   for (i = 0; i < SCRMAX; i++)        /* For all positions on screen. */
       {
       if (erz->nam)                   /* If not at end of table...    */
           {
           where[i] = erz;             /* Set table index for this one.*/
           unpack_ersatz(erz, erznam, defnam); /* Unpack to ASCII.     */
           setstr(screen, (i*2)+1, erznam, sizeof(erznam)); /* Load name   */
           setstr(screen, (i*2)+2, defnam, sizeof(defnam)); /* Load def.   */
           shwfld(screen, (i*2)+1);
           shwfld(screen, (i*2)+2);
           erz += 1;                   /* Advance pointer.             */
           }
       else                            /* If at end of table....       */
           {
           where[i] = nil;             /* No index for this entry.     */
           hidfld(screen, (i*2)+1);
           hidfld(screen, (i*2)+2);
           }
       }                               /* End for loop.                */
   }

/* --- end load_fields() --- */


/***************************
**     unpack_ersatz()     *
****************************
** Function to unpack an ersatz table entry into an ASCII form.
**
** Arguments/Dependencies:
**
**  eptr    => erstaz table entry to unpack.
**  enm     => string buffer which will receive the ersatz device name.
**  def     => string buffer which will receive the expanded definition.
**
** Returned/Side effects:
**
**  Upon return, strings *enm and *def contain the ASCII representation of
**      the ersatz name and expanded definiition, respectivly.
**
*/
void unpack_ersatz(eptr, enm, def)
ERZPTR eptr;
char *enm, *def;
   {
   char *cptr;                         /* Working character pointer.   */
   rad50 *rptr;                        /* Working RAD50 pointer.       */

   *enm = *def = '\0';                 /* Zap the passed strings.      */
   if (eptr->nam == 0) return(0);      /* If no ersatz device, return. */

   /* Unpack the ersatz device name into the name return buffer.
   */
   rptr = &eptr->nam;                  /* Index the ersatz name.       */
   cptr = enm;                         /* Index the return buffer.     */
   unpack(&rptr, &cptr);               /* Unpack the ersatz name.      */
   unpack(&rptr, &cptr);               /*   Both halves of it.         */
   while (*--cptr == ' ') ;            /* Backup until non-space.      */
   cptr += 1;                          /* Advance to space char.       */
   *cptr++ = ':';                      /* Replace it with a colon.     */
   *cptr = '\0';                       /* Terminate the name.          */


   /* Unpack the ersatz defintion into the definit
ion return buffer.
   */
   cptr = def;                         /* Index the definition buffer. */

   /* If there is a CPU ID associated with this ersatz definition
   ** then it gets unpacked first.
   */
   if (eptr->cpu)                      /* If there is a CPU id...      */
       {
       dcvt(eptr->cpu, 0, _ot_mem, &cptr);  /* Convert to decimal.     */
       *cptr++ = '-';                 /* Add a trailing dash char.     */
       }

   /* Unpack the device and unit (if defined).
   */
   if (eptr->dev)                      /* If there is a device...      */
       {
       rptr = &eptr->dev;              /* Index the device spec.       */
       unpack(&rptr, &cptr);           /* Unpack into buffer.          */
       while (*--cptr == ' ');         /* Backup over trailing sp.     */
       cptr += 1;                      /* Point to 1st trailing sp.    */
       dcvt(eptr->unt, 0, _ot_mem, &cptr);/* Cvt unit nbr into buff.   */
       *cptr++ = ':';                  /* Add colon terminator.        */
       }

   /* Unpack the file name associated with this definition (if any).
   */
   if (eptr->fil)                      /* If there is a name...        */
       {
       rptr = &eptr->fil;              /* Index the file name.         */
       unpack(&rptr, &cptr);           /* Unpack into output buffer.   */
       unpack(&rptr, &cptr);           /*   Both halves of it.         */
       while (*--cptr == ' ');         /* Backup over trailing space.  */
       cptr += 1;                      /* Point to 1st trailing space. */
       }

   /* If the ersatz definition has an associated extension, buffer it.
   */
   if (eptr->ext)                      /* If there is an extension...  */
       {
       *cptr++ = '.';                  /* Leading period character.    */
       rptr = &eptr->ext;              /* Index the extension.         */
       unpack(&rptr, &cptr);           /* Unpack extension into buff.  */
       while (*--cptr == ' ');         /* Backup over trailing space.  */
       cptr += 1;                      /* Index 1st trailing space.    */
       }

   /* Buffer PPN (if avail.)
   */
   if (eptr->ppn)                      /* If there is a PPN...         */
       {
       *cptr++ = '[';                  /* Leading "[" character.       */
       ocvt(((eptr->ppn >> 8) & 255), 0, _ot_mem, &cptr); /* 1st part  */
       *cptr++ = ',';                  /* Comma separator.             */
       ocvt((eptr->ppn & 255), 0, _ot_mem, &cptr); /* 2nd part.        */
       *cptr++ = ']';                  /* Conclude with "]" char.      */
       }

   *cptr = '\0';                       /* Terminate the buffer.        */
   }

/* --- end unpack_ersatz() --- */


/***************************
**       dspscr()          *
****************************
** Function to display an ESP screen. All altered fields will be updated.
**
** Arguments/Dependencies:
**
**  scr     => pointer to the screen to be displayed. This screen is assumed
**              to have already been opened via an opnscr() call.
**
** Returned/Side effects:
**
**  Returns nothing. Passed screen is displayed/updated.
**
*/
void dspscr(scr)
pointer scr;
   {
   int chr, fld;                       /* Temporary edit char, field.  */

   chr = ESP_MENU;                     /* Set edit char to MENU.       */
   fld = 1;                            /* Set edit field to field 1.   */
   gtscr(screen, &chr, &fld);          /* Get the screen.              */
   }

/* --- end dspscr() --- */


/***************************
**     pack_ersatz()       *
****************************
** Function to pack an ersatz definition from ASCII string form into ersatz
**  table form.
**
** Arguments/Dependencies:
**
**  eptr    => Type ERSATZ object which receives packed ersatz specification.
**  enm     => String buffer which holds ersatz device name to be packed.
**  def     => String buffer which holds ersatz definition to be packed.
**
** Returned/Side effects:
**
**  Returns ddb error codes.
**  Upon return, structure *eptr contains the packed definition.
**
*/
int pack_ersatz(eptr, enm, def)
ERZPTR eptr;
char *enm;
char *def;
   {
   char *cptr;                         /* Temp character pointer.      */
   rad50 *rptr;                        /* Temp RAD50 pointer.          */
   ddb wrkddb;                         /* Temp DDB for FSPEC call.     */

   clear(*eptr);                       /* Preclear return structure.   */

   /* Pack the ersatz device name.
   */
   rptr = &eptr->nam;                  /* Index ersatz name field.     */
   cptr = enm;                         /* index the ASCII name.        */
   pack(&cptr, &rptr);                 /* Pack the ersatz name.        */
   pack(&cptr, &rptr);                 /*  Both halves of it.          */
   if (eptr->nam == 0) return(_d_espc);/* If no name then error.       */

   /* Pack the ersatz device definition.
   */
   cptr = def;                         /* Index the device definition. */
   byp(&cptr);                         /* Bypass leading white space.  */
   clear(wrkddb);                      /* Preclear the work DDB.       */
   fspec(&cptr, &wrkddb, \%%%\);       /* Load DDB fields from string. */
   if (wrkddb.ext == \%%%\) wrkddb.ext = 0;    /* Adj. ext. if needed. */
   if (wrkddb.err == 0)                /* If no error...               */
       {
       eptr->cpu = wrkddb.cpu;         /*  Copy DDB flds to structure. */
       eptr->dev = wrkddb.dev;         /*   Get the device.            */
       eptr->unt = wrkddb.drv;         /*   Get the unit (drive).      */
       eptr->fil = wrkddb.fil;         /*   Get the file name.         */
       eptr->ext = wrkddb.ext;         /*   Get the extension.         */
       eptr->ppn = wrkddb.ppn;         /*   Get the PPN.               */
       }
   return(wrkddb.err);                 /* Return DDB error code.       */
   }

/* --- end pack_ersatz() --- */


/***************************
**      edit_ersatz()      *
****************************
** Function to allow editing (changing) the current "page" of ersatz devices.
**
** Arguments/Dependencies:
**
**  No arguments. Relies on screen being loaded with ersatz definitions.
**
** Returned/Side effects:
**
**  The ersatz table entries indicated in the where[] array will be altered.
**
*/
void edit_ersatz()
   {
   ERSATZ edef;                        /* Temp table entry.            */
   int result;                         /* Temp result buf.             */
   int tstchr;                         /* Temp ESP edit character val. */
   int i;                              /* Loop index.                  */

   edtchr = ESP_BEGLIN;                /* Initialize to ^U.            */
   edtfld = 1;                         /* Start editing at field 1.    */

   setpui(screen, DSPEDT, 2);          /* Change disp/edit fld to EDIT */
   shwfld(screen, EDTPRM);             /* Enable edit mode prompt.     */

   for (;;)                            /* Loop for ever.               */
       {
       gtscr(screen, &edtchr, &edtfld);    /* Edit the screen.         */
       tstchr = (edtchr & 255);            /* Mask off character value.*/
       if (tstchr == ESP_MENU) break;      /* If MENU then stop loop.  */
       if (tstchr == ESP_EXECUTE) break;   /* If EXEC then stop loop.  */
       }

   /* If loop was exited with EXECUTE keypress then update ersatz table.
   */
   if (tstchr == ESP_EXECUTE)          /* If EXECUTE pressed...        */
       {
       for(i = 0; i < SCRMAX; i++)     /*  For as many as are on scr.  */
           {
           getstr(screen, (i*2)+1, erznam, sizeof(erznam)); /* get name*/
           getstr(screen, (i*2)+2, defnam, sizeof(defnam)); /* get def.*/
           result = pack_ersatz(&edef, erznam, defnam);     /* Pack.   */

           if (result == 0)                /* If not error from pack.. */
               {
               jlock();                    /* Lock sys while fiddling. */
               where[i]->nam = edef.nam;   /*  Copy the ersatz name.   */
               where[i]->cpu = edef.cpu;   /*  Copy the CPU id.        */
               where[i]->dev = edef.dev;   /*  Copy the device spec.   */
               where[i]->unt = edef.unt;   /*  Copy the unit (drive).  */
               where[i]->fil = edef.fil;   /*  Copy the file name.     */
               where[i]->ext = edef.ext;   /*  Copy the extension.     */
               where[i]->ppn = edef.ppn;   /*  Copy the PPN.           */
               junlok();                   /* Release system lock.     */
               }

           /* Unpack the defintion and reload into screen.
           ** This insures that the screen is same as ersatz definition.
           */
           unpack_ersatz(where[i], erznam, defnam);         /* Unpack. */
           setstr(screen, (i*2)+1, erznam, sizeof(erznam)); /* Load name.*/
           setstr(screen, (i*2)+2, defnam, sizeof(defnam)); /* Load def.*/
           }
       }
   else                                /* If not EXECUTE...            */
       {
       load_fields(curscr);            /*  Reload display fields.      */
       }
   setpui(screen, DSPEDT, 1);          /* Reset disp/edit fld to disp. */
   hidfld(screen, EDTPRM);             /* Hide edit mode prompt fld.   */
   }

/* --- end edit_ersatz() --- */

/* --- end of ERZ.C file --- */