From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 30-AUG-1990 19:04:11.43
To:	MRGATE::"ARISIA::EVERHART"
CC:	
Subj:	Re: Determining memory size

Received:  by crdgw1.ge.com (5.57/GE 1.70)
	 id AA13869; Wed, 29 Aug 90 23:00:17 EDT
Received: From UCBVAX.BERKELEY.EDU by CRVAX.SRI.COM with TCP; Wed, 29 AUG 90 19:33:39 PDT
Received: by ucbvax.Berkeley.EDU (5.63/1.42)
	id AA11945; Wed, 29 Aug 90 19:25:31 -0700
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 30 Aug 90 13:14:10 GMT
From: shlump.nac.dec.com!mountn.dec.com!hkov04.dec.com!terence@decuac.dec.com  (Terence Lee @HKO, Digital Equipment Corporation)
Organization: Digital Equipment Corporation, Hong Kong
Subject: Re: Determining memory size
Message-Id: <1891@mountn.dec.com>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com


    Here is a program that I use to display physical memory information on
    my VAXstation. For those who want to know how to get the physical memory
    size, take a look at the size_memory() routine.

    REQUIRES cmexec to execute. Use at own risk.

    -Terry

/******************************************************************************
 *                                                                            *
 *	Program: PHMEM.C                                                      *
 *	    This program display physical memory information on a window and  *
 *	    allows the user to free the memory (ONLY if FREEMEM.EXE exists)   *
 *	    when desired.                                                     *
 *                                                                            *
 *	Notes:                                                                *
 *	    - To link this program, use the following command:                *
 *	      $ link phmem,sys$input/opt                                      *
 *	      sys$system:sys.stb/sel                                          *
 *	      sys$share:decw$xlibshr/share                                    *
 *	      ^Z                                                              *
 *                                                                            *
 *	Author:                                                               *
 *	    Terence Lee (DEC/HKO), 2-Aug-1990                                 *
 *                                                                            *
 *	Modification history:                                                 *
 *                                                                            *
 ******************************************************************************/

#include <time.h>
#include <ssdef.h>
#include <stdio.h>
#include <clidef.h>
#include <jpidef.h>
#include <syidef.h>
#include <descrip.h>
#include <x11/xlib.h>
#include <x11/xutil.h>

#define USED_FONT	"-*-Menu-*-*-*--*-120-*-*-*-*-*-*"
#define DISMISS		"Dismiss"	/* Label of the Dismiss button. */
#define FREEMEM		"Freemem"	/* Label of the Freemem button. */
#define MAXTIMEOUT	60		/* Maximum timeout period. */
#define MAXPROCESS	20		/* Maximum 20 processes. */
#define TIMEOUT		3		/* Timeout in 3 seconds. */
#define HPROC		5		/* Top n processes. */
#define NDT$_MPM0	0x0040		/* Multiport adapter type code. */
#define NDT$_MPM1	0x0041		/* Multiport adapter type code. */
#define NDT$_MPM2	0x0042		/* Multiport adapter type code. */
#define NDT$_MPM3	0x0043		/* Multiport adapter type code. */
#define RPB$C_MEMDSCSIZ	0x0008		/* Size of a memory descriptor. */
#define RPB$C_NMEMDSC	0x0008		/* Max. number of memory descriptor. */
#define RPB$L_BOOTR5	0x0030		/* Address RPB copy of R5. */
#define RPB$L_MEMDSC	0x00bc		/* Address of memory descriptor. */
#define RPB$S_PAGCNT	0x0018		/* Size of Page count. */
#define RPB$S_TR	0x0008		/* Size of TR number. */
#define RPB$V_MPM	0x000b		/* Use MPM only. */
#define RPB$V_PAGCNT	0x0000		/* Start bit of Page count. */
#define RPB$V_TR	0x0018		/* Start bit of TR number. */
#define RPB$V_USEMPM	0x000c		/* Use MPM also. */

typedef struct {
    short   status;
    short   count;
    long    other_info;
} IOSB;					/* IO status block definition. */
typedef struct {
    short   buffer_length;
    short   item_code;
    char    *buffer_address;
    short   *return_length_address;
} ITMLST_3;				/* VMS item list 3 definition. */
typedef struct {
    char    prcnam[16];
    long    pages;
} HIGHEST_PROCESS;			/* Highest processes. */

