/* 
** openurl.library - universal URL display and browser launcher library
** Written by Troels Walsted Hansen <troels@stud.cs.uit.no>
** Placed in the public domain.
**
** This is the one and only source file for the library.
*/

#define __USE_SYSBASE
#include <proto/exec.h>

#include <clib/dos_protos.h>
#include <clib/utility_protos.h>
#include <clib/intuition_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/openurl_protos.h>

#include <pragmas/dos_pragmas.h>
#include <pragmas/utility_pragmas.h>
#include <pragmas/intuition_pragmas.h>
#include <pragmas/iffparse_pragmas.h>
#include <pragmas/openurl_pragmas.h>

#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dostags.h>
#include <libraries/iffparse.h>
#include <prefs/prefhdr.h>
#include <libraries/openurl.h>

#include <string.h>
#include <ctype.h>

#include "handler.h"

extern void kprintf(const char *, ...);

#define REG(x) register __ ## x
#define LIB __saveds __asm

/**************************************************************************
*
* Prototypes for library and Local, non-library functions.
*
*/

LIB BOOL LIB_URL_OpenA(REG(a0) STRPTR url, REG(a1) struct TagItem *tags);
LIB struct URL_Prefs *LIB_URL_GetPrefs(VOID);
LIB VOID LIB_URL_FreePrefs(REG(a0) struct URL_Prefs *up);
LIB BOOL LIB_URL_SetPrefs(REG(a0) struct URL_Prefs *up, REG(d0) BOOL permanent);
LIB struct URL_Prefs *LIB_URL_GetDefaultPrefs(VOID);

static BOOL CopyList(struct List *dst, struct List *src, ULONG size);
static VOID FreeList(struct List *list, ULONG size);
static BOOL SendRexxMsg(struct MsgPort *replyport, STRPTR rxport, STRPTR rxcmd);
static STRPTR FindRexxPort(struct List *list, STRPTR name);
static BOOL isdigits(STRPTR str);

static struct URL_Prefs *CopyPrefs(struct URL_Prefs *old_p);
static BOOL SavePrefs(STRPTR filename, struct URL_Prefs *up);
static BOOL LoadPrefs(STRPTR filename);
static BOOL SetDefaultPrefsV1(struct URL_Prefs *up);
static VOID SetDefaultPrefsV2(struct URL_Prefs *up);

static VOID SPrintf(STRPTR to, STRPTR fmt, ...);

/**************************************************************************
*
* Definitions and global variables.
*
*/

#define FINDPORT_NUM  100  /* how many FindPort() to do while waiting */
#define FINDPORT_TIME  10  /* how many seconds to spread those FindPort() over */

#define PREFS_VERSION ((UBYTE)2)

#define PREFS_NAME_USE  "ENV:OpenURL.prefs"
#define PREFS_NAME_SAVE "ENVARC:OpenURL.prefs"

#define ID_BRWS MAKE_ID('B','R','W','S')
#define ID_DEFS MAKE_ID('D','E','F','S')

#define BRWS_SIZE (sizeof(struct URL_BrowserNode) - sizeof(struct MinNode))
#define DEFS_SIZE (4 * sizeof(BOOL))

struct DosLibrary *DOSBase = NULL;
struct Library *UtilityBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;
struct Library *IFFParseBase = NULL;
struct URL_Prefs *Prefs = NULL;
struct SignalSemaphore PrefsSemaphore, HandlerSemaphore;

struct Process *HandlerProcess = NULL;
struct MsgPort *HandlerMsgPort;

/**************************************************************************
*
* SAS/C library init
*
*/

