Subject: BSD to HP-UX porting tricks (LONG) (last updated: 16-Jun-1999)
From: [email-masked] (System Admin (Mike Peterson))
Newsgroups: comp.sys.hp.hpux

HP TRICKS (home-grown and from news postings)
	=============================================

Things to watch out for:

TIOCNOTTY     - this is not available on HP-UX (use setsid()); trying
		to open '/dev/tty' from a "daemon" will also fail with
		status -1.
setpgrp       - this doesn't get rid of the controlling terminal
		if you use -lBSD (use setsid() instead).
BSD signals   - you almost certainly want them if your program came from
		a BSD system (e.g. Sun); this breaks 'setpgrp' (and
		'system' if your process has active child processes)
		among other things (see 'man bsdproc' and notes below).
		If all you need are BSD signals, try the replacement signal
		routine below which gives BSD semantics without all the
		other damage that using "-lBSD" causes.
/etc/utmp     - contains all sorts of records, not just current logins;
		old login records are found if file is read blindly.
setproctitle  - doesn't work as expected (see notes below for a version
		that really does work on HP-UX).
compiler defs - in K&R mode (the default), the HP C compiler defines "hpux";
		in ANSI mode (-Aa or -Ae), the HP C compiler defines "__hpux";
		the gcc (Gnu) C compiler defines "__hpux__". Thanks to
		Jarkko Hietaniemi <[email-masked]> for this note.

***************************************************************************

John Agosta <[email-masked]>, from HP has contributed some notes on
the HP Porting Guide (which incorporates many of the things from this
posting).

Announcing the SunOS to HP-UX 9.05 Porting Guide
------------------------------------------------

If you are migrating software to HP-UX, or are just new to the HP-UX
development environment, HP has a new document which should help ease your
transition.  It is the "SunOS to HP-UX 9.05 Porting Guide".

This guide provides, in one easily readable document, information and
hints on porting from SunOS 4.1.3 to HP-UX 9.0x.  A porting experience
from SunOS to HP-UX need not, and should not be a painful one.  HP-UX has
many of the same system calls and tools found on SunOS.  This guide should
direct you through most of the known differences, and facilitate your
introduction to the development environment on HP-UX.

The guide is an assimilation of white papers, discussions on the Internet,
and most importantly, the real life porting experiences of customer and HP
engineers, channel partners, and ISVs porting from SunOS to HP-UX.

Even though this guide centers on the differences between SunOS 4.1.3 and
HP-UX, many customers who are new to HP-UX have found this document
extremely valuable.  This guide is highly recommended to anyone moving
from *SunOS, *Solaris, *AIX, or any other form of *UNIX to HP-UX.

How to Obtain the Porting Guide
-------------------------------

Electronic copies of the guide are available via the Interworks Library.
The Library may be accessed via:

	FTP:      www.interworks.org
	WWW URL:  ftp://www.interworks.org

/pub/comp.hp/porting_info/
	sun_hpux_port_ascii_0295	ASCII version of the Porting Guide
	sun_hpux_port_html_0295.tar	WWW HTML version of the Porting Guide
	sun_hpux_port_ps_0295.tar	Postscript (level 3) version

Hard copies are available free of charge ONLY through your local HP Sales
Representative.  Please reference HP Literature Distribution Center
document number 5963-5416E.

I have appended the Table of Contents for the "SunOS to HP-UX 9.05
Porting Guide" to the end of this posting.

***************************************************************************

Individual routines you may find helpful:
-----------------------------------------

getwd:
------

/*
 * Replace 'SIZEOFARG-1' with the declared size of "arg", minus 1.
 *
 * For non-ANSI C, you can use -lBSD to load getwd, but see
 * 'man bsdproc' for other things that will been changed/damaged.
 */

#if defined(__hpux) || defined(__hpux__)
#include <unistd.h>
#define getwd(arg)	getcwd(arg, (size_t) SIZEOFARG-1)
#else
char	*getwd();
#endif

getrusage:
----------

From: [email-masked] (Scott Miller)
Newsgroups: comp.sys.hp
Subject: Re: Where is getrusage()? (Summary)
Organization: UF EE Department

getrusage() is in the syscall includes

Here is the code fragment I used:

#if defined(hpux) || defined(__hpux) || defined(__hpux__)
#include <sys/syscall.h>
#define getrusage(a, b)  syscall(SYS_GETRUSAGE, a, b)
#endif /* hpux */

srandom, random:
----------------

For a conversion to a "better" generator, use:

#define srandom srand48
#define random lrand48

For a simple conversion, use:

From: [email-masked] (Mike O'Connor)
Newsgroups: comp.sys.hp
Subject: Re: random and srandom on HP9000/720 with HPUX-8.07

#define srandom srand
#define random rand

From: [email-masked] (Walter Misar)

> #define srandom srand48
> #define random lrand48

This can have very strange effects since lrand() returns numbers between
0 and 2^31, but random() is supposed to return 31 bit random numbers.
Perhaps a better solution would be:

#define random() ((long)(drand48()*(unsigned)(1<<31)))

getdtablesize:
--------------

/*
 * getdtablesize ()
 *
 * Returns the maximum number of file descriptors allowed.
 */

#include <unistd.h>

	int
getdtablesize ()
{
	return(sysconf(_SC_OPEN_MAX));
}

usleep:
-------

/*
 *  NAME:
 *      usleep     -- This is the precision timer for Test Set
 *                    Automation. It uses the select(2) system
 *                    call to delay for the desired number of
 *                    micro-seconds. This call returns ZERO
 *                    (which is usually ignored) on successful
 *                    completion, -1 otherwise.
 *
 *  ALGORITHM:
 *      1) We range check the passed in microseconds and log a
 *         warning message if appropriate. We then return without
 *         delay, flagging an error.
 *      2) Load the Seconds and micro-seconds portion of the
 *         interval timer structure.
 *      3) Call select(2) with no file descriptors set, just the
 *         timer, this results in either delaying the proper
 *         ammount of time or being interupted early by a signal.
 *
 *  HISTORY:
 *      Added when the need for a subsecond timer was evident.
 *
 *  AUTHOR:
 *      Michael J. Dyer                   Telephone:   AT&T 414.647.4044
 *      General Electric Medical Systems        GE DialComm  8 *767.4044
 *      P.O. Box 414  Mail Stop 12-27         Sect'y   AT&T 414.647.4584
 *      Milwaukee, Wisconsin  USA 53201                      8 *767.4584
 *      internet:  [email-masked]     GEMS WIZARD e-mail: DYER
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/types.h>

int     usleep( unsigned long int microSeconds )
{
        unsigned int            Seconds, uSec;
        int                     nfds, readfds, writefds, exceptfds;
        struct  timeval         Timer;

        nfds = readfds = writefds = exceptfds = 0;

        if( (microSeconds == (unsigned long) 0)
                || microSeconds > (unsigned long) 4000000 )
        {
                errno = ERANGE;         /* value out of range */
                perror( "usleep time out of range ( 0 -> 4000000 ) " );
                return -1;
        }

        Seconds = microSeconds / (unsigned long) 1000000;
        uSec    = microSeconds % (unsigned long) 1000000;

        Timer.tv_sec            = Seconds;
        Timer.tv_usec           = uSec;

        if( select( nfds, &readfds, &writefds, &exceptfds, &Timer ) < 0 )
        {
                perror( "usleep (select) failed" );
                return -1;
        }

        return 0;
}

flock:
------

/*
 * flock (fd, operation)
 *
 * This routine performs some file locking like the BSD 'flock'
 * on the object described by the int file descriptor 'fd',
 * which must already be open.
 *
 * The operations that are available are:
 *
 * LOCK_SH  -  get a shared lock.
 * LOCK_EX  -  get an exclusive lock.
 * LOCK_NB  -  don't block (must be ORed with LOCK_SH or LOCK_EX).
 * LOCK_UN  -  release a lock.
 *
 * Return value: 0 if lock successful, -1 if failed.
 *
 * Note that whether the locks are enforced or advisory is
 * controlled by the presence or absence of the SETGID bit on
 * the executable.
 *
 * Note that there is no difference between shared and exclusive
 * locks, since the 'lockf' system call in SYSV doesn't make any
 * distinction.
 *
 * The file "<sys/file.h>" should be modified to contain the definitions
 * of the available operations, which must be added manually (see below
 * for the values).
 */

#include <unistd.h>
#include <sys/file.h>
#include <errno.h>

#ifndef LOCK_SH
#define LOCK_SH 1
#endif
#ifndef LOCK_EX
#define LOCK_EX 2
#endif
#ifndef LOCK_NB
#define LOCK_NB 4
#endif
#ifndef LOCK_UN
#define LOCK_UN 8
#endif

extern int errno;

	int
flock (int fd, int operation)
{
	int i;

	switch (operation) {

	/* LOCK_SH - get a shared lock */
	case LOCK_SH:
	/* LOCK_EX - get an exclusive lock */
	case LOCK_EX:
		i = lockf (fd, F_LOCK, 0);
		break;

	/* LOCK_SH|LOCK_NB - get a non-blocking shared lock */
	case LOCK_SH|LOCK_NB:
	/* LOCK_EX|LOCK_NB - get a non-blocking exclusive lock */
	case LOCK_EX|LOCK_NB:
		i = lockf (fd, F_TLOCK, 0);
		if (i == -1)
			if ((errno == EAGAIN) || (errno == EACCES))
				errno = EWOULDBLOCK;
		break;

	/* LOCK_UN - unlock */
	case LOCK_UN:
		i = lockf (fd, F_ULOCK, 0);
		break;

	/* Default - can't decipher operation */
	default:
		i = -1;
		errno = EINVAL;
		break;
	}

	return (i);
}