globalref mmg$gl_phypgcnt;		/* Number of physical pages. */
globalref sch$gl_freecnt;		/* Number of free pages. */
globalref sch$gl_mfycnt;		/* Number of modified pages. */
globalref exe$gl_confregl;		/* Address of TR/adapter type array. */
globalref exe$gl_rpb;			/* Address of RPB. */
unsigned long status;			/* Return status. */
IOSB	iosb;				/* IO status block. */
Display	*dpy;				/* X window graphic display. */
Screen	*scr;				/* The graphic display's screen. */
Window	dwin;				/* The display window. */
Pixmap	dpix;				/* Backing store for display window. */
Window	b1win,b2win;			/* The button windows. */
Pixmap	b1pix,b2pix;			/* Backing store for buttons. */
GC	gc1,gc2;			/* The windows' graphic contexts. */
XEvent	event;				/* The event that we are looking for. */
int	depth;				/* Depth of the screen. */
int	dx,dy;				/* Position of the display window. */
int	dwidth,dheight;			/* Dimension of the display window. */
int	b1x,b1y,b2x,b2y;		/* Position of the buttons. */
int	bwidth,bheight;			/* Dimension of the buttons. */
int	font_w,font_h;			/* Maximum bounding box of font. */
int	baseline;			/* Baseline of font. */
int	mem_phy_pages;			/* Size in page of physical memory. */
int	timeout = TIMEOUT;		/* Timeout period. */
int	hproc = HPROC;			/* Top n processes. */
float	darea;				/* Number of lines in display area. */
float	h_dwin,w_dwin;			/* Size of the window in character. */
float	h_bwin,w_bwin;			/* Size of the buttons in character. */
char	buf[64];			/* Just a character buffer. */
$DESCRIPTOR(freemem,"freemem");		/* Descriptor of the freemem command. */
$DESCRIPTOR(freep,"Free memory");	/* Descriptor for the process name. */
int	size_memory();			/* CMEXEC routine to get memory size. */

