/*
 * Lib9 is miscellany from the Plan 9 C library that doesn't
 * fit into libutf or into libfmt, but is still missing from traditional
 * Unix C libraries.
 */
#ifndef _LIB9H_
#define _LIB9H_ 1

#if defined(__cplusplus)
extern "C" {
#endif
                                                                                

#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <fcntl.h>
#include <assert.h>
#include <setjmp.h>

#ifndef _FMTH_
#	include <fmt.h>
#endif

#define	nil	((void*)0)
#define	nelem(x)	(sizeof(x)/sizeof((x)[0]))

#define _NEEDUCHAR 1
#define _NEEDUSHORT 1
#define _NEEDUINT 1
#define _NEEDULONG 1

#if defined(__linux__)
#	include <sys/types.h>
#	if defined(__USE_MISC)
#		undef _NEEDUSHORT
#		undef _NEEDUINT
#		undef _NEEDULONG
#	endif
#endif
#if defined(__FreeBSD__)
#	include <sys/types.h>
#	if !defined(_POSIX_SOURCE)
#		undef _NEEDUSHORT
#		undef _NEEDUINT
#	endif
#endif

typedef signed char schar;
typedef unsigned int u32int;
#ifdef _NEEDUCHAR
	typedef unsigned char uchar;
#endif
#ifdef _NEEDUSHORT
	typedef unsigned short ushort;
#endif
#ifdef _NEEDUINT
	typedef unsigned int uint;
#endif
#ifdef _NEEDULONG
	typedef unsigned long ulong;
#endif
typedef unsigned long long uvlong;
typedef long long vlong;

/* rfork to create new process running fn(arg) */

#if defined(__FreeBSD__)
#undef RFFDG
#undef RFNOTEG
#undef RFPROC
#undef RFMEM
#undef RFNOWAIT
#undef RFCFDG
#endif

enum
{
/*	RFNAMEG		= (1<<0), */
/*	RFENVG		= (1<<1), */
	RFFDG		= (1<<2),
	RFNOTEG		= (1<<3),
	RFPROC		= (1<<4),
	RFMEM		= (1<<5),
	RFNOWAIT	= (1<<6),
/*	RFCNAMEG	= (1<<10), */
/*	RFCENVG		= (1<<11), */
	RFCFDG		= (1<<12),
/*	RFREND		= (1<<13), */
/*	RFNOMNT		= (1<<14) */
};
extern int		ffork(int, void(*)(void*), void*);

/* wait for processes */
#define wait _p9wait
typedef struct Waitmsg Waitmsg;
struct Waitmsg
{
	int pid;	/* of loved one */
	ulong time[3];	/* of loved one & descendants */
	char	*msg;
};
extern	int		await(char*, int);
extern	Waitmsg*	wait(void);

/* synchronization */
typedef struct Lock Lock;
struct Lock
{
	int val;
};

extern int		_tas(void*);
extern void	lock(Lock*);
extern void	unlock(Lock*);
extern int		canlock(Lock*);

typedef struct QLp QLp;
struct QLp
{
	int		inuse;
	QLp		*next;
	int		state;
};

typedef struct QLock QLock;
struct QLock
{
	Lock	lock;
	int		locked;
	QLp		*head;
	QLp		*tail;
};

extern void	qlock(QLock*);
extern void	qunlock(QLock*);
extern int		canqlock(QLock*);
extern void	_qlockinit(ulong (*)(ulong, ulong));

typedef struct RWLock RWLock;
struct RWLock
{
	Lock	lock;
	int		readers;
	int		writer;
	QLp		*head;
	QLp		*tail;
};

extern void	rlock(RWLock*);
extern void	runlock(RWLock*);
extern int		canrlock(RWLock*);
extern void	wlock(RWLock*);
extern void	wunlock(RWLock*);
extern int		canwlock(RWLock*);

typedef struct Rendez Rendez;
struct Rendez
{
	QLock	*l;
	QLp		*head;
	QLp		*tail;
};

extern void	rsleep(Rendez*);
extern int		rwakeup(Rendez*);
extern int		rwakeupall(Rendez*);

extern ulong	rendezvous(ulong, ulong);

/* one of a kind */
extern void	sysfatal(char*, ...);
extern int		nrand(int);
extern void	setmalloctag(void*, ulong);
extern void	setrealloctag(void*, ulong);
extern void	*mallocz(ulong, int);
extern long	readn(int, void*, long);
extern void	exits(char*);
extern void	_exits(char*);
extern ulong	getcallerpc(void*);

/* string routines */
extern char*	strecpy(char*, char*, char*);
extern int		tokenize(char*, char**, int);
extern int		cistrncmp(char*, char*, int);
extern int		cistrcmp(char*, char*);
extern char*	cistrstr(char*, char*);
extern int		getfields(char*, char**, int, int, char*);
extern int		gettokens(char *, char **, int, char *);

/* formatting helpers */
extern int		dec64(uchar*, int, char*, int);
extern int		enc64(char*, int, uchar*, int);
extern int		dec32(uchar*, int, char*, int);
extern int		enc32(char*, int, uchar*, int);
extern int		dec16(uchar*, int, char*, int);
extern int		enc16(char*, int, uchar*, int);
extern int		encodefmt(Fmt*);

/* error string */
enum
{
	ERRMAX = 128
};
extern void	rerrstr(char*, uint);
extern void	werrstr(char*, ...);
extern int		errstr(char*, uint);

/* compiler directives on plan 9 */
#define	USED(x)	if(x){}else{}
#define	SET(x)	((x)=0)

/* command line */
extern char	*argv0;
#define	ARGBEGIN	for((argv0||(argv0=*argv)),argv++,argc--;\
			    argv[0] && argv[0][0]=='-' && argv[0][1];\
			    argc--, argv++) {\
				char *_args, *_argt;\
				Rune _argc;\
				_args = &argv[0][1];\
				if(_args[0]=='-' && _args[1]==0){\
					argc--; argv++; break;\
				}\
				_argc = 0;\
				while(*_args && (_args += chartorune(&_argc, _args)))\
				switch(_argc)
#define	ARGEND		SET(_argt);USED(_argt);USED(_argc);USED(_args);}USED(argv);USED(argc);
#define	ARGF()		(_argt=_args, _args="",\
				(*_argt? _argt: argv[1]? (argc--, *++argv): 0))
#define	EARGF(x)	(_argt=_args, _args="",\
				(*_argt? _argt: argv[1]? (argc--, *++argv): ((x), abort(), (char*)0)))

#define	ARGC()		_argc

#define OREAD O_RDONLY
#define OWRITE O_WRONLY
#define AEXIST 0
#define AREAD 4
#define AWRITE 2
#define AEXEC 1
#define ORCLOSE 8
#define OCEXEC 16

#define dup dup2
#define exec execv
#define seek lseek
#define getwd getcwd

#if defined(__cplusplus)
}
#endif

#endif	/* _LIB9H_ */