/*
 * An alternative version was posted by James Gritton
 * ([email-masked]) in comp.sys.hp.
 * As far as I can tell, it works the same as the above
 * except for the "errno" values returned (and it defaults
 * an invalid operation to "unlock").
 * The definitions of LOCK_xx should be put into <sys/file.h> and/or
 * <fcntl.h>.
 * Note: this was typed in, so it may not work as given.
 */

/*
 *#include <fcntl.h>
 *#define LOCK_SH 1
 *#define LOCK_EX 2
 *#define LOCK_NB 4
 *#define LOCK_UN 8
 *
 *	int
 *flock (int fd, int operation)
 *{
 *	struct flock fl;
 *
 *	switch (operation & ~LOCK_NB) {
 *	case LOCK_SH:
 *		fl.l_type = F_RDLCK;
 *		break;
 *	case LOCK_EX:
 *		fl.l_type = F_WRLCK;
 *		break;
 *	default:
 *		fl.l_type = F_UNLCK;
 *		break;
 *	}
 *
 *	fl.l_whence = SEEK_SET;
 *	fl.l_start = fl.l_len = 0L;
 *
 *	return fcntl (fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &fl);
 *}
 */

getclktck:
----------

/*
 * getclktck ()
 *
 * Returns the value of CLK_TCK (timer resolution).
 */

#include <unistd.h>

	int
getclktck ()
{
	return(sysconf(_SC_CLK_TCK));
}

getload (works on a wide variety of systems):
---------------------------------------------

/*
 * Load average test routine - from batchd.c.
 *
 * Version: 1999/03/08.
 *
 * To build on SunOS 4.1.X:
 *	make loadave
 *
 * To build on Solaris 2.X:
 *	make CFLAGS="-g -DSUNOS5 -Dindex=strchr" LDFLAGS="-lkvm -lelf" loadave
 *
 * To build on HP-UX:
 *	make loadave
 *
 * To build on SGI IRIX:
 *	make loadave
 *
 * To build on DEC OSF (RISCos) - untested:
 *	make CFLAGS="-g -Dsun" loadave
 *
 * To build a generic version that does not read the kernel,
 * add "-DNOKMEM" to CFLAGS in the above, as in:
 *	make CFLAGS="-g -DNOKMEM" loadave
 *	make CFLAGS="-O -DNOKMEM" loadave
 *	make CFLAGS="-g -DSUNOS5 -Dindex=strchr -DNOKMEM" loadave
 *	make CFLAGS="-g -Dsun -DNOKMEM" loadave
 * In the above, '-g' can be replaced by '-O'.
 *
 * Note that 'loadave' will have to be able to read the kernel
 * symbol table and /dev/kmem (or equivalent). This means that
 * 'loadave' will need to belong to the same group as '/dev/kmem',
 * and be setgid to that group, which is done with the command:
 *	chmod g+s loadave
 * which will have to be run by 'root'. The generic version does
 * not require these special permissions.
 */

#include <stdio.h>

static void getload(double *aves);

main(argc, argv)
	int argc;
	char **argv;
{
	double load[3];

	getload(load);
	printf("load averages are %f, %f, %f\n", load[0], load[1], load[2]);
}

/*
 * getload (ave)
 *
 * This routine returns 3 double precision floats containing
 * the load averages in 'ave'.
 * If routine fails, all load averages are returned as 0.
 */

#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#include <strings.h>
#include <fcntl.h>
#include <nlist.h>
#include <errno.h>

extern int errno;

#define STRSIZ	512			/* Sprintf buffer size */
static char errstr[STRSIZ];		/* Global sprintf buffer */

int ugetloads(float *a);
static void mperror(char *s);
static char *syserr();

#define merror(a1)		fprintf(stderr,"%s",a1)
#define merror1(fmt,a1)		{ sprintf(errstr,fmt,a1); merror(errstr); }

struct	nlist nl[] = {
#ifdef stardent
# define unixpath "/unix"
	{ "avenrun" },
#else
#ifdef __hpux
# define unixpath "/hp-ux"
#ifdef __hppa       /* series 700 & 800 */
	{ "avenrun" },
#else               /* series 300 & 400 */
	{ "_avenrun" },
#endif
#else
# define unixpath "/vmunix"
#ifdef SUNOS5
	{ "avenrun" },
#else
	{ "_avenrun" },
#endif
#endif
#endif
	{ 0 },
};

#if defined(__osf__)
	static void
getload(a)
	double *a;
{
	struct tbl_loadavg la;

	if (table(TBL_LOADAVG,0,(char *)&la,1,sizeof(la)) < 0) {
		merror("table() failed\n");
		a[0] = a[1] = a[2] = 0;
	} else {
		a[0] = (double) ((float)la.tl_avenrun.l[0]/(float)la.tl_lscale);
		a[1] = (double) ((float)la.tl_avenrun.l[1]/(float)la.tl_lscale);
		a[2] = (double) ((float)la.tl_avenrun.l[2]/(float)la.tl_lscale);
	}
}
#else /* __osf__ */
#ifndef RISCos
	static void
getload(a)
	double *a;
{
	int i;
	static int kmem = -1;
#ifdef SUNOS5
# include <kvm.h>
	static kvm_t *k;
#endif
#if defined(vax) || defined(__hpux)
	double avenrun[3];
#else
	long avenrun[3];
#endif

/* Use 'uptime' output for BSD-like systems with no /dev/kmem */
#ifdef NOKMEM
	float aves[3];

	i = ugetloads(aves);
	if( i == -1 ){
		merror("ugetloads failed\n");
		goto failed;
	}
	for (i = 0; i < 3; i++)
		a[i] = aves[i];

#else /*NOKMEM*/

	if(kmem == -1) {
#ifdef SUNOS5
		if ((k = kvm_open(0, 0, 0, O_RDONLY, 0)) == 0) {
			mperror("kvm_open failed");
			goto failed;
		}
		kvm_nlist(k, nl);
		if (nl[0].n_type==0) {
			merror(" No namelist\n");
			goto failed;
		}
#else
#ifdef sgi
# include <sys/sysmp.h>
	nl[0].n_value = sysmp(MP_KERNADDR, MPKA_AVENRUN) & 0x7fffffff;
#else
		nlist(unixpath, nl);
		if (nl[0].n_type==0) {
			merror1("%s: No namelist\n", unixpath);
			goto failed;
		}
#ifdef stardent
		nl[0].n_value &= 0x7fffffff;
#endif
#endif
		if((kmem = open("/dev/kmem", 0)) == -1) {
			mperror("Can't open(/dev/kmem)");
			goto failed;
		}
#endif /* SUNOS5 */
	}
#ifdef SUNOS5
	if (kvm_read(k, nl[0].n_value, (char *)avenrun, sizeof avenrun) != sizeof avenrun) {
		mperror("Can't kvm_read");
		goto failed;
	}
	kvm_close(k);
#else
	if( lseek(kmem, (off_t)nl[0].n_value, 0) == -1 ){
		mperror("Can't lseek in kmem");
		goto failed;
	}
	if( read(kmem, (char *)avenrun, sizeof(avenrun)) != sizeof(avenrun) ){
		mperror("Can't read kmem");
		goto failed;
	}
#endif /* SUNOS5 */
	for (i = 0; i < 3; i++)
#if defined(sun) || defined(sequent)
		a[i] = (double) avenrun[i] / FSCALE;
#else
#ifdef sgi
		a[i] = (double) avenrun[i] / 1024;
#else
#if defined(BSD4_2) || defined(__hpux)
		a[i] = (double) avenrun[i];
#else
#ifdef stardent
		a[i] = (double) avenrun[i] / (1<<16);
#else
		a[i] = (double) avenrun[i] / 1024;
#endif /*stardent*/
#endif /*BSD4_2*/
#endif /*sgi*/
#endif /*sun*/
#endif /*NOKMEM*/
	return;
failed:;
	a[0] = a[1] = a[2] = 0;
}
#else /*RISCos*/
#include <sys/fixpoint.h>
	static void
getload(a)
	int *a;
{
	int i;
	static int kmem = -1;
	fix avenrun[3];

	if(kmem == -1) {
		nlist("/unix", nl);
		if (nl[0].n_type==0) {
			merror("/unix: No namelist\n");
			goto failed;
		}
		if((kmem = open("/dev/kmem", 0)) == -1) {
			mperror("Can't open(/dev/kmem)");
			goto failed;
		}
	}
	if( lseek(kmem, (off_t)nl[0].n_value, 0) == -1 ){
		mperror("Can't lseek in kmem");
		goto failed;
	}
	if( read(kmem, (char *)avenrun, sizeof(avenrun)) != sizeof(avenrun) ){
		mperror("Can't read kmem");
		goto failed;
	}
	for (i = 0; i < 3; i++)
	        a[i] = (int) FIX_TO_INT(avenrun[i]) + .5;
	return;
failed:;
	a[0] = a[1] = a[2] = 0;
}
#endif /* RISCOS */
#endif /* __osf__ */

/* ugetloads(ls)
 * float ld[3];
 *
 * Puts the 1, 5, and 15 minute load averages in the float
 * array passed to it.  This program calls upon uptime(1)
 * which could have different ways of printing ie. with bsd4.2
 * "   9:34pm  up 11 hrs,  3 users,  load average: 0.25, 0.22, 0.24  "
 *                                notice the commas -- ^ --- ^.
 * while bsd4.1 does not print commas.  The BSD41 define will
 * take care of this if that is your system, it defaults to
 * the 4.2 version.
 *
 * Author:
 *  John Bien
 *  {ihnp4 | ucbvax | decvax}!trwrb!jsb
 *
 * This routine taken from comp.sources.unix: Volume 4, Issue 78
 */