main(argc,argv)
int	argc;
char	**argv;
{
	long	ppgcnt,gpgcnt,pages,pidadr,i,j,finish,col1,col2;
	char	prcnam[16],*endptr;
	HIGHEST_PROCESS hp[MAXPROCESS];
	ITMLST_3 jpi_item[] = {
	    15,JPI$_PRCNAM,prcnam,0,4,JPI$_PPGCNT,&ppgcnt,0,
	    4,JPI$_GPGCNT,&gpgcnt,0,0,0,0,0
	};
	time_t	start,now;

	while (++argv,--argc) {
	    if (!strcmp("-p",*argv) && (++argv,--argc)) {
		hproc = strtol(*argv,&endptr,10);
		if (hproc > MAXPROCESS || hproc < 1 || *endptr)
		    hproc = HPROC;
		continue;
	    }
	    if (!strcmp("-t",*argv) && (++argv,--argc)) {
		timeout = strtol(*argv,&endptr,10);
		if (timeout > MAXTIMEOUT || timeout < 1 || *endptr)
		    timeout = TIMEOUT;
		continue;
	    }
	    xfail("usage: phmem [-p nprocesses] [-t seconds]\n");
	}
	darea = 0.5 + hproc + 2;
	h_bwin = 1.5; w_bwin = 7;
	h_dwin = 0.5 + hproc + 2 + h_bwin + 1; w_dwin = 25;
	connect_x_server();
	col1 = font_w; col2 = font_w * 18;

	/*
	 *  Get size of physical memory in page. The size_memory() routine
	 *  is a hack. It is a VAXC translation of the VMS SHOW MEMORY
	 *  command's size_memory() routine. CMEXEC required, so, be
	 *  careful!
	 */

	status = sys$cmexec(size_memory,0);
	if (status != SS$_NORMAL) vmsfail(status);

	/*
	 *  Loop until someone press one of the mouse button on the Dismiss
	 *  button.
	 */

	for (finish = 0; !finish; ) {
	    for (i = 0; i < hproc; i++) hp[i].pages = 0;

	    /*
	     *	Loop the system and find out the top "hproc" processes that
	     *	used most of the physical memory. The physical memory
	     *	usuage information can be obtained by adding jpi$_ppgcnt
	     *	and jpi$_gpgcnt.
	     */

	    for (pidadr = -1; ; ) {
		status = sys$getjpiw(0,&pidadr,0,jpi_item,&iosb,0,0);
		if (status == SS$_NOMOREPROC) break;
		if (status != SS$_NORMAL) vmsfail(status);
		if (iosb.status != SS$_NORMAL) vmsfail(iosb.status);
		pages = ppgcnt + gpgcnt;
		for (i = 0; i < hproc; i++) {
		    if (pages > hp[i].pages) {
			for (j = hproc - 2; j >= i; j--) {
			    strcpy(hp[j + 1].prcnam,hp[j].prcnam);
			    hp[j + 1].pages = hp[j].pages;
			}
			strcpy(hp[i].prcnam,prcnam);
			hp[i].pages = pages;
			break;
		    }
		}
	    }

	    /*
	     *	Print the information about physical memory usage of the
	     *	system onto the backing store. The backing store will
	     *	be used to repaint the window.
	     *	Memory usage information:
	     *	    Total pages = mmg$gl_phypgcnt
	     *	    Free pages = sch$gl_freecnt
	     *	    Modified pages = sch$gl_mfycnt
	     *	    Used pages = Total pages - Free pages - Modified pages
	     */

	    XFillRectangle(dpy,dpix,gc2,0,0,dwidth,(int)(font_h * darea));
	    for (i = 0; i < hproc; i++) {
		j = (int)(font_h * (i + 0.5) + baseline);
		sprintf(buf,"%d",hp[i].pages);
		XDrawString(dpy,dpix,gc1,
		    col1,j,hp[i].prcnam,strlen(hp[i].prcnam));
		XDrawString(dpy,dpix,gc1,col2,j,buf,strlen(buf));
	    }
	    sprintf(buf,"Total: %dMb    Free Pages: %d (%d%%)",
		mem_phy_pages * 512 / 1024 / 1024,sch$gl_freecnt,
		sch$gl_freecnt * 100 / mem_phy_pages);
	    XDrawString(dpy,dpix,gc1,col1,
		(int)(font_h * (hproc + 1.0) + baseline),buf,strlen(buf));
	    XCopyArea(dpy,dpix,dwin,gc1,0,0,dwidth,(int)(font_h * darea),0,0);

	    /*
	     *	Wait for "timeout" seconds to elapse before we go back to
	     *	update the information again.
	     */

	    for (now = start = time(0); now - start < timeout; now = time(0)) {
		if (!dispatch_events()) {
		    finish = 1;
		    break;
		}
	    }
	}
	close_connection();
}

/*
 *  X window stuff. Connect to the X server, create a window with a Dismiss
 *  button and a Freemem button. The Physical memory usage information will
 *  be displayed in that window.
 */

