#pragma module SYSVER "X-13"
/*	.TITLE	SYSVER		- Set system version id			   */
/*	.IDENT	'X0.13'                                                    */
/*  									   */
/***************************************************************************/
/*                                                                         */
/*  COPYRIGHT (c) 1992 BY                                		   */
/*  DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS.                 */
/*  ALL RIGHTS RESERVED.						   */
/* 									   */
/*  THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED  */
/*  ONLY IN  ACCORDANCE WITH  THE  TERMS  OF  SUCH  LICENSE  AND WITH THE  */
/*  INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR  ANY  OTHER  */
/*  COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY  */
/*  OTHER PERSON.  NO TITLE TO AND OWNERSHIP OF  THE  SOFTWARE IS  HEREBY  */
/*  TRANSFERRED.							   */
/* 									   */
/*  THE INFORMATION IN THIS SOFTWARE IS  SUBJECT TO CHANGE WITHOUT NOTICE  */
/*  AND  SHOULD  NOT  BE  CONSTRUED AS  A COMMITMENT BY DIGITAL EQUIPMENT  */
/*  CORPORATION.							   */
/* 									   */
/*  DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE  OR  RELIABILITY OF ITS  */
/*  SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL.		   */
/* 									   */
/*									   */
/***************************************************************************/
/*  									   */
/*++									   */
/* FACILITY:	KITTING							   */
/*  									   */
/* ABSTRACT:   	Modifies the Alpha system version id in SYS$BASE_IMAGE.EXE.*/
/*  									   */
/* ENVIRONMENT:	User mode						   */
/*  									   */
/* AUTHOR:  Ron Sommer, 	CREATION DATE:  4-Feb-1992		   */
/*  									   */
/* MODIFIED BY:								   */
/*                                                                         */
/* X-13 MAS		Mary A. Sullivan	08-Apr-1994                */
/*	Include sysver_addr.h to define sva, rather than                   */
/*	hardcoding the value.						   */
/*                                                                         */
/*                                                                         */
/* X-12 BMAN            Brian Man               07-Apr-1994		   */
/*      Hardcode V6.1-SSB System Version address into sva		   */
/*                                                                         */
/* X-9	AEM0200		Anne McElearney		02-Nov-1993		   */
/*	Use short names for fields in eisddef.				   */
/*									   */
/*									   */
/* X-9	AEM0114		Anne McElearney		10-May-1993		   */
/*	Remove path from #include statements for EISDDEF and EIHDDEF	   */
/*      so they will be included from sys$lib_c.tlb instead of [lib.obj]   */
/*									   */
/* X-8	AEM0113		Anne McElearney		03-May-1993		   */
/*	Make changes for C header files.				   */
/*									   */
/* 									   */
/* DFW075		David F. Wall		7-Oct-1992		   */
/*	Modified test against value_length in function			   */
/*	sysver_replace_ascii to account for null string terminator	   */
/*									   */
/* DFW072		David F. Wall		2-Sep-1992		   */
/*	Value of constant BUFSIZ has changed from 512 to 8192.  Will	   */
/*	use new constant. VAXBUFSIZ throughout.  Changed SVA for DELTA	   */
/*--  									   */


/* Include files and define external constants */

#pragma nomember_alignment	/* Temporary fix */
#include <stdio.h>
#include <stat.h>
#include <stdlib.h>                                                          
#include <file.h>                
#pragma member_alignment	/* Temporary fix */
#include <eihddef.h>
#include <eisddef.h>

/* ==================================================================	*/
/*	Alpha C version - when compiling and linking an Alpha image.    */
/*      sysver_addr.h will define the location of the system version    */
/*      string:  unsigned long sva = 0x<address>;                       */
/* ==================================================================	*/

#include "sysver_addr.h"

#define BYTE_OFFSET_MASK 0X1FFUL
#define RMS_EOF 98938 
#define TRUE 1
#define FALSE 0
#define DOUBLE_QUOTE 34
#define SINGLE_QUOTE 39
#define VAXBUFSIZ 512
/********************************************************************/
/* The following fields in EIHD and EISD are defined in lower case  */
/* in the .h file.  #defines were added to allow the code to work   */
/* without modifying each instance of the field.		    */
/********************************************************************/
#define EIHD$L_HDRBLKCNT eihd$l_hdrblkcnt
#define EIHD$L_ISDOFF eihd$l_isdoff
#define EISD$R_ADDRESS_OVERLAY eisd$r_address_overlay
#define EISD$L_SECSIZE eisd$l_secsize
#define EISD$L_VBN eisd$l_vbn
#define EISD$L_EISDSIZE eisd$l_eisdsize