FILE *popen();

ugetloads(ld)
float ld[3];
{
    FILE *stream;
    int i;

    if((stream = popen("uptime","r")) == NULL)
	return(-1);

#ifdef BSD41
    i = fscanf(stream,"%*[^l] load average: %f %f %f", &ld[0],&ld[1],&ld[2]);
#else
    i = fscanf(stream,"%*[^l] load average: %f, %f, %f", &ld[0],&ld[1],&ld[2]);
#endif /* BSD41 */
    pclose(stream);
    return i == 3 ? 0 : -1;
}

/* Routine to print messages to stderr, appending the system error message */

	static void
mperror(char *s)
{
	char *p;
	char str[STRSIZ];	/* must have own internal buffer */

	if( (p=index(s,'\n')) != NULL )
		*p = '\0';
	sprintf(str,"%s: %s\n", s, syserr());
	if( p )
		*p = '\n';
	merror(str);
}

/* Routine to get the last system error message */

extern int sys_nerr;
extern char *sys_errlist[];

	static char *
syserr()
{
	static char buf[80];

	if (errno >= 0 && errno < sys_nerr)
		return(sys_errlist[errno]);
	sprintf(buf,"Unknown error %d", errno);
	return(buf);
}

getloadavg (works only on HP-UX 9.x):
-------------------------------------

/*
 * From: Daniel Hannigan <[email-masked]>
 *
 * This will only work on HPUX > 9.0
 */

#include <sys/pstat.h>

getloadavg (ave, na)
double *ave;
int na;
{
        struct pst_dynamic pstd;
        int i;

        if (pstat_getdynamic (&pstd, sizeof (struct pst_dynamic), 0, 0)) {
                for (i=0; i< na; ++i)
                        *(ave + i) = 0.0;
                return (-1);
        }

        switch (na) {
                case 3:
                        *(ave + 2) = pstd.psd_avg_15_min;
                case 2:
                        *(ave + 1) = pstd.psd_avg_5_min;
                case 1:
                        *(ave) = pstd.psd_avg_1_min;
                default:
                        break;
        }
        return (0);
}

getloadavg.c (works on a wide variety of systems):
--------------------------------------------------

/*
 * From: Drazen Kacar <[email-masked]>
 *
 * It doesn't test for getloadavg() on Solaris 7,
 * but that can easily be added.
 */

/* Get the system load averages.
   Copyright (C) 1985, 86, 87, 88, 89, 91, 92, 93, 1994, 1995
	Free Software Foundation, Inc.

This file is part of XEmacs.

XEmacs is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.

XEmacs is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with XEmacs; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */

#ifndef __CYGWIN32__

/* Compile-time symbols that this file uses:

   FIXUP_KERNEL_SYMBOL_ADDR()	Adjust address in returned struct nlist.
   KERNEL_FILE			Pathname of the kernel to nlist.
   LDAV_CVT()			Scale the load average from the kernel.
				Returns a double.
   LDAV_SYMBOL			Name of kernel symbol giving load average.
   LOAD_AVE_TYPE		Type of the load average array in the kernel.
				Must be defined unless one of
				apollo, DGUX, NeXT, or UMAX is defined;
				otherwise, no load average is available.
   NLIST_STRUCT			Include nlist.h, not a.out.h, and
				the nlist n_name element is a pointer,
				not an array.
   NLIST_NAME_UNION		struct nlist has an n_un member, not n_name.
   LINUX_LDAV_FILE		[__linux__]: File containing load averages.

   Specific system predefines this file uses, aside from setting
   default values if not emacs:

   apollo
   BSD				Real BSD, not just BSD-like.
   convex
   DGUX
   hpux
   MSDOS			No-op for MSDOS.
   NeXT
   sgi
   sequent			Sequent Dynix 3.x.x (BSD)
   _SEQUENT_			Sequent DYNIX/ptx 1.x.x (SYSV)
   sony_news                    NEWS-OS (works at least for 4.1C)
   UMAX
   UMAX4_3
   WIN32			No-op for Windows95/NT.
   __linux__			Linux: assumes /proc filesystem mounted.
   				Support from Michael K. Johnson.
   __NetBSD__			NetBSD: assumes /kern filesystem mounted.
   __OpenBSD__			OpenBSD: ditto.

   In addition, to avoid nesting many #ifdefs, we internally set
   LDAV_DONE to indicate that the load average has been computed.

   We also #define LDAV_PRIVILEGED if a program will require
   special installation to be able to call getloadavg.  */

/* This should always be first.  */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>

/* Both the Emacs and non-Emacs sections want this.  Some
   configuration files' definitions for the LOAD_AVE_CVT macro (like
   sparc.h's) use macros like FSCALE, defined here.  */
#ifdef unix
#include <sys/param.h>
#endif

#ifdef XEMACS
#include "lisp.h"
#include "sysfile.h" /* for encapsulated open, close, read, write */
#endif /* XEMACS */

/* Exclude all the code except the test program at the end
   if the system has its own `getloadavg' function.

   The declaration of `errno' is needed by the test program
   as well as the function itself, so it comes first.  */

#include <errno.h>

#ifndef errno
extern int errno;
#endif

#ifndef HAVE_GETLOADAVG

/* The existing Emacs configuration files define a macro called
   LOAD_AVE_CVT, which accepts a value of type LOAD_AVE_TYPE, and
   returns the load average multiplied by 100.  What we actually want
   is a macro called LDAV_CVT, which returns the load average as an
   unmultiplied double.

   For backwards compatibility, we'll define LDAV_CVT in terms of
   LOAD_AVE_CVT, but future machine config files should just define
   LDAV_CVT directly.  */

#if !defined(LDAV_CVT) && defined(LOAD_AVE_CVT)
#define LDAV_CVT(n) (LOAD_AVE_CVT (n) / 100.0)
#endif

#ifdef XEMACS
#if defined (HAVE_KSTAT_H)
#include <kstat.h>
#endif /* HAVE_KSTAT_H */
#endif /* XEMACS */

#if !defined (BSD) && defined (ultrix)
/* Ultrix behaves like BSD on Vaxen.  */
#define BSD
#endif

#ifdef NeXT
/* NeXT in the 2.{0,1,2} releases defines BSD in <sys/param.h>, which
   conflicts with the definition understood in this file, that this
   really is BSD. */
#undef BSD

/* NeXT defines FSCALE in <sys/param.h>.  However, we take FSCALE being
   defined to mean that the nlist method should be used, which is not true.  */
#undef FSCALE
#endif

/* Set values that are different from the defaults, which are
   set a little farther down with #ifndef.  */

/* Some shorthands.  */

#if defined (HPUX) && !defined (hpux)
#define hpux
#endif

#if defined(hp300) && !defined(hpux)
#define MORE_BSD
#endif

#if defined(ultrix) && defined(mips)
#define decstation
#endif

#if (defined(sun) && defined(SVR4)) || defined (SOLARIS2)
#define SUNOS_5
#endif

#if defined (__osf__) && (defined (__alpha) || defined (__alpha__))
#define OSF_ALPHA
#include <netdb.h>
#include <netinet/in.h>		/* Needed for Digital UNIX V3 */
#include <net/proto_net.h>
#include <sys/table.h>
#endif

#if defined (__osf__) && (defined (mips) || defined (__mips__))
#define OSF_MIPS
#include <sys/table.h>
#endif

/* UTek's /bin/cc on the 4300 has no architecture specific cpp define by
   default, but _MACH_IND_SYS_TYPES is defined in <sys/types.h>.  Combine
   that with a couple of other things and we'll have a unique match.  */
#if !defined (tek4300) && defined (unix) && defined (m68k) && defined (mc68000) && defined (mc68020) && defined (_MACH_IND_SYS_TYPES)
#define tek4300			/* Define by emacs, but not by other users.  */
#endif

/* VAX C can't handle multi-line #ifs, or lines longer than 256 chars.  */
#ifndef LOAD_AVE_TYPE

#ifdef MORE_BSD
#define LOAD_AVE_TYPE long
#endif

#ifdef sun
#define LOAD_AVE_TYPE long
#endif

#ifdef decstation
#define LOAD_AVE_TYPE long
#endif

#ifdef _SEQUENT_
#define LOAD_AVE_TYPE long
#endif

#ifdef sgi
#define LOAD_AVE_TYPE long
#endif

#ifdef SVR4
#define LOAD_AVE_TYPE long
#endif

#ifdef sony_news
#define LOAD_AVE_TYPE long
#endif

#ifdef sequent
#define LOAD_AVE_TYPE long
#endif

#ifdef OSF_ALPHA
#define LOAD_AVE_TYPE long
#endif

#if defined (ardent) && defined (titan)
#define LOAD_AVE_TYPE long
#endif

#ifdef tek4300
#define LOAD_AVE_TYPE long
#endif

#if defined(alliant) && defined(i860) /* Alliant FX/2800 */
#define LOAD_AVE_TYPE long
#endif

#ifdef _AIX
#define LOAD_AVE_TYPE long
#endif

#ifdef convex
#define LOAD_AVE_TYPE double
#ifndef LDAV_CVT
#define LDAV_CVT(n) (n)
#endif
#endif

#endif /* No LOAD_AVE_TYPE.  */