int connect_x_server()
{
	XSetWindowAttributes xswa;
	XSizeHints xsh;
	XWMHints xwmh;
	XFontStruct *xfs;
	XGCValues xgcv;
	XPoint	xp[6];
	ITMLST_3 syi_item[] = { 15,SYI$_NODENAME,buf,0,0,0,0,0 };

	/*
	 *  Get the node name and use it as the window title.
	 */

	status = sys$getsyiw(0,0,0,syi_item,&iosb,0,0);
	if (status != SS$_NORMAL) vmsfail(status);
	if (iosb.status != SS$_NORMAL) vmsfail(iosb.status);

	/*
	 *  Now, try to open the X server connection. Ready? Get the screen.
	 *  We have to load the font too. We need the font information to
	 *  calculate the windows' size.
	 */

	dpy = XOpenDisplay(dpy);
	if (!dpy) xfail("phmem: cannot connect to the X server\n");
	scr = DefaultScreenOfDisplay(dpy);
	depth = DefaultDepthOfScreen(scr);
	xfs = XLoadQueryFont(dpy,USED_FONT);
	if (!xfs) xfail("phmem: cannot load font\n");

	/*
	 *  Calculate the maximum height of the characters in the font
	 *  (including line spacing). The formula is:
	 *	maximum height of character =
	 *	    xfs->max_bounds.ascent + xfs->max_bounds.descent
	 *	line spacing =
	 *	    xfs->ascent - xfs->max_bounds.ascent +
	 *	    xfs->descent - xfs->max_bounds.descent
	 *	font height =
	 *	    maximum height of character + line spacing
	 *
	 *  Calculate the maximum width of the characters in the font.
	 *  The formula is:
	 *	font width =
	 *	    xfs->max_bounds.rbearing - xfs->min_bounds.lbearing
	 *
	 *  The baseline of the font is xfs->max_bounds.ascent. Xlib will
	 *  print characters relative to this baseline.
	 */

	font_h = xfs->max_bounds.ascent + xfs->max_bounds.descent +
	    xfs->ascent - xfs->max_bounds.ascent +
	    xfs->descent - xfs->max_bounds.descent;
	font_w = xfs->max_bounds.rbearing - xfs->min_bounds.lbearing;
	baseline = xfs->max_bounds.ascent;

	/*
	 *  Create the main window and a pixmap for backing store. We don't
	 *  rely on the X server to do backing store for us, and indeed, some
	 *  server didn't implement it. For this window, we only want the
	 *  expose event.
	 */

	dwidth = (int)(w_dwin * font_w);
	dheight = (int)(h_dwin * font_h);
	dx = (WidthOfScreen(scr) - dwidth) / 2 - 1;
	dy = (HeightOfScreen(scr) - dheight) / 2 - 1;
	xswa.background_pixel = WhitePixelOfScreen(scr);
	xswa.event_mask = ExposureMask;
	dwin = XCreateWindow(dpy,RootWindowOfScreen(scr),dx,dy,
	    dwidth,dheight,1,depth,InputOutput,DefaultVisualOfScreen(scr),
	    CWEventMask | CWBackPixel,&xswa);
	dpix = XCreatePixmap(dpy,dwin,dwidth,dheight,depth);
	if (!dpix) xfail("phmem: cannot create pixmap for main window\n");

	/*
	 *  Create the Dismiss button and the Freemem button. Each of them
	 *  will have their own backing store. For these windows, we only
	 *  want the button press event.
	 */

	bwidth = (int)(w_bwin * font_w);
	bheight = (int)(h_bwin * font_h);
	b1x = (dwidth - (bwidth + 2) * 2) / 3;
	b1y = (int)(font_h * (h_dwin - h_bwin - 1) - 1);
	b2x = (dwidth - (bwidth + 2) * 2) / 3 * 2 + (bwidth + 2);
	b2y = b1y;
	xswa.event_mask = ButtonPressMask;
	b1win = XCreateWindow(dpy,dwin,b1x,b1y,
	    bwidth,bheight,1,depth,InputOutput,DefaultVisualOfScreen(scr),
	    CWEventMask | CWBackPixel,&xswa);
	b1pix = XCreatePixmap(dpy,b1win,bwidth,bheight,depth);
	if (!b1pix) xfail("phmem: cannot create pixmap for button\n");
	b2win = XCreateWindow(dpy,dwin,b2x,b2y,
	    bwidth,bheight,1,depth,InputOutput,DefaultVisualOfScreen(scr),
	    CWEventMask | CWBackPixel,&xswa);
	b2pix = XCreatePixmap(dpy,b2win,bwidth,bheight,depth);
	if (!b2pix) xfail("phmem: cannot create pixmap for button\n");

	/*
	 *  The bit value of a newly created pixmap is undefine, therefore
	 *  we have to clear up the pixmap and set it to the color we want.
	 *  Since the pixmap only takes the foreground value to define the
	 *  pixel value, therefore, in order to use XFillRectangle() to
	 *  cleanup the pixmap, we need a gc whose foreground value equals
	 *  to the window's background so that the pixmap will be having
	 *  the same color as the window's background. We also need a gc
	 *  whose foreground value is different from that of the window's
	 *  background so that we can use it to draw graphic objects on
	 *  the window.
	 */

	xgcv.font = xfs->fid;
	xgcv.foreground = BlackPixelOfScreen(scr);
	xgcv.background = WhitePixelOfScreen(scr);
	gc1 = XCreateGC(dpy,dwin,GCForeground | GCBackground | GCFont,&xgcv);
	xgcv.foreground = WhitePixelOfScreen(scr);
	xgcv.background = BlackPixelOfScreen(scr);
	gc2 = XCreateGC(dpy,dwin,GCForeground | GCBackground | GCFont,&xgcv);
	XFillRectangle(dpy,dpix,gc2,0,0,dwidth,dheight);
	XFillRectangle(dpy,b1pix,gc2,0,0,bwidth,bheight);
	XFillRectangle(dpy,b2pix,gc2,0,0,bwidth,bheight);

	/*
	 *  Heres comes the button labels. Xlib draw text relative to the
	 *  baseline and the origin. The baseline is logically viewed as
	 *  the x axis and a character's baseline is equal to the ascent
	 *  of that character. We also need to draw the shadow of the
	 *  buttons on the main window. Finally, lets set the buttons'
	 *  backing stores as their background pixmap so that we don't
	 *  need to handle their expose event.
	 */

	XDrawString(dpy,b1pix,gc1,
	    (bwidth - XTextWidth(xfs,DISMISS,strlen(DISMISS))) / 2,
	    (int)(font_h * (h_bwin - 1) * 0.5 + baseline),
	    DISMISS,strlen(DISMISS));
	XSetWindowBackgroundPixmap(dpy,b1win,b1pix);
	XDrawString(dpy,b2pix,gc1,
	    (bwidth - XTextWidth(xfs,FREEMEM,strlen(FREEMEM))) / 2,
	    (int)(font_h * (h_bwin - 1) * 0.5 + baseline),
	    FREEMEM,strlen(FREEMEM));
	XSetWindowBackgroundPixmap(dpy,b2win,b2pix);
	xp[0].x = b1x + bwidth + 2; xp[0].y = b1y;
	xp[1].x = 0; xp[1].y = bheight + 2;
	xp[2].x = -(bwidth + 2); xp[2].y = 0;
	xp[3].x = 5; xp[3].y = 5;
	xp[4].x = bwidth + 2; xp[4].y = 0;
	xp[5].x = 0; xp[5].y = -(bheight + 2);
	XFillPolygon(dpy,dpix,gc1,xp,6,Nonconvex,CoordModePrevious);
	xp[0].x = b2x + bwidth + 2; xp[0].y = b2y;
	XFillPolygon(dpy,dpix,gc1,xp,6,Nonconvex,CoordModePrevious);
	XSetWindowBackgroundPixmap(dpy,dwin,dpix);

	/*
	 *  Here comes the size and the position of the main window.
	 */

	xsh.x = dx;
	xsh.y = dy;
	xsh.width = xsh.min_width = xsh.max_width = dwidth;
	xsh.height = xsh.min_height = xsh.max_height = dheight;
	xsh.flags = PPosition | PSize | PMinSize | PMaxSize;

	/*
	 *  We don't want input focus. We don't have input anyway.
	 */

	xwmh.input = 0;
	xwmh.flags = InputHint;

	/*
	 *  Tell the window manager about the main window's attribute and then
	 *  map the windows one at a time. Hope that the window manager will
	 *  follow the hints.
	 */

	XSetNormalHints(dpy,dwin,&xsh);
	XSetWMHints(dpy,dwin,&xwmh);
	XStoreName(dpy,dwin,strcat(buf,": memory usage"));
	XMapWindow(dpy,b1win);
	XMapWindow(dpy,b2win);
	XMapWindow(dpy,dwin);
}

