From a28627a6a55d71c90961e94941747933dc3819a8 Mon Sep 17 00:00:00 2001 From: weidai Date: Fri, 6 Dec 2002 22:02:46 +0000 Subject: [PATCH] add script-driven testing --- GNUmakefile | 15 +- adhoc.cpp.proto | 8 + cryptest.dsp | 69 +++++++- datatest.cpp | 434 ++++++++++++++++++++++++++++++++++++++++++++++++ factory.h | 90 ++++++++++ regtest.cpp | 34 ++++ secblock.h | 9 + strciphr.cpp | 2 +- test.cpp | 15 ++ 9 files changed, 671 insertions(+), 5 deletions(-) create mode 100644 adhoc.cpp.proto create mode 100644 datatest.cpp create mode 100644 factory.h create mode 100644 regtest.cpp diff --git a/GNUmakefile b/GNUmakefile index 43086790..375e531d 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,6 +1,9 @@ # can't use -fno-rtti yet because it causes problems with exception handling in GCC 2.95.2 CXXFLAGS = -g -# uncomment the next two lines to do a release build +# Uncomment the next two lines to do a release build. +# Note that you must define NDEBUG for your own application if you define it for Crypto++. +# Also, make sure you run the validation tests and test your own program thoroughly +# after turning on -O2. The GCC optimizer may have bugs that cause it to generate incorrect code. # CXXFLAGS = -O2 -DNDEBUG -ffunction-sections -fdata-sections # LDFLAGS = -Wl,--gc-sections ARFLAGS = -cr # ar needs the dash on OpenBSD @@ -29,14 +32,13 @@ CXX = g++ endif SRCS = $(wildcard *.cpp) - ifeq ($(SRCS),) # workaround wildcard function bug in GNU Make 3.77 SRCS = $(shell ls *.cpp) endif OBJS = $(SRCS:.cpp=.o) # test.o needs to be after bench.o for cygwin 1.1.4 (possible ld bug?) -TESTOBJS = bench.o test.o validat1.o validat2.o validat3.o +TESTOBJS = bench.o test.o validat1.o validat2.o validat3.o adhoc.o datatest.o regtest.o LIBOBJS = $(filter-out $(TESTOBJS),$(OBJS)) all: cryptest.exe @@ -54,6 +56,13 @@ cryptest.exe: libcryptopp.a $(TESTOBJS) nolib: $(OBJS) # makes it faster to test changes $(CXX) -o ct $(CXXFLAGS) $(OBJS) $(LDFLAGS) $(LDLIBS) +adhoc.cpp: adhoc.cpp.proto +ifeq ($(wildcard adhoc.cpp),) + cp adhoc.cpp.proto adhoc.cpp +else + touch adhoc.cpp +endif + .SUFFIXES: .cpp .cpp.o: diff --git a/adhoc.cpp.proto b/adhoc.cpp.proto new file mode 100644 index 00000000..8e7f9c28 --- /dev/null +++ b/adhoc.cpp.proto @@ -0,0 +1,8 @@ +extern int (*AdhocTest)(int argc, char *argv[]); + +int MyAdhocTest(int argc, char *argv[]) +{ + return 0; +} + +static int s_i = (AdhocTest = &MyAdhocTest, 0); diff --git a/cryptest.dsp b/cryptest.dsp index 515e1a5a..3f536783 100644 --- a/cryptest.dsp +++ b/cryptest.dsp @@ -1,5 +1,5 @@ # Microsoft Developer Studio Project File - Name="cryptest" - Package Owner=<4> -# Microsoft Developer Studio Generated Build File, Format Version 60000 +# Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 @@ -369,6 +369,61 @@ SOURCE=.\xtrdh342.dat # PROP Default_Filter ".cpp;.h" # Begin Source File +SOURCE=.\adhoc.cpp +# End Source File +# Begin Source File + +SOURCE=.\adhoc.cpp.proto + +!IF "$(CFG)" == "cryptest - Win32 FIPS 140 Release" + +# Begin Custom Build +InputPath=.\adhoc.cpp.proto + +"adhoc.cpp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + if exist adhoc.cpp touch adhoc.cpp + if not exist adhoc.cpp copy $(InputPath) adhoc.cpp + +# End Custom Build + +!ELSEIF "$(CFG)" == "cryptest - Win32 FIPS 140 Debug" + +# Begin Custom Build +InputPath=.\adhoc.cpp.proto + +"adhoc.cpp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + if exist adhoc.cpp touch adhoc.cpp + if not exist adhoc.cpp copy $(InputPath) adhoc.cpp + +# End Custom Build + +!ELSEIF "$(CFG)" == "cryptest - Win32 Release" + +# Begin Custom Build +InputPath=.\adhoc.cpp.proto + +"adhoc.cpp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + if exist adhoc.cpp touch adhoc.cpp + if not exist adhoc.cpp copy $(InputPath) adhoc.cpp + +# End Custom Build + +!ELSEIF "$(CFG)" == "cryptest - Win32 Debug" + +# Begin Custom Build +InputPath=.\adhoc.cpp.proto + +"adhoc.cpp" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + if exist adhoc.cpp touch adhoc.cpp + if not exist adhoc.cpp copy $(InputPath) adhoc.cpp + +# End Custom Build + +!ENDIF + +# End Source File +# Begin Source File + SOURCE=.\bench.cpp # End Source File # Begin Source File @@ -377,6 +432,18 @@ SOURCE=.\bench.h # End Source File # Begin Source File +SOURCE=.\datatest.cpp +# End Source File +# Begin Source File + +SOURCE=.\factory.h +# End Source File +# Begin Source File + +SOURCE=.\regtest.cpp +# End Source File +# Begin Source File + SOURCE=.\test.cpp # End Source File # Begin Source File diff --git a/datatest.cpp b/datatest.cpp new file mode 100644 index 00000000..1be7d895 --- /dev/null +++ b/datatest.cpp @@ -0,0 +1,434 @@ +#include "factory.h" +#include "integer.h" +#include "filters.h" +#include "hex.h" +#include "randpool.h" +#include "files.h" +#include "trunhash.h" +#include +#include + +USING_NAMESPACE(CryptoPP) +USING_NAMESPACE(std) + +RandomPool & GlobalRNG(); +void RegisterFactories(); + +typedef std::map TestData; + +class TestFailure : public Exception +{ +public: + TestFailure() : Exception(OTHER_ERROR, "Validation test failed") {} +}; + +static const TestData *s_currentTestData = NULL; + +void OutputTestData(const TestData &v) +{ + for (TestData::const_iterator i = v.begin(); i != v.end(); ++i) + { + cerr << i->first << ": " << i->second << endl; + } +} + +void SignalTestFailure() +{ + OutputTestData(*s_currentTestData); + throw TestFailure(); +} + +void SignalTestError() +{ + OutputTestData(*s_currentTestData); + throw Exception(Exception::OTHER_ERROR, "Unexpected error during validation test"); +} + +class TestDataNameValuePairs : public NameValuePairs +{ +public: + TestDataNameValuePairs(const TestData &data) : m_data(data) {} + + virtual bool GetVoidValue(const char *name, const std::type_info &valueType, void *pValue) const + { + TestData::const_iterator i = m_data.find(name); + if (i == m_data.end()) + return false; + + const std::string &value = i->second; + + if (valueType == typeid(int)) + *reinterpret_cast(pValue) = atoi(value.c_str()); + else if (valueType == typeid(Integer)) + *reinterpret_cast(pValue) = Integer((std::string(value) + "h").c_str()); + else + throw ValueTypeMismatch(name, typeid(std::string), valueType); + + return true; + } + +private: + const TestData &m_data; +}; + +const std::string & GetRequiredDatum(const TestData &data, const char *name) +{ + TestData::const_iterator i = data.find(name); + if (i == data.end()) + SignalTestError(); + return i->second; +} + +void PutDecodedDatumInto(const TestData &data, const char *name, BufferedTransformation &target) +{ + std::string s1 = GetRequiredDatum(data, name), s2; + + int repeat = 1; + if (s1[0] == 'r') + { + repeat = atoi(s1.c_str()+1); + s1 = s1.substr(s1.find(' ')+1); + } + + if (s1[0] == '\"') + s2 = s1.substr(1, s1.find('\"', 1)-1); + else if (s1.substr(0, 2) == "0x") + StringSource(s1.substr(2), true, new HexDecoder(new StringSink(s2))); + else + StringSource(s1, true, new HexDecoder(new StringSink(s2))); + + while (repeat--) + target.Put((const byte *)s2.data(), s2.size()); +} + +std::string GetDecodedDatum(const TestData &data, const char *name) +{ + std::string s; + PutDecodedDatumInto(data, name, StringSink(s).Ref()); + return s; +} + +void TestKeyPairValidAndConsistent(CryptoMaterial &pub, const CryptoMaterial &priv) +{ + if (!pub.Validate(GlobalRNG(), 3)) + SignalTestFailure(); + if (!priv.Validate(GlobalRNG(), 3)) + SignalTestFailure(); + +/* EqualityComparisonFilter comparison; + pub.Save(ChannelSwitch(comparison, "0")); + pub.AssignFrom(priv); + pub.Save(ChannelSwitch(comparison, "1")); + comparison.ChannelMessageSeriesEnd("0"); + comparison.ChannelMessageSeriesEnd("1"); +*/ +} + +void TestSignatureScheme(TestData &v) +{ + std::string name = GetRequiredDatum(v, "Name"); + std::string test = GetRequiredDatum(v, "Test"); + + std::auto_ptr signer(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + std::auto_ptr verifier(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + + TestDataNameValuePairs pairs(v); + std::string keyFormat = GetRequiredDatum(v, "KeyFormat"); + + if (keyFormat == "DER") + verifier->AccessMaterial().Load(StringStore(GetDecodedDatum(v, "PublicKey")).Ref()); + else if (keyFormat == "Component") + verifier->AccessMaterial().AssignFrom(pairs); + + if (test == "Verify" || test == "NotVerify") + { + VerifierFilter verifierFilter(*verifier, NULL, VerifierFilter::SIGNATURE_AT_BEGIN); + PutDecodedDatumInto(v, "Signature", verifierFilter); + PutDecodedDatumInto(v, "Message", verifierFilter); + verifierFilter.MessageEnd(); + if (verifierFilter.GetLastResult() == (test == "NotVerify")) + SignalTestFailure(); + } + else if (test == "PublicKeyValid") + { + if (!verifier->GetMaterial().Validate(GlobalRNG(), 3)) + SignalTestFailure(); + } + else + goto privateKeyTests; + + return; + +privateKeyTests: + if (keyFormat == "DER") + signer->AccessMaterial().Load(StringStore(GetDecodedDatum(v, "PrivateKey")).Ref()); + else if (keyFormat == "Component") + signer->AccessMaterial().AssignFrom(pairs); + + if (test == "KeyPairValidAndConsistent") + { + TestKeyPairValidAndConsistent(verifier->AccessMaterial(), signer->GetMaterial()); + } + else if (test == "Sign") + { + SignerFilter f(GlobalRNG(), *signer, new HexEncoder(new FileSink(cout))); + StringSource ss(GetDecodedDatum(v, "Message"), true, new Redirector(f)); + SignalTestFailure(); + } + else if (test == "DeterministicSign") + { + SignalTestError(); + assert(false); // TODO: implement + } + else if (test == "RandomSign") + { + SignalTestError(); + assert(false); // TODO: implement + } + else if (test == "GenerateKey") + { + SignalTestError(); + assert(false); + } + else + { + SignalTestError(); + assert(false); + } +} + +void TestEncryptionScheme(TestData &v) +{ + std::string name = GetRequiredDatum(v, "Name"); + std::string test = GetRequiredDatum(v, "Test"); + + std::auto_ptr encryptor(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + std::auto_ptr decryptor(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + + std::string keyFormat = GetRequiredDatum(v, "KeyFormat"); + + if (keyFormat == "DER") + { + decryptor->AccessMaterial().Load(StringStore(GetDecodedDatum(v, "PrivateKey")).Ref()); + encryptor->AccessMaterial().Load(StringStore(GetDecodedDatum(v, "PublicKey")).Ref()); + } + else if (keyFormat == "Component") + { + TestDataNameValuePairs pairs(v); + decryptor->AccessMaterial().AssignFrom(pairs); + encryptor->AccessMaterial().AssignFrom(pairs); + } + + if (test == "DecryptMatch") + { + std::string decrypted, expected = GetDecodedDatum(v, "Plaintext"); + StringSource ss(GetDecodedDatum(v, "Ciphertext"), true, new PK_DecryptorFilter(*decryptor, new StringSink(decrypted))); + if (decrypted != expected) + SignalTestFailure(); + } + else if (test == "KeyPairValidAndConsistent") + { + TestKeyPairValidAndConsistent(encryptor->AccessMaterial(), decryptor->GetMaterial()); + } + else + { + SignalTestError(); + assert(false); + } +} + +void TestDigestOrMAC(TestData &v, bool testDigest) +{ + std::string name = GetRequiredDatum(v, "Name"); + std::string test = GetRequiredDatum(v, "Test"); + + member_ptr mac; + member_ptr hash; + HashTransformation *pHash = NULL; + + if (testDigest) + { + hash.reset(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + pHash = hash.get(); + } + else + { + mac.reset(ObjectFactoryRegistry::Registry().CreateObject(name.c_str())); + pHash = mac.get(); + std::string key = GetDecodedDatum(v, "Key"); + mac->SetKey((const byte *)key.c_str(), key.size()); + } + + if (test == "Verify" || test == "VerifyTruncated" || test == "NotVerify") + { + int digestSize = pHash->DigestSize(); + if (test == "VerifyTruncated") + digestSize = atoi(GetRequiredDatum(v, "TruncatedSize").c_str()); + TruncatedHashModule thash(*pHash, digestSize); + HashVerificationFilter verifierFilter(thash, NULL, HashVerificationFilter::HASH_AT_BEGIN); + PutDecodedDatumInto(v, "Digest", verifierFilter); + PutDecodedDatumInto(v, "Message", verifierFilter); + verifierFilter.MessageEnd(); + if (verifierFilter.GetLastResult() == (test == "NotVerify")) + SignalTestFailure(); + } + else + { + SignalTestError(); + assert(false); + } +} + +bool GetField(std::istream &is, std::string &name, std::string &value) +{ + is >> name; + if (name.empty()) + return false; + + if (name[name.size()-1] != ':') + SignalTestError(); + name.erase(name.size()-1); + + while (is.peek() == ' ') + is.ignore(1); + + // VC60 workaround: getline bug + char buffer[4]; + value.resize(0); + bool continueLine; + + do + { + do + { + is.get(buffer, sizeof(buffer)); + value += buffer; + } + while (buffer[0] != 0); + is.clear(); + is.ignore(); + + if (value[value.size()-1] == '\\') + { + value.resize(value.size()-1); + continueLine = true; + } + else + continueLine = false; + + std::string::size_type i = value.find('#'); + if (i != std::string::npos) + value.erase(i); + } + while (continueLine); + + return true; +} + +void OutputPair(const NameValuePairs &v, const char *name) +{ + Integer x; + bool b = v.GetValue(name, x); + assert(b); + cout << name << ": \\\n "; + x.Encode(HexEncoder(new FileSink(cout), false, 64, "\\\n ").Ref(), x.MinEncodedSize()); + cout << endl; +} + +void OutputNameValuePairs(const NameValuePairs &v) +{ + std::string names = v.GetValueNames(); + string::size_type i = 0; + while (i < names.size()) + { + string::size_type j = names.find_first_of (';', i); + + if (j == string::npos) + return; + else + { + std::string name = names.substr(i, j-i); + if (name.find(':') == string::npos) + OutputPair(v, name.c_str()); + } + + i = j + 1; + } +} + +bool RunTestDataFile(const char *filename) +{ + RegisterFactories(); + + std::ifstream file(filename); + TestData v; + s_currentTestData = &v; + std::string name, value, lastAlgName; + unsigned int totalTests = 0, failedTests = 0; + + while (file) + { + while (file.peek() == '#') + file.ignore(INT_MAX, '\n'); + + if (file.peek() == '\n') + v.clear(); + + if (!GetField(file, name, value)) + break; + v[name] = value; + + if (name == "Test") + { + bool failed = true; + std::string algType = GetRequiredDatum(v, "AlgorithmType"); + + if (lastAlgName != GetRequiredDatum(v, "Name")) + { + lastAlgName = GetRequiredDatum(v, "Name"); + cout << "Testing " << algType.c_str() << " algorithm " << lastAlgName.c_str() << ".\n"; + } + + try + { + if (algType == "Signature") + TestSignatureScheme(v); + else if (algType == "AsymmetricCipher") + TestEncryptionScheme(v); + else if (algType == "MessageDigest") + TestDigestOrMAC(v, true); + else if (algType == "MAC") + TestDigestOrMAC(v, false); + else + SignalTestError(); + failed = false; + } + catch (TestFailure &) + { + cout << "\nTest failed.\n"; + } + catch (CryptoPP::Exception &e) + { + cout << "\nCryptoPP::Exception caught: " << e.what() << endl; + } + catch (std::exception &e) + { + cout << "\nstd::exception caught: " << e.what() << endl; + } + + if (failed) + { + cout << "Skipping to next test.\n"; + failedTests++; + } + else + cout << "."; + + totalTests++; + } + } + cout << "\nTests complete. Total tests = " << totalTests << ". Failed tests = " << failedTests << ".\n"; + if (failedTests != 0) + cout << "SOME TESTS FAILED!\n"; + return failedTests == 0; +} diff --git a/factory.h b/factory.h new file mode 100644 index 00000000..85d66316 --- /dev/null +++ b/factory.h @@ -0,0 +1,90 @@ +#ifndef CRYPTOPP_OBJFACT_H +#define CRYPTOPP_OBJFACT_H + +#include "cryptlib.h" +#include + +NAMESPACE_BEGIN(CryptoPP) + +template +class ObjectFactory +{ +public: + virtual AbstractClass * CreateObject() const =0; +}; + +template +class DefaultObjectFactory : public ObjectFactory +{ +public: + AbstractClass * CreateObject() const + { + return new ConcreteClass; + } + +}; + +template +class ObjectFactoryRegistry +{ +public: + ~ObjectFactoryRegistry() + { + for (Map::iterator i = m_map.begin(); i != m_map.end(); ++i) + { + delete i->second; + i->second = NULL; + } + } + + void RegisterFactory(const char *name, ObjectFactory *factory) + { + m_map[name] = factory; + } + + const ObjectFactory * GetFactory(const char *name) const + { + Map::const_iterator i = m_map.find(name); + return i == m_map.end() ? NULL : i->second; + } + + AbstractClass *CreateObject(const char *name) const + { + const ObjectFactory *factory = GetFactory(name); + return factory ? factory->CreateObject() : NULL; + } + + static ObjectFactoryRegistry & Registry() + { + static ObjectFactoryRegistry s_registry; + return s_registry; + } + +private: + typedef std::map *> Map; + Map m_map; +}; + +template +void RegisterDefaultFactoryFor(const char *name, AbstractClass *Dummy1=NULL, ConcreteClass *Dummy2=NULL) +{ + ObjectFactoryRegistry::Registry().RegisterFactory(name, new DefaultObjectFactory); +} + +template +void RegisterPublicKeyCryptoSystemDefaultFactories(const char *name, SchemeClass *dummy=NULL) +{ + RegisterDefaultFactoryFor(name); + RegisterDefaultFactoryFor(name); +} + +template +void RegisterSignatureSchemeDefaultFactories(const char *name, SchemeClass *dummy=NULL) +{ + RegisterDefaultFactoryFor(name); + RegisterDefaultFactoryFor(name); +} + +NAMESPACE_END + +#endif diff --git a/regtest.cpp b/regtest.cpp new file mode 100644 index 00000000..38d80c25 --- /dev/null +++ b/regtest.cpp @@ -0,0 +1,34 @@ +#include "factory.h" + +#include "dh.h" +#include "esign.h" +#include "md2.h" +#include "trunhash.h" +#include "rw.h" +#include "md5.h" +#include "rsa.h" +#include "ripemd.h" +#include "dsa.h" + +USING_NAMESPACE(CryptoPP) + +void RegisterFactories() +{ + RegisterDefaultFactoryFor("DH"); + RegisterDefaultFactoryFor("SHA-1"); + RegisterDefaultFactoryFor("SHA-256"); + RegisterDefaultFactoryFor("SHA-384"); + RegisterDefaultFactoryFor("SHA-512"); + RegisterDefaultFactoryFor >("HMAC(MD5)"); + RegisterDefaultFactoryFor >("HMAC(SHA-1)"); + RegisterDefaultFactoryFor >("HMAC(RIPEMD-160)"); + RegisterPublicKeyCryptoSystemDefaultFactories > >("RSA/OAEP-MGF1(SHA-1)"); + RegisterPublicKeyCryptoSystemDefaultFactories >("DLIES(NoCofactorMultiplication, KDF2(SHA-1), XOR, HMAC(SHA-1), DHAES)"); + RegisterSignatureSchemeDefaultFactories("DSA(1363)"); + RegisterSignatureSchemeDefaultFactories >("NR(1363)/EMSA1(SHA-1)"); + RegisterSignatureSchemeDefaultFactories >("DSA-1363/EMSA1(SHA-1)"); + RegisterSignatureSchemeDefaultFactories >("RSA/PKCS1-1.5(MD2)"); + RegisterSignatureSchemeDefaultFactories >("RSA/PKCS1-1.5(SHA-1)"); + RegisterSignatureSchemeDefaultFactories >("ESIGN/EMSA5-MGF1(SHA-1)"); + RegisterSignatureSchemeDefaultFactories >("RW/EMSA2(SHA-1)"); +} diff --git a/secblock.h b/secblock.h index 31997b63..e07c6a21 100644 --- a/secblock.h +++ b/secblock.h @@ -371,6 +371,15 @@ inline void swap(CryptoPP::SecBlock &a, CryptoPP::SecBlock &b) a.swap(b); } +#if defined(_STLPORT_VERSION) && !defined(_STLP_MEMBER_TEMPLATE_CLASSES) +template +inline CryptoPP::AllocatorWithCleanup<_Tp2>& +__stl_alloc_rebind(CryptoPP::AllocatorWithCleanup<_Tp1>& __a, const _Tp2*) +{ + return (CryptoPP::AllocatorWithCleanup<_Tp2>&)(__a); +} +#endif + NAMESPACE_END #endif diff --git a/strciphr.cpp b/strciphr.cpp index 694d1587..78e1a3f0 100644 --- a/strciphr.cpp +++ b/strciphr.cpp @@ -16,7 +16,7 @@ byte AdditiveCipherTemplate::GenerateByte() m_leftOver = policy.GetBytesPerIteration(); } - return KeystreamBufferEnd()[-m_leftOver--]; + return *(KeystreamBufferEnd()-m_leftOver--); } template diff --git a/test.cpp b/test.cpp index 77719fe5..926d98d3 100644 --- a/test.cpp +++ b/test.cpp @@ -19,6 +19,7 @@ #include "osrng.h" #include "wait.h" #include "fips140.h" +#include "factory.h" #include "validate.h" #include "bench.h" @@ -78,6 +79,11 @@ void FIPS140_GenerateRandomFiles(); bool Validate(int, bool, const char *); +void RegisterFactories(); +bool RunTestDataFile(const char *filename); + +int (*AdhocTest)(int argc, char *argv[]) = NULL; + #ifdef __BCPLUSPLUS__ int cmain(int argc, char *argv[]) #elif defined(_MSC_VER) @@ -199,6 +205,10 @@ int main(int argc, char *argv[]) return 0; case 't': { + if (command == "tv") + { + return !RunTestDataFile(argv[2]); + } // VC60 workaround: use char array instead of std::string to workaround MSVC's getline bug char passPhrase[MAX_PHRASE_LENGTH], plaintext[1024]; @@ -277,6 +287,11 @@ int main(int argc, char *argv[]) else if (command == "ft") ForwardTcpPort(argv[2], argv[3], argv[4]); return 0; + case 'a': + if (AdhocTest) + return (*AdhocTest)(argc, argv); + else + return 0; default: FileSource usage("usage.dat", true, new FileSink(cout)); return 1;