#ifdef OSF_ALPHA
/* <sys/param.h> defines an incorrect value for FSCALE on Alpha OSF/1,
   according to [email-masked].  */
#undef FSCALE
#define FSCALE 1024.0
#endif

#if defined(alliant) && defined(i860) /* Alliant FX/2800 */
/* <sys/param.h> defines an incorrect value for FSCALE on an
   Alliant FX/2800 Concentrix 2.2, according to [email-masked].  */
#undef FSCALE
#define FSCALE 100.0
#endif

#ifndef	FSCALE

/* SunOS and some others define FSCALE in sys/param.h.  */

#ifdef MORE_BSD
#define FSCALE 2048.0
#endif

#if defined(MIPS) || defined(SVR4) || defined(decstation)
#define FSCALE 256
#endif

#if defined (sgi) || defined (sequent)
/* Sometimes both MIPS and sgi are defined, so FSCALE was just defined
   above under #ifdef MIPS.  But we want the sgi value.  */
#undef FSCALE
#define	FSCALE 1000.0
#endif

#if defined (ardent) && defined (titan)
#define FSCALE 65536.0
#endif

#ifdef tek4300
#define FSCALE 100.0
#endif

#ifdef _AIX
#define FSCALE 65536.0
#endif

#endif	/* Not FSCALE.  */

#if !defined (LDAV_CVT) && defined (FSCALE)
#define	LDAV_CVT(n) (((double) (n)) / FSCALE)
#endif

/* VAX C can't handle multi-line #ifs, or lines longer that 256 characters.  */
#ifndef NLIST_STRUCT

#ifdef MORE_BSD
#define NLIST_STRUCT
#endif

#ifdef sun
#define NLIST_STRUCT
#endif

#ifdef decstation
#define NLIST_STRUCT
#endif

#ifdef hpux
#define NLIST_STRUCT
#endif

#if defined (_SEQUENT_) || defined (sequent)
#define NLIST_STRUCT
#endif

#ifdef sgi
#define NLIST_STRUCT
#endif

#ifdef SVR4
#define NLIST_STRUCT
#endif

#ifdef sony_news
#define NLIST_STRUCT
#endif

#ifdef OSF_ALPHA
#define NLIST_STRUCT
#endif

#if defined (ardent) && defined (titan)
#define NLIST_STRUCT
#endif

#ifdef tek4300
#define NLIST_STRUCT
#endif

#ifdef butterfly
#define NLIST_STRUCT
#endif

#if defined(alliant) && defined(i860) /* Alliant FX/2800 */
#define NLIST_STRUCT
#endif

#ifdef _AIX
#define NLIST_STRUCT
#endif

#endif /* defined (NLIST_STRUCT) */

#if defined(sgi) || (defined(mips) && !defined(BSD))
#define FIXUP_KERNEL_SYMBOL_ADDR(nl) ((nl)[0].n_value &= ~(1 << 31))
#endif

#if !defined (KERNEL_FILE) && defined (sequent)
#define KERNEL_FILE "/dynix"
#endif

#if !defined (KERNEL_FILE) && defined (hpux)
#define KERNEL_FILE "/hp-ux"
#endif

#if !defined(KERNEL_FILE) && (defined(_SEQUENT_) || defined(MIPS) || defined(SVR4) || defined(ISC) || defined (sgi) || defined(SVR4) || (defined (ardent) && defined (titan)))
#define KERNEL_FILE "/unix"
#endif

#if !defined (LDAV_SYMBOL) && defined (alliant)
#define LDAV_SYMBOL "_Loadavg"
#endif

#if !defined(LDAV_SYMBOL) && ((defined(hpux) && !defined(hp9000s300)) || defined(_SEQUENT_) || defined(SVR4) || defined(ISC) || defined(sgi) || (defined (ardent) && defined (titan)) || defined (_AIX))
#define LDAV_SYMBOL "avenrun"
#endif

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <stdio.h>

/* LOAD_AVE_TYPE should only get defined if we're going to use the
   nlist method.  */
#if !defined(LOAD_AVE_TYPE) && (defined(BSD) || defined(LDAV_CVT) || defined(KERNEL_FILE) || defined(LDAV_SYMBOL))
#define LOAD_AVE_TYPE double
#endif

#ifdef LOAD_AVE_TYPE

#ifndef NLIST_STRUCT
#include <a.out.h>
#else /* NLIST_STRUCT */
#include <nlist.h>
#endif /* NLIST_STRUCT */

#ifdef SUNOS_5
#include <fcntl.h>
#include <kvm.h>
#endif

#ifndef KERNEL_FILE
#define KERNEL_FILE "/vmunix"
#endif /* KERNEL_FILE */

#ifndef LDAV_SYMBOL
#define LDAV_SYMBOL "_avenrun"
#endif /* LDAV_SYMBOL */

#ifndef LDAV_CVT
#define LDAV_CVT(n) ((double) (n))
#endif /* !LDAV_CVT */

#endif /* LOAD_AVE_TYPE */

#ifdef NeXT
#ifdef HAVE_MACH_MACH_H
#include <mach/mach.h>
#else
#include <mach.h>
#endif
#endif /* NeXT */

#ifdef sgi
#include <sys/sysmp.h>
#endif /* sgi */

#ifdef UMAX
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/syscall.h>

#ifdef UMAX_43
#include <machine/cpu.h>
#include <inq_stats/statistics.h>
#include <inq_stats/sysstats.h>
#include <inq_stats/cpustats.h>
#include <inq_stats/procstats.h>
#else /* Not UMAX_43.  */
#include <sys/sysdefs.h>
#include <sys/statistics.h>
#include <sys/sysstats.h>
#include <sys/cpudefs.h>
#include <sys/cpustats.h>
#include <sys/procstats.h>
#endif /* Not UMAX_43.  */
#endif /* UMAX */

#ifdef DGUX
#include <sys/dg_sys_info.h>
#endif

#ifdef XEMACS
#if defined (HAVE_SYS_PSTAT_H)
#include <sys/pstat.h>
#endif /* HAVE_SYS_PSTAT_H (on HPUX) */
#endif /* XEMACS */

#if defined(HAVE_FCNTL_H) || defined(_POSIX_VERSION)
#include <fcntl.h>
#else
#include <sys/file.h>
#endif

/* Avoid static vars inside a function since in HPUX they dump as pure.  */

#ifdef NeXT
static processor_set_t default_set;
static int getloadavg_initialized;
#endif /* NeXT */

#ifdef UMAX
static unsigned int cpus = 0;
static unsigned int samples;
#endif /* UMAX */

#ifdef DGUX
static struct dg_sys_info_load_info load_info;	/* what-a-mouthful! */
#endif /* DGUX */

#ifdef LOAD_AVE_TYPE
/* File descriptor open to /dev/kmem */
static int channel;
/* Nonzero iff channel is valid.  */
static int getloadavg_initialized;
/* Offset in kmem to seek to read load average, or 0 means invalid.  */
static long offset;

#ifndef sgi
static struct nlist nl[2];
#endif /* not sgi */

#ifdef SUNOS_5
static kvm_t *kd;
#endif /* SUNOS_5 */

#ifndef countof
# define countof(x) (sizeof (x) / sizeof (*(x)))
#endif

#endif /* LOAD_AVE_TYPE */

/* Put the 1 minute, 5 minute and 15 minute load averages
   into the first NELEM elements of LOADAVG.
   Return the number written (never more than 3, but may be less than NELEM),
   or -1 if an error occurred.  */