LIB int __UserLibInit(REG(a6) struct Library *OpenURLBase)
{
	int retval = 1;
	BPTR seglist;
	struct MsgPort *mp = NULL;
	struct HandlerMsg hm = {0};

	/* open libraries */

	SysBase = *(struct ExecBase **)4;

	if(!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 36)))
		goto done;

	if(!(UtilityBase = OpenLibrary("utility.library", 36)))
		goto done;

	if(!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 36)))
		goto done;

	if(!(IFFParseBase = OpenLibrary("iffparse.library", 36)))
		goto done;

	/* load prefs */

	InitSemaphore(&PrefsSemaphore);

	if(!LoadPrefs(PREFS_NAME_USE) && !(Prefs = LIB_URL_GetDefaultPrefs()))
		goto done;

	/* load and launch handler */

	if(!(mp = CreateMsgPort()))
		goto done;

	if(!(seglist = LoadSeg("L:OpenURL-Handler")))
	{
		struct EasyStruct es;
		es.es_StructSize   = sizeof(struct EasyStruct);
		es.es_Flags        = 0;
		es.es_Title        = "OpenURL Error";
		es.es_TextFormat   = "openurl.library was unable to load\nL:OpenURL-Handler into memory.\nPlease reinstall.";
		es.es_GadgetFormat = "Ok";
		EasyRequestArgs(NULL, &es, NULL, NULL);
		goto done;
	}

	HandlerProcess = CreateNewProcTags(NP_Seglist,     seglist,
	                                   NP_Name,        "OpenURL ARexx Handler",
	                                   NP_Priority,    0,
	                                   NP_StackSize,   4096,
	                                   NP_Input,       NULL,
	                                   NP_Output,      NULL,
	                                   NP_CloseInput,  FALSE,
	                                   NP_CloseOutput, FALSE,
	                                   NP_CurrentDir,  NULL,
	                                   NP_HomeDir,     NULL,
	                                   NP_CopyVars,    FALSE,
	                                   TAG_DONE);

	if(!HandlerProcess) goto done;

	/* use a startup msg to synchronize with the handler */

	InitSemaphore(&HandlerSemaphore);

	hm.hm_Msg.mn_ReplyPort = mp;
	hm.hm_Msg.mn_Length    = sizeof(struct HandlerMsg);
	hm.hm_Type             = HMT_STARTUP;
	hm.hm_Semaphore        = &HandlerSemaphore;

	PutMsg(&(HandlerProcess->pr_MsgPort), (struct Message *)&hm);
	WaitPort(mp);
	GetMsg(mp);

	/* check that handler initialized its resources correctly */

	if(!hm.hm_Success)
	{
		HandlerProcess = NULL;
		goto done;
	}

	HandlerMsgPort = hm.hm_MsgPort;
	retval         = 0;

done:
	if(mp) DeleteMsgPort(mp);

	if(retval)
	{
		CloseLibrary(IFFParseBase);
		CloseLibrary((struct Library *)IntuitionBase);
		CloseLibrary(UtilityBase);
		CloseLibrary((struct Library *)DOSBase);
	}

	return(retval);
}

/**************************************************************************/

LIB void __UserLibCleanup(REG(a6) struct Library *OpenURLBase)
{
	/* shutdown handler process */

	if(!AttemptSemaphore(&HandlerSemaphore))
	{
		/* process is still alive, and holding semaphore.
		   signal it to terminate */

		Signal((struct Task *)HandlerProcess, SIGBREAKF_CTRL_C);
	}
	else ReleaseSemaphore(&HandlerSemaphore);

	/* free other resources */

	if(Prefs) LIB_URL_FreePrefs(Prefs);
	CloseLibrary(IFFParseBase);
	CloseLibrary((struct Library *)IntuitionBase);
	CloseLibrary(UtilityBase);
	CloseLibrary((struct Library *)DOSBase);
}

/**************************************************************************
*
* Library functions
*
*/

