/* DClock.c *****************************************************************
 *
 *	DClock --------	A Dumb Clock utility, my idea of how a clock
 *			should really be like.
 *
 *	Author --------	Olaf 'Olsen' Barthel, MXM
 *			Brabeckstrasse 35
 *			D-3000 Hannover 71
 *
 *			Federal Republic of Germany
 *
 *	This  program  truly is in the PUBLIC DOMAIN.  Written on a cold
 *	and  damp  September  evening,  hoping the next morning would be
 *	better.
 *
 *	Compiled using Aztec C 5.0a, CygnusEd Professional 2 & ARexx.
 *
 ***************************************************************************/

	/* This belongs to ARP. */

#define NO_PRAGMAS

#include <libraries/arpbase.h>

	/* ARP help messages. */

char *CLI_Template	= "Quit/S,Info/S,Version/S,Quiet/S,Refresh/S,Readclock/S,Beep/K,Click/K,\
Speech/K,Hour/K,Seconds/K,SetEnv/K,Countdown=Tea/K,Sound/K,Textcolour/K,Backcolour/K,\
Clickvolume/K,Priority/K,Page/K,Alarm/K,Alarmtime/K";

char *CLI_Help		= "\nUsage: \33[1mDClock\33[0m [Quit] [Info] [Version] [Quiet] [Refresh] [Readclock]\
\n              [Beep On|Off] [Click On|Off] [Speech On|Off]\
\n              [Hour On|Off] [Seconds On|Off] [SetEnv On|Off]\
\n              [Countdown/Tea #|Off] [Sound \"Name\"|Off]\
\n              [Textcolour #] [Backcolour #]\
\n              [Clickvolume 0..64] [Priority -128..127] [Page 0..4]\
\n              [Alarm On|Off|Show] [Alarmtime HH:MM:SS]\n";

	/* Where to find the different arguments (quite a lot). */

#define ARG_QUIT	 1
#define ARG_INFO	 2
#define ARG_VERSION	 3
#define ARG_QUIET	 4
#define ARG_REFRESH	 5
#define ARG_READCLOCK	 6
#define ARG_BEEP	 7
#define ARG_CLICK	 8
#define ARG_SPEECH	 9
#define ARG_HOUR	10
#define ARG_SECONDS	11
#define ARG_SETENV	12
#define ARG_COUNTDOWN	13
#define ARG_SOUND	14
#define ARG_TEXTCOLOUR	15
#define ARG_BACKCOLOUR	16
#define ARG_CLICKVOLUME	17
#define ARG_PRIORITY	18
#define ARG_PAGE	19
#define ARG_ALARM	20
#define ARG_ALARMTIME	21

	/* Pop-up requester support structure. */

struct PopSupport
{
	ULONG	ps_Flags;
	LONG	ps_TimeOut;
};

	/* Pop-up requester support flags. */

#define PS_CENTER	0x00000001	/* Center window on screen. */
#define PS_TIMEOUT	0x00000002	/* Wait for timeout. */
#define PS_DONTMOVE	0x00000004	/* Don't move the pointer. */
#define PS_BEEP		0x00000008	/* Beep on startup. */
#define PS_STAYACTIVE	0x00000010	/* Keep the window activated. */

	/* Prototypes. */

LONG	Chk_Abort(VOID);
VOID	_wb_parse(VOID);

LONG	MovePointer(struct Screen *mp_Screen, LONG mp_X, LONG mp_Y);
LONG	WriteConsole(struct Window *wc_Window, UBYTE *wc_String);
VOID	CalcDimensions(UBYTE *cd_String, LONG *cd_Width, LONG *cd_Height, LONG cd_MaxWidth);
VOID	EraseGadget(struct RastPort *RPort, struct Gadget *Gadget);
LONG	PopRequest(struct Window *pr_ParentWindow, UBYTE *pr_TitleText, UBYTE *pr_BodyText, UBYTE *pr_PosText, UBYTE *pr_NegText, LONG pr_Default, struct PopSupport *pr_ExtraInfo);
VOID	ProcessIcon(VOID);
LONG	FindChunk(ULONG ChunkName,BPTR FileHandle);
LONG	LoadChimeSound(char *Name);

VOID	main(int argc,char **argv);

	/* For Workbench startability we need access to the icon info. */

struct Library	*IconBase;
BOOL		 FromWorkbench = FALSE;

	/* Some more libraries... */

extern struct IntuitionBase	*IntuitionBase;
extern struct GfxBase		*GfxBase;
extern struct ExecBase		*SysBase;

	/* Main communication structure. */

struct DSeg *DSeg;

	/* Stubs, don't need these. */

LONG Chk_Abort(VOID) { return(0); }
VOID _wb_parse(VOID) {}

	/* Our current version tag. */

const char *VersionTag = "$VER: DClock 1.27 (26 Jul 1990)\n\r";

	/* MovePointer(mp_Screen,mp_X,mp_Y) :
	 *
	 *	Moves the mouse pointer to a given position
	 *	on a screen.
	 */

LONG
MovePointer(struct Screen *mp_Screen,LONG mp_X,LONG mp_Y)
{
	static struct InputEvent mp_StaticEvent =
	{
		NULL,
		IECLASS_POINTERPOS,
		0,
		IECODE_NOBUTTON,
		0,0,0,0
	};

	struct MsgPort		*mp_InputPort;
	struct IOStdReq		*mp_InputRequest;
	struct InputEvent	*mp_FakeEvent;

	LONG mp_Success = FALSE;

	if(mp_InputPort = (struct MsgPort *)CreatePort(NULL,0))
	{
		if(mp_InputRequest = (struct IOStdReq *)CreateStdIO(mp_InputPort))
		{
			if(!OpenDevice("input.device",0,(struct IORequest *)mp_InputRequest,0))
			{
				if(mp_FakeEvent = (struct InputEvent *)AllocMem(sizeof(struct InputEvent),MEMF_PUBLIC))
				{
					CopyMem(&mp_StaticEvent,mp_FakeEvent,sizeof(struct InputEvent));

					mp_InputRequest -> io_Command	= IND_WRITEEVENT;
					mp_InputRequest -> io_Flags	= 0;
					mp_InputRequest -> io_Length	= sizeof(struct InputEvent);
					mp_InputRequest -> io_Data	= (APTR)mp_FakeEvent;

					mp_FakeEvent -> ie_X = mp_X;
					mp_FakeEvent -> ie_Y = mp_Y;

					if(!(mp_Screen -> ViewPort . Modes & HIRES))
						mp_FakeEvent -> ie_X *= 2;

					if(!(mp_Screen -> ViewPort . Modes & LACE))
						mp_FakeEvent -> ie_Y *= 2;

					mp_FakeEvent -> ie_Y += (2 * mp_Screen -> TopEdge);

					DoIO((struct IORequest *)mp_InputRequest);

					mp_Success = TRUE;

					FreeMem(mp_FakeEvent,sizeof(struct InputEvent));
				}

				CloseDevice((struct IORequest *)mp_InputRequest);
			}

			DeleteStdIO(mp_InputRequest);
		}

		DeletePort(mp_InputPort);
	}

	return(mp_Success);
}

	/* WriteConsole(wc_Window,wc_String) :
	 *
	 *	Writes a string to a window (via console.device).
	 */