int
getloadavg (double loadavg[], int nelem)
{
  int elem = 0;			/* Return value.  */

#ifdef NO_GET_LOAD_AVG
#define LDAV_DONE
  /* Set errno to zero to indicate that there was no particular error;
     this function just can't work at all on this system.  */
  errno = 0;
  elem = -2;
#endif /* NO_GET_LOAD_AVG */

#if ! defined (LDAV_DONE) && defined (HAVE_KSTAT_H) && defined (HAVE_LIBKSTAT)
#define LDAV_DONE
/* getloadavg is best implemented using kstat (kernel stats), on
   systems (like SunOS5) that support it, since you don't need special
   privileges to use it.

   Initial implementation courtesy Zlatko Calusic <[email-masked]>.
   Integrated to XEmacs by Hrvoje Niksic <[email-masked]>.
   Additional cleanup by Hrvoje Niksic, based on code published by
   Casper Dik <[email-masked]>.  */
  kstat_ctl_t *kc;
  kstat_t *ksp;
  static char *avestrings[] = { "avenrun_1min",
				"avenrun_5min",
				"avenrun_15min" };

  if (nelem > countof (avestrings))
    nelem = countof (avestrings);

  kc = kstat_open ();
  if (!kc)
    return -1;
  ksp = kstat_lookup (kc, "unix", 0, "system_misc");
  if (!ksp)
    {
      kstat_close (kc);
      return -1;
    }
  if (kstat_read (kc, ksp, 0) < 0)
    {
      kstat_close (kc);
      return -1;
    }
  for (elem = 0; elem < nelem; elem++)
    {
      kstat_named_t *kn =
	(kstat_named_t *) kstat_data_lookup (ksp, avestrings[elem]);
      if (!kn)
	{
	  kstat_close (kc);
	  return -1;
	}
      loadavg[elem] = (double)kn->value.ul / FSCALE;
    }
  kstat_close (kc);
#endif /* HAVE_KSTAT_H && HAVE_LIBKSTAT */

#if !defined (LDAV_DONE) && defined (HAVE_SYS_PSTAT_H)
#define LDAV_DONE
  /* This is totally undocumented, and is not guaranteed to work, but
     mayhap it might ....  If it does work, it will work only on HP-UX
     8.0 or later.  -- Darryl Okahata <[email-masked]> */
#undef LOAD_AVE_TYPE		/* Make sure these don't exist. */
#undef LOAD_AVE_CVT
#undef LDAV_SYMBOL
  struct pst_dynamic	procinfo;
  union pstun		statbuf;

  statbuf.pst_dynamic = &procinfo;
  if (pstat (PSTAT_DYNAMIC, statbuf, sizeof (struct pst_dynamic), 0, 0) == -1)
    return (-1);
  loadavg[elem++] = procinfo.psd_avg_1_min;
  loadavg[elem++] = procinfo.psd_avg_5_min;
  loadavg[elem++] = procinfo.psd_avg_15_min;
#endif	/* HPUX */

#if !defined (LDAV_DONE) && defined (__linux__)
#define LDAV_DONE
#undef LOAD_AVE_TYPE

#ifndef LINUX_LDAV_FILE
#define LINUX_LDAV_FILE "/proc/loadavg"
#endif

  char ldavgbuf[40];
  double load_ave[3];
  int fd, count;

  fd = open (LINUX_LDAV_FILE, O_RDONLY);
  if (fd == -1)
    return -1;
  count = read (fd, ldavgbuf, 40);
  (void) close (fd);
  if (count <= 0)
    return -1;

  count = sscanf (ldavgbuf, "%lf %lf %lf",
		  &load_ave[0], &load_ave[1], &load_ave[2]);
  if (count < 1)
    return -1;

  for (elem = 0; elem < nelem && elem < count; elem++)
    loadavg[elem] = load_ave[elem];
#endif /* __linux__ */

#if !defined (LDAV_DONE) && defined (__NetBSD__) || defined (__OpenBSD__)
#define LDAV_DONE
#undef LOAD_AVE_TYPE

#ifndef NETBSD_LDAV_FILE
#define NETBSD_LDAV_FILE "/kern/loadavg"
#endif

  unsigned long int load_ave[3], scale;
  int count;
  FILE *fp;

  fp = fopen (NETBSD_LDAV_FILE, "r");
  if (fp == NULL)
    return -1;
  count = fscanf (fp, "%lu %lu %lu %lu\n",
		  &load_ave[0], &load_ave[1], &load_ave[2],
		  &scale);
  (void) fclose (fp);
  if (count != 4)
    return -1;

  for (elem = 0; elem < nelem; elem++)
    loadavg[elem] = (double) load_ave[elem] / (double) scale;
#endif /* __NetBSD__ or __OpenBSD__ */

#if !defined (LDAV_DONE) && defined (NeXT)
#define LDAV_DONE
  /* The NeXT code was adapted from iscreen 3.2.  */

  host_t host;
  struct processor_set_basic_info info;
  unsigned info_count;

  /* We only know how to get the 1-minute average for this system,
     so even if the caller asks for more than 1, we only return 1.  */

  if (!getloadavg_initialized)
    {
      if (processor_set_default (host_self (), &default_set) == KERN_SUCCESS)
	getloadavg_initialized = 1;
    }

  if (getloadavg_initialized)
    {
      info_count = PROCESSOR_SET_BASIC_INFO_COUNT;
      if (processor_set_info (default_set, PROCESSOR_SET_BASIC_INFO, &host,
			     (processor_set_info_t) &info, &info_count)
	  != KERN_SUCCESS)
	getloadavg_initialized = 0;
      else
	{
	  if (nelem > 0)
	    loadavg[elem++] = (double) info.load_average / LOAD_SCALE;
	}
    }

  if (!getloadavg_initialized)
    return -1;
#endif /* NeXT */

#if !defined (LDAV_DONE) && defined (UMAX)
#define LDAV_DONE
/* UMAX 4.2, which runs on the Encore Multimax multiprocessor, does not
   have a /dev/kmem.  Information about the workings of the running kernel
   can be gathered with inq_stats system calls.
   We only know how to get the 1-minute average for this system.  */

  struct proc_summary proc_sum_data;
  struct stat_descr proc_info;
  double load;
  REGISTER unsigned int i, j;

  if (cpus == 0)
    {
      REGISTER unsigned int c, i;
      struct cpu_config conf;
      struct stat_descr desc;

      desc.sd_next = 0;
      desc.sd_subsys = SUBSYS_CPU;
      desc.sd_type = CPUTYPE_CONFIG;
      desc.sd_addr = (char *) &conf;
      desc.sd_size = sizeof conf;

      if (inq_stats (1, &desc))
	return -1;

      c = 0;
      for (i = 0; i < conf.config_maxclass; ++i)
	{
	  struct class_stats stats;
	  memset ((char *) &stats, 0, sizeof stats);

	  desc.sd_type = CPUTYPE_CLASS;
	  desc.sd_objid = i;
	  desc.sd_addr = (char *) &stats;
	  desc.sd_size = sizeof stats;

	  if (inq_stats (1, &desc))
	    return -1;

	  c += stats.class_numcpus;
	}
      cpus = c;
      samples = cpus < 2 ? 3 : (2 * cpus / 3);
    }

  proc_info.sd_next = 0;
  proc_info.sd_subsys = SUBSYS_PROC;
  proc_info.sd_type = PROCTYPE_SUMMARY;
  proc_info.sd_addr = (char *) &proc_sum_data;
  proc_info.sd_size = sizeof (struct proc_summary);
  proc_info.sd_sizeused = 0;

  if (inq_stats (1, &proc_info) != 0)
    return -1;

  load = proc_sum_data.ps_nrunnable;
  j = 0;
  for (i = samples - 1; i > 0; --i)
    {
      load += proc_sum_data.ps_nrun[j];
      if (j++ == PS_NRUNSIZE)
	j = 0;
    }

  if (nelem > 0)
    loadavg[elem++] = load / samples / cpus;
#endif /* UMAX */

#if !defined (LDAV_DONE) && defined (DGUX)
#define LDAV_DONE
  /* This call can return -1 for an error, but with good args
     it's not supposed to fail.  The first argument is for no
     apparent reason of type `long int *'.  */
  dg_sys_info ((long int *) &load_info,
	       DG_SYS_INFO_LOAD_INFO_TYPE,
	       DG_SYS_INFO_LOAD_VERSION_0);

  if (nelem > 0)
    loadavg[elem++] = load_info.one_minute;
  if (nelem > 1)
    loadavg[elem++] = load_info.five_minute;
  if (nelem > 2)
    loadavg[elem++] = load_info.fifteen_minute;
#endif /* DGUX */

#if !defined (LDAV_DONE) && defined (apollo)
#define LDAV_DONE
/* Apollo code from [email-masked] (Ray Lischner).

   This system call is not documented.  The load average is obtained as
   three long integers, for the load average over the past minute,
   five minutes, and fifteen minutes.  Each value is a scaled integer,
   with 16 bits of integer part and 16 bits of fraction part.

   I'm not sure which operating system first supported this system call,
   but I know that SR10.2 supports it.  */

  extern void proc1_$get_loadav ();
  unsigned long load_ave[3];

  proc1_$get_loadav (load_ave);

  if (nelem > 0)
    loadavg[elem++] = load_ave[0] / 65536.0;
  if (nelem > 1)
    loadavg[elem++] = load_ave[1] / 65536.0;
  if (nelem > 2)
    loadavg[elem++] = load_ave[2] / 65536.0;
#endif /* apollo */

#if !defined (LDAV_DONE) && defined (OSF_MIPS)
#define LDAV_DONE

  struct tbl_loadavg load_ave;
  table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
  loadavg[elem++]
    = (load_ave.tl_lscale == 0
       ? load_ave.tl_avenrun.d[0]
       : (load_ave.tl_avenrun.l[0] / (double) load_ave.tl_lscale));
#endif	/* OSF_MIPS */

#if !defined (LDAV_DONE) && (defined (MSDOS) || defined (WIN32))
#define LDAV_DONE

  /* A faithful emulation is going to have to be saved for a rainy day.  */
  for ( ; elem < nelem; elem++)
    {
      loadavg[elem] = 0.0;
    }
#endif  /* MSDOS */

#if !defined (LDAV_DONE) && defined (OSF_ALPHA)
#define LDAV_DONE

  struct tbl_loadavg load_ave;
  table (TBL_LOADAVG, 0, &load_ave, 1, sizeof (load_ave));
  for (elem = 0; elem < nelem; elem++)
    loadavg[elem]
      = (load_ave.tl_lscale == 0
       ? load_ave.tl_avenrun.d[elem]
       : (load_ave.tl_avenrun.l[elem] / (double) load_ave.tl_lscale));
#endif /* OSF_ALPHA */

#if !defined (LDAV_DONE) && defined(LOAD_AVE_TYPE)

  /* UNIX-specific code -- read the average from /dev/kmem.  */

#define LDAV_PRIVILEGED		/* This code requires special installation.  */

  LOAD_AVE_TYPE load_ave[3];

  /* Get the address of LDAV_SYMBOL.  */
  if (offset == 0)
    {
#ifndef sgi
#ifndef NLIST_STRUCT
      strcpy (nl[0].n_name, LDAV_SYMBOL);
      strcpy (nl[1].n_name, "");
#else /* NLIST_STRUCT */
#ifdef NLIST_NAME_UNION
      nl[0].n_un.n_name = LDAV_SYMBOL;
      nl[1].n_un.n_name = 0;
#else /* not NLIST_NAME_UNION */
      nl[0].n_name = (char *) LDAV_SYMBOL;
      nl[1].n_name = 0;
#endif /* not NLIST_NAME_UNION */
#endif /* NLIST_STRUCT */

#ifndef SUNOS_5
      if (
#if !(defined (_AIX) && !defined (ps2))
	  nlist (KERNEL_FILE, nl)
#else  /* _AIX */
	  knlist (nl, 1, sizeof (nl[0]))
#endif
	  >= 0)
	  /* Omit "&& nl[0].n_type != 0 " -- it breaks on Sun386i.  */
	  {
#ifdef FIXUP_KERNEL_SYMBOL_ADDR
	    FIXUP_KERNEL_SYMBOL_ADDR (nl);
#endif
	    offset = nl[0].n_value;
	  }
#endif /* !SUNOS_5 */
#else  /* sgi */
	  int ldav_off;

	  ldav_off = sysmp (MP_KERNADDR, MPKA_AVENRUN);
	  if (ldav_off != -1)
	  offset = (long) ldav_off & 0x7fffffff;
#endif /* sgi */
	}

  /* Make sure we have /dev/kmem open.  */
  if (!getloadavg_initialized)
    {
#ifndef SUNOS_5
      channel = open ("/dev/kmem", 0);
      if (channel >= 0)
	{
	  /* Set the channel to close on exec, so it does not
	     litter any child's descriptor table.  */
#ifdef FD_SETFD
#ifndef FD_CLOEXEC
#define FD_CLOEXEC 1
#endif
	  (void) fcntl (channel, F_SETFD, FD_CLOEXEC);
#endif
	  getloadavg_initialized = 1;
	}
#else /* SUNOS_5 */
      /* We pass 0 for the kernel, corefile, and swapfile names
	 to use the currently running kernel.  */
      kd = kvm_open (0, 0, 0, O_RDONLY, 0);
      if (kd != 0)
	{
	  /* nlist the currently running kernel.  */
	  kvm_nlist (kd, nl);
	  offset = nl[0].n_value;
	  getloadavg_initialized = 1;
	}
#endif /* SUNOS_5 */
    }

  /* If we can, get the load average values.  */
  if (offset && getloadavg_initialized)
    {
      /* Try to read the load.  */
#ifndef SUNOS_5
      if (lseek (channel, offset, 0) == -1L
	  || read (channel, (char *) load_ave, sizeof (load_ave))
	  != sizeof (load_ave))
	{
	  close (channel);
	  getloadavg_initialized = 0;
	}
#else  /* SUNOS_5 */
      if (kvm_read (kd, offset, (char *) load_ave, sizeof (load_ave))
	  != sizeof (load_ave))
        {
          kvm_close (kd);
          getloadavg_initialized = 0;
	}
#endif /* SUNOS_5 */
    }

  if (offset == 0 || !getloadavg_initialized)
    return -1;

  if (nelem > 0)
    loadavg[elem++] = LDAV_CVT (load_ave[0]);
  if (nelem > 1)
    loadavg[elem++] = LDAV_CVT (load_ave[1]);
  if (nelem > 2)
    loadavg[elem++] = LDAV_CVT (load_ave[2]);

#define LDAV_DONE
#endif /* !LDAV_DONE && LOAD_AVE_TYPE */

  return elem;
}