LIB BOOL LIB_URL_OpenA(REG(a0) STRPTR url, REG(a1) struct TagItem *tags)
{
	BOOL retval = FALSE;
	struct List portlist;

	struct MsgPort *mp;
	STRPTR rxcmd = NULL;

	BOOL Show, ToFront, NewWindow, Launch;
	struct URL_BrowserNode *bn;

	NewList(&portlist);
	ObtainSemaphore(&PrefsSemaphore);

	/* some arexx initialization */

	if(!(mp = CreateMsgPort()))
		goto done;

	if(!(rxcmd = AllocVec(REXX_CMD_LEN + strlen(url) + 1, MEMF_PUBLIC)))
		goto done;

	/* parse the arguments */

	Show      = GetTagData(URL_Show,         Prefs->up_DefShow,         tags);
	ToFront   = GetTagData(URL_BringToFront, Prefs->up_DefBringToFront, tags);
	NewWindow = GetTagData(URL_NewWindow,    Prefs->up_DefNewWindow,    tags);
	Launch    = GetTagData(URL_Launch,       Prefs->up_DefLaunch,       tags);

	/* make a copy of the global list of named ports */

	Forbid();
	retval = CopyList(&portlist, &SysBase->PortList, sizeof(struct Node));
	Permit();

	if(!retval) goto done;

	/* try to find one of the browsers in the list */

	for(bn = (struct URL_BrowserNode *)Prefs->up_BrowserList.mlh_Head;
	    bn->ubn_Node.mln_Succ;
	    bn = (struct URL_BrowserNode *)bn->ubn_Node.mln_Succ)
	{
		STRPTR port = FindRexxPort(&portlist, bn->ubn_Port);
		if(port)
		{
			/* send uniconify msg */

			if(Show && *bn->ubn_ShowCmd)
				SendRexxMsg(mp, port, bn->ubn_ShowCmd);

			/* send screentofront command */

			if(ToFront && *bn->ubn_ToFrontCmd)
				SendRexxMsg(mp, port, bn->ubn_ToFrontCmd);

			/* try sending openurl msg */

			SPrintf(rxcmd, NewWindow ? bn->ubn_OpenURLWCmd : bn->ubn_OpenURLCmd, url);
			if(!*rxcmd) continue;
			retval = SendRexxMsg(mp, port, rxcmd);
			if(retval) goto done;
		}
	}

	/* no running browser, launch a new one */

	if(!Launch) goto done;

	for(bn = (struct URL_BrowserNode *)Prefs->up_BrowserList.mlh_Head;
	    bn->ubn_Node.mln_Succ;
	    bn = (struct URL_BrowserNode *)bn->ubn_Node.mln_Succ)
	{
		STRPTR shellcmd, filepart;
		char c = '\0';
		BPTR lock;

		if(!*bn->ubn_Path) continue;

		/* compose commandline */

		if(bn->ubn_Flags & UBNF_URLONCMDLINE)
		{
			if(!(shellcmd = AllocVec(strlen(bn->ubn_Path) + strlen(url) + 4, MEMF_ANY)))
				break;

			SPrintf(shellcmd, "%s \"%s\"", bn->ubn_Path, url);
		}
		else shellcmd = bn->ubn_Path;

		filepart = FilePart(bn->ubn_Path);

		if(filepart)
		{
			c         = *filepart;
			*filepart = '\0';
		}

		lock = Lock(bn->ubn_Path, ACCESS_READ);

		if(filepart) *filepart = c;

		/* start the browser */

		if(SystemTags(shellcmd, SYS_Asynch,    TRUE,
		                        SYS_Input,     Open("NIL:", MODE_NEWFILE),
		                        SYS_Output,    NULL,
		                 lock ? NP_CurrentDir : TAG_IGNORE, lock,
		                        TAG_DONE))
		{
			if(lock) UnLock(lock);
			FreeVec(shellcmd);
			continue;
		}
		else FreeVec(shellcmd);

		if(!(bn->ubn_Flags & UBNF_URLONCMDLINE))
		{
			LONG i, delay;

			/* send urlopen command */

			SPrintf(rxcmd, bn->ubn_OpenURLCmd, url);
			if(!*rxcmd) goto done;

			/* (busy) wait for the port to appear */

			delay = (FINDPORT_TIME * TICKS_PER_SECOND) / FINDPORT_NUM;

			for(i = 0; i < FINDPORT_NUM; i++)
			{
				STRPTR rxport;
				Forbid();
				rxport = FindRexxPort(&SysBase->PortList, bn->ubn_Port);
				Permit();

				if(rxport)
				{
					retval = SendRexxMsg(mp, rxport, rxcmd);
					goto done;
				}

				if(SetSignal(0, 0) & SIGBREAKF_CTRL_C)
					goto done;

				Delay(delay);
			}
		}
		else
		{
			retval = TRUE;
			break;
		}
	}

done:
	ReleaseSemaphore(&PrefsSemaphore);
	FreeList(&portlist, sizeof(struct Node));
	if(rxcmd) FreeVec(rxcmd);
	if(mp)    DeleteMsgPort(mp);
	return(retval);
}