LONG
WriteConsole(struct Window *wc_Window,UBYTE *wc_String)
{
	struct IOStdReq	*wc_ConWrite;
	struct MsgPort	*wc_ConPort;

	LONG wc_Success = FALSE;

	if(wc_ConPort = (struct MsgPort *)CreatePort(NULL,0))
	{
		if(wc_ConWrite = (struct IOStdReq *)CreateStdIO(wc_ConPort))
		{
			wc_ConWrite -> io_Data	= (APTR)wc_Window;
			wc_ConWrite -> io_Length= sizeof(struct Window);

			if(!OpenDevice("console.device",0,(struct IORequest *)wc_ConWrite,0))
			{
				wc_ConWrite -> io_Command	= CMD_WRITE;
				wc_ConWrite -> io_Data		= (APTR)"\2330 p";
				wc_ConWrite -> io_Length	= -1;

				DoIO((struct IORequest *)wc_ConWrite);

				wc_ConWrite -> io_Data		= (APTR)wc_String;
				wc_ConWrite -> io_Length	= -1;

				DoIO((struct IORequest *)wc_ConWrite);

				wc_Success = TRUE;

				CloseDevice((struct IORequest *)wc_ConWrite);
			}

			DeleteStdIO(wc_ConWrite);
		}

		DeletePort(wc_ConPort);
	}

	return(wc_Success);
}

	/* CalcDimensions(cd_String,cd_Width,cd_Height,cd_MaxWidth) :
	 *
	 *	Calculates the width and height of a formatted
	 *	string block.
	 */

VOID
CalcDimensions(UBYTE *cd_String,LONG *cd_Width,LONG *cd_Height,LONG cd_MaxWidth)
{
	LONG i,cd_InLine = 0,cd_NumLines = 0;

	*cd_Width = *cd_Height = 0;

	for(i = 0 ; i < strlen(cd_String) ; i++)
	{
		if(cd_String[i] == '\n' || cd_InLine == cd_MaxWidth)
		{
			if(cd_InLine > *cd_Width)
				*cd_Width = cd_InLine;

			cd_NumLines++;
			cd_InLine = 0;

			continue;
		}

		if(cd_String[i] == '\33')
		{
			while(cd_String[i] != 'm' && cd_String[i] != 'w' && i < strlen(cd_String))
				i++;

			if(i >= strlen(cd_String))
				i = strlen(cd_String) - 1;

			continue;
		}

		if(cd_String[i] < ' ')
			continue;

		cd_InLine++;
	}

	*cd_Height = cd_NumLines;

	if(cd_InLine > *cd_Width)
		*cd_Width = cd_InLine;
}

	/* EraseGadget(RPort,Gadget) :
	 *
	 *	Clears the background of a gadget.
	 */

VOID
EraseGadget(struct RastPort *RPort,struct Gadget *Gadget)
{
	SetAPen(RPort,2);

	SetDrMd(RPort,JAM1);

	RectFill(RPort,Gadget -> LeftEdge,Gadget -> TopEdge,Gadget -> LeftEdge + Gadget -> Width - 1,Gadget -> TopEdge + Gadget -> Height - 1);

	SetAPen(RPort,1);
}

	/* PopRequest(pr_ParentWindow,pr_TitleText,pr_BodyText,pr_PosText,pr_NegText,pr_Default,pr_ExtraInfo) :
	 *
	 *	Creates a pop-up requester, note: this function is reentrant,
	 *	just like all functions above.
	 */