#endif /* ! HAVE_GETLOADAVG */

#ifdef TEST
void
main (int argc, char **argv)
{
  int naptime = 0;

  if (argc > 1)
    naptime = atoi (argv[1]);

  while (1)
    {
      double avg[3];
      int loads;

      errno = 0;		/* Don't be misled if it doesn't set errno.  */
      loads = getloadavg (avg, 3);
      if (loads == -1)
	{
	  perror ("Error getting load average");
	  exit (1);
	}
      if (loads > 0)
	printf ("1-minute: %f  ", avg[0]);
      if (loads > 1)
	printf ("5-minute: %f  ", avg[1]);
      if (loads > 2)
	printf ("15-minute: %f  ", avg[2]);
      if (loads > 0)
	putchar ('\n');

      if (naptime == 0)
	break;
      sleep (naptime);
    }

  exit (0);
}
#endif /* TEST */

#else

/* Emulate getloadavg.  */
int
getloadavg (double loadavg[], int nelem)
{
  int i;

  /* A faithful emulation is going to have to be saved for a rainy day.  */
  for (i = 0; i < nelem; i++)
    {
      loadavg[i] = 0.0;
    }
  return i;
}

#endif /*__GNUWIN32__*/

Dissociate from controlling terminal:
-------------------------------------

From: [email-masked] (Jan D.)
Organization: Swedish Institute of Space Physics, Uppsala, Sweden

The code above should look like this on HP-UX (POSIX ?):

	#include <unistd.h>	/* For _SC_OPEN_MAX */

	long	tblsiz = sysconf(_SC_OPEN_MAX);

	if (fork())
	    exit(0);

	setsid();	/* Disassociate from controlling terminal */
	for (c = 0; c < tblsiz; c++)
	    (void) close(c);
	(void) open("/", O_RDONLY);
	(void) dup2(0, 1);
	(void) dup2(0, 2);

Here's the deal regarding '-lBSD':

setpgrp() (in libc) is equivalent to setsid().
setpgrp(pid, pgrp) (in -lBSD) is equivalent to POSIX setpgid(pid, pgrp).
setpgrp2(pid, pgrp) is also equivalent to POSIX setpgid(pid, pgrp).

If you don't link with -lBSD you can replace setsid() in with setpgrp()
if you wan't. They both will get rid of the controlling terminal.

setpgrp(pid, pgrp) (-lBSD style), setpgid(pid, pgrp)
and setpgrp2(pid, pgrp) will NOT remove the controlling terminal.

Thus: The only way (I know of) in HP-UX to remove the controlling terminal
is with setsid() or setpgrp() in libc. The only way in POSIX to get
rid of the controlling terminal is with setsid().

setlinebuf:
-----------

/*
 * setlinebuf (FILE *fp)
 *
 * Routine to set line buffering on "fp".
 */

#include <stdio.h>

	int
setlinebuf (FILE *fp)
{
	(void) setvbuf (fp, NULL, _IOLBF, 0);
	return(0);
}

alloca:
-------

/*
	alloca -- (mostly) portable public-domain implementation -- D A Gwyn

	last edit:	86/05/30	rms
	   include config.h, since on VMS it renames some symbols.
	   Use xmalloc instead of malloc.

	This implementation of the PWB library alloca() function,
	which is used to allocate space off the run-time stack so
	that it is automatically reclaimed upon procedure exit,
	was inspired by discussions with J. Q. Johnson of Cornell.

	It should work under any C implementation that uses an
	actual procedure stack (as opposed to a linked list of
	frames).  There are some preprocessor constants that can
	be defined when compiling for your specific system, for
	improved efficiency; however, the defaults should be okay.

	The general concept of this implementation is to keep
	track of all alloca()-allocated blocks, and reclaim any
	that are found to be deeper in the stack than the current
	invocation.  This heuristic does not reclaim storage as
	soon as it becomes invalid, but it will do so eventually.

	As a special case, alloca(0) reclaims storage without
	allocating any.  It is a good idea to use alloca(0) in
	your main control loop, etc. to force garbage collection.
*/
#ifndef lint
static char	SCCSid[] = "@(#)alloca.c	1.1";	/* for the "what" utility */
#endif

#ifdef emacs
#include "config.h"
#ifdef static
/* actually, only want this if static is defined as ""
   -- this is for usg, in which emacs must undefine static
   in order to make unexec workable
   */
#ifndef STACK_DIRECTION
you
lose
-- must know STACK_DIRECTION at compile-time
#endif /* STACK_DIRECTION undefined */
#endif static
#else
#define xmalloc malloc
#endif emacs

#if defined(X3J11) || defined(__STDC__)
typedef void	*pointer;		/* generic pointer type */
#else
typedef char	*pointer;		/* generic pointer type */
#endif

#define	NULL	0			/* null pointer constant */

extern void	free();
extern pointer	xmalloc();

/*
	Define STACK_DIRECTION if you know the direction of stack
	growth for your system; otherwise it will be automatically
	deduced at run-time.

	STACK_DIRECTION > 0 => grows toward higher addresses
	STACK_DIRECTION < 0 => grows toward lower addresses
	STACK_DIRECTION = 0 => direction of growth unknown
*/

#ifndef STACK_DIRECTION
#define	STACK_DIRECTION	0		/* direction unknown */
#endif

#if STACK_DIRECTION != 0

#define	STACK_DIR	STACK_DIRECTION	/* known at compile-time */

#else	/* STACK_DIRECTION == 0; need run-time code */

static int	stack_dir;		/* 1 or -1 once known */
#define	STACK_DIR	stack_dir

static void
find_stack_direction (/* void */)
{
  static char	*addr = NULL;	/* address of first
				   `dummy', once known */
  auto char	dummy;		/* to get stack address */

  if (addr == NULL)
    {				/* initial entry */
      addr = &dummy;

      find_stack_direction ();	/* recurse once */
    }
  else				/* second entry */
    if (&dummy > addr)
      stack_dir = 1;		/* stack grew upward */
    else
      stack_dir = -1;		/* stack grew downward */
}

#endif	/* STACK_DIRECTION == 0 */

/*
	An "alloca header" is used to:
	(a) chain together all alloca()ed blocks;
	(b) keep track of stack depth.

	It is very important that sizeof(header) agree with malloc()
	alignment chunk size.  The following default should work okay.
*/

#ifndef	ALIGN_SIZE
#define	ALIGN_SIZE	sizeof(double)
#endif

