/*
** librem.c
**
** Libraries and Devices Manager: DismountLibDev()
**
** $Id: librem.c,v 3.0 94/08/09 17:29:36 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 <dos/dos.h>
#include <dos/dosasl.h>

#include <proto/dos.h>
#include <proto/exec.h>

#include <usrh/filefinder.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

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

/*
 * NAME
 * clear_libnodes -- clears remove flag from all nodes
 *
 * FUNCTION
 * The lib_pad field of the Library structure is used as a boolean value
 * to flag if the node shall be removed. This function clear this flag
 * (no nodes to remove).
 *
 * INPUT
 * ldlist - a pointer to the Library list.
 *
 * RESULT
 * none
 */

static VOID clear_libnodes(struct List *ldlist)
{
	register struct Library *libnode;

	libnode = (struct Library *)ldlist->lh_Head;
	while (libnode->lib_Node.ln_Succ) {
		libnode->lib_pad = 0;
		libnode = (struct Library *)libnode->lib_Node.ln_Succ;
	}
}

/*
 * NAME
 * remove_libnodes -- removes all flagged nodes
 *
 * FUNCTION
 * The lib_pad field of the Library structure is used as a boolean value
 * to flag if the node shall be removed. This function removes all nodes
 * that have this flag set.
 *
 * INPUTS
 * ldlist - a pointer to the flagged Library list.
 * type   - node type: NT_LIBRARY or NT_DEVICE.
 *
 * RESULT
 * none
 */

static VOID remove_libnodes(struct List *ldlist,UBYTE type)
{
	struct List *syslist;
	register struct Library *libnode;

	syslist = (type == NT_LIBRARY ?
		&SysBase->LibList : &SysBase->DeviceList);

	libnode = (struct Library *)ldlist->lh_Head;
	while (libnode->lib_Node.ln_Succ) {

		if (libnode->lib_pad) {
			struct Node *node;

			Forbid();

			if (node = FindName(syslist,libnode->lib_Node.ln_Name))
				if (type == NT_LIBRARY)
					RemLibrary((struct Library *)node);
				else
					RemDevice((struct Device *)node);

			Permit();
		}

		libnode = (struct Library *)libnode->lib_Node.ln_Succ;
	}
}

/*
 * NAME
 * DismountLibDev -- dismounts a list of libraries and/or devices
 *
 * SYNOPSIS
 * rc = DismountLibDev(names,type);
 *
 * LONG DismountLibDev(UBYTE **,UBYTE);
 *
 * FUNCTION
 * It attempts to remove all the matching nodes of the libraries or
 * devices list. This function accepts standard AmigaDOS patterns.
 *
 * INPUTS
 * names - a pointer to an array of pointers to lib/dev names. Standard
 *         AmigaDOS patterns accepted. The array must be NULL terminated.
 * type  - the list type: NT_LIBRARY or NT_DEVICE.
 *
 * RESULTS
 * rc - level of failure:
 *      RETURN_OK		all libraries/devices successfully removed,
 *      RETURN_WARN	some libraries/devices refused to remove,
 *      RETURN_ERROR user break and so on,
 *      RETURN_FAIL	total failure.
 *      Error messages will be printed (if required) to stdout.
 */

#define SAFE_SET_RC(rc,level) if ((rc) < (level)) (rc) = (level)

LONG DismountLibDev(UBYTE **names,UBYTE type)
{
	LONG rc;
	struct List *before,*after;
	struct NodeMatcher *nm;

	rc = 0;

	if (before = CloneLibDev(type)) {

		if (nm = CreateNodeMatcher(names,before)) {
			register struct Library *libnode;

			/*
			 * NOTES
			 *
			 * The lib_pad field of cloned nodes is used as a boolean value
			 * to flag the removed nodes.
			 *
			 * To be safe, we'll modify ONLY clones of system lists.
			 */

			clear_libnodes(before);

			/*
			 * loop for all patterns and mark all matching nodes.
			 * Marked nodes will be removed.
			 */

			while (libnode = (struct Library *)MatchNodes(nm))
				if (!libnode->lib_pad
				&&  !libnode->lib_OpenCnt)
					libnode->lib_pad = TRUE;

			if (IoErr()) {
				PrintFault(IoErr(),NULL);
				rc = RETURN_ERROR;
			}

			DeleteNodeMatcher(nm);
		} else {
			PrintFault(ERROR_NO_FREE_STORE,NULL);
			rc = RETURN_ERROR;
		}

		if (!rc) {

			/*
			 * Request removal of every marked node.
			 */

			remove_libnodes(before,type);

			/*
			 * Display results: compare the old lib/dev list
			 * with the new list, removed nodes appear only
			 * in the old list.
			 */

			if (after = CloneLibDev(type)) {
				register LONG nodes;
				register struct Library *libnode;

				nodes = 0;
				libnode = (struct Library *)before->lh_Head;
				while (libnode->lib_Node.ln_Succ) {

					if (libnode->lib_pad)
						if (!FindName(after,libnode->lib_Node.ln_Name)) {
							Printf("%s %s removed\n",
								(type == NT_LIBRARY ? "Library" : "Device"),
								libnode->lib_Node.ln_Name);

							++nodes;
						} else {
							Printf("%s %s - failed to remove\n",
								(type == NT_LIBRARY ? "Library" : "Device"),
								libnode->lib_Node.ln_Name);

							SAFE_SET_RC(rc,5);
						}

					libnode = (struct Library *)libnode->lib_Node.ln_Succ;

					/*
					 * check for user break
					 */

					if (CheckSignal(SIGBREAKF_CTRL_C)) {
						PrintFault(ERROR_BREAK,NULL);
						rc = RETURN_ERROR;
						break;	/* leave loop */
					}
				}

				if (!rc && !nodes)
					Printf("No %s to remove\n",
						(type == NT_LIBRARY ? "Libraries" : "Devices"));

				FreeVec(after);	/* free new clone */
			} else {
				PrintFault(ERROR_NO_FREE_STORE,NULL);
				rc = RETURN_ERROR;
			}
		}

		FreeVec(before);	/* free old clone */
	} else {
		PrintFault(ERROR_NO_FREE_STORE,NULL);
		rc = RETURN_FAIL;
	}

	return rc;
}
