/*
** libclone.c
**
** Libraries and Devices Manager: CloneLibDev()
**
** $Id: libclone.c,v 3.0 94/08/09 17:27:29 GF Exp Locker: GF $
**
**************************************************************************
**
** Copyright © 1994 by Gabriele Falcioni. All Rights Reserved.
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
**
** For inquiries, comments, donations, bug reports, write to:
**
** Gabriele Falcioni
** via E. Cialdini, 50
** I-60122 Ancona
** ITALY
**
*/

#include <string.h>

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/libraries.h>
#include <exec/execbase.h>

#include <proto/exec.h>

#include "libman.h"

/* --- My favourite macros -------------------------------------------- */

#define STDMEM (MEMF_PUBLIC|MEMF_CLEAR)

#define ConStruct(type)		((type *)AllocMem(sizeof(type),STDMEM))
#define DiStruct(ptr)		FreeMem((ptr),sizeof(*(ptr)))
#define ClearStruct(ptr)	memset((ptr),0,sizeof(*(ptr)))

#ifndef FOREVER
#define FOREVER for (;;)
#endif

#ifndef STREQ
#define STREQ(s1,s2) (!strcmp((s1),(s2)))
#endif

/* --- Utilities ------------------------------------------------------ */

/*
 * NAME
 * trim_idstring -- trims id string to be nicely printed
 *
 * FUNCTION
 * Every library terminates id strings in a different style. This function
 * checks the id string for linefeed and carriage return. If found they
 * are removed to nicely print the id string.
 *
 * INPUT
 * string to modify.
 *
 * RESULT
 * none.
 */
 
static void trim_idstring(UBYTE *s)
{
	for (;;) {
		switch (*s) {
			case '\n':
			case '\r':
				*s='\0';
			case 0:
				return;
		}
		++s;
	}
}

/* -------------------------------------------------------------------- */

/*
 * NAME
 * calc_workspace -- returns the required storage length for a given list
 *
 * NOTE
 * This function must be called in Forbid() state.
 *
 * INPUT
 * a pointer to the first node of the list.
 *
 * RESULT
 * length in bytes of the required storage.
 *
 */

static LONG calc_workspace(struct Node *ldn)
{
	LONG len;

	len = sizeof(struct List);

	while (ldn->ln_Succ) {
		len += sizeof(struct Library);
		len += strlen(ldn->ln_Name) + 1;

		if (((struct Library *)ldn)->lib_IdString)
			len += strlen((UBYTE *)((struct Library *)ldn)->lib_IdString) +1;
		else ++len;

		if (len & 1) ++len;	/* WORD ALIGN */

		ldn = ldn->ln_Succ;
	}

	return len;
}

/*
 * NAME
 * clone_ldlist -- clones a system list (NT_LIBRARY or NT_DEVICE)
 *
 * NOTES
 * This function must be called in Forbid() state.
 * Every libnode IdString is corrected: trailing CR/LF are stripped away.
 *
 * INPUTS
 * ldl       - a pointer to the cloned list header.
 * ldn       - a pointer to the first node to clone.
 * avail_mem - length in bytes of remaining memory.
 *
 * RESULT
 * TRUE if list succesfully cloned,
 * FALSE if not enough memory: because of multitasking list may change.
 */

static BOOL clone_ldlist(
	struct List *ldl,
	struct Library *ldn,
	LONG avail_mem)
{
	UBYTE *wsptr,*wsend;
	struct Library *clone;

	wsptr  = (UBYTE *)ldl;
	wsend  = wsptr+avail_mem;
	wsptr += sizeof(struct List);

	while (ldn->lib_Node.ln_Succ) {
		clone = (struct Library *)wsptr;
		wsptr += sizeof(struct Library);
		if (wsptr > wsend) return FALSE;

		*clone = *ldn;	/* CLONE STRUCTURE */

		clone->lib_Node.ln_Name = wsptr;
		wsptr += strlen(ldn->lib_Node.ln_Name) +1;
		if (wsptr > wsend) return FALSE;

		strcpy(
			clone->lib_Node.ln_Name,
			ldn->lib_Node.ln_Name
		);	/* CLONE NAME */

		clone->lib_IdString = (APTR)wsptr;
		if (ldn->lib_IdString) wsptr += strlen((UBYTE *)ldn->lib_IdString) +1;
		else ++wsptr;

		if ((ULONG)wsptr & 1) ++wsptr;	/* WORD ALIGN */
		if (wsptr > wsend) return FALSE;

		if (ldn->lib_IdString)
			strcpy(
				(UBYTE *)clone->lib_IdString,
				(UBYTE *)ldn->lib_IdString
			);	/* CLONE IDSTRING */

		/* CORRECT IDSTRING (remove trailing CR/LF) */
		trim_idstring(clone->lib_IdString);

		AddTail(ldl,(struct Node *)clone);		/* "CLONE" LINK */
		ldn = (struct Library *)ldn->lib_Node.ln_Succ;
	}

	return TRUE;
}

/*
** LDScanner()
**
** 
** of the current configuration.
**
** ARGUMENTS:
**
** type - one of NT_LIBRARY or NT_DEVICE
**
** RESULTS:
**
** A ptr to the list clone.
** Note: the memory used by the complete list and related data
** is AllocVec()'ed. The pointer to this memory block is the
** ptr to the list itself.
**
*/

/*
 * NAME
 * CloneLibDev -- clones the libraries or devices system list
 *
 * SYNOPSIS
 * clone = CloneLibDev(type);
 *
 * struct List *CloneLibDev(UBYTE);
 *
 * FUNCTION
 * It clones the system libraries or devices list. The clone is created
 * in a block of memory AllocVec()ed. To free the block do simply a
 * FreeVec() using the pointer to the cloned list header.
 *
 * INPUTS
 * type - the list type: NT_LIBRARY or NT_DEVICE.
 *
 * RESULTS
 * clone - a pointer to the cloned list header or NULL if not enough memory.
 *         Also used to free the cloned list when they are no more needed.
 */

struct List *CloneLibDev(UBYTE type)
{
	LONG wslen;
	struct Library *first_node;
	struct List *ldl;

	Forbid();

	FOREVER {

		first_node = (struct Library *)(type == NT_LIBRARY ?
			SysBase->LibList.lh_Head : SysBase->DeviceList.lh_Head);

		/*
		 * calc required space.
		 */

		wslen = calc_workspace(first_node);
		ldl = (struct List *)AllocVec(wslen,STDMEM);

		if (!ldl) break;

		NewList(ldl);
		ldl->lh_Type = type;

		first_node = (struct Library *)(type == NT_LIBRARY ?
			SysBase->LibList.lh_Head : SysBase->DeviceList.lh_Head);

		if (clone_ldlist(ldl,first_node,wslen)) break;	/* success */

		/*
		 * Memory requirements have changed, retry.
		 */

		FreeVec(ldl);
	}

	Permit();

	return ldl;
}
