/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



#include <math.h>
#include "system.h"

#ifdef NO_PTHREAD_RTL

static pthread_mutex_t getrtl_mutex = PTHREAD_MUTEX_INITIALIZER;

/* struct passwd differs on some platforms */
#if defined NETBSD
struct passwd *getpwnam_r(const char* name, struct passwd* s, char* buffer, int size )
{
  	struct passwd* res;

  	pthread_mutex_lock(&getrtl_mutex);

  	if ( (res = getpwnam(name)) )
  	{
		int nname, npasswd, nclass, ngecos, ndir;

		nname= strlen(res->pw_name)+1;
		npasswd= strlen(res->pw_passwd)+1;
		nclass= strlen(res->pw_class)+1;
		ngecos= strlen(res->pw_gecos)+1;
		ndir= strlen(res->pw_dir)+1;

		if (nname+npasswd+nclass+ngecos
				+ndir+strlen(res->pw_shell) < size)
		{
    		memcpy(s, res, sizeof(struct passwd));

        	strcpy(buffer, res->pw_name);
      		s->pw_name = buffer;
			buffer += nname;

        	strcpy(buffer, res->pw_passwd);
      		s->pw_passwd = buffer;
			buffer += npasswd;

        	strcpy(buffer, res->pw_class);
      		s->pw_class = buffer;
			buffer += nclass;

        	strcpy(buffer, res->pw_gecos);
      		s->pw_gecos = buffer;
			buffer += ngecos;

        	strcpy(buffer, res->pw_dir);
      		s->pw_dir = buffer;
			buffer += ndir;

        	strcpy(buffer, res->pw_shell);
      		s->pw_shell = buffer;

   			res = s;
		}
		else
			res = NULL;
	}

	pthread_mutex_unlock(&getrtl_mutex);

        return(res);
}

int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer,
           size_t buflen, struct passwd **result)
{
  struct passwd* res;
  int retval = 0;

  pthread_mutex_lock(&getrtl_mutex);

  if ( (res = getpwuid(uid)) )
  {
    size_t pw_name, pw_passwd, pw_class, pw_gecos, pw_dir, pw_shell;

    pw_name = strlen(res->pw_name)+1;
    pw_passwd = strlen(res->pw_passwd)+1;
    pw_class = strlen(res->pw_class)+1;
    pw_gecos = strlen(res->pw_gecos)+1;
    pw_dir = strlen(res->pw_dir)+1;
    pw_shell = strlen(res->pw_shell)+1;

    if (pw_name+pw_passwd+pw_class+pw_gecos
                                 +pw_dir+pw_shell < buflen)
    {
      memcpy(pwd, res, sizeof(struct passwd));

      strncpy(buffer, res->pw_name, pw_name);
      pwd->pw_name = buffer;
      buffer += pw_name;

      strncpy(buffer, res->pw_passwd, pw_passwd);
      pwd->pw_passwd = buffer;
      buffer += pw_passwd;

      strncpy(buffer, res->pw_class, pw_class);
      pwd->pw_class = buffer;
      buffer += pw_class;

      strncpy(buffer, res->pw_gecos, pw_gecos);
      pwd->pw_gecos = buffer;
      buffer += pw_gecos;

      strncpy(buffer, res->pw_dir, pw_dir);
      pwd->pw_dir = buffer;
      buffer += pw_dir;

      strncpy(buffer, res->pw_shell, pw_shell);
      pwd->pw_shell = buffer;
      buffer += pw_shell;

      *result = pwd ;
      retval = 0 ;

    }
    else
        retval =  ENOMEM;
  }
  else
      retval = errno ;

  pthread_mutex_unlock(&getrtl_mutex);

  return retval;
}

struct tm *localtime_r(const time_t *timep, struct tm *buffer)
{
	struct tm* res;

  	pthread_mutex_lock(&getrtl_mutex);

	if ( (res = localtime(timep)))
	{
		memcpy(buffer, res, sizeof(struct tm));
		res = buffer;
	}

	pthread_mutex_unlock(&getrtl_mutex);

	return res;
}

struct tm *gmtime_r(const time_t *timep, struct tm *buffer)
{
	struct tm* res;

  	pthread_mutex_lock(&getrtl_mutex);

	if ( (res = gmtime(timep)) )
	{
		memcpy(buffer, res, sizeof(struct tm));
		res = buffer;
	}

	pthread_mutex_unlock(&getrtl_mutex);

	return res;
}
#endif  /* defined NETBSD */

#ifdef SCO
#include <pwd.h>
#include <shadow.h>
#include <sys/types.h>

struct spwd *getspnam_r(const char *name, struct spwd* s, char* buffer, int size )
{
  	struct spwd* res;

  	pthread_mutex_lock(&getrtl_mutex);

  	if ( res = getspnam(name) )
  	{
		int nnamp;

		nnamp = strlen(res->sp_namp)+1;

		if (nnamp+strlen(res->sp_pwdp) < size) {
    		memcpy(s, res, sizeof(struct spwd));

        	strcpy(buffer, res->sp_namp);
      		s->sp_namp = buffer;
			buffer += nnamp;

        	strcpy(buffer, res->sp_pwdp);
      		s->sp_pwdp = buffer;

   			res = s;
		}
		else
			res = NULL;
	}