/**************************************************************************/

LIB struct URL_Prefs *LIB_URL_GetPrefs(VOID)
{
	struct URL_Prefs *p;

	/* make a copy of the prefs structure and return that */

	ObtainSemaphoreShared(&PrefsSemaphore);
	p = CopyPrefs(Prefs);
	ReleaseSemaphore(&PrefsSemaphore);
	return(p);
}

/**************************************************************************/

LIB VOID LIB_URL_FreePrefs(REG(a0) struct URL_Prefs *up)
{
	/* free a prefs structure */
	FreeList((struct List *)&up->up_BrowserList, sizeof(struct URL_BrowserNode));
	FreeMem(up, sizeof(struct URL_Prefs));
}

/**************************************************************************/

LIB BOOL LIB_URL_SetPrefs(REG(a0) struct URL_Prefs *p, REG(d0) BOOL permanent)
{
	BOOL retval = FALSE;
	struct URL_Prefs *new_p;

	ObtainSemaphore(&PrefsSemaphore);

	/* copy prefs structure */

	if(!(new_p = CopyPrefs(p)))
		goto done;

	if(new_p->up_Version == 1)
	{
		SetDefaultPrefsV2(new_p);
		new_p->up_Version = PREFS_VERSION;
	}

	LIB_URL_FreePrefs(Prefs);
	Prefs = new_p;

	/* and save it to disk */

	if(!SavePrefs(PREFS_NAME_USE, Prefs)) goto done;
	if(permanent && !SavePrefs(PREFS_NAME_SAVE, Prefs))
		goto done;

	retval = TRUE;
done:
	ReleaseSemaphore(&PrefsSemaphore);
	return(retval);
}


/**************************************************************************/

LIB struct URL_Prefs *LIB_URL_GetDefaultPrefs(VOID)
{
	struct URL_Prefs *p;

	if(!(p = AllocMem(sizeof(struct URL_Prefs), MEMF_CLEAR)))
		return(NULL);

	p->up_Version = PREFS_VERSION;
	p->up_Flags |= UPF_ISDEFAULTS;

	if(!SetDefaultPrefsV1(p))
	{
		LIB_URL_FreePrefs(p);
		return(NULL);
	}

	SetDefaultPrefsV2(p);

	return(p);
}

/**************************************************************************
*
* Utility functions used by the library functions.
*
*/

static BOOL CopyList(struct List *dst, struct List *src, ULONG size)
{
	struct Node *n, *new;

	/* copy src list into dst, and return success */

	for(n = src->lh_Head; n->ln_Succ; n = n->ln_Succ)
	{
		if(!(new = AllocMem(size, MEMF_ANY)))
		{
			FreeList(dst, size);
			return(FALSE);
		}

		memcpy(new, n, size);
		AddTail(dst, new);
	}

	return(TRUE);
}

/**************************************************************************/

static VOID FreeList(struct List *list, ULONG size)
{
	struct Node *n;

	while((n = RemHead(list)))
		FreeMem(n, size);
}

/**************************************************************************/

static BOOL SendRexxMsg(struct MsgPort *replyport, STRPTR rxport, STRPTR rxcmd)
{
	struct HandlerMsg hm = {0};
	hm.hm_Msg.mn_ReplyPort = replyport;
	hm.hm_Msg.mn_Length    = sizeof(struct HandlerMsg);
	hm.hm_Type             = HMT_AREXX;
	hm.hm_ARexxPort        = rxport;
	hm.hm_ARexxCmd         = rxcmd;

	/* check that the handler is still there */

	if(!AttemptSemaphore(&HandlerSemaphore))
	{
		/* yup, still there */

		PutMsg(HandlerMsgPort, (struct Message *)&hm);
		WaitPort(replyport);
		GetMsg(replyport);
		return(hm.hm_Success);
	}
	else
	{
		/* eek, gone */

		ReleaseSemaphore(&HandlerSemaphore);
		return(FALSE);
	}
}