LONG
PopRequest(struct Window *pr_ParentWindow,UBYTE *pr_TitleText,UBYTE *pr_BodyText,UBYTE *pr_PosText,UBYTE *pr_NegText,LONG pr_Default,struct PopSupport *pr_ExtraInfo)
{
	static struct NewWindow pr_StaticNewWindow =
	{
		0,0,
		0,1,
		0,1,
		VANILLAKEY | MOUSEBUTTONS | GADGETUP | CLOSEWINDOW,
		RMBTRAP | WINDOWDRAG | WINDOWDEPTH,
		(struct Gadget *)NULL,
		(struct Image *)NULL,
		(STRPTR)NULL,
		(struct Screen *)NULL,
		(struct BitMap *)NULL,
		0,0,
		0,0,
		WBENCHSCREEN
	};

	static struct Gadget pr_StaticGadget =
	{
		(struct Gadget *)NULL,
		0,0,
		0,0,
		GADGHBOX,
		RELVERIFY | GADGIMMEDIATE,
		BOOLGADGET,
		(APTR)NULL,
		(APTR)NULL,
		(struct IntuiText *)NULL,
		NULL,
		(APTR)NULL,
		0,
		(APTR)NULL
	};

	static struct TextAttr pr_TextAttr =
	{
		(UBYTE *)"topaz.font",
		8,
		FS_NORMAL,
		FPF_ROMFONT
	};

	struct NewWindow	*pr_NewWindow;
	struct Window		*pr_Window;
	struct IntuiMessage	*pr_IMsg;
	struct Gadget		*pr_PosGadget = NULL,*pr_NegGadget = NULL,*pr_TempGadget;

	struct Screen		 pr_Screen,*pr_FirstScreen;
	struct TextFont		*pr_TextFont;

	LONG			 pr_Width,pr_Height;
	LONG			 pr_Result = FALSE;

	LONG			 pr_TickCount = 0;

	ULONG			 pr_IntuiLock;

	if(!pr_BodyText)
		return(pr_Result);

	if(!(pr_NewWindow = (struct NewWindow *)AllocMem(sizeof(struct NewWindow),MEMF_PUBLIC)))
		return(pr_Result);

	CopyMem(&pr_StaticNewWindow,pr_NewWindow,sizeof(struct NewWindow));

	pr_IntuiLock = LockIBase(NULL);

	pr_FirstScreen = IntuitionBase -> FirstScreen;

	UnlockIBase(pr_IntuiLock);

	if(pr_ParentWindow)
	{
		pr_NewWindow -> Type	= CUSTOMSCREEN;
		pr_NewWindow -> Screen	= pr_ParentWindow -> WScreen;
	}
	else
		OpenWorkBench();

	if(!GetScreenData(&pr_Screen,sizeof(struct Screen),pr_NewWindow -> Type,pr_NewWindow -> Screen))
	{
		FreeMem(pr_NewWindow,sizeof(struct NewWindow));
		return(pr_Result);
	}

	CalcDimensions(pr_BodyText,&pr_Width,&pr_Height,(pr_Screen . Width - 6) / 8);

	if(pr_Height > (pr_Screen . Height - 15 - 13) / 8)
	{
		FreeMem(pr_NewWindow,sizeof(struct NewWindow));
		return(pr_Result);
	}

	pr_NewWindow -> Width	= pr_Width * 8 + 6 + 4;
	pr_NewWindow -> Height	= pr_Height * 8 + 15 + 19 + 2;

	if(pr_TitleText)
		pr_NewWindow -> Title = pr_TitleText;
	else
		pr_NewWindow -> Title = (UBYTE *)"System Request";

	if(!pr_PosText && !pr_NegText)
		pr_NegText = (UBYTE *)"Okay";

	if(pr_PosText)
	{
		if(!(pr_PosGadget = (struct Gadget *)AllocMem(sizeof(struct Gadget),MEMF_PUBLIC)))
		{
			FreeMem(pr_NewWindow,sizeof(struct NewWindow));
			return(pr_Result);
		}

		CopyMem(&pr_StaticGadget,pr_PosGadget,sizeof(struct Gadget));

		pr_PosGadget -> Width	= 8 * strlen(pr_PosText) + 4;
		pr_PosGadget -> Height	= 8 + 2;

		pr_PosGadget -> LeftEdge= 3 + 2 + 3;
		pr_PosGadget -> TopEdge	= pr_NewWindow -> Height - 13 - 1;
	}

	if(pr_NegText)
	{
		if(!(pr_NegGadget = (struct Gadget *)AllocMem(sizeof(struct Gadget),MEMF_PUBLIC)))
		{
			FreeMem(pr_NewWindow,sizeof(struct NewWindow));

			if(pr_PosGadget)
				FreeMem(pr_PosGadget,sizeof(struct Gadget));

			return(pr_Result);
		}

		CopyMem(&pr_StaticGadget,pr_NegGadget,sizeof(struct Gadget));

		pr_NegGadget -> Width	= 8 * strlen(pr_NegText) + 4;
		pr_NegGadget -> Height	= 8 + 2;

		pr_Width = pr_NegGadget -> Width + 6 + 4 + 6;

		if(pr_PosGadget)
			pr_Width += (pr_PosGadget -> Width + 12);

		if(pr_NewWindow -> Width < pr_Width)
			pr_NewWindow -> Width = pr_Width;

		pr_NegGadget -> LeftEdge= pr_NewWindow -> Width - pr_NegGadget -> Width - 3 - 2 - 3;
		pr_NegGadget -> TopEdge	= pr_NewWindow -> Height - 13 - 1;

		pr_NegGadget -> GadgetID= 1;
	}

	if(!pr_NegGadget && pr_NewWindow -> Width < pr_PosGadget -> Width + 6 + 4 + 6)
		pr_NewWindow -> Width = pr_PosGadget -> Width + 6 + 4 + 6;

	if(pr_Default && !pr_PosGadget)
		pr_Default = FALSE;

	if(!pr_Default && !pr_NegGadget)
		pr_Default = TRUE;

	if(pr_Default)
		pr_TempGadget = pr_PosGadget;
	else
		pr_TempGadget = pr_NegGadget;

	pr_NewWindow -> LeftEdge= pr_Screen . MouseX - (pr_TempGadget -> LeftEdge + pr_TempGadget -> Width / 2);
	pr_NewWindow -> TopEdge	= pr_Screen . MouseY - (pr_TempGadget -> TopEdge + pr_TempGadget -> Height / 2);

	while(pr_NewWindow -> LeftEdge < 0)
		pr_NewWindow -> LeftEdge++;

	while(pr_NewWindow -> TopEdge < 0)
		pr_NewWindow -> TopEdge++;

	while(pr_NewWindow -> LeftEdge + pr_NewWindow -> Width >= pr_Screen . Width)
		pr_NewWindow -> LeftEdge--;

	while(pr_NewWindow -> TopEdge + pr_NewWindow -> Height >= pr_Screen . Height)
		pr_NewWindow -> TopEdge--;

	if(!(pr_TextFont = (struct TextFont *)OpenFont(&pr_TextAttr)))
	{
		if(pr_PosGadget)
			FreeMem(pr_PosGadget,sizeof(struct Gadget));

		if(pr_NegGadget)
			FreeMem(pr_NegGadget,sizeof(struct Gadget));

		FreeMem(pr_NewWindow,sizeof(struct NewWindow));

		return(pr_Result);
	}

	if(pr_ExtraInfo)
	{
		if(pr_ExtraInfo -> ps_Flags & PS_CENTER)
		{
			pr_NewWindow -> LeftEdge	= (pr_Screen . Width - pr_NewWindow -> Width) / 2;
			pr_NewWindow -> TopEdge		= (pr_Screen . Height - pr_NewWindow -> Height) / 2;
		}

		if(pr_ExtraInfo -> ps_Flags & PS_TIMEOUT)
			pr_NewWindow -> IDCMPFlags |= INTUITICKS;

		if(pr_ExtraInfo -> ps_Flags & PS_STAYACTIVE)
			pr_NewWindow -> IDCMPFlags |= INACTIVEWINDOW;
	}

	if(!(pr_Window = (struct Window *)OpenWindow(pr_NewWindow)))
	{
		CloseFont(pr_TextFont);

		if(pr_PosGadget)
			FreeMem(pr_PosGadget,sizeof(struct Gadget));

		if(pr_NegGadget)
			FreeMem(pr_NegGadget,sizeof(struct Gadget));

		FreeMem(pr_NewWindow,sizeof(struct NewWindow));

		return(pr_Result);
	}

	SetFont(pr_Window -> RPort,pr_TextFont);

	WriteConsole(pr_Window,pr_BodyText);

	if(pr_PosGadget)
	{
		AddGadget(pr_Window,pr_PosGadget,1);

		EraseGadget(pr_Window -> RPort,pr_PosGadget);

		Move(pr_Window -> RPort,pr_PosGadget -> LeftEdge + 2,pr_PosGadget -> TopEdge + 1 + pr_TextFont -> tf_Baseline);
		Text(pr_Window -> RPort,pr_PosText,strlen(pr_PosText));
	}

	if(pr_NegGadget)
	{
		AddGadget(pr_Window,pr_NegGadget,1);

		EraseGadget(pr_Window -> RPort,pr_NegGadget);

		Move(pr_Window -> RPort,pr_NegGadget -> LeftEdge + 2,pr_NegGadget -> TopEdge + 1 + pr_TextFont -> tf_Baseline);
		Text(pr_Window -> RPort,pr_NegText,strlen(pr_NegText));
	}

	MoveScreen(pr_Window -> WScreen,0,- pr_Window -> WScreen -> TopEdge);
	ScreenToFront(pr_Window -> WScreen);
	ActivateWindow(pr_Window);

	if(pr_ExtraInfo && pr_ExtraInfo -> ps_Flags & PS_DONTMOVE)
		goto Skip1;

	MovePointer(pr_Window -> WScreen,
		pr_Window -> LeftEdge + (pr_TempGadget -> LeftEdge + pr_TempGadget -> Width / 2),
		pr_Window -> TopEdge + (pr_TempGadget -> TopEdge + pr_TempGadget -> Height / 2));

Skip1:	if(pr_ExtraInfo)
	{
		if(pr_ExtraInfo -> ps_Flags & PS_BEEP)
			DisplayBeep(pr_Window -> WScreen);

		if(pr_ExtraInfo -> ps_Flags & PS_TIMEOUT)
			pr_TickCount = pr_ExtraInfo -> ps_TimeOut * 10;
	}

	FOREVER
	{
		ULONG pr_Class,pr_Code,pr_Qualifier;
		struct Gadget *pr_Gadget;

		WaitPort(pr_Window -> UserPort);

		if(pr_IMsg = (struct IntuiMessage *)GetMsg(pr_Window -> UserPort))
		{
			pr_Class	= pr_IMsg -> Class;
			pr_Code		= pr_IMsg -> Code;
			pr_Qualifier	= pr_IMsg -> Qualifier;
			pr_Gadget	= (struct Gadget *)pr_IMsg -> IAddress;

			ReplyMsg((struct Message *)pr_IMsg);

			if(pr_Class == INTUITICKS)
			{
				if(pr_TickCount)
					pr_TickCount--;
				else
				{
					pr_Result = pr_Default;
					break;
				}

				continue;
			}

			if(pr_Class == INACTIVEWINDOW)
			{
				MoveScreen(pr_Window -> WScreen,0,- pr_Window -> WScreen -> TopEdge);
				ScreenToFront(pr_Window -> WScreen);
				ActivateWindow(pr_Window);

				if(!(pr_ExtraInfo -> ps_Flags & PS_DONTMOVE))
				{
					MovePointer(pr_Window -> WScreen,
						pr_Window -> LeftEdge + (pr_TempGadget -> LeftEdge + pr_TempGadget -> Width / 2),
						pr_Window -> TopEdge + (pr_TempGadget -> TopEdge + pr_TempGadget -> Height / 2));
				}

				if(pr_ExtraInfo -> ps_Flags & PS_BEEP)
					DisplayBeep(pr_Window -> WScreen);

				continue;
			}

			if(pr_Class == GADGETUP)
			{
				if(pr_Gadget -> GadgetID == 0)
					pr_Result = TRUE;

				break;
			}

			if(pr_Class == CLOSEWINDOW)
				break;

			if(pr_Class == VANILLAKEY)
			{
				pr_Code = Toupper(pr_Code);

				if((pr_Code == 'Y' || pr_Code == 'J' || pr_Code == 'V' || pr_Code == 'C' || pr_Code == 'R' || pr_Code == '\r') && pr_PosText)
				{
					pr_Result = TRUE;
					break;
				}

				if((pr_Code == 'N' || pr_Code == 'Q' || pr_Code == 'B' || pr_Code == '\33') && pr_NegText)
					break;

				continue;
			}

			if(pr_Class == MOUSEBUTTONS && pr_Code == MENUDOWN)
			{
				MovePointer(pr_Window -> WScreen,
					pr_Window -> LeftEdge+ (pr_TempGadget -> LeftEdge + pr_TempGadget -> Width / 2),
					pr_Window -> TopEdge + (pr_TempGadget -> TopEdge  + pr_TempGadget -> Height / 2));
			}
		}
	}

	CloseFont(pr_TextFont);

	if(pr_PosGadget)
	{
		RemoveGadget(pr_Window,pr_PosGadget);
		FreeMem(pr_PosGadget,sizeof(struct Gadget));
	}

	if(pr_NegGadget)
	{
		RemoveGadget(pr_Window,pr_NegGadget);
		FreeMem(pr_NegGadget,sizeof(struct Gadget));
	}

	FreeMem(pr_NewWindow,sizeof(struct NewWindow));

	if(pr_FirstScreen != pr_Window -> WScreen)
		ScreenToBack(pr_Window -> WScreen);

	CloseWindow(pr_Window);

	return(pr_Result);
}

	/* FindChunk(ChunkName,FileHandle):
	 *
	 *	Tries to locate a an iff-chunk inside a file.
	 */