  	pthread_mutex_unlock(&getrtl_mutex);

  	return res;
}

struct passwd *getpwnam_r(const char* name, struct passwd* s, char* buffer, int size )
{
  	struct passwd* res;

  	pthread_mutex_lock(&getrtl_mutex);

  	if ( res = getpwnam(name) )
  	{
		int nname, npasswd, nage;
		int ncomment, ngecos, ndir;

		nname= strlen(res->pw_name)+1;
		npasswd= strlen(res->pw_passwd)+1;
		nage= strlen(res->pw_age)+1;
		ncomment= strlen(res->pw_comment)+1;
		ngecos= strlen(res->pw_gecos)+1;
		ndir= strlen(res->pw_dir)+1;

		if (nname+npasswd+nage+ncomment+ngecos+ndir
				+strlen(res->pw_shell) < size)
		{
    		memcpy(s, res, sizeof(struct passwd));

        	strcpy(buffer, res->pw_name);
      		s->pw_name = buffer;
			buffer += nname;

        	strcpy(buffer, res->pw_passwd);
      		s->pw_passwd = buffer;
			buffer += npasswd;

        	strcpy(buffer, res->pw_age);
      		s->pw_age = buffer;
			buffer += nage;

        	strcpy(buffer, res->pw_comment);
      		s->pw_comment = buffer;
			buffer += ncomment;

        	strcpy(buffer, res->pw_gecos);
      		s->pw_gecos = buffer;
			buffer += ngecos;

        	strcpy(buffer, res->pw_dir);
      		s->pw_dir = buffer;
			buffer += ndir;

        	strcpy(buffer, res->pw_shell);
      		s->pw_shell = buffer;

   			res = s;
		}
		else
			res = NULL;
	}

	pthread_mutex_unlock(&getrtl_mutex);

  	return res;
}
#endif /* defined SCO */

#if !defined(FREEBSD) || (__FreeBSD_version < 601103)

extern int h_errno;

struct hostent *gethostbyname_r(const char *name, struct hostent *result,
								char *buffer, int buflen, int *h_errnop)
{
	/* buffer layout: 	name\0
	 *					array_of_pointer_to_aliases
	 *					NULL
	 *					alias1\0...aliasn\0
	 *                  array_of_pointer_to_addresses
	 * 					NULL
	 * 					addr1addr2addr3...addrn
	 */
  	struct hostent* res;

  	pthread_mutex_lock(&getrtl_mutex);

  	if ( (res = gethostbyname(name)) )
  	{
		int nname, naliases, naddr_list, naliasesdata, n;
		char **p, **parray, *data;

		/* Check buffer size before copying, we want to leave the
		 * buffers unmodified in case something goes wrong.
		 *
		 * Is this required?
		 */

		nname= strlen(res->h_name)+1;

		naliases = naddr_list = naliasesdata = 0;

		for ( p = res->h_aliases; *p != NULL; p++) {
			naliases++;
			naliasesdata += strlen(*p)+1;
		}

		for ( p = res->h_addr_list; *p != NULL; p++)
			naddr_list++;

		if ( nname
			 + (naliases+1)*sizeof(char*) + naliasesdata
			 + (naddr_list+1)*sizeof(char*) + naddr_list*res->h_length
			 <= buflen )
		{
    		memcpy(result, res, sizeof(struct hostent));

        	strcpy(buffer, res->h_name);
      		result->h_name = buffer;
			buffer += nname;

			parray = (char**)buffer;
			result->h_aliases = parray;
			data = buffer + (naliases+1)*sizeof(char*);
			for ( p = res->h_aliases; *p != NULL; p++) {
				n = strlen(*p)+1;
				*parray++ = data;
				memcpy(data, *p, n);
				data += n;
			}
			*parray = NULL;
			buffer = data;
			parray = (char**)buffer;
			result->h_addr_list = parray;
			data = buffer + (naddr_list+1)*sizeof(char*);
			for ( p = res->h_addr_list; *p != NULL; p++) {
				*parray++ = data;
				memcpy(data, *p, res->h_length);
				data += res->h_length;
			}
			*parray = NULL;

   			res = result;
		}
		else
		{
			errno = ERANGE;
			res = NULL;
		}
	}
	else
	{
		*h_errnop = h_errno;
	}

	pthread_mutex_unlock(&getrtl_mutex);

  	return res;
}
#endif /* !defined(FREEBSD) || (__FreeBSD_version < 601103) */

#if defined(MACOSX)
/*
 * Add support for resolving Mac native alias files (not the same as unix alias files)
 * returns 0 on success.
 */
