/*
 * $QNXLicenseC:  
 * Copyright 2007, QNX Software Systems. All Rights Reserved.
 *
 * You must obtain a written license from and pay applicable license fees to QNX 
 * Software Systems before you may reproduce, modify or distribute this software, 
 * or any work that includes all or part of this software.   Free development 
 * licenses are available for evaluation and non-commercial purposes.  For more 
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *  
 * This file may contain contributions from others.  Please review this entire 
 * file for other proprietary rights or license notices, as well as the QNX 
 * Development Suite License Guide at http://licensing.qnx.com/license-guide/ 
 * for other information.
 * $
*/

#include <unistd.h>		// determines whether _POSIX_SPAWN is defined or not
#ifdef _POSIX_SPAWN

#include "posix_spawnattr.h"



/*
 * =============================================================================
 * 
 * 							Internal Support Routines
 * 
 * =============================================================================
*/

/*******************************************************************************
 * posix_spawnattr_t_once_init
 * 
 * Called from posix_spawn() only once, this routine will initialize the
 * default_posix_spawnattr_t object to a properly keyed value as this is not
 * possible to do at link time
 * 
 * All functions within this file will use _default_posix_spawnattr_t to return
 * default values. Only when posix_spawn() is first called with either an <attrp>
 * of NULL or an <attrp> which has never had any _setxxx() operations made on it
 * will the 'default_posix_spawnattr_t' object be used and hence initialized
*/
void posix_spawnattr_t_once_init(void)
{
	SET_ATTRP(&default_posix_spawnattr_t, (void *)&_default_posix_spawnattr_t);
}

/*******************************************************************************
 * _posix_spawnattr_t_extend
 * 
 * If <attrp> == NULL, this function will allocate and initialize to default
 * values, memory for an internal '_posix_spawnattr_t' structure plus <delta_size>
 * bytes.
 * If <attrp> != NULL, this function will reallocate the memory pointed to by
 * <attrp> plus <delta_size> bytes.
 * The <delta_size> bytes added to <attrp> will be added as if the memory was
 * inserted at <insert_offset> from the start of <attrp>. That is, the remainder
 * of the <attrp> structure starting at <insert_offset> will be relocated
 * <delta_size> bytes from its current location, effectively creating a 'hole'
 * at <insert_offset> of <delta_size> bytes.
 * 
 * The <delta_size> bytes inserted will be cleared. It is the callers
 * repsonsibility to put useful information there.
 * 
 * Returns a pointer to the _posix_spawnattr_t structure or NULL if memory could
 * not be allocated.
*/
_posix_spawnattr_t *_posix_spawnattr_t_extend(_posix_spawnattr_t *attrp, size_t delta_size, off_t insert_offset)
{
	const size_t new_size = SPAWNATTR_T_SIZE(attrp) + delta_size;
	/* this variable represents the alignment adjustment required for 'to' and 'len' below. We need it now */
	const size_t align_size = ROUNDUP((uintptr_t)attrp + insert_offset + delta_size, 8) - 
							 	((uintptr_t)attrp + insert_offset + delta_size);

	/* ensure zeroed, aligned memory the first time */
	if ((attrp == NULL) && ((attrp = calloc(1, new_size + align_size)) != NULL)) {
		/*
		 * first time 'posix_spawnattr_t' allocation, so set up defaults. It is the
		 * callers responsibility to initialize the 'delta_size' memory
		*/
		memcpy(attrp, &_default_posix_spawnattr_t, sizeof(*attrp));
	} else {
		attrp = realloc(attrp, new_size + align_size);
	
		if (attrp != NULL) {
			/* figure out if there is anything to move */
			uintptr_t from = (uintptr_t)attrp + insert_offset;
			uintptr_t to = ROUNDUP(from + delta_size, 8);	// need to keep successive vsect structures aligned
			size_t len = new_size - (insert_offset + delta_size);	// 'align_size' cancels out
			/* we need to move 'delta_size' bytes starting from <insert> to create the gap for the new data */
			if (len > 0) {
				memmove((void *)to, (void *)from, len);
				memset((void *)from, 0, to - from);
			}
		}
	}
	return attrp;
}

/*=====================   D E B U G   and   T E S T   ==========================*/

/*
 * normally the number of entries for a variable length structure is part of the
 * structure, like the partition however we will just calculate for structure
 * simplicity.
 * Note we need to deal with the initial case of size == 0
*/
#define NUM_EXAMPLE_ENTRIES(j)	((j->size - ((j->size==0) ? 0 : offsetof(_posix_spawnattr_example_t, example))) / sizeof(example_attr->example))

