From: system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson))
Subject: BSD to HP-UX porting tricks (LONG) (last updated: 13-Nov-1992)
Organization: University of Toronto Chemistry Department
Date: Fri, 1 Jan 1993 06:00:05 GMT

	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' will also fail with status -1.
setpgrp       - this doesn't get rid of the controlling terminal
		if you use -lBSD (use setsid()).
BSD signals   - you almost certainly want them if your program came from
		a BSD system (e.g. Sun); this breaks 'setpgrp' among other
		things (see 'man bsdproc' and notes below).
/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).


getwd:
------

/* Replace 'SIZEOFARG-1' with the declared size of "arg", minus 1 */

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


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

From: scot@pawnee.ugrad.ee.ufl.edu (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:

#ifdef 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: mjo@snclib.snc.edu (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


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:  mike@sherlock.med.ge.com     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
 * (gritton@byu.edu) 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));
}


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

/*
 * getloadavg (ave, n)
 *
 * This routine returns 'n' double precision floats containing
 * the load averages in 'ave'; at most 3 values will be returned.
 *
 * Return value: 0 if successful, -1 if failed (and all load
 * averages are returned as 0).
 */

#include <sys/types.h>
#include <stdio.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"
	{ "_avenrun" },
#endif
#endif
	{ 0 },
};

#ifndef RISCos
	int
getloadavg (double *a, int na)
{
	int i, nreturn;
	static int kmem = -1;
#if defined(vax) || defined(__hpux)
	double avenrun[3];
#else
	long avenrun[3];
#endif
#ifdef NOKMEM
	float aves[3];
#endif /* NOKMEM */

	nreturn = na;
	if ( nreturn < 0 )
		nreturn = 0;
	if ( nreturn > 3 )
		nreturn = 3;

#ifdef NOKMEM
/* Use 'uptime' output for BSD-like systems with no /dev/kmem */

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

#else /*NOKMEM*/

	if(kmem == -1) {
#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;
		}
	}
	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 < nreturn; i++)
#if defined(sun) || defined(sequent)
		a[i] = avenrun[i] / FSCALE;
#else 
#ifdef sgi
		a[i] = avenrun[i] / 1024;
#else
#if defined(BSD4_2) || defined(__hpux)
		a[i] = avenrun[i];
#else 
#ifdef stardent
		a[i] = (double)avenrun[i] / (1<<16);
#else
		a[i] = avenrun[i] / 1024;
#endif /*stardent*/
#endif /*BSD4_2*/
#endif /*sgi*/
#endif /*sun*/
#endif /*NOKMEM*/
	return(0); 
failed:;
	for (i = 0; i < nreturn; i++)
		a[i] = 0;
	return(-1);
}
#else /*RISCos*/
#include <sys/fixpoint.h>
	static
getloadavg (double *a, int na)
{
	int i, nreturn;
	static int kmem = -1;
	fix avenrun[3];

	nreturn = na;
	if ( nreturn < 0 )
		nreturn = 0;
	if ( nreturn > 3 )
		nreturn = 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 < nreturn; i++)
	        a[i] = (int) FIX_TO_INT(avenrun[i]) + .5;
	return(0);
failed:;
	for (i = 0; i < nreturn; i++)
		a[i] = 0;
	return(-1);
}
#endif /* RISCOS */

/* 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);
}


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

From: jhd@irfu.se (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 (tml@tik.vtt.fi),
      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(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(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 */
}