/**************************************************************************/

static STRPTR FindRexxPort(struct List *list, STRPTR name)
{
	struct Node *n;
	ULONG len;

	/* find a rexx port, trying various permutations of the name */

	len = strlen(name);

	for(n = list->lh_Head; n->ln_Succ; n = n->ln_Succ)
	{
		if(n->ln_Name && !strncmp(n->ln_Name, name, len) &&
		  (n->ln_Name[len] == '\0' ||
		  (n->ln_Name[len] == '.' && isdigits(&n->ln_Name[len+1]))))
		{
			return(n->ln_Name);
		}
	}

	return(NULL);
}

/**************************************************************************/

static BOOL isdigits(STRPTR str)
{
	for(;;)
	{
		if(!*str)
			return(TRUE);
		else if(!isdigit(*str))
			return(FALSE);

		str++;
	}
}

/**************************************************************************/

static struct URL_Prefs *CopyPrefs(struct URL_Prefs *old_p)
{
	struct URL_Prefs *new_p;

	/* make a copy of a prefs structure */

	if(!(new_p = AllocMem(sizeof(struct URL_Prefs), MEMF_ANY)))
		return(NULL);

	memcpy(new_p, old_p, sizeof(struct URL_Prefs));
	NewList((struct List *)&new_p->up_BrowserList);

	if(!CopyList((struct List *)&new_p->up_BrowserList, 
	             (struct List *)&old_p->up_BrowserList,
	             sizeof(struct URL_BrowserNode)))
	{
		LIB_URL_FreePrefs(new_p);
		return(NULL);
	}

	return(new_p);
}

/**************************************************************************/

static BOOL SavePrefs(STRPTR filename, struct URL_Prefs *up)
{
	BOOL retval = FALSE;
	struct IFFHandle *iffh;
	struct PrefHeader prhd;
	struct URL_BrowserNode *bn;

	/* init iff handle */

	if(!(iffh = AllocIFF()))
		goto done;

	if(!(iffh->iff_Stream = Open(filename, MODE_NEWFILE)))
		goto done;

	InitIFFasDOS(iffh);

	if(OpenIFF(iffh, IFFF_WRITE))
		goto done;

	/* init file as IFF PREF FORM */

	if(PushChunk(iffh, ID_PREF, ID_FORM, IFFSIZE_UNKNOWN))
		goto done;

	/* write pref header */

	if(PushChunk(iffh, ID_PREF, ID_PRHD, sizeof(struct PrefHeader)))
		goto done;

	prhd.ph_Version = up->up_Version;
	prhd.ph_Type    = 0;
	prhd.ph_Flags   = 0;

	if(WriteChunkBytes(iffh, &prhd, sizeof(struct PrefHeader)) != sizeof(struct PrefHeader))
		goto done;

	if(PopChunk(iffh))
		goto done;

	/* write browser nodes */

	for(bn = (struct URL_BrowserNode *)Prefs->up_BrowserList.mlh_Head;
	    bn->ubn_Node.mln_Succ;
	    bn = (struct URL_BrowserNode *)bn->ubn_Node.mln_Succ)
	{
		if(PushChunk(iffh, ID_PREF, ID_BRWS, BRWS_SIZE))
			goto done;

		if(WriteChunkBytes(iffh, &bn->ubn_Flags, BRWS_SIZE) != BRWS_SIZE)
			goto done;

		if(PopChunk(iffh))
			goto done;
	}

	/* write defaults */

	if(PushChunk(iffh, ID_PREF, ID_DEFS, DEFS_SIZE))
		goto done;

	if(WriteChunkBytes(iffh, &Prefs->up_DefShow, DEFS_SIZE) != DEFS_SIZE)
		goto done;

	if(PopChunk(iffh))
		goto done;

	/* pop the IFF PREF FORM chunk */

	if(PopChunk(iffh))
		goto done;

	retval = TRUE;
done:
	if(iffh)
	{
		CloseIFF(iffh);
		Close(iffh->iff_Stream);
		FreeIFF(iffh);
	}

	if(!retval) DeleteFile(filename);	

	return(retval);
}