int posix_spawnattr_addexample(posix_spawnattr_t *_Restrict attrp, uint64_t example_val)
{
	if (attrp == NULL) return EINVAL;
	else {
		/* need to realloc the attribute structure. Let's find out by how much */
		size_t extend_size;
		size_t extend_size_adjust;
		off_t extend_pos;
		_posix_spawnattr_t *_attrp = GET_ATTRP(attrp);
		_posix_spawnattr_example_t *example_attr;

		if (_attrp == NULL) {
			/* the 'vsect.example' field is already part of the structure size. We only need
			 * the extra bytes required for a '_posix_spawnattr_example_t' object */
			extend_size = sizeof(*example_attr) - sizeof(_attrp->vsect.example);
			/* on the first allocation, we need to add back in the size of the _attrp->vsect.example field */
			extend_size_adjust = sizeof(_attrp->vsect.example);
			extend_pos = 0;	// not required since _attrp == NULL
		} else if ((example_attr = EXAMPLE_T_ADDR(_attrp))->size < sizeof(*example_attr)) {
			/* the '_posix_spawnattr_example_t' pointer is already part of the structure
			 * size. We only need the extra bytes required for a '_posix_spawnattr_example_t' object */
			extend_size = sizeof(*example_attr) - sizeof(_attrp->vsect.example);
			/* on the first allocation, we need to add back in the size of the _attrp->vsect.example field */
			extend_size_adjust = EXAMPLE_T_SIZE(_attrp);
			/* we need to calculate the insert offset as there may be a part of _posix_spawnattr_t
			 * structure following this field that is already initialized. That offset is the field
			 * immediately following the '_posix_spawnattr_example_t' object (based on its size)
			 * ie. (example_attr - _attrp) gives us the offset of 'example_attr'. We then
			 * 		skip ahead EXAMPLE_T_SIZE() bytes. The position will be 8 byte aligned */
			extend_pos = (off_t)(((uintptr_t)example_attr - (uintptr_t)_attrp) + EXAMPLE_T_SIZE(_attrp));
		} else {
			/*
			 * have some example vals's already. New size is existing size
			 * plus space for one more 'val'
			*/
			extend_size = sizeof(example_attr->example);
			extend_size_adjust = 0;
			extend_pos = (off_t)(((uintptr_t)example_attr - (uintptr_t)_attrp) + EXAMPLE_T_SIZE(_attrp));
		}

		if ((_attrp = _posix_spawnattr_t_extend(_attrp, extend_size, extend_pos)) == NULL) {
			return ENOMEM;
		}
		/* reset 'example_attr' to the location of the possibly relocated '_attrp' */
		example_attr = EXAMPLE_T_ADDR(_attrp);

		/* update the partition structure with the new entry */
		example_attr->example[NUM_EXAMPLE_ENTRIES(example_attr)] = example_val;

		/* update the size of the _posix_spawnattr_example_t structure that has been allocated */
		example_attr->size += (extend_size + extend_size_adjust);

		/* reset posix_spawnattr_t object pointer */
		SET_ATTRP(attrp, _attrp);
		return EOK;
	}
}

int posix_spawnattr_getexample(const posix_spawnattr_t *_Restrict attrp, int *num, uint64_t vals[])
{
	if (!valid_attrp(attrp) || (num == NULL) || (*num < 0)) {
		return EINVAL;
	} else {
		_posix_spawnattr_t *_attrp = GET_ATTRP(attrp);
		_posix_spawnattr_example_t *example_attr;

		if (_attrp == NULL) {
			*num = 0;
			return EOK;
		}

		example_attr = EXAMPLE_T_ADDR(_attrp);
		if (example_attr->size < sizeof(*example_attr)) {	// could check for zero but this is safer
			*num = 0;
		} else {
			unsigned num_entries = min(NUM_EXAMPLE_ENTRIES(example_attr), *num);
			unsigned i;

			for (i=0; i<num_entries; i++) {
				vals[i] = example_attr->example[i];
			}
			if (num_entries < NUM_EXAMPLE_ENTRIES(example_attr)) {
				*num = -(NUM_EXAMPLE_ENTRIES(example_attr) - num_entries);
			} else {
				*num = NUM_EXAMPLE_ENTRIES(example_attr);
			}
		}
		return EOK;
	}
}


#endif	/* _POSIX_SPAWN */