LONG
FindChunk(ULONG ChunkName,BPTR FileHandle)
{
	LONG 	OldPosition;
	ULONG	FormType = 0;

		/* The format of a typical IFF-chunk. */

	struct
	{
		ULONG	IFF_Type;
		ULONG	IFF_Length;
	} Chunk;

		/* Remember initial file position. */

	OldPosition = Seek(FileHandle,0,OFFSET_CURRENT);

		/* Try to find it. */

	FOREVER
	{
			/* Read the first bytes. */

		if(Read(FileHandle,&Chunk,sizeof(Chunk)) != sizeof(Chunk))
		{
			Seek(FileHandle,OldPosition,OFFSET_BEGINNING);
			return(FALSE);
		}

			/* Is it a FORM-chunk? */

		if(OldPosition == 0 && FormType == 0 && Chunk . IFF_Type == 'FORM')
		{
			Read(FileHandle,&FormType,sizeof(LONG));

				/* Check the form type. */

			if(FormType == ChunkName)
				return(TRUE);

			continue;
		}

			/* Is it the chunk type we want? */

		if(Chunk . IFF_Type == ChunkName)
			return(TRUE);

			/* Skip chunk. */

		Seek(FileHandle,Chunk . IFF_Length,OFFSET_CURRENT);
	}
}

	/* LoadChimeSound(Name):
	 *
	 *	Loads an IFF-8SVX-soundfile to be used as the hour
	 *	chime sound.
	 */