/**************************************************************************/

static BOOL LoadPrefs(STRPTR filename)
{
	BOOL retval = FALSE;
	struct IFFHandle *iffh = NULL;
	struct ContextNode *cn;
	struct PrefHeader prhd;

	ObtainSemaphore(&PrefsSemaphore);

	/* allocate main prefs structure */

	if(!(Prefs = AllocMem(sizeof(struct URL_Prefs), MEMF_CLEAR)))
		goto done;

	Prefs->up_Version = PREFS_VERSION;
	NewList((struct List *)&Prefs->up_BrowserList);

	/* init iff handle */

	if(!(iffh = AllocIFF()))
		goto done;

	if(!(iffh->iff_Stream = Open(filename, MODE_OLDFILE)))
		goto done;

	InitIFFasDOS(iffh);

	if(OpenIFF(iffh, IFFF_READ))
		goto done;

	/* stop at these chunks */

	if(StopChunk(iffh, ID_PREF, ID_PRHD))
		goto done;

	if(StopChunk(iffh, ID_PREF, ID_DEFS))
		goto done;

	if(StopChunk(iffh, ID_PREF, ID_BRWS))
		goto done;

	/* check that we got a prefheader of the right version */

	if(ParseIFF(iffh, IFFPARSE_SCAN))
		goto done;

	if(!(cn = CurrentChunk(iffh)))
		goto done;

	if((cn->cn_Type != ID_PREF) || (cn->cn_ID != ID_PRHD) || 
	   (cn->cn_Size != sizeof(struct PrefHeader)))
		goto done;

	if(ReadChunkBytes(iffh, &prhd, cn->cn_Size) != cn->cn_Size)
		goto done;

	if(prhd.ph_Version > PREFS_VERSION)
		goto done;

	if(prhd.ph_Version == 1)
		SetDefaultPrefsV2(Prefs);

	/* read browser nodes */

	while(1)
	{
		ULONG error;

		error = ParseIFF(iffh, IFFPARSE_SCAN);
		if(error == IFFERR_EOF) break;
		else if(error) goto done;

		if(!(cn = CurrentChunk(iffh)))
			goto done;

		if(cn->cn_Type != ID_PREF)
			continue;

		if((cn->cn_ID == ID_BRWS) && (cn->cn_Size == BRWS_SIZE))
		{
			struct URL_BrowserNode *bn;

			if(!(bn = AllocMem(sizeof(struct URL_BrowserNode), MEMF_CLEAR)))
				goto done;

			if(ReadChunkBytes(iffh, &bn->ubn_Flags, cn->cn_Size) != cn->cn_Size)
			{
				FreeMem(bn, sizeof(struct URL_BrowserNode));
				goto done;
			}

			AddTail((struct List *)&Prefs->up_BrowserList, (struct Node *)bn);
		}
		else if((cn->cn_ID == ID_DEFS) && (cn->cn_Size == DEFS_SIZE))
		{
			if(ReadChunkBytes(iffh, &Prefs->up_DefShow, cn->cn_Size) != cn->cn_Size)
				goto done;
		}	
	}

	retval = TRUE;
done:
	if(iffh)
	{
		CloseIFF(iffh);
		Close(iffh->iff_Stream);
		FreeIFF(iffh);
	}

	if(Prefs && !retval)
	{
		LIB_URL_FreePrefs(Prefs);
		Prefs = NULL;
	}

	ReleaseSemaphore(&PrefsSemaphore);

	return(retval);
}

/**************************************************************************/