int CLI$PRESENT (long[]);
int CLI$GET_VALUE (long[], long[], long);
extern long LIB$GET_INPUT(long[],long[],long);
extern long LIB$PUT_OUTPUT(long[],long);
int CLI$DCL_PARSE(int, long[], long[], long[], long[]);
int CLI$DISPATCH(int);
int SYSVER_SHOW();
int SYSVER_REPLACE();
int SYSVER_WRITE();
int SYSVER_EXIT();
int sysver_show_ascii(unsigned long, unsigned long,long, long);
int sysver_replace_ascii(unsigned long,unsigned long, long, char *, short);
int xlate_addr(long, long *);
extern long sysvercmd(); 

/* Global cells */                            

char unsigned *buffer;
char unsigned *buffer1;
char unsigned *buffer2;

char file[] ="SYS$BASE_IMAGE.EXE";

char prompt[] = "SYSVERSION> ";
long prompt_desc[2] = {(sizeof prompt)-1, &prompt};

char ascii_qual[] ="ASCII";
long ascii_desc[2] = {(sizeof ascii_qual)-1, &ascii_qual};

char quad_qual[] ="QUAD";
long quad_desc[2] = {(sizeof quad_qual)-1, &quad_qual};

char long_qual[] ="LONG";
long long_desc[2] = {(sizeof long_qual)-1, &long_qual};
        
int hdr_size,image_size,exit_flag;
unsigned long pc;
unsigned long mod_sys_ver = FALSE ;


/* Main routine */               
main()
{
    int fd;
    char c;              
    long actual_length;
    int i,n;
    int file_size,hdr_blkcnt, status;
    struct stat statbuf;
    char *p;
    EIHD *eihd;

/* Display banner */

/*        printf("\n    Set System Version Utility - X0.1\n\n"); */

/* Open filename */

    if ((fd = open(file, O_RDONLY, 0)) == -1)
    {
    	perror(">>> SYS$BASE_IMAGE.EXE can not be opened");
        return;
    }

/* Get buffer1 and read 1st block of image header */

    p= buffer1 = malloc(VAXBUFSIZ);
    read(fd, p, VAXBUFSIZ); 

/* Calculate size and start of image */

    stat(file,&statbuf);
    file_size = statbuf.st_size;    
    eihd = buffer1;
    hdr_blkcnt = (int) eihd -> EIHD$L_HDRBLKCNT;
    image_size = file_size - (hdr_blkcnt * VAXBUFSIZ);
    hdr_size = (hdr_blkcnt - 1) * VAXBUFSIZ;

/* Read in remaining header block */

    if (hdr_size > 0)
    {
	lseek(fd, VAXBUFSIZ, 0);
	p= buffer2 = malloc(hdr_size);
        i=0;
        while (i < hdr_size)
        {
	    n = read(fd, p, VAXBUFSIZ); 
            i = i + n;
            p = p + n;
        }
    }
 
/* Seek to start of image and read into buffer */

    lseek(fd, hdr_size+VAXBUFSIZ, 0);
    p= buffer = malloc(image_size+VAXBUFSIZ);
    while ((n=read(fd, p, VAXBUFSIZ)) > 0)
    {
	p = p + n;
    }

/* Close file */
 
    close(fd);

/* Enter command/dispatch loop */

    exit_flag = FALSE;
    status = 0;
    while ((status != RMS_EOF) && (exit_flag == FALSE)) {     
        status = CLI$DCL_PARSE(0, sysvercmd, LIB$GET_INPUT, LIB$GET_INPUT, &prompt_desc);
        if (status & 1) { 
            CLI$DISPATCH(0);
        }
    }

/* Close file & release memory */

    free(buffer);
    free(buffer1);
    free(buffer2);
}    


/*	SHOW the current value for the system version cell.

    	This displays ASCII character strings.
    	Valid qualifiers are /QUAD and /LONG.
    	/QUAD is the default.
*/

SYSVER_SHOW()
{
    unsigned long pa;

/*	Valid the address	*/

    if (!(xlate_address(sva, &pa)))
    {
    	printf(">>> Invalid address\n");
        return(1);
    }

/*	Show a LONG word?	*/

    if (CLI$PRESENT(&long_desc) & 1)
        if (CLI$PRESENT(&ascii_desc) & 1)
            return(sysver_show_ascii(pa,sva,4,1));

/*	Show a QUAD word?	*/

    if (CLI$PRESENT(&quad_desc) & 1)
        if (CLI$PRESENT(&ascii_desc) & 1)
    	    return(sysver_show_ascii(pa,sva,8,1));
}


/*	Routine that actually displays the current system version	*/

sysver_show_ascii(unsigned long pa,unsigned long sva, long bytes, long count)
{
    unsigned long paddr,vaddr;
    int addr,i,j;

    paddr = pa;
    vaddr = sva;
    for (j=0; j < count; j++) {
        printf("%08X: ",vaddr);
        for (i=0; i < bytes; i++) {
            if ((buffer[paddr+i] < 32) || (buffer[paddr+i] > 126))
                putchar('.');
            else
                putchar(buffer[paddr+i]);
        }                        
        printf("\n");
        paddr = paddr + bytes;
        vaddr = vaddr + bytes;
    }
    pc = pc + (bytes * count);
}