/*
 *  Whats next? Is there any interesting event in the event queue?
 */

int dispatch_events()
{
	if (XPending(dpy)) {
	    XNextEvent(dpy,&event);
	    switch (event.type) {
		case Expose:
		    return(repaint_window());
		case ButtonPress:
		    return(fix_button());
	    }
	}
	return(1);
}

/*
 *  Expose event? Great! Repaint the event window using its own backing store.
 */

int repaint_window()
{
	/*
	 *  Since same window may have more than 1 expose events, therefore,
	 *  in order to optimize redisplay, we will ignore all expose events
	 *  until we found 1 that has a zero counts. Xlib sets the count to
	 *  0 when no more expose events follow for this window.
	 */

	if (event.xexpose.count != 0) return;

	/*
	 *  From the event structure, find out the event window and repaint
	 *  it. The window will be repainted using its own backing store.
	 */

	if (event.xexpose.window == dwin) {
	    XCopyArea(dpy,dpix,dwin,gc1,0,0,dwidth,dheight,0,0);
	    return(1);
	}
}

/*
 *  ButtonPress event? O.K. lets fix the button.
 */

int fix_button()
{
	/*
	 *  If the event window is one of the buttons, then make that button
	 *  into reverse video. In the XCopyPlane() operation, the plane's 1s
	 *  will be replaced by the gc's foreground color, and the 0s will be
	 *  replaced by the gc's background color.
	 */

	if (event.xbutton.window == b1win) {
	    XCopyPlane(dpy,b1pix,b1pix,gc1,0,0,bwidth,bheight,0,0,1);
	    XSetWindowBackgroundPixmap(dpy,b1win,b1pix);
	    XClearWindow(dpy,b1win);
	    return(0);
	}
	if (event.xbutton.window == b2win) {
	    XCopyPlane(dpy,b2pix,b2pix,gc1,0,0,bwidth,bheight,0,0,1);
	    XSetWindowBackgroundPixmap(dpy,b2win,b2pix);
	    XClearWindow(dpy,b2win);
	    XSync(dpy,1);
	    status = lib$spawn(&freemem,0,0,&CLI$M_NOWAIT,&freep,0,0,0,0,0,0,0);
	    if (status != SS$_NORMAL && status != SS$_DUPLNAM) vmsfail(status);
	    XSync(dpy,0);
	    XCopyPlane(dpy,b2pix,b2pix,gc1,0,0,bwidth,bheight,0,0,1);
	    XSetWindowBackgroundPixmap(dpy,b2win,b2pix);
	    XClearWindow(dpy,b2win);
	    return(1);
	}
}