typedef union hdr
{
  char	align[ALIGN_SIZE];	/* to force sizeof(header) */
  struct
    {
      union hdr *next;		/* for chaining headers */
      char *deep;		/* for stack depth measure */
    } h;
} header;

/*
	alloca( size ) returns a pointer to at least `size' bytes of
	storage which will be automatically reclaimed upon exit from
	the procedure that called alloca().  Originally, this space
	was supposed to be taken from the current stack frame of the
	caller, but that method cannot be made to work for some
	implementations of C, for example under Gould's UTX/32.
*/

static header *last_alloca_header = NULL; /* -> last alloca header */

pointer
alloca (size)			/* returns pointer to storage */
     unsigned	size;		/* # bytes to allocate */
{
  auto char	probe;		/* probes stack depth: */
  register char	*depth = &probe;

#if STACK_DIRECTION == 0
  if (STACK_DIR == 0)		/* unknown growth direction */
    find_stack_direction ();
#endif

				/* Reclaim garbage, defined as all alloca()ed storage that
				   was allocated from deeper in the stack than currently. */

  {
    register header	*hp;	/* traverses linked list */

    for (hp = last_alloca_header; hp != NULL;)
      if (STACK_DIR > 0 && hp->h.deep > depth
	  || STACK_DIR < 0 && hp->h.deep < depth)
	{
	  register header	*np = hp->h.next;

	  free ((pointer) hp);	/* collect garbage */

	  hp = np;		/* -> next header */
	}
      else
	break;			/* rest are not deeper */

    last_alloca_header = hp;	/* -> last valid storage */
  }

  if (size == 0)
    return NULL;		/* no allocation required */

  /* Allocate combined header + user data storage. */

  {
    register pointer	new = xmalloc (sizeof (header) + size);
    /* address of header */

    ((header *)new)->h.next = last_alloca_header;
    ((header *)new)->h.deep = depth;

    last_alloca_header = (header *)new;

    /* User storage begins just after header. */

    return (pointer)((char *)new + sizeof(header));
  }
}

setproctitle:
-------------

From: Tor Lillqvist ([email-masked]),
      Technical Research Centre of Finland,
      Laboratory for Information Processing (VTT/TIK).

Q: How can I write to the argv[] strings in a program so that the
   altered strings show up in 'ps'?

A: In HP-UX you can't do it by clobbering the argv strings, but with the
   undocumented pstat syscall.  This code is from sendmail 5.65c (routine
   conf.c). Modify to your taste (especially remove the " (sendmail)").

/*
**  SETPROCTITLE -- set process title for ps
**
**	Parameters:
**		fmt -- a printf style format string.
**		a, b, c -- possible parameters to fmt.
**
**	Returns:
**		none.
**
**	Side Effects:
**		Clobbers argv of our main procedure so ps(1) will
**		display the title.
*/

/*VARARGS1*/
void
#ifdef __STDC__
setproctitle(const char *fmt, ...)
#else /* !__STDC__ */
setproctitle(fmt, va_alist)
	const char *fmt;
va_dcl
#endif /* __STDC__ */
{
#if defined(SETPROCTITLE) && !defined(SYSV)
	va_list	args;
	register char *p;
	register int i;
#if (defined(hpux) || defined(__hpux) || defined(__hpux__)) && defined(PSTAT_SETCMD)
	union pstun un;
#else
	extern char **Argv;
	extern char *LastArgv;
#endif
	char buf[MAXLINE];

# ifdef __STDC__
	va_start(args, fmt);
# else /* !__STDC__ */
	va_start(args);
# endif /* __STDC__ */
	(void) vsprintf(buf, fmt, args);
	va_end(args);

#if (defined(hpux) || defined(__hpux) || defined(__hpux__)) && defined(PSTAT_SETCMD)
	(void) sprintf(buf + strlen(buf), " (sendmail)");
	un.pst_command = buf;
	pstat(PSTAT_SETCMD, un, strlen(buf), 0, 0);
#else
	/* make ps print "(sendmail)" */
	p = Argv[0];
	*p++ = '-';

	i = strlen(buf);
	if (i > LastArgv - p - 2)
	{
		i = LastArgv - p - 2;
		buf[i] = '\0';
	}
	(void) strcpy(p, buf);
	p += i;
	while (p < LastArgv)
		*p++ = ' ';
#endif
#endif /* SETPROCTITLE && !SYSV */
}

utimes:
-------

/*
 * utimes (BSD equivalent of utime(2) - set file mod and access times)
 * (No attempt to reproduce same error code expect that they both do
 * return -1 on error and 0 on success)
 *
 * From: [email-masked] (Michael J. Corrigan)
 */

#include <sys/types.h>
#include <sys/time.h>
#include <utime.h>

int utimes(file,tvp) char *file; struct timeval *tvp;
{
	struct utimbuf ut;
	time_t now;

	now = time((time_t *)NULL);
	if (tvp == (struct timeval *)NULL) {
		ut.actime = now;
		ut.modtime = now;
	} else {
		ut.actime = tvp++->tv_sec;
		ut.modtime = tvp->tv_sec;
	}
	return(utime(file,&ut));
}

insque:
-------

/*
 * For insque() functionality, the insque.c from emacs 18.59
 * compiles/works under HP-UX 8.x and 9.x.
 *
 * From: [email-masked] (Mike O'Connor, Ford Motor Company)
 */

/* This file implements the insque and remque functions of BSD.
   It is not compiled by default, because that change would be too risky
   to install right now.  If you find that HAVE_X_MENU leads to linker errors
   because these functions are undefined, then compile this file
   and arrange to link it in.  */

struct qelem {
  struct    qelem *q_forw;
  struct    qelem *q_back;
  char q_data[1];
};

/* Insert ELEM into a doubly-linked list, after PREV.  */

void
insque (elem, prev)
     struct qelem *elem, *prev;
{
  struct qelem *next = prev->q_forw;
  prev->q_forw = elem;
  if (next)
    next->q_back = elem;
  elem->q_forw = next;
  elem->q_back = prev;
}

/* Unlink ELEM from the doubly-linked list that it is in.  */

remque (elem)
     struct qelem *elem;
{
  struct qelem *next = elem->q_forw;
  struct qelem *prev = elem->q_back;
  if (next)
    next->q_back = prev;
  if (prev)
    prev->q_forw = next;
}

gethostid:
----------

/*
 * From: [email-masked] (David DiGiacomo)
 */

#define _INCLUDE_HPUX_SOURCE
#include <sys/utsname.h>

int
gethostid()
{
	struct utsname uts;

	if (uname(&uts) < 0)
		return 0;

	return atoi(uts.idnumber);
}

rmdir:
------

From Pat Lynch ([email-masked]):

rmdir(2) (when attempting to remove a directory that still has
files in it) returns -1 with errno = ENOTEMPTY on Domain/OS BSD,
but errno = EEXIST on HPUX.

signal:
-------

I've come across a few situations where linking in the bsd library
has caused more problems than solved them (bash comes to mind).
This is especially true when the only real problem is the code to be
ported assumes BSD signal semantics and gets unreliable signals from HPUX.
In this situation, I compile and link in the following function:

/*
 *	signal.c
 *
 * PURPOSE
 *	provide berkeley signals with signal interface
 *
 * Created On      : Wed Jun 23 08:47:42 1993
 * Author          : Andre Srinivasan ([email-masked])
 *
 * HISTORY
 *
 */
#include <signal.h>

void (*signal(int iSig, void (*pAction)(int)))(int)
{
struct sigaction saNew, saOld;

saNew.sa_handler = pAction;
sigemptyset(&saNew.sa_mask);
saNew.sa_flags = 0;

if (sigaction(iSig, &saNew, &saOld) < 0)
     return(SIG_ERR);

return(saOld.sa_handler);
}

***************************************************************************