/*	REPLACE the current value in the system version cell.

    	This replaces with ASCII character strings.
    	Valid qualifiers are /QUAD and /LONG.
    	/QUAD is the default.
*/

SYSVER_REPLACE()
{
    char label1[] ="REPLACE_VALUE";
    long label1_desc[2] = {(sizeof label1)-1, &label1};
    char value[256];
    long value_desc[2] = {sizeof value, &value};
    short value_length = 0;
    unsigned long pa;

/*	Valid the address	*/

    if (!(xlate_address(sva, &pa)))
    {
        printf(">>> Invalid address\n");
        return(1);
    }

/*	Get new character string	*/

    if (CLI$PRESENT(&label1_desc) & 1) 
        if (CLI$GET_VALUE(&label1_desc, &value_desc, &value_length) & 1)
            value[value_length] = 0;

    if (value_length == 0)
    {
    	    return(1);
    }

/*	Replace a LONG word?	*/

    if (CLI$PRESENT(&long_desc) & 1)
        if (CLI$PRESENT(&ascii_desc) & 1)
            return(sysver_replace_ascii(pa,sva,4, &value, value_length));

/*	Replace a QUAD word?	*/

    if (CLI$PRESENT(&quad_desc) & 1)
        if (CLI$PRESENT(&ascii_desc) & 1)
            return(sysver_replace_ascii(pa,sva,8, &value, value_length));
}
                                                      

/*	Routine that actually replaces the current system version	*/

sysver_replace_ascii(unsigned long pa,unsigned long sva, long bytes, char * value,
                    short value_length)
{
    int i;

    if ((value[0] == DOUBLE_QUOTE) || (value[0] == SINGLE_QUOTE))
    {
        value = value + 1;
        value_length = value_length - 1;
    }
    if ((value[value_length-1] == DOUBLE_QUOTE) || 
        (value[value_length-1] == SINGLE_QUOTE))
            value_length = value_length - 1;

    for (i=bytes-1; i >= 0; i--) {
        if (i >= value_length) /* Account for null terminator */
            buffer[pa+i] = ' ';
        else
            buffer[pa+i] = (char unsigned) value[i];
    }
    pc = pc + bytes;
    mod_sys_ver = TRUE ;	/* Indicate we replaced data */
}


/*	Write out a new version of the file.
    	This will only happen if the version cell was actually replaced.
*/

SYSVER_WRITE()
{
    char unsigned *p;
    int i,n;                
    int fd;

/* Did we modify the system version */

    if (mod_sys_ver == FALSE)
    {
    	return(0);
    }

/* Open filename for update */
    
    if ((fd = creat(file,0,"mrs=512","rfm=fix","fop=cbt")) == -1)
    {
    	perror(">>> Create SYS$BASE_IMAGE.EXE");
        return;
    }

    p= buffer1;
    write(fd, p, VAXBUFSIZ);
                    
    if (hdr_size > 0)
    {
	p= buffer2;
        i=0;
        while (i < hdr_size)
        {
	    n = write(fd, p, VAXBUFSIZ); 
            i = i + n;
            p = p + n;
        }
    }
 
    p= buffer;
    i=0;
    while (i < image_size)
    {
        n=write(fd, p, VAXBUFSIZ);
        i = i + n;
	p = p + n;
    }

    close(fd);

/*    printf("    %d bytes written to %s\n",VAXBUFSIZ+hdr_size+image_size,file);	*/

    return(1);
}


/*	Exit this utility	*/

SYSVER_EXIT()
{
    exit_flag = TRUE;
    return(1);
}


/*	Calculate the actual location of the system version cell
    	within the image.
*/

xlate_address(unsigned long sva, unsigned long *pa)
{
    EIHD *eihd;
    EISD *eisd;
    int offset,done,vbn;
    unsigned char *p;

    eihd = buffer1;
    eisd = buffer1 + (eihd -> EIHD$L_ISDOFF);
    done = FALSE;
    p = eisd;

    while (((eisd -> EISD$L_EISDSIZE) !=0) && (!done))
    {
        if ((sva >= ((unsigned long) eisd ->eisd$l_virt_addr)) &&
            (sva <  ((unsigned long) eisd ->eisd$l_virt_addr) + 
             (eisd -> EISD$L_SECSIZE)))
        {
            done = TRUE;
            vbn = (eisd -> EISD$L_VBN) - 1;
            offset = sva - ((unsigned long) eisd ->eisd$l_virt_addr);
        }
        else
            {
            p = p + (eisd -> EISD$L_EISDSIZE);
            eisd = p;
            }
    }

    if (done)
    {
	*pa = (vbn * VAXBUFSIZ) - hdr_size - VAXBUFSIZ + offset;
        return(TRUE);
    }
    else
    {
        *pa = 0;
        return(FALSE);
    }

} 