/*
 *  If a X server connection exists, then close it.
 */

int close_connection()
{
	if (dpy) XCloseDisplay(dpy);
}

/*
 *  Calculate the total amount of physical memory in use by system. This total
 *  does not include multiport memory being used as shared memory.
 */

int size_memory()
{
	register r0,r1,r2,r3,r4,r5;
	int	temp,local_memory,shared_memory;

	/*
	 *  Get address of TR/adapter type array, address of RPB, address of
	 *  memory descriptors, and maximum number of memory descriptor.
	 */

	r0 = exe$gl_confregl;
	r1 = exe$gl_rpb;
	r2 = RPB$L_MEMDSC + r1;
	r5 = RPB$C_NMEMDSC;

	/*
	 *  Now, loop the memory descriptors.
	 */

	local_memory = shared_memory = 0;
	while (*(int *)r2) {

	    /*
	     *	Get TR number and page count. Convert the TR number to
	     *	adapter type.
	     */

	    temp = 32 - (RPB$V_TR + RPB$S_TR);
	    r3 = *(int *)r2 << temp >> (temp + RPB$V_TR);
	    temp = 32 - (RPB$V_PAGCNT + RPB$S_PAGCNT);
	    r4 = *(int *)r2 << temp >> (temp + RPB$V_PAGCNT);
	    r3 = *(int *)(r0 + r3);

	    /*
	     *	If adapter is not within MPM range, then this is local
	     *	memory, otherwise, this is multiport memory.
	     */

	    if ((unsigned)r3 < NDT$_MPM0 || (unsigned)r3 > NDT$_MPM3)
		local_memory += r4;
	    else
		shared_memory += r4;

	    /*
	     *	Get next memory descriptor. Loop until end of memory
	     *	descriptor list or maximum reached.
	     */

	    r2 += RPB$C_MEMDSCSIZ;
	    if (!(--r5)) break;
	}

	/*
	 *  Multiprocessor configuration (MPM)? The system is only using the
	 *  shared memory.
	 */

	if (*(int *)(RPB$L_BOOTR5 + r1) && (1 << RPB$V_MPM)) {
	    mem_phy_pages = shared_memory;
	    return(1);
	}

	/*
	 *  The system is using the local memory.
	 */

	mem_phy_pages = local_memory;

	/*
	 *  If USEMPM, the system is using the shared memory too.
	 */

	if (*(int *)(RPB$L_BOOTR5 + r1) && (1 << RPB$V_USEMPM)) {
	    mem_phy_pages += shared_memory;
	}
	return(1);
}

/*
 *  X error! Lets get the hell out of here. But don't forget to clean up the
 *  X server connection before we leave.
 */

int xfail(msg)
char	*msg;
{
	close_connection();
	fputs(msg,stderr);
	exit(0);
}

/*
 *  VMS error! Lets get the hell out of here. But don't forget to clean up the
 *  X server connection before we leave.
 */

int vmsfail(status)
unsigned long status;
{
	close_connection();
	exit(status);
}

================================================================================
Terence Lee          | terence%hkov04.dec@decwrl.dec.com
                     | root%hkvs04.dec@decwrl.dec.com
=====================+==========================================================
From Middlesex, UWO  | Just forget what I've said. I don't know it neither.
================================================================================