LONG
LoadChimeSound(char *Name)
{
	BPTR SoundHandle;

	APTR SoundData;
	LONG SoundRate,SoundLength,SoundVolume;

		/* The format of the VoiceHeader chunk. */

	struct
	{
		ULONG	oneShotHiSamples,
			repeatHiSamples,
			samplesPerHiCycle;
		UWORD	samplesPerSec;
		UBYTE	ctOctave,
			sCompression;
		LONG	volume;
	} VoiceHeader;

		/* No name? Reset to defaults. */

	if(!Strcmp(Name,"OFF"))
	{
		ObtainSemaphore(DSeg -> SoundSemaphore);

			/* Free the memory. */

		if(DSeg -> SoundLength && DSeg -> SoundData)
			FreeMem(DSeg -> SoundData,DSeg -> SoundLength);

			/* Mark sound slot as vacant. */

		DSeg -> SoundLength	= 0;
		DSeg -> SoundData	= NULL;

		ReleaseSemaphore(DSeg -> SoundSemaphore);

		return(TRUE);
	}

		/* Open the file for reading. */

	if(!(SoundHandle = Open(Name,MODE_OLDFILE)))
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","No such sound file found!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m No such sound file found!");

		return(FALSE);
	}

		/* Is it a sound file? */

	if(!FindChunk('8SVX',SoundHandle))
	{
		Close(SoundHandle);

		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","File is not an IFF-8SVX-file!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m File is not an IFF-8SVX-file!");

		return(FALSE);
	}

		/* Look for the VoiceHeader. */

	if(!FindChunk('VHDR',SoundHandle))
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","Didn't find VHDR chunk!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m Didn't find VHDR chunk!");

		Close(SoundHandle);
		return(FALSE);
	}

		/* Read the header. */

	if(Read(SoundHandle,&VoiceHeader,sizeof(VoiceHeader)) != sizeof(VoiceHeader))
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","Error while reading file!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m Error while reading file!");

		Close(SoundHandle);
		return(FALSE);
	}

		/* Fill in the more important information. */

	SoundLength	= VoiceHeader . oneShotHiSamples + VoiceHeader . repeatHiSamples;
	SoundRate	= ((GfxBase -> DisplayFlags & PAL) ? 3546895 : 3579545) / VoiceHeader . samplesPerSec;
	SoundVolume	= (VoiceHeader . volume > 64 ? 64 : VoiceHeader . volume);

		/* Proceed with the body chunk. */

	if(!FindChunk('BODY',SoundHandle))
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","Didn't find BODY chunk!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m Didn't find BODY chunk!");

		Close(SoundHandle);
		return(FALSE);
	}

		/* Allocate space for the sound data. */

	if(!(SoundData = (APTR)AllocMem(SoundLength,MEMF_PUBLIC | MEMF_CHIP)))
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","Not enough memory for sound data!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m Not enough memory for sound data!");

		Close(SoundHandle);
		return(FALSE);
	}

		/* Read the sound data. */

	if(Read(SoundHandle,SoundData,SoundLength) != SoundLength)
	{
		if(FromWorkbench)
			PopRequest(NULL,"DClock problem:","Error while reading file!",NULL,"Continue?",FALSE,NULL);
		else
			Puts("\33[1mDClock:\33[0m Error while reading file!");

		FreeMem(SoundData,SoundLength);
		Close(SoundHandle);
		return(FALSE);
	}

		/* Close the file. */

	Close(SoundHandle);

		/* Lock access to sound data. */

	ObtainSemaphore(DSeg -> SoundSemaphore);

		/* Free last sound. */

	if(DSeg -> SoundLength && DSeg -> SoundData)
		FreeMem(DSeg -> SoundData,DSeg -> SoundLength);

		/* Fill in the data. */

	DSeg -> SoundData	= SoundData;
	DSeg -> SoundLength	= SoundLength;
	DSeg -> SoundRate	= SoundRate;
	DSeg -> SoundVolume	= SoundVolume;

		/* Unlock it again. */

	ReleaseSemaphore(DSeg -> SoundSemaphore);

	return(TRUE);
}

	/* ProcessIcon():
	 *
	 *	To add startability from Workbench this function
	 *	checks for the approritate tooltype entries.
	 */

VOID
ProcessIcon()
{
	struct DiskObject *DiskObject;
	extern struct WBStartup *WBenchMsg;
	char *Option;

		/* No need to continue? */

	if(!DSeg || !FromWorkbench)
	{
		if(!DSeg && FromWorkbench)
			PopRequest(NULL,"DClock problem:","Can't find \33[1m\"DClock-Handler\"\33[0m!\nCheck L: and current directory.",NULL,"Continue?",FALSE,NULL);

		return;
	}

		/* Try to open the icon.library. */

	if(!(IconBase = (struct Library *)OpenLibrary("icon.library",0)))
	{
		PopRequest(NULL,"DClock problem:","Unable to open \33[1micon.library\33[0m!",NULL,"Continue?",FALSE,NULL);
		return;
	}

		/* Can we read the icon file please? */

	if(!(DiskObject = (struct DiskObject *)GetDiskObject(WBenchMsg -> sm_ArgList -> wa_Name)))
	{
		PopRequest(NULL,"DClock problem:","Couldn't read icon information file!",NULL,"Continue?",FALSE,NULL);

		CloseLibrary(IconBase);
		return;
	}

		/* Adjust Click volume? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"CLICKVOLUME"))
	{
		if(Atol(Option) > 64 || Atol(Option) < 0)
			PopRequest(NULL,"DClock problem:","Illegal value for click volume (must be between 0 and 64)!",NULL,"Continue?",FALSE,NULL);
		else
			DSeg -> ClickVolume = Atol(Option);
	}

		/* Adjust task priority? */

	if((Option = FindToolType(DiskObject -> do_ToolTypes,"PRIORITY")) && DSeg -> Child)
	{
		if(Atol(Option) > 127 || Atol(Option) < -128)
			PopRequest(NULL,"DClock problem:","Illegal value for handler task priority (must be between -128 and 127)!",NULL,"Continue?",FALSE,NULL);
		else
		{
			DSeg -> Priority = Atol(Option);
			SetTaskPri(DSeg -> Child,DSeg -> Priority);
		}
	}

		/* Turn DisplayBeep() off? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"BEEP"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Beep = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Beep = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Beep settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Turn keyboard click off? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"CLICK"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Click = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Click = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Click settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Set text colour? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"TEXTCOLOUR"))
		DSeg -> TextColour = Atol(Option);

		/* Set background colour? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"BACKCOLOUR"))
		DSeg -> BackColour = Atol(Option);

		/* Turn alarm on? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"ALARM"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Alarm = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Alarm = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Alarm settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Set alarm time? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"ALARMTIME"))
	{
		char TimeBuff[3];
		LONG TheTime;

		TimeBuff[2] = 0;

		TimeBuff[0] = Option[0];
		TimeBuff[1] = Option[1];

		TheTime = Atol(TimeBuff);

		if(TheTime >= 0 && TheTime <= 23)
			DSeg -> AlarmHour = TheTime;
		else
			PopRequest(NULL,"DClock problem:","Illegal value for alarm hour (must be between 0 and 23)!",NULL,"Continue?",FALSE,NULL);

		TimeBuff[0] = Option[3];
		TimeBuff[1] = Option[4];

		TheTime = Atol(TimeBuff);

		if(TheTime >= 0 && TheTime <= 59)
			DSeg -> AlarmMinute = TheTime;
		else
			PopRequest(NULL,"DClock problem:","Illegal value for alarm minute (must be between 0 and 59)!",NULL,"Continue?",FALSE,NULL);

		TimeBuff[0] = Option[6];
		TimeBuff[1] = Option[7];

		TheTime = Atol(TimeBuff);

		if(TheTime >= 0 && TheTime <= 59)
			DSeg -> AlarmSecond = TheTime;
		else
			PopRequest(NULL,"DClock problem:","Illegal value for alarm seconds (must be between 0 and 59)!",NULL,"Continue?",FALSE,NULL);
	}

		/* Set environment variables? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"SETENV"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> SetEnv = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> SetEnv = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Environment settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Start countdown? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"COUNTDOWN"))
	{
		if(Atol(Option) > 0)
			DSeg -> Countdown = Atol(Option);
		else
			PopRequest(NULL,"DClock problem:","Countdown must be greater than 0!",NULL,"Continue?",FALSE,NULL);
	}

		/* Set hour chime? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"HOUR"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Hour = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Hour = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Hour settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Turn seconds off? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"SECONDS"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Seconds = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Seconds = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Second settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Load a chime sound? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"SOUND"))
		LoadChimeSound(Option);

		/* Show which page on startup? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"PAGE"))
	{
		if(Atol(Option) < 0 || Atol(Option) > 4 || (Atol(Option) == 4 && DSeg -> Countdown < 1))
			PopRequest(NULL,"DClock problem:","Illegal value for page!",NULL,"Continue?",FALSE,NULL);
		else
			DSeg -> Page = Atol(Option);
	}

		/* Turn the speech off? */

	if(Option = FindToolType(DiskObject -> do_ToolTypes,"SPEECH"))
	{
		if(!Strcmp(Option,"OFF"))
			DSeg -> Speech = FALSE;
		else
		{
			if(!Strcmp(Option,"ON"))
				DSeg -> Speech = TRUE;
			else
				PopRequest(NULL,"DClock problem:","Speech settings not understood (must be OFF or ON)!",NULL,"Continue?",FALSE,NULL);
		}
	}

		/* Read the system clock? */

	if(FindToolType(DiskObject -> do_ToolTypes,"READCLOCK"))
	{
		if(!ReadClock())
			PopRequest(NULL,"DClock problem:","Could not find battery backed up clock!",NULL,"Continue?",FALSE,NULL);
	}

		/* Free the rest... */

	FreeDiskObject(DiskObject);

	CloseLibrary(IconBase);
}

	/* main(argc,argv):
	 *
	 *	That's where all the trouble starts.
	 */