"SunOS to HP-UX 9.05 Porting Guide" Table of Contents
-----------------------------------------------------

	Forward

	Introduction

	Development Environment Overview ................................... 1

	   Compilers	                                            1-1
	      Compiler Versions                                     1-1
	      Compiler Licensing                                    1-2
	      Location of Compilers                                 1-2
	      Compiler Directives                                   1-3

	   Location of Files and Utilities                          1-3
	      C and C++ Header Files                                1-3
	      X11 and Motif Header Files                            1-4
	      System Libraries                                      1-4
	      X11 and Motif Libraries                               1-4
	      Other Utilities                                       1-5
	      Debug Environment                                     1-6

	   HP LaserROM/UX                                           1-6

	   HP-UX Patches                                            1-7

	   SunOS to HP-UX Porting References                        1-7

	Compilers .......................................................... 2

	   C Compiler                                               2-1
	      Product Overview                                      2-1
	      Key Features                                          2-2
	      Performance and Optimization                          2-2
	      Shared Library Support                                2-3
	      C Compiler Ordering Information                       2-3

	   C Language Tips and Traps                                2-4
	      ANSI C Compiler Compile Line                          2-4
	      Compiler Warning: Could Not Find
		   Include File sys/filio.h	                    2-5
	      PATH_MAX Not Defined                                  2-5
	      SIG_PF Not Found                                      2-5
	      S_un Undefined Member on HP-UX                        2-5
	      <varargs.h> Problems                                  2-6
	      Unexpected Symbols: u_long, u_short                   2-6
	      ANSI C and Include File <rpc/rpc.h>                   2-6
	      Compiler Fails With "Not Enough Space" Message        2-6
	      Large Executable Dumps Core                           2-6
	      Error Message: Too Many Defines                       2-6
	      Null Pointer Dereferencing                            2-7

	   MIN and MAX header file differences                      2-7

	   TCGETS and TCSETS                                        2-7

	   SunOS to HP-UX C Compiler Options Cross Reference        2-8
	      C Compiler Options Cross Reference                    2-9

	   C++ Compiler                                             2-12
	      Overview of HP C++ Version A.03.50                    2-12
	      HP C++ Run-Time Libraries                             2-13
	      HP C++ Compiler Ordering Information                  2-13
	      C++ Compiler Tips and Traps                           2-14
	      SunOS to HP-UX C++ Compiler Options Cross Reference   2-16
	      SunOS C++ and HP-UX C++ Compiler Options              2-17

	   FORTRAN Compiler                                         2-20
	      FORTRAN Compiler Tips and Traps                       2-20

	   Pascal Compiler                                          2-22
	      Product Overview                                      2-22
	      Optimization                                          2-22
	      Shared Libraries                                      2-22
	      Comments Differences                                  2-23
	      Data Type Differences                                 2-23
	   Pascal Compiler Options Cross Reference                  2-24

	   GNU gcc/g++                                              2-27
	      g++ Language Tips and Traps                           2-27
	      C++ Functional Prototypes                             2-27

	   Compiler Environment Variables                           2-28
	      CCOPTS Environment Variable                           2-28
	      TMPDIR Environment Variable                           2-29

	Other Development Tools & Information .............................. 3

	   make		                                            3-1
	      make patch: PHCO_4762                                 3-1
	      make Environment Variable Differences                 3-1

	   imake                                                    3-2
	      imake and Long Continuation Lines                     3-3

	   GNU gmake                                                3-3

	   Linker                                                   3-3

	   Incremental Linking                                      3-4
	      Blink Link                                            3-4

	   HP-UX Debug Environment                                  3-5
	      HP Distributed Debugging Environment
		   (HP/DDE) Version 3.05                            3-6
	      GNU Debugging                                         3-7
	      dbx Debug Interfaces                                  3-7

	   Notes on Debugging Shared Libraries with HP/DDE          3-7
	      Loading Debugging Information for Shared Libraries    3-8
	      Making Shared Libraries Writable                      3-8
	      A Shared Library Debugging Session                    3-9

	   Notes on Debugging Shared Libraries with xdb             3-10
	      Debugging an Application Using Shared Libraries       3-10
	      Accessing a Function Within a Shared Library          3-10
	      Debugging a Dynamically Loaded Shared Library         3-10
	      Adopting a Program                                    3-10

	   Software Performance Analysis Tools                      3-11
	      HP/PAK Version 6.05                                   3-11
	      HP GlancePlus/UX                                      3-11
	      Prof and Gprof                                        3-12
	      Profile-based Optimization                            3-13

	   HP SoftBench                                             3-13

	   RCS                                                      3-14

	   SCCS                                                     3-14

	   trace (public domain)                                    3-14

	Libraries .......................................................... 4

	   Archived Libraries                                       4-1
	      ar                                                    4-1
	      ranlib                                                4-1

	   Shared Libraries                                         4-1
	      When Are the Objects Attached? (binding)              4-1
	      Creating Position-Independent Code (PIC)
		   and Shared Libraries                             4-2
	      Finding Shared Libraries: $SHLIB_PATH                 4-3
	      Enabling Dynamic Path Searching                       4-3
		 With chatr(1)                                      4-3
		 With the Linker                                    4-3
	      Absolute Paths to Libraries                           4-4
	      Shared Library Performance Considerations             4-5
	      Shared Library Debugging                              4-5

	   Overview of HP-UX Math Libraries                         4-6
	      How to Select Versions of the Math Libraries          4-6

	Graphics ........................................................... 5

	   OPEN LOOK to OSF/Motif                                   5-1
	      Learning OSF/Motif                                    5-2
	      Convincing Your Users There Is Another Way            5-2

	   Your Existing Code                                       5-2

	   Porting Strategy                                         5-3
	      Porting Utilities                                     5-4
	      Graphics Tool Positioning                             5-4

	   Where to Get OSF/Motif for SunOS                         5-5

	   Guidelines for Converting OLIT to OSF/Motif              5-5
	      Resolving Conflicting Resources                       5-7
	      Window Manager Differences                            5-8
	      Other OLIT Port Issues                                5-8

	   Curses                                                   5-9

	Signals	............................................................ 6

	   BSD4.2 Compatibility                                     6-2

	   Asynchronous I/O                                         6-3
	      Examples                                              6-3
		 BSD4.3 Example                                     6-3
		 HP-UX Example                                      6-4

	   select() and poll()                                      6-4

	Interprocess Communication (IPC) ................................... 7

	   FIFOs                                                    7-1

	   Messages                                                 7-1

	   Pipes                                                    7-1

	   RPC                                                      7-2
	      ONC RPC                                               7-2
	      DCE RPC                                               7-3

	   Semaphores                                               7-3

	   Shared Memory                                            7-3

	   Sockets                                                  7-3
	      TCP Socket Performance                                7-5

	   Streams                                                  7-6
	      STREAMS Porting Concerns                              7-6

	   BMS and Tooltalk                                         7-7

	   DCE Threads                                              7-7

	Miscellaneous ...................................................... 8

	   Making Daemons                                           8-1

	   Unique CPU Identification                                8-2

	   Stack Dump Function                                      8-2

	   Programmatically Obtaining Process Status                8-2

	   Programmatically Obtaining Ethernet Addresses            8-4

	   PS Instruction "Test and Set"                            8-4

	   Porting Device Drivers	                            8-5
	      Compile the Driver                                    8-5
	      Add the Driver to the Configuration Files             8-5
	      Generate the conf.c and config.mk Files               8-6
	      Build and Install a New Kernel                        8-6

	   Process Virtual Address Space                            8-7
	      Dealing With Data Alignment                           8-7
		 Alternatives for Dealing with Alignment Bus Errors 8-7
		 Examples of Code Expansion with Alignment Options  8-8

	   Large Data Space Processes                               8-11
	      System default test case                              8-12
	      Increase MAXDSIZ test case                            8-12
	      Add an Additional Swap Device Test Case               8-12
	      Increase MAXSWAPCHUNKS Size Test Case                 8-13
	      Relink the Programs with -N Test Case                 8-15

	   Hint for Debugging an X11 Application                    8-15

	   Eliminating Core Dumps                                   8-15

	   Obtaining data from /etc/utmp                            8-16

	   Floating Point                                           8-17
	      Exception Conditions                                  8-17
	      Inexact Result (Rounding)                             8-18

	HP on the Net ...................................................... 9

	   University of Liverpool Archive                          9-1

	   Interworks Software Archive                              9-2

	   GNU Software Archive                                     9-2

	   HP World Wide Web Sites                                  9-3
	      HP's WWW Home Page                                    9-3
	      HP WWW SupportLine Page                               9-3
	      HP WWW Patch Server Page                              9-3
	      Other HP-Related WWW Sites                            9-3

	   User Groups                                              9-4
	      Interworks                                            9-4
	      Interex                                               9-4

	   News Groups                                              9-4
	      The comp.sys.hp.* hierarchy                           9-4

	Public Domain Made Easy ............................................ A

	   Building Public Domain Software on HP-UX                 A-1

	   Overview                                                 A-1

	   C Compiler                                               A-2

	   make                                                     A-2

	   imake                                                    A-2
	      imake distribution                                    A-2
	      Location of imake Configuration Files                 A-3
	      Modifications to the Configuration File site.def      A-3
	      Location of Certain Include Files and Libraries       A-4

	   Motif and Xt                                             A-5

	   Xaw and Xmu                                              A-6

	   Example: Building the Cutview Program                    A-7
	      Simple Imakefile Containing a ComplexProgramTarget    A-7
	      Steps to Building the Software                        A-7

	System and Library Command Cross Reference ......................... B

	   SunOS and HP-UX System Call Differences	            B-1

	   SunOS and HP-UX Library Routine Differences              B-24

	pstat(2) Man Page .................................................. C

	DBX to XDB Command Summary Cross-Reference ......................... D

	   Execution and Tracing                                    D-1

	   Displaying and Naming Data                               D-2

	   Accessing Source Files                                   D-3

	   Miscellaneous                                            D-3

	   Machine Level                                            D-4

	Series 700/800 Compatibility ....................................... E

	   Compatibility: What Does It Mean?                        E-1
	      What is Not Common                                    E-1

	   Non-Kernel Differences Between Series 700 and Series 800 E-3
	      New Math Libraries                                    E-3
	      X11R5 Server                                          E-4
	      Building One Executable for Series 700/800            E-4
	      Building Two Executables for Series 700/800           E-4

	Product Information ................................................ F

	   Accent STP                                               F-1

	   ALSYS, INC.                                              F-2

	   Bluestone                                                F-2

	   Hewlett-Packard Company (HP)                             F-3

	   Integrated Computer Solutions, Inc. (ICS)                F-5

	   iXOS Software                                            F-6

	   Melillo Consulting                                       F-6

	   Qualix                                                   F-7

	   UIM/X                                                    F-7

	   UniPress Software, Inc.                                  F-8

	   V. I. Corporation                                        F-9

	   XView Library                                            F-9

* Sun, SunOS, Solaris, Sun Microsystems are registered trademarks of Sun
  Microsystems, Inc.

* UNIX is registered trademarks of UNIX Systems Laboratories, Inc.
  (Novell) in the USA and other countries.

* AIX is registered trademarks of IBM

* All other trademarks and registered trademarks are the property of their
  respective holders.

***************************************************************************

End of BSD to HP-UX porting tricks.