// Copyright (c) 2003 David Muse
// See the COPYING file for more information

#include <rudiments/rpcentry.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>
#include <rudiments/error.h>

// for rpcent, functions
#include <netdb.h>
#ifdef RUDIMENTS_HAVE_RPCENT_H
	#include <rpc/rpcent.h>
#endif
#ifdef RUDIMENTS_HAVE_RPC_H
	#include <rpc/rpc.h>
#endif

// Some systems (notably cygwin 1.5.7-1) define getrpcbyname and getrpcbynumber
// in their header files but then either don't implement them or don't export
// the symbols.
#if (defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) || \
	defined(RUDIMENTS_HAVE_GETRPCBYNAME)) && \
	(defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R) || \
	defined(RUDIMENTS_HAVE_GETRPCBYNUMBER)) \

#include <stdio.h>
#include <stdlib.h>

#define MAXBUFFER	(32*1024)

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class rpcentryprivate {
	friend class rpcentry;
	private:
		rpcent	*_re;
		#if defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) && \
				defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
			rpcent	_rebuffer;
			char	*_buffer;
		#endif
};

// LAME: not in the class
#if (!defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) || \
	!defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R))
static mutex	*_remutex;
#endif


rpcentry::rpcentry() {
	pvt=new rpcentryprivate;
	pvt->_re=NULL;
	#if defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
		rawbuffer::zero(&pvt->_rebuffer,sizeof(pvt->_rebuffer));
		pvt->_buffer=NULL;
	#endif
}

rpcentry::~rpcentry() {
	#if defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
		delete[] pvt->_buffer;
	#endif
	delete pvt;
}

const char *rpcentry::getName() const {
	return pvt->_re->r_name;
}

int rpcentry::getNumber() const {
	return pvt->_re->r_number;
}

const char * const *rpcentry::getAliasList() const {
	return pvt->_re->r_aliases;
}

bool rpcentry::needsMutex() {
	#if !defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) || \
		!defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
		return true;
	#else
		return false;
	#endif
}

void rpcentry::setMutex(mutex *mtx) {
	#if !defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) || \
		!defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
		_remutex=mtx;
	#endif
}

bool rpcentry::initialize(const char *name) {
	return initialize(name,0);
}

bool rpcentry::initialize(int number) {
	return initialize(NULL,number);
}

bool rpcentry::initialize(const char *rpcname, int number) {

	#if defined(RUDIMENTS_HAVE_GETRPCBYNAME_R) && \
		defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R)
		if (pvt->_re) {
			pvt->_re=NULL;
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
		}
		// getrpcbyname_r is goofy.
		// It will retrieve an arbitrarily large amount of data, but
		// requires that you pass it a pre-allocated buffer.  If the
		// buffer is too small, it returns an ENOMEM and you have to
		// just make the buffer bigger and try again.
		for (int size=1024; size<MAXBUFFER; size=size+1024) {
			pvt->_buffer=new char[size];
			#if defined(RUDIMENTS_HAVE_GETRPCBYNAME_R_5) && \
				defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R_5)
			if (!((rpcname)
				?(getrpcbyname_r(rpcname,&pvt->_rebuffer,
							pvt->_buffer,size,
							&pvt->_re))
				:(getrpcbynumber_r(number,&pvt->_rebuffer,
							pvt->_buffer,size,
							&pvt->_re)))) {
				return (pvt->_re!=NULL);
			}
			#elif defined(RUDIMENTS_HAVE_GETRPCBYNAME_R_4) && \
				defined(RUDIMENTS_HAVE_GETRPCBYNUMBER_R_4)
			if ((rpcname)
				?(pvt->_re=getrpcbyname_r(rpcname,
							&pvt->_rebuffer,
							pvt->_buffer,size))
				:(pvt->_re=getrpcbynumber_r(number,
							&pvt->_rebuffer,
							pvt->_buffer,size))) {
				return true;
			}
			#endif
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
			pvt->_re=NULL;
			if (error::getErrorNumber()!=ENOMEM) {
				return false;
			}
		}
		return false;
	#else
		pvt->_re=NULL;
		return (!(remutex && !remutex->lock()) &&
			((pvt->_re=((rpcname)
				?getrpcbyname(const_cast<char *>(rpcname))
				:getrpcbynumber(number)))!=NULL) &&
			!(remutex && !remutex->unlock()));
	#endif
}

bool rpcentry::getAliasList(const char *name, char ***aliaslist) {
	rpcentry	re;
	if (re.initialize(name)) {
		int	counter;
		for (counter=0; re.getAliasList()[counter]; counter++);
		char	**alias=new char *[counter+1];
		alias[counter]=NULL;
		for (int i=0; i<counter; i++) {
			alias[i]=charstring::duplicate(re.getAliasList()[i]);
		}
		*aliaslist=alias;
		return true;
	}
	return false;
}

bool rpcentry::getName(int number, char **name) {
	rpcentry	re;
	if (re.initialize(number)) {
		*name=charstring::duplicate(re.getName());
		return true;
	}
	return false;
}

bool rpcentry::getAliasList(int number, char ***aliaslist) {
	rpcentry	re;
	if (re.initialize(number)) {
		int	counter;
		for (counter=0; re.getAliasList()[counter]; counter++);
		char	**alias=new char *[counter+1];
		alias[counter]=NULL;
		for (int i=0; i<counter; i++) {
			alias[i]=charstring::duplicate(re.getAliasList()[i]);
		}
		*aliaslist=alias;
		return true;
	}
	return false;
}

void rpcentry::print() const {

	if (!pvt->_re) {
		return;
	}

	printf("Name: %s\n",getName());
	printf("Number: %d\n",getNumber());
	printf("Alias list:\n");
	for (int i=0; getAliasList()[i]; i++) {
		printf("	%s\n",getAliasList()[i]);
	}
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif

#endif


syntax highlighted by Code2HTML, v. 0.9.1