VOID
main(int argc,char **argv)
{
	BOOL		 Quiet = FALSE;
	struct Process	*ThatsMe = (struct Process *)SysBase -> ThisTask;

		/* Init important pointers. */

	if(!ThatsMe -> pr_CLI)
		FromWorkbench = TRUE;

		/* Are we already running? */

	DSeg = (struct DSeg *)FindPort(PORTNAME);

		/* Restart? */

	if(FromWorkbench && DSeg)
	{
		ProcessIcon();
		exit(RETURN_OK);
	}

		/* Yeah... push it! */

	if(FromWorkbench)
		goto PushIt;

		/* Be quiet? */

	if(argv[ARG_QUIET])
		Quiet = TRUE;

		/* No argument and DClock's already running? */

	if(argc < 2 && DSeg)
	{
		Printf("Can't install \33[1mDClock\33[0m, handler process \33[33malready running\33[31m\7!\n");

		exit(RETURN_OK);
	}

		/* Read the system clock? */

	if(argv[ARG_READCLOCK])
	{
		if(!ReadClock() && !Quiet)
			Puts("\33[1mDClock:\33[0m Could not find battery backed up clock.");
	}

		/* User wants information. */

	if(argv[ARG_INFO])
	{
		Printf("\n\33[1m\33[33mDClock\33[0m\33[31m - A \33[33mD\33[31mumb \33[33mClock\33[31m. Renders time and date\n");
		Printf("         into the Workbench title bar. Place\n");
		Printf("         \33[33mDClock-Handler\33[31m in L:, \33[33mDClock\33[31m somewhere\n");
		Printf("         in C: or in SYS:. Type '\33[1m\33[33mDClock\33[31m\33[0m' to\n");
		Printf("         install, '\33[1m\33[33mDClock quit\33[31m\33[0m' to remove.\n\n");
		Printf("         \33[33mDClock\33[31m is \33[1mfree\33[0m and in the \33[33m\33[1mPUBLIC-DOMAIN\33[31m\33[0m!\n\n");

		Printf("\33[1m\33[33mAuthor\33[31m\33[0m - Olaf Barthel, MXM\n");
		Printf("         Brabeckstrasse 35\n");
		Printf("         D-3000 Hannover 71\n\n");

		Printf("	 Federal Republic of Germany.\n\n");

		exit(RETURN_OK);
	}

		/* Print current release version number. */

	if(argv[ARG_VERSION])
	{
		Printf("\33[1mDClock\33[0m version 1.%ld, created \33[33mThursday, 27-Jul-90, 11:32\33[31m.\n",DSeg ? DSeg -> Revision : REVISION);
		exit(RETURN_OK);
	}

		/* Terminate dumb clock? */

	if(argv[ARG_QUIT])
	{
		Printf("Removing \33[1m\33[33mDClock\33[31m\33[0m (DClock-Handler revision 1.%ld), ",DSeg ? DSeg -> Revision : -1);

			/* Segment not loaded. */

		if(!DSeg)
		{
			Printf("failed!\7\n");
			exit(RETURN_FAIL);
		}

			/* We are the caller. */

		DSeg -> Father = (struct Task *)ThatsMe;

			/* Child still present? */

		if(DSeg -> Child)
		{
			Signal(DSeg -> Child,SIGBREAKF_CTRL_D);
			Wait(SIGBREAKF_CTRL_D);
		}

			/* Remove port and associated data. */

		RemPort(&DSeg -> Port);
		FreeMem(DSeg -> Port . mp_Node . ln_Name,sizeof(PORTNAME));

		if(DSeg -> SoundData && DSeg -> SoundLength)
			FreeMem(DSeg -> SoundData,DSeg -> SoundLength);

		FreeMem(DSeg -> SoundSemaphore,sizeof(struct SignalSemaphore));

		if(DSeg -> Segment)
			UnLoadSeg(DSeg -> Segment);

		FreeMem(DSeg,DSeg -> SegSize);

		Printf("OK.\n");

		exit(RETURN_OK);
	}

		/* Create global communication structure. */

PushIt:	if(!DSeg)
	{
		if(DSeg = (struct DSeg *)AllocMem(sizeof(struct DSeg),MEMF_PUBLIC | MEMF_CLEAR))
		{
			ULONG Micros;

				/* Dummy MessagePort. */

			DSeg -> Port . mp_Flags			= PA_IGNORE;
			DSeg -> Port . mp_Node . ln_Pri		= 1;
			DSeg -> Port . mp_Node . ln_Type	= NT_MSGPORT;
			DSeg -> Port . mp_Node . ln_Name	= AllocMem(sizeof(PORTNAME),MEMF_PUBLIC);
			DSeg -> Child				= NULL;
			DSeg -> Father				= (struct Task *)ThatsMe;
			DSeg -> SoundSemaphore			= (struct SignalSemaphore *)AllocMem(sizeof(struct SignalSemaphore),MEMF_PUBLIC | MEMF_CLEAR);

			if(!DSeg -> Port . mp_Node . ln_Name || !DSeg -> SoundSemaphore)
				goto Purge;

				/* For future expansion of this structure;
				 * this will insure that any version of
				 * DClock will be able to free the memory
				 * occupied by the segment.
				 */

			DSeg -> SegSize			= sizeof(struct DSeg);

				/* We'll wait for this signal to come from
				 * the handler process.
				 */

			DSeg -> RingBack		= SIGBREAKF_CTRL_C;

				/* Enable beep and disable click feature. */

			DSeg -> Beep			= TRUE;
			DSeg -> Click			= FALSE;

				/* So we have valid time counter. */

			CurrentTime(&DSeg -> LastSecs,&Micros);

				/* Click volume and handler priority. */

			DSeg -> ClickVolume		= 64;
			DSeg -> Priority		= 5;

				/* Rendering colours. */

			DSeg -> TextColour		= 0;
			DSeg -> BackColour		= 1;

				/* Set alarm default. */

			DSeg -> Alarm			= FALSE;

			DSeg -> AlarmHour		= 12;
			DSeg -> AlarmMinute		= 0;
			DSeg -> AlarmSecond		= 0;

				/* Disable countdown & hour chime. */

			DSeg -> Countdown		= 0;
			DSeg -> Hour			= FALSE;

				/* Seconds are by default enabled. */

			DSeg -> Seconds			= TRUE;

				/* Set environment variables. */

			DSeg -> SetEnv			= FALSE;

				/* Install the current revision number. */

			DSeg -> Revision		= REVISION;

				/* Init port. */

			strcpy(DSeg -> Port . mp_Node . ln_Name,PORTNAME);

			if(!FromWorkbench)
				Printf("Installing \33[33m\33[1mDClock\33[0m\33[31m, ");

				/* Load the handler code. */

			DSeg -> Segment = LoadSeg("DClock-Handler");

			if(!DSeg -> Segment)
				DSeg -> Segment = LoadSeg("L:DClock-Handler");

			if(!DSeg -> Segment)
			{
				if(!FromWorkbench)
					Printf("unable to find \33[33mL:DClock-Handler\33[31m\7!\n");
				else
					PopRequest(NULL,"DClock problem:","Can't find \33[1m\"DClock-Handler\"\33[0m!\nCheck L: and current directory.",NULL,"Continue?",FALSE,NULL);

Purge:				if(DSeg)
				{
					if(DSeg -> Port . mp_Node . ln_Name)
						FreeMem(DSeg -> Port . mp_Node . ln_Name,sizeof(PORTNAME));

					if(DSeg -> SoundSemaphore)
						FreeMem(DSeg -> SoundSemaphore,sizeof(struct SignalSemaphore));

					FreeMem(DSeg,DSeg -> SegSize);
				}
			}
			else
			{
					/* Install the port and start the handler. */

				AddPort(&DSeg -> Port);

				CreateProc("DClock-Handler",DSeg -> Priority,DSeg -> Segment,4096);

					/* Wait for child task to ring back... */

				Wait(SIGBREAKF_CTRL_C);

				if(!DSeg -> Child)
				{
					if(!FromWorkbench)
						Printf("\33[33mFAILED!\33[31m (care to retry?)\n");
					else
						PopRequest(NULL,"DClock problem:","Handler process didn't reply startup-message.",NULL,"Continue?",FALSE,NULL);

					RemPort(&DSeg -> Port);
					FreeMem(DSeg -> Port . mp_Node . ln_Name,sizeof(PORTNAME));

					if(DSeg -> Segment)
						UnLoadSeg(DSeg -> Segment);

					FreeMem(DSeg,DSeg -> SegSize);

					if(FromWorkbench)
						Delay(TICKS_PER_SECOND * 2);

					exit(RETURN_FAIL);
				}
				else
				{
					if(!FromWorkbench)
						Printf("\33[3mOkay. \33[0m\33[33mDClock v1.%ld\33[31m, by \33[4mMXM\33[0m. \33[1mPUBLIC DOMAIN\33[0m.\n",REVISION);
					else
						PopRequest(NULL,"DClock Message:","Handler process successfully installed!\n\n\33[33mDClock\33[31m (C) Copyright 1989, 1990 \33[3mby MXM.\33[0m \33[1mPUBLIC DOMAIN\33[0m.",NULL,"Continue?",FALSE,NULL);
				}
			}
		}
	}

		/* Deal with the icon... */

	if(FromWorkbench)
	{
		ProcessIcon();
		exit(RETURN_OK);
	}

		/* Change click volume. */

	if(argv[ARG_CLICKVOLUME] && DSeg)
	{
		if(Atol(argv[ARG_CLICKVOLUME]) >= 0 && Atol(argv[ARG_CLICKVOLUME]) <= 64 )
		{
			if(!Quiet)
				Printf("\33[1mDClock:\33[0m Click volume set to %ld.\n",DSeg -> ClickVolume = Atol(argv[ARG_CLICKVOLUME]));
		}
		else
			Puts(CLI_Help);
	}

		/* Change handler priority? */

	if(argv[ARG_PRIORITY] && DSeg)
	{
		if(DSeg -> Child)
		{
			if(!Quiet)
				Printf("\33[1mDClock:\33[0m Handler priority set to %ld.\n",DSeg -> Priority = Atol(argv[ARG_PRIORITY]));

			SetTaskPri(DSeg -> Child,DSeg -> Priority);
		}
		else
			Puts("\33[1mDClock:\33[0m Unable to find handler task.");
	}

		/* Turn beeping on/off? */

	if(argv[ARG_BEEP] && DSeg)
	{
		if(!Strcmp(argv[ARG_BEEP],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Beep disabled.");

			DSeg -> Beep = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_BEEP],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Beep enabled.");

				DSeg -> Beep = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

		/* Turn clicking on/off? */

	if(argv[ARG_CLICK])
	{
		if(!Strcmp(argv[ARG_CLICK],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Click disabled.");

			DSeg -> Click = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_CLICK],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Click enabled.");

				DSeg -> Click = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

		/* Select new front colour? */

	if(argv[ARG_TEXTCOLOUR])
	{
		DSeg -> TextColour = Atol(argv[ARG_TEXTCOLOUR]);

		if(!Quiet)
			Printf("\33[1mDClock:\33[0m Text colour set to %ld.\n",DSeg -> TextColour);
	}

		/* Select new background colour? */

	if(argv[ARG_BACKCOLOUR])
	{
		DSeg -> BackColour = Atol(argv[ARG_BACKCOLOUR]);

		if(!Quiet)
			Printf("\33[1mDClock:\33[0m Background colour set to %ld.\n",DSeg -> BackColour);
	}

		/* Set/check alarm status. */

	if(argv[ARG_ALARM])
	{
		if(!Strcmp(argv[ARG_ALARM],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Alarm disabled.");

			DSeg -> Alarm = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_ALARM],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Alarm enabled.");

				DSeg -> Alarm = TRUE;
			}
			else
			{
				if(!Strcmp(argv[ARG_ALARM],"SHOW"))
					Printf("\33[1mDClock:\33[0m Current alarm time is %02ld:%02ld:%02ld.\n",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
				else
					Puts(CLI_Help);
			}
		}
	}

		/* Adjust alarm time. */

	if(argv[ARG_ALARMTIME])
	{
		char TimeBuff[3];
		LONG TheTime;

		TimeBuff[2] = 0;

		if(strlen(argv[ARG_ALARMTIME]) != 8)
		{
			Puts("\33[1mDClock:\33[0m Alarm time format = HH:MM:SS, example: 09:03:07.");
			exit(RETURN_ERROR);
		}

		TimeBuff[0] = argv[ARG_ALARMTIME][0];
		TimeBuff[1] = argv[ARG_ALARMTIME][1];

		TheTime = Atol(TimeBuff);

		if(TheTime < 0 || TheTime > 23)
		{
			Puts("\33[1mDClock:\33[0m Illegal time value, Hours must be within 0 ... 23.");
			exit(RETURN_ERROR);
		}

		DSeg -> AlarmHour	= TheTime;

		TimeBuff[0] = argv[ARG_ALARMTIME][3];
		TimeBuff[1] = argv[ARG_ALARMTIME][4];

		TheTime = Atol(TimeBuff);

		if(TheTime < 0 || TheTime > 59)
		{
			Puts("\33[1mDClock:\33[0m Illegal time value, Minutes must be within 0 ... 59.");
			exit(RETURN_ERROR);
		}

		DSeg -> AlarmMinute	= TheTime;

		TimeBuff[0] = argv[ARG_ALARMTIME][6];
		TimeBuff[1] = argv[ARG_ALARMTIME][7];

		TheTime = Atol(TimeBuff);

		if(TheTime < 0 || TheTime > 59)
		{
			Puts("\33[1mDClock:\33[0m Illegal time value, Seconds must be within 0 ... 59.");
			exit(RETURN_ERROR);
		}

		DSeg -> AlarmSecond	= TheTime;

		if(!Quiet)
			Printf("\33[1mDClock:\33[0m Alarm time set to %02ld:%02ld:%02ld.\n",DSeg -> AlarmHour,DSeg -> AlarmMinute,DSeg -> AlarmSecond);
	}

	if(argv[ARG_REFRESH])
	{
		if(CloseWorkBench())
			OpenWorkBench();

		if(!Quiet)
			Puts("\33[1mDClock:\33[0m Main window refreshed.");
	}

		/* Set environment variables? */

	if(argv[ARG_SETENV])
	{
		if(!Strcmp(argv[ARG_SETENV],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Environment variables disabled.");

			DSeg -> SetEnv = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_SETENV],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Environment variables enabled.");

				DSeg -> SetEnv = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

		/* Set countdown timeout? */

	if(argv[ARG_COUNTDOWN])
	{
		if(Atol(argv[ARG_COUNTDOWN]) > 0)
		{
			DSeg -> Countdown = Atol(argv[ARG_COUNTDOWN]);

			if(!Quiet)
				Printf("\33[1mDClock:\33[0m Countdown set to %ld.\n",DSeg -> Countdown);
		}
		else
			Puts(CLI_Help);
	}

		/* Enable/disable the hour chime? */

	if(argv[ARG_HOUR])
	{
		if(!Strcmp(argv[ARG_HOUR],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Hour chime disabled.");

			DSeg -> Hour = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_HOUR],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Hour chime enabled.");

				DSeg -> Hour = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

		/* Display seconds or not? */

	if(argv[ARG_SECONDS])
	{
		if(!Strcmp(argv[ARG_SECONDS],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Seconds disabled.");

			DSeg -> Seconds = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_SECONDS],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Seconds enabled.");

				DSeg -> Seconds = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

		/* Load a sound file? */

	if(argv[ARG_SOUND])
	{
		if(LoadChimeSound(argv[ARG_SOUND]))
		{
			if(!Strcmp(argv[ARG_SOUND],"OFF") && !Quiet)
				Puts("\33[1mDClock:\33[0m Chime sound reset.");
		}
	}

		/* Show a special page on startup? */

	if(argv[ARG_PAGE] && DSeg)
	{
		UBYTE Page = Atol(argv[ARG_PAGE]);

		if(Page >= 0 && Page <= 4 || (Page == 4 && DSeg -> Countdown > 0))
		{
			if(!Quiet)
				Printf("\33[1mDClock:\33[0m Showing display page %ld.\n",DSeg -> Page = Page);
		}
		else
			Puts("\33[1mDClock:\33[0m Page value out of limits (must be 0-3/4).");
	}

		/* Turn the speech on/off? */

	if(argv[ARG_SPEECH])
	{
		if(!Strcmp(argv[ARG_SPEECH],"OFF"))
		{
			if(!Quiet)
				Puts("\33[1mDClock:\33[0m Speech now off.");

			DSeg -> Speech = FALSE;
		}
		else
		{
			if(!Strcmp(argv[ARG_SPEECH],"ON"))
			{
				if(!Quiet)
					Puts("\33[1mDClock:\33[0m Speech now on.");

				DSeg -> Speech = TRUE;
			}
			else
				Puts(CLI_Help);
		}
	}

	exit(RETURN_OK);
}