int macxp_resolveAlias(char *path, int buflen)
{
  FSRef aFSRef;
  OSStatus nErr;
  Boolean bFolder;
  Boolean bAliased;
  char *unprocessedPath = path;

  if ( *unprocessedPath == '/' )
    unprocessedPath++;

  int nRet = 0;
  while ( !nRet && unprocessedPath && *unprocessedPath )
    {
      unprocessedPath = strchr( unprocessedPath, '/' );
      if ( unprocessedPath )
	*unprocessedPath = '\0';

      nErr = noErr;
      bFolder = FALSE;
      bAliased = FALSE;
      if ( FSPathMakeRef( (const UInt8 *)path, &aFSRef, 0 ) == noErr )
	{
	  nErr = FSResolveAliasFileWithMountFlags( &aFSRef, TRUE, &bFolder, &bAliased, kResolveAliasFileNoUI );
	  if ( nErr == nsvErr )
	    {
	      errno = ENOENT;
	      nRet = -1;
	    }
	  else if ( nErr == noErr && bAliased )
	    {
	      char tmpPath[ PATH_MAX ];
	      if ( FSRefMakePath( &aFSRef, (UInt8 *)tmpPath, PATH_MAX ) == noErr )
		{
		  int nLen = strlen( tmpPath ) + ( unprocessedPath ? strlen( unprocessedPath + 1 ) + 1 : 0 );
		  if ( nLen < buflen && nLen < PATH_MAX )
		    {
		      if ( unprocessedPath )
			{
			  int nTmpPathLen = strlen( tmpPath );
			  strcat( tmpPath, "/" );
			  strcat( tmpPath, unprocessedPath + 1 );
			  strcpy( path, tmpPath);
			  unprocessedPath = path + nTmpPathLen;
			}
		      else if ( !unprocessedPath )
			{
			  strcpy( path, tmpPath);
			}
		    }
		  else
		    {
		      errno = ENAMETOOLONG;
		      nRet = -1;
		    }
		}
	    }
	}

      if ( unprocessedPath )
	*unprocessedPath++ = '/';
    }

  return nRet;
}

#endif  /* defined MACOSX */

#endif /* NO_PTHREAD_RTL */

#if (defined (LINUX) && (GLIBC >= 2))
/* The linux kernel thread implementation, always return the pid of the
   thread subprocess and not of the main process. So we save the main
   pid at startup
*/

// Directly from libc.so.6, obviously missing from some unistd.h:
extern __pid_t __getpid(void);

static pid_t pid = -1;

static void savePid(void) __attribute__((constructor));

static void savePid(void)
{
	if (pid == -1)
		pid = __getpid();
}

pid_t getpid(void)
{
	if (pid == -1)
		savePid();

	return (pid);
}
#endif /*  (defined (LINUX) && (GLIBC >= 2)) */

#ifdef NO_PTHREAD_SEMAPHORES
int sem_init(sem_t* sem, int pshared, unsigned int value)
{
	pthread_mutex_init(&sem->mutex, PTHREAD_MUTEXATTR_DEFAULT);
	pthread_cond_init(&sem->increased, PTHREAD_CONDATTR_DEFAULT);

	sem->value = (int)value;
	return 0;
}

int sem_destroy(sem_t* sem)
{
	pthread_mutex_destroy(&sem->mutex);
	pthread_cond_destroy(&sem->increased);
	sem->value = 0;
	return 0;
}

int sem_wait(sem_t* sem)
{
	pthread_mutex_lock(&sem->mutex);

	while (sem->value <= 0)
	{
		pthread_cond_wait(&sem->increased, &sem->mutex);
	}

	sem->value--;
	pthread_mutex_unlock(&sem->mutex);

	return 0;
}

int sem_trywait(sem_t* sem)
{
	int result = 0;

	pthread_mutex_lock(&sem->mutex);

	if (sem->value > 0)
	{
		sem->value--;
	}
	else
	{
		errno = EAGAIN;
		result = -1;
	}

	pthread_mutex_unlock(&sem->mutex);

	return result;
}

int sem_post(sem_t* sem)
{
	pthread_mutex_lock(&sem->mutex);

	sem->value++;

	pthread_mutex_unlock(&sem->mutex);

	pthread_cond_signal(&sem->increased);

	return 0;
}
#endif

#if defined(FREEBSD)
char *fcvt(double value, int ndigit, int *decpt, int *sign)
{
  static char ret[256];
  char buf[256],zahl[256],format[256]="%";
  char *v1,*v2;

  if (value==0.0) value=1e-30;

  if (value<0.0) *sign=1; else *sign=0;

  if (value<1.0)
  {
    *decpt=(int)log10(value);
	value*=pow(10.0,1-*decpt);
	ndigit+=*decpt-1;
	if (ndigit<0) ndigit=0;
  }
  else
  {
    *decpt=(int)log10(value)+1;
  }

  sprintf(zahl,"%d",ndigit);
  strcat(format,zahl);
  strcat(format,".");
  strcat(format,zahl);
  strcat(format,"f");

  sprintf(buf,format,value);

  if (ndigit!=0)
  {
    v1=strtok(buf,".");
    v2=strtok(NULL,".");
	strcpy(ret,v1);
    strcat(ret,v2);
  }
  else
  {
    strcpy(ret,buf);
  }

  return(ret);
}

#endif
