networkingtestandutils/exampleinternetutilities/PING/PING.CPP
changeset 0 af10295192d8
child 37 052078dda061
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networkingtestandutils/exampleinternetutilities/PING/PING.CPP	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,1028 @@
+// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// Started by MWT, June 1997
+// 
+//
+
+#include <pingeng.h>
+#include <e32cons.h>
+#include <c32comm.h>
+#include <nifman.h>
+
+#ifdef __WINS__
+#define CDRV1_PATH _L("ECDRV")
+#define COMM_PATH _L("ECOMM")
+#endif
+
+LOCAL_C TInt ProgramL();
+
+const TInt  KHistoryBufferSize = 20;
+
+_LIT(KPrompt, "Command>");
+
+const TInt KPromptLength = 8;
+const TInt KQuitReturn = -100;
+const TInt KHelpReturn = -101;
+
+class CCircList : public CBase
+	{
+public:
+	CCircList(TInt aLength);
+	~CCircList();
+	TInt Add(const TDesC& aLine);
+	const HBufC& operator[](TInt anIndex) const;
+	TInt Count() const;
+
+private:
+	RArray<HBufC*> iBufPtrArray;
+	TInt iMaxLength;
+	};
+
+class CPingTestKeyStroke;
+class CPingTestUi : public CBase, public MPingNotificationHandler
+    {
+public:
+	static CPingTestUi* NewL();
+	void StartL(const TPingOptions& aOptions);
+	~CPingTestUi();
+	
+	virtual void Pinging(const TNameRecord& aRecord, TInt aBytes) const;
+	virtual void Sent() const;
+	virtual void Reply(const TInetAddr& aFrom, TInt aBytes, TInt aSeq, TTimeIntervalMicroSeconds32 aTime) const;
+	virtual void Icmp4Message(const TInetAddr& aFrom, TInt aType, TInt aCode, const TDesC8& aRestOfIt) const;
+	virtual void Icmp6Message(const TInetAddr& aFrom, TInt aType, TInt aCode) const;
+	virtual void Finished(const TNameRecord& aRecord, TInt aNrTransmitted, TInt aNrReceived, TInt aNrDuplicates, TInt aMin, TInt aMax, TInt aSum, TInt aError);
+
+	void SetParams(TBool aFloodFormat);
+	void SetKeyStrokeActive();
+
+	void KeyStroke();
+	void KeyStrokeDoCancel();
+	void AddToHistory(const TDesC& aLine);
+	void DisplayHistory(TInt aLine, TInt aPromptLen, TDes& aDes) const;
+	inline CConsoleBase& Console() { return *iConsole; }
+
+	TInt NrReceivedPackets() const;
+
+private:
+	void ConstructL();
+	void InitialiseL();
+
+private:
+	TBool iFloodFormat;
+	CConsoleBase* iConsole;
+	CPingTestKeyStroke* iKeyHandler;
+	CCircList* iHistory;
+	CPingEng* iEngine;
+	TInt iNrReceivedPackets;
+	};
+
+inline TInt CPingTestUi::NrReceivedPackets() const
+	{
+	
+	return iNrReceivedPackets;
+	}
+
+class CPingTestKeyStroke : public CActive
+	{
+public:
+	CPingTestKeyStroke(CPingTestUi& aUi);
+	~CPingTestKeyStroke();
+	void ReStart();
+	void RunL();
+	void DoCancel();
+private:
+	CPingTestUi& iUi;
+	};
+
+class TPingTestParser
+	{
+public:
+	// returns EFalse if the user wants to exit, ETrue otherwise
+	TBool ParseCommandLine(CPingTestUi& aUI);
+	TInt ParseCLArguments(TDes& aCommandLine,CPingTestUi& aUI);
+	
+	const TPingOptions& Options() const;
+	const TPtrC& BadArgument() const;
+
+private:
+	TPingOptions iOptions;
+	TPtrC iCLArgument;
+	};
+
+const TPtrC& TPingTestParser::BadArgument() const
+	{
+	
+	return iCLArgument;
+	}
+
+
+GLDEF_C TInt E32Main()
+	{
+	
+	__UHEAP_MARK;
+	// Standard stuff
+	CTrapCleanup* trap = CTrapCleanup::New();
+	if(trap==NULL)
+		return KErrNoMemory;
+
+	TRAPD(err, ProgramL());
+	
+	delete trap;
+	__UHEAP_MARKEND;
+	return err;
+	}
+
+LOCAL_C TInt ProgramL()
+	{
+	
+	TInt ret = 0;
+
+	CActiveScheduler* as = new(ELeave) CActiveScheduler;
+	CleanupStack::PushL(as);
+	CActiveScheduler::Install(as);
+
+	HBufC *argsBuf = HBufC::NewMaxLC(512);
+	TPtr args(argsBuf->Des());
+	User::CommandLine(args);
+
+	CPingTestUi* ui=CPingTestUi::NewL();
+	CleanupStack::PushL(ui);
+
+	TPingTestParser parser;
+	if (args.Length()!=0)
+		{
+		TInt res = parser.ParseCLArguments(args,*ui);
+		if (res != KErrNone)
+			{
+			ui->Console().Printf(_L("Invalid argument %S - result %d\n"), &parser.BadArgument(), res);
+			}
+		else
+			{
+			ui->SetParams(parser.Options().iInterval.Int()<200000);
+			ui->SetKeyStrokeActive();
+			ui->StartL(parser.Options());
+			CActiveScheduler::Start();
+			}
+	    ui->Console().Getch();
+		}
+	else
+		for(;;)
+		{
+			ret = parser.ParseCommandLine(*ui);
+			if(ret == KQuitReturn)
+				{
+				break;
+				}
+			if(ret != KHelpReturn)
+			{
+				ui->SetParams(parser.Options().iInterval.Int()<200000);
+				ui->SetKeyStrokeActive();
+				ui->StartL(parser.Options());
+				CActiveScheduler::Start();
+			}
+		}
+
+	ret = ui->NrReceivedPackets() == 0;
+
+	CleanupStack::PopAndDestroy(ui);
+	CleanupStack::PopAndDestroy(argsBuf);
+	CleanupStack::PopAndDestroy(as);
+
+	return ret;
+	}
+
+CPingTestUi* CPingTestUi::NewL()
+//
+// Create new test UI
+//
+	{
+
+	CPingTestUi* ui = new (ELeave) CPingTestUi;
+	CleanupStack::PushL(ui);
+	ui->ConstructL();
+	CleanupStack::Pop(ui);
+	return ui;
+	}
+
+void CPingTestUi::InitialiseL()
+//
+// Ensure stuff is loaded etc
+//
+	{
+	
+#ifndef __EPOC32__
+	User::LoadPhysicalDevice(CDRV1_PATH);    
+    	User::LoadLogicalDevice(COMM_PATH);
+#endif
+
+	
+//	User::LeaveIfError(Nifman::CheckIniConfig());
+	}
+
+
+void CPingTestUi::ConstructL()
+//
+// Contruct engine and console
+//
+	{
+	
+	iConsole = Console::NewL(_L("Ping"),TSize(KConsFullScreen,KConsFullScreen));
+	_LIT(KPhbkSyncCMI, "phbsync.cmi");
+    	(void)StartC32WithCMISuppressions(KPhbkSyncCMI);
+	
+	iEngine=CPingEng::NewL(*this);
+	iKeyHandler = new (ELeave) CPingTestKeyStroke(*this);
+	iHistory = new (ELeave) CCircList(KHistoryBufferSize);
+		
+	InitialiseL();
+	}
+
+CPingTestUi::~CPingTestUi()
+//
+// Delete console
+//
+	{
+	
+	delete iHistory;
+	delete iKeyHandler;
+	delete iConsole;
+	delete iEngine;
+	}
+
+void CPingTestUi::StartL(const TPingOptions& aOptions)
+	{
+	
+	iEngine->StartL(aOptions);
+	}
+
+void CPingTestUi::AddToHistory(const TDesC& aLine)
+//
+// Add line to history buffer
+//
+	{
+	
+	iHistory->Add(aLine);
+	}
+	
+void CPingTestUi::DisplayHistory(TInt aLine, TInt aPromptLen, TDes& aDes) const
+	{
+	
+	aDes.SetLength(0);
+	if(!iHistory->Count())
+		{
+		return;
+		}
+	const HBufC& line=(*iHistory)[aLine];
+	iConsole->SetPos(aPromptLen);
+	iConsole->ClearToEndOfLine();
+	iConsole->Write(line);
+	aDes.Append(line);
+	}
+
+void CPingTestUi::Pinging(const TNameRecord& aRecord, TInt aBytes) const
+//
+//	Who are we pinging ??
+//
+	{
+
+	TName ipaddr;
+	TInetAddr& addr = (TInetAddr&)aRecord.iAddr;
+	addr.Output(ipaddr);
+
+	if(aRecord.iName.Length())
+		{
+		iConsole->Printf(_L("Pinging %S [%S] with %d bytes of data\n"), &aRecord.iName, &ipaddr, aBytes);
+		}
+	else
+		{
+		iConsole->Printf(_L("Pinging %S with %d bytes of data\n"), &ipaddr, aBytes);
+		}
+	}
+
+void CPingTestUi::Reply(const TInetAddr& aFrom, TInt aBytes, TInt aIcmpSeq, TTimeIntervalMicroSeconds32 aTime) const
+//
+// Reply from remote host
+//
+	{
+
+	if(iFloodFormat)
+		{
+		iConsole->Write(_L("."));
+		return;
+		}
+
+	TName inetaddr;
+	aFrom.Output(inetaddr);
+
+	if(aTime.Int()<15000)
+		{
+		iConsole->Printf(_L("Reply from %S  len=%d seq %d time<15ms\n"), &inetaddr, aBytes, aIcmpSeq);
+		}
+	else
+		{
+		iConsole->Printf(_L("Reply from %S  len=%d seq %d time=%dms\n"), &inetaddr, aBytes, aIcmpSeq, aTime.Int()/1000);
+		}
+	}
+
+void CPingTestUi::Sent() const
+//
+//
+//
+	{
+
+	if(iFloodFormat)
+		{
+		iConsole->Write(_L("\b"));
+		}
+	}
+
+void CPingTestUi::Icmp4Message(const TInetAddr& aAddr, TInt aType, TInt aCode, const TDesC8& aData) const
+//
+//
+//
+	{
+
+	TBuf<39> inetaddr;
+	aAddr.Output(inetaddr);
+	iConsole->Printf(_L("Reply from %S: len=%d "), &inetaddr, aData.Length()+4);
+
+	switch(aType)
+		{
+	case KIPv4PingTypeEchoReply:
+		iConsole->Printf(_L("Echo Reply\n"));
+		break;
+
+	case KIPv4PingTypeUnreachable:
+		switch(aCode)
+			{
+		case KIPv4PingCodeUnreachNet:
+			iConsole->Printf(_L("Destination Net Unreachable\n"));
+			break;
+		case KIPv4PingCodeUnreachHost:
+			iConsole->Printf(_L("Destination Host Unreachable\n"));
+			break;
+		case KIPv4PingCodeUnreachProtocol:
+			iConsole->Printf(_L("Destination Protocol Unreachable\n"));
+			break;
+		case KIPv4PingCodeUnreachPort:
+			iConsole->Printf(_L("Destination Port Unreachable\n"));
+			break;
+		case KIPv4PingCodeUnreachNeedFrag:
+			iConsole->Printf(_L("Fragmentation Needed and DF Set\n"));
+			break;
+		case KIPv4PingCodeUnreachSrcRouteFail:
+			iConsole->Printf(_L("Source Route Failed\n"));
+			break;
+		case KIPv4PingCodeUnreachNetUnknown:
+			iConsole->Printf(_L("Destination Network Unknown\n"));
+			break;
+		case KIPv4PingCodeUnreachHostUnknown:
+			iConsole->Printf(_L("Destination Host Unknown\n"));
+			break;
+		case KIPv4PingCodeUnreachSrcHostIsolated:
+			iConsole->Printf(_L("Source Host Isolated\n"));
+			break;
+		case KIPv4PingCodeUnreachNetProhibited:
+			iConsole->Printf(_L("Destination Network Prohibited\n"));
+			break;
+		case KIPv4PingCodeUnreachHostProhibited:
+			iConsole->Printf(_L("Destination Host Prohibited\n"));
+			break;
+		case KIPv4PingCodeUnreachNetTOS:
+			iConsole->Printf(_L("Network Unreachable for TOS\n"));
+			break;
+		case KIPv4PingCodeUnreachHostTOS:
+			iConsole->Printf(_L("Host Unreachable for TOS\n"));
+			break;
+		case KIPv4PingCodeUnreachProhibited:
+			iConsole->Printf(_L("Prohibited by Filtering\n"));
+			break;
+		case KIPv4PingCodeUnreachPrecVolation:
+			iConsole->Printf(_L("Precedence Violation\n"));
+			break;
+		case KIPv4PingCodeUnreachPrecCutoff:
+			iConsole->Printf(_L("Precedence Cutoff in Effect\n"));
+			break;
+
+		default:
+			iConsole->Printf(_L("Dest Unreachable, Bad Code: %d\n"), aCode);
+			break;
+			}
+		break;
+
+	case KIPv4PingTypeSourceQuench:
+		iConsole->Printf(_L("Source Quench\n"));
+		break;
+
+	case KIPv4PingTypeRedirect:
+		switch(aCode) 
+			{
+		case KIPv4PingCodeRedirectNet:
+			iConsole->Printf(_L("Redirect Network"));
+			break;
+		case KIPv4PingCodeRedirectHost:
+			iConsole->Printf(_L("Redirect Host"));
+			break;
+		case KIPv4PingCodeRedirectNetTOS:
+			iConsole->Printf(_L("Redirect Type of Service and Network"));
+			break;
+		case KIPv4PingCodeRedirectHostTOS:
+			iConsole->Printf(_L("Redirect Type of Service and Host"));
+			break;
+		default:
+			iConsole->Printf(_L("Redirect, Bad Code: %d"), aCode);
+			break;
+			}
+		iConsole->Printf(_L("(New addr: %d.%d.%d.%d)\n"), aData[0], aData[1], aData[2], aData[3]);
+		break;
+
+	case KIPv4PingTypeTimeExceeded:
+		switch(aCode)
+			{
+		case KIPv4PingCodeExceedInTransit:
+			iConsole->Printf(_L("Time to live exceeded\n"));
+			break;
+		case KIPv4PingCodeExceedInReasm:
+			iConsole->Printf(_L("Frag reassembly time exceeded\n"));
+			break;
+		default:
+			iConsole->Printf(_L("Time exceeded, Bad Code: %d\n"), aCode);
+			break;
+			}
+		break;
+	case KIPv4PingTypeBadParameter:
+		iConsole->Printf(_L("Parameter problem: pointer = 0x%02x\n"), *(TUint*)aData.Ptr());
+		break;
+	default:
+		iConsole->Printf(_L("Bad ICMPv4 type: %d\n"), aType);
+		}
+	}
+
+void CPingTestUi::Icmp6Message(const TInetAddr& aAddr, TInt aType, TInt aCode) const
+	{
+	
+	TBuf<39> inetaddr;
+	aAddr.Output(inetaddr);
+	iConsole->Printf(_L("Reply from %S: "), &inetaddr);
+
+	switch(aType)
+		{
+		case KIPv6PingTypeEchoReply:
+			iConsole->Printf(_L("Echo Reply\n"));
+			break;
+
+		case KIPv6PingTypeUnreachable:
+			switch (aType)
+				{
+			case KIPv6PingCodeNoRoute:
+				iConsole->Printf(_L("No route to destination\n"));
+				break;
+			case KIPv6PingCodeAdminProhibited:
+				iConsole->Printf(_L("Communication administratively prohibited\n"));
+				break;
+			case KIPv6PingCodeAddressUnreachable:
+				iConsole->Printf(_L("Address unreachable\n"));
+				break;
+			case KIPv6PingCodePortUnreachable:
+				iConsole->Printf(_L("Port unreachable\n"));
+				break;
+			default:
+				iConsole->Printf(_L("Unreachable, bad code: %d\n"), aCode);
+				break;
+				}
+			break;
+
+		case KIPv6PingTypePacketTooBig:
+			iConsole->Printf(_L("Packet too big"));
+			break;
+
+		case KIPv6PingTypeTimeExeeded:
+			switch (aType)
+				{
+			case KIPv6PingCodeHLExeeded:
+				iConsole->Printf(_L("Hop limit exceeded in transit\n"));
+				break;
+			case KIPv6PingCodeFragReassemblyExeeded:
+				iConsole->Printf(_L("Fragment reassembly time exceeded\n"));
+				break;
+			default:
+				iConsole->Printf(_L("Time exceeded, bad code: %d\n"), aCode);
+				}
+			break;
+
+		case KIPv6PingTypeParamProblem:
+			switch(aCode)
+				{
+			case KIPv6PingCodeErroneousHeader:
+				iConsole->Printf(_L("Erroneous header field encountered\n"));
+				break;
+			case KIPv6PingCodeNextHeaderUnrecognised:
+				iConsole->Printf(_L("Unrecognised next header type encountered\n"));
+				break;
+			case KIPv6PingCodeIPv6OptionUnrecognised:
+				iConsole->Printf(_L("Unrecognised IPv6 option encountered\n"));
+				break;
+			default:
+				iConsole->Printf(_L("Parameter problem, bad code: %d\n"), aCode);
+				break;
+				}
+			break;
+
+		default:
+			iConsole->Printf(_L("Bad ICMPv6 type, value: %d\n"), aType);
+		}
+	}
+
+void CPingTestUi::SetParams(TBool aFloodFormat)
+//
+// 
+//
+	{
+	
+	iFloodFormat=aFloodFormat;
+	}
+
+void CPingTestUi::Finished(const TNameRecord& aRecord, TInt aNrTransmitted, TInt aNrReceived, TInt, TInt aMin, TInt aMax, TInt aSum, TInt aError)
+//
+// Pinger finished
+//
+	{
+
+	iConsole->Printf(_L("\n"));
+	if(aError!=KErrNone)
+		{
+		iConsole->Printf(_L("Error %d\n"),aError);
+		}
+
+	if(aNrTransmitted)
+		{
+
+		
+		TInt loss = aNrReceived>=aNrTransmitted ? 0 : (aNrTransmitted-aNrReceived)*100/aNrTransmitted;
+
+		iConsole->Printf(_L("Statistics %S\n"), &aRecord.iName);
+		iConsole->Printf(_L("%d transmitted %d received %d%% packet loss\n"), aNrTransmitted, aNrReceived, loss);
+
+		if(aNrReceived)
+			{
+			TInt avg = aSum/aNrReceived/1000;
+			iConsole->Printf(_L("Round-trip %d min %d avg %d max\n"), aMin/1000, avg, aMax/1000);
+			}
+
+		iNrReceivedPackets = aNrReceived;
+		}
+	else
+		{
+		iNrReceivedPackets = 0;
+		}
+	iKeyHandler->Cancel();
+	CActiveScheduler::Stop();
+	}	
+
+void CPingTestUi::KeyStroke()
+//
+// Key was pressed
+//
+	{
+	
+	if(iKeyHandler->iStatus==KErrNone)
+		{
+
+	    	if(iConsole->KeyCode()==EKeyEscape)
+			{
+			iConsole->Printf(_L("\nAborted\n"));
+			iEngine->CancelAndFinished();
+			return;
+			}
+
+		}
+	SetKeyStrokeActive();
+	}
+
+void CPingTestUi::SetKeyStrokeActive()
+//
+//
+//
+	{
+	
+	iConsole->Read(iKeyHandler->iStatus);
+	iKeyHandler->ReStart();
+	}
+
+void CPingTestUi::KeyStrokeDoCancel()
+//
+// Cancel the read
+//
+	{
+
+	iConsole->ReadCancel();
+	}
+
+CPingTestKeyStroke::CPingTestKeyStroke(CPingTestUi& aUi)
+//
+// Key reader
+//
+	: CActive(0), iUi(aUi)
+	{
+	
+	CActiveScheduler::Add(this);
+	}
+
+CPingTestKeyStroke::~CPingTestKeyStroke()
+//
+// Destruct means cancel
+//
+	{
+
+	Cancel();
+	}
+	
+void CPingTestKeyStroke::RunL()
+//
+// Key pressed
+//
+	{
+
+	iUi.KeyStroke();
+	}
+
+void CPingTestKeyStroke::DoCancel()
+//
+// Cancel key stroke
+//
+	{
+
+	iUi.KeyStrokeDoCancel();
+	}
+
+void CPingTestKeyStroke::ReStart()
+	{
+	
+	SetActive();
+	}
+
+const TPingOptions& TPingTestParser::Options() const
+	{
+	
+	return iOptions;
+	}
+
+
+TInt TPingTestParser::ParseCLArguments(TDes& aCommandLine,CPingTestUi& aUi)
+	{
+	iOptions = TPingOptions();	//Reset iOptions;
+	iOptions.iDestname = _L("127.0.0.1");
+	TLex lex(aCommandLine);
+
+	for(iCLArgument.Set(lex.NextToken()); iCLArgument.Length(); iCLArgument.Set(lex.NextToken()))
+		{
+		if(iCLArgument.Length()==2)
+			{
+			if(!iCLArgument.CompareF(_L("-A")))
+				{
+				iOptions.iResolveAddress=ETrue;
+				}
+			else
+				{
+				iOptions.iDestname=iCLArgument;
+				}
+			if(!iCLArgument.CompareF(_L("-C")))
+				{
+				iOptions.iPrompt = ETrue;
+				}
+			else
+				{
+				iOptions.iPrompt = EFalse;
+				}
+			}
+		else if(iCLArgument.Length()>2)
+			{
+
+			TLex val(iCLArgument.Mid(2));
+			TInt num;
+
+			TPtrC cmd = iCLArgument.Mid(0,2);
+
+			if(!cmd.CompareF(_L("-N")))
+				{
+				if(val.Val(iOptions.iNumberOfPings) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(iOptions.iNumberOfPings<0)
+					{
+					return KErrUnderflow;
+					}
+				}
+			else if(!cmd.CompareF(_L("-I")))
+				{
+				if(val.Val(num) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(num<0)
+					{
+					return KErrUnderflow;
+					}
+				else
+					{
+					iOptions.iInterval=num;
+					}
+				}
+			else if(!cmd.CompareF(_L("-W")))
+				{
+				if(val.Val(num) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(num<0)
+					{
+					return KErrUnderflow;
+					}
+				else
+					{
+					iOptions.iWait=num;
+					}
+				}
+			else if(!cmd.CompareF(_L("-S")))
+				{
+				if(val.Val(iOptions.iPingSize) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(iOptions.iPingSize<8)
+					{
+					return KErrUnderflow;
+					}
+				}
+			else if(!cmd.CompareF(_L("-P")))
+				{
+				if(val.Val(iOptions.iPreload) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(iOptions.iPreload<0)
+					{
+					return KErrUnderflow;
+					}
+				}
+			else if(!cmd.CompareF(_L("-B")))
+				{
+				if(val.Val(iOptions.iBacklog) != KErrNone)
+					{
+					return KErrArgument;
+					}
+				else if(iOptions.iBacklog<0)
+					{
+					return KErrUnderflow;
+					}
+				}
+			else if(!cmd.CompareF(_L("-C")))
+				{
+				// barf if connection override already set:
+				if(iOptions.iConnSnap || iOptions.iConnIap)
+					{
+					return KErrArgument;
+					}
+
+				// extract values from argument
+				TInt snap=0;
+				TInt iap=0;
+								
+				if(iCLArgument.FindF(_L("SNAP")) == 2)
+					{
+					TLex val(iCLArgument.Mid(6));
+					if(val.Val(snap) != KErrNone)
+						{
+						return KErrArgument;
+						}
+					}
+				else if(iCLArgument.FindF(_L("IAP")) == 2)
+					{
+					TLex val(iCLArgument.Mid(5));
+					if(val.Val(iap) != KErrNone)
+						{
+						return KErrArgument;
+						}
+					}
+				else if(val.Val(iap) != KErrNone) // plain number to be interpreted as IAP
+					{
+					return KErrArgument;
+					}
+				
+				// apply value to options structure, and tell the user we're overriding connection
+				if(snap)
+					{
+					aUi.Console().Printf(_L("Will start connection with SNAP %d\n"), snap);
+					iOptions.iConnSnap = snap;
+					}
+				else if (iap)
+					{
+					aUi.Console().Printf(_L("Will start connection with IAP %d\n"), iap);
+					iOptions.iConnIap = iap;
+					}
+				else
+					{
+					// 0 was specified.. what were you thinking?
+					User::Leave(-1005);
+					return KErrArgument;
+					}
+				}
+			else
+				{
+				iOptions.iDestname = iCLArgument;
+				}
+			}
+		else
+			{
+			iOptions.iDestname = iCLArgument;	
+			}
+		}
+
+	return KErrNone;
+	}
+
+
+
+TBool TPingTestParser::ParseCommandLine(CPingTestUi& aUi)
+	{
+	
+	TInt res = KErrNone;
+
+	do
+		{
+		TBuf<0x100> command;
+		aUi.Console().Printf(KPrompt);
+		
+		TKeyCode key, was=EKeyNull;
+		TInt histpos=-1;
+		while((key=aUi.Console().Getch())!=EKeyEnter)
+			{
+			if(command.Length()>=0x100)
+				{
+				User::Beep(440, 500000);
+				}
+			else if(key==EKeyBackspace || key==EKeyLeftArrow || key==EKeyDelete)
+				{
+				if(command.Length())
+					{
+					aUi.Console().Printf(_L("\b \b"));
+					command.SetLength(command.Length()-1);
+					}
+				}
+			else if(key == EKeyUpArrow)
+				{
+				if(was==EKeyDownArrow)
+					{
+					histpos--;
+					}
+				was=key;
+				aUi.DisplayHistory(histpos--, KPromptLength, command);
+				}
+			else if(key == EKeyDownArrow)
+				{
+				if(was==EKeyUpArrow)
+					{
+					++histpos;
+					}
+				was=key;
+				aUi.DisplayHistory(++histpos, KPromptLength, command);
+				}
+			else if(key>=EKeySpace && key<=EKeyDelete)
+				{
+				aUi.Console().Printf(_L("%c"), key);
+				command.Append(TChar(key));
+				}
+			}
+
+		aUi.Console().Printf(_L("\n"));
+		aUi.AddToHistory(command);
+
+		_LIT(KQuitCommand, "quit");
+		_LIT(KQCommand, "q");
+		_LIT(KExitCommand, "exit");
+
+		if (command == KQuitCommand || command == KQCommand || command == KExitCommand)
+			{
+			return KQuitReturn;
+			}
+
+		_LIT(KHelpCommand, "help");
+		if (command == KHelpCommand)
+			{
+			aUi.Console().Printf(_L("Usage: [options] destination\n\nwhere options are\n"));
+			aUi.Console().Printf(_L("    -a         resolve address to hostname\n"));
+			aUi.Console().Printf(_L("    -c         prompt for interface choice\n"));
+			aUi.Console().Printf(_L("    -cIAP3     start connection with IAP 3\n"));
+			aUi.Console().Printf(_L("    -cSNAP77   start connection with SNAP 77\n"));
+			aUi.Console().Printf(_L("    -h         print out this screen\n"));
+			aUi.Console().Printf(_L("    -n<number> number of pings\n"));
+			aUi.Console().Printf(_L("    -i<number> interval between pings\n"));
+			aUi.Console().Printf(_L("    -s<number> number of bytes in request\n"));
+			aUi.Console().Printf(_L("    -p<number> preload\n"));
+			aUi.Console().Printf(_L("    -w<number> time to wait for replies\n"));
+			aUi.Console().Printf(_L("    -b<number> maximum number of unanswered requests\n"));	
+			aUi.Console().Printf(_L("    quit, q or exit to finish\n\n"));
+			return KHelpReturn;
+			}
+
+		res = ParseCLArguments(command,aUi);
+		if (res != KErrNone)
+			{
+			aUi.Console().Printf(_L("Invalid argument %S - result %d\n"), &BadArgument(), res);
+			break;
+			}
+		} while (res!=KErrNone);
+
+    	return ETrue;
+	}
+
+//
+// Create new circular buffer for command line history
+//
+CCircList::CCircList(TInt aLength)
+: iMaxLength(aLength)
+{}
+
+CCircList::~CCircList()
+//
+// Delete contents
+//
+	{
+	
+	for(TInt i = iBufPtrArray.Count()-1; i>=0; --i)
+		{
+		delete iBufPtrArray[i];
+		}
+	iBufPtrArray.Close();
+	}
+
+TInt CCircList::Add(const TDesC& aLine)
+//
+// Add a new line to the buffer
+//
+	{
+
+	if(!aLine.Length())
+		{
+		return KErrNotFound;
+		}
+
+	HBufC* buf=NULL;
+
+	//Check to see if string matches last in array, if so return and don't add it.
+	if(iBufPtrArray.Count())
+		{
+		if(!iBufPtrArray[iBufPtrArray.Count()-1]->Compare(aLine))
+			{
+			return KErrNone;
+			}
+		}
+
+	buf=aLine.Alloc();
+	if(buf==NULL)
+		{
+		return KErrNoMemory;		//Ensures we can't put a null pointer into the arrray.
+		}
+	
+	if (iBufPtrArray.Count()>=iMaxLength)	//Check to see if array is now too large,if so remove first element.
+		{
+		iBufPtrArray.Remove(0);
+		}
+
+	return iBufPtrArray.Append(buf);
+	}
+
+TInt CCircList::Count() const
+	{
+	
+	return iBufPtrArray.Count();
+	}
+
+const HBufC& CCircList::operator[](TInt aIndex) const
+//
+// Return index relative to last element added
+// doesn't matter if index is out of range because we're wrapping it
+//
+	{
+	
+	aIndex=aIndex%iBufPtrArray.Count();
+	if(aIndex<0)
+		{
+		aIndex=iBufPtrArray.Count()+aIndex;
+		}
+
+	return *iBufPtrArray[aIndex];	//Dont need to check index, since RArray will do bounds checking.
+	}