static BOOL SetDefaultPrefsV1(struct URL_Prefs *up)
{
	struct URL_BrowserNode *bn;

	NewList((struct List *)&up->up_BrowserList);

	if(!(bn = AllocMem(sizeof(struct URL_BrowserNode), MEMF_CLEAR)))
		return(FALSE);

	bn->ubn_Flags |= UBNF_URLONCMDLINE;
	strcpy(bn->ubn_Name, "AMosaic");
	strcpy(bn->ubn_Port, "AMOSAIC");
	strcpy(bn->ubn_ShowCmd, "SHOW");
	strcpy(bn->ubn_ToFrontCmd, "SCREEN FRONT");
	strcpy(bn->ubn_OpenURLCmd, "JUMP URL \"%s\"");

	AddTail((struct List *)&up->up_BrowserList, (struct Node *)bn);

	if(!(bn = AllocMem(sizeof(struct URL_BrowserNode), MEMF_CLEAR)))
		return(FALSE);

	bn->ubn_Flags |= UBNF_URLONCMDLINE;
	strcpy(bn->ubn_Name, "AWeb");
	strcpy(bn->ubn_Path, "AWeb-II:AWeb");
	strcpy(bn->ubn_Port, "AWEB");
	strcpy(bn->ubn_ShowCmd, "ICONIFY SHOW");
	strcpy(bn->ubn_ToFrontCmd, "SCREENTOFRONT");
	strcpy(bn->ubn_OpenURLCmd, "OPEN \"%s\"");
	strcpy(bn->ubn_OpenURLWCmd, "NEW \"%s\"");

	AddTail((struct List *)&up->up_BrowserList, (struct Node *)bn);

	if(!(bn = AllocMem(sizeof(struct URL_BrowserNode), MEMF_CLEAR)))
		return(FALSE);

	bn->ubn_Flags |= UBNF_URLONCMDLINE;
	strcpy(bn->ubn_Name, "IBrowse");
	strcpy(bn->ubn_Port, "IBROWSE");
	strcpy(bn->ubn_ShowCmd, "SHOW");
	strcpy(bn->ubn_ToFrontCmd, "SCREENTOFRONT");
	strcpy(bn->ubn_OpenURLCmd, "GOTOURL \"%s\"");
	strcpy(bn->ubn_OpenURLWCmd, "NEWWINDOW \"%s\"");

	AddTail((struct List *)&up->up_BrowserList, (struct Node *)bn);

	if(!(bn = AllocMem(sizeof(struct URL_BrowserNode), MEMF_CLEAR)))
		return(FALSE);

	bn->ubn_Flags |= UBNF_URLONCMDLINE;
	strcpy(bn->ubn_Name, "Voyager");

	if(GetVar("Vapor/Voyager_LASTUSEDDIR", bn->ubn_Path, 
	          UBN_PATH_LEN, GVF_GLOBAL_ONLY) != -1)
		AddPart(bn->ubn_Path, "V", UBN_PATH_LEN);

	strcpy(bn->ubn_Port, "VOYAGER");
	strcpy(bn->ubn_ShowCmd, "SHOW");
	strcpy(bn->ubn_ToFrontCmd, "SCREENTOFRONT");
	strcpy(bn->ubn_OpenURLCmd, "OPENURL \"%s\"");
	strcpy(bn->ubn_OpenURLWCmd, "OPENURL \"%s\" NEWWIN");

	AddTail((struct List *)&up->up_BrowserList, (struct Node *)bn);

	return(TRUE);
}

/**************************************************************************/

static VOID SetDefaultPrefsV2(struct URL_Prefs *up)
{
	up->up_DefShow         = TRUE;
	up->up_DefBringToFront = TRUE;
	up->up_DefNewWindow    = FALSE;
	up->up_DefLaunch       = TRUE;
}

/**************************************************************************/

static VOID SPrintf(STRPTR to, STRPTR fmt, ...)
{
	static ULONG fmtfunc = 0x16C04E75;
 	RawDoFmt(fmt, &fmt + 1, (APTR)&fmtfunc, to);
}
