diff options
| author | jhurst <jhurst@cinecert.com> | 2015-05-21 00:10:39 +0000 |
|---|---|---|
| committer | jhurst <> | 2015-05-21 00:10:39 +0000 |
| commit | 282deda913e656d38df6bb04c685d1e2346cd29b (patch) | |
| tree | 3a93fb5272cf9d6fb19a6f1c2f6c1cb9876103af /src | |
| parent | 887003223e52b4fb2b9ca135dcb8a23e1f133cd4 (diff) | |
compile fixes
Diffstat (limited to 'src')
| -rw-r--r-- | src/AS_02.h | 16 | ||||
| -rw-r--r-- | src/AS_02_TimedText.cpp | 1 | ||||
| -rwxr-xr-x | src/AS_DCP.h | 1 | ||||
| -rwxr-xr-x | src/AS_DCP_MXF.cpp | 43 | ||||
| -rw-r--r-- | src/MDD.cpp | 8 | ||||
| -rw-r--r-- | src/ST2052_TextParser.cpp | 252 | ||||
| -rw-r--r-- | src/TimedText_Parser.cpp | 1 | ||||
| -rwxr-xr-x | src/Wav.cpp | 4 | ||||
| -rwxr-xr-x | src/Wav.h | 4 | ||||
| -rwxr-xr-x | src/as-02-unwrap.cpp | 2 | ||||
| -rwxr-xr-x | src/asdcp-util.cpp | 2 | ||||
| -rwxr-xr-x | src/asdcp-wrap.cpp | 3 | ||||
| -rwxr-xr-x | src/klvsplit.cpp | 2 | ||||
| -rwxr-xr-x | src/phdr-unwrap.cpp | 2 |
14 files changed, 306 insertions, 35 deletions
diff --git a/src/AS_02.h b/src/AS_02.h index 78f9229..17561fd 100644 --- a/src/AS_02.h +++ b/src/AS_02.h @@ -334,6 +334,22 @@ namespace AS_02 using ASDCP::TimedText::ResourceList_t; // + class Type5UUIDFilenameResolver : public ASDCP::TimedText::IResourceResolver + { + typedef std::map<Kumu::UUID, std::string> ResourceMap; + + ResourceMap m_ResourceMap; + std::string m_Dirname; + KM_NO_COPY_CONSTRUCT(Type5UUIDFilenameResolver); + + public: + Type5UUIDFilenameResolver(); + virtual ~Type5UUIDFilenameResolver(); + Result_t OpenRead(const std::string& dirname); + Result_t ResolveRID(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf) const; + }; + + // class ST2052_TextParser { class h__TextParser; diff --git a/src/AS_02_TimedText.cpp b/src/AS_02_TimedText.cpp index 7faa7ce..47d3566 100644 --- a/src/AS_02_TimedText.cpp +++ b/src/AS_02_TimedText.cpp @@ -626,7 +626,6 @@ AS_02::TimedText::MXFWriter::h__Writer::Finalize() } m_IndexWriter.m_Duration = m_FramesWritten = m_TDesc.ContainerDuration; - fprintf(stderr, "m_IndexWriter.m_Duration=%d\n", m_IndexWriter.m_Duration); Result_t result = m_State.Goto_FINAL(); diff --git a/src/AS_DCP.h b/src/AS_DCP.h index 7231fb5..cb78dbc 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -1485,6 +1485,7 @@ namespace ASDCP { public: LocalFilenameResolver(); + virtual ~LocalFilenameResolver(); Result_t OpenRead(const std::string& dirname); Result_t ResolveRID(const byte_t* uuid, FrameBuffer& FrameBuf) const; }; diff --git a/src/AS_DCP_MXF.cpp b/src/AS_DCP_MXF.cpp index b5ad865..32225be 100755 --- a/src/AS_DCP_MXF.cpp +++ b/src/AS_DCP_MXF.cpp @@ -310,24 +310,39 @@ ASDCP::RawEssenceType(const std::string& filename, EssenceType_t& type) { type = ESS_JPEG_2000; } - else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) + else if ( std::string((const char*)FB.RoData() + 8, 4) == "WAVE" ) { - switch ( WavHeader.samplespersec ) + if ( std::string((const char*)FB.RoData(), 4) == "RIFF" ) { - case 48000: type = ESS_PCM_24b_48k; break; - case 96000: type = ESS_PCM_24b_96k; break; - default: - return RESULT_FORMAT; + result = WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset); + + if ( ASDCP_SUCCESS(result) ) + { + switch ( WavHeader.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec); + result = RESULT_FORMAT; + } + } } - } - else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) - { - switch ( RF64Header.samplespersec ) + else { - case 48000: type = ESS_PCM_24b_48k; break; - case 96000: type = ESS_PCM_24b_96k; break; - default: - return RESULT_FORMAT; + result = RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset); + + if ( ASDCP_SUCCESS(result) ) + { + switch ( RF64Header.samplespersec ) + { + case 48000: type = ESS_PCM_24b_48k; break; + case 96000: type = ESS_PCM_24b_96k; break; + default: + DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec); + result = RESULT_FORMAT; + } + } } } else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) ) diff --git a/src/MDD.cpp b/src/MDD.cpp index cae083d..7698ff5 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -1124,16 +1124,16 @@ static const ASDCP::MDDEntry s_MDD_Table[] = { { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 362 0x04, 0x01, 0x03, 0x02, 0x0b, 0x00, 0x00, 0x00 }, {0}, false, "GenericPictureEssenceDescriptor_AlternativeCenterCuts" }, - { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 363 + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 363 0x04, 0x01, 0x05, 0x01, 0x13, 0x00, 0x00, 0x00 }, {0x32, 0x05}, true, "GenericPictureEssenceDescriptor_ActiveHeight" }, - { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 364 + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 364 0x04, 0x01, 0x05, 0x01, 0x14, 0x00, 0x00, 0x00 }, {0x32, 0x04}, true, "GenericPictureEssenceDescriptor_ActiveWidth" }, - { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 365 + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 365 0x04, 0x01, 0x05, 0x01, 0x15, 0x00, 0x00, 0x00 }, {0x32, 0x06}, true, "GenericPictureEssenceDescriptor_ActiveXOffset" }, - { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 366 + { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 366 0x04, 0x01, 0x05, 0x01, 0x16, 0x00, 0x00, 0x00 }, {0x32, 0x07}, true, "GenericPictureEssenceDescriptor_ActiveYOffset" }, { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 367 diff --git a/src/ST2052_TextParser.cpp b/src/ST2052_TextParser.cpp index 9a8dd2a..b00ebe6 100644 --- a/src/ST2052_TextParser.cpp +++ b/src/ST2052_TextParser.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2013-2014, John Hurst +Copyright (c) 2013-2015, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AS_02_internal.h" #include "KM_xml.h" +#include <openssl/sha.h> using namespace Kumu; using namespace ASDCP; @@ -40,6 +41,133 @@ using Kumu::DefaultLogSink; const char* c_tt_namespace_name = "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt"; + +//------------------------------------------------------------------------------------------ + +// +// +static byte_t s_id_prefix[16] = { + // RFC 4122 type 5 + // 2067-2 5.4.5 + 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, + 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 +}; + +// +void +gen_png_name_id(const std::string& image_name, UUID& asset_id) +{ + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, s_id_prefix, 16); + SHA1_Update(&ctx, (byte_t*)image_name.c_str(), image_name.length()); + + const ui32_t sha_len = 20; + byte_t bin_buf[sha_len]; + SHA1_Final(bin_buf, &ctx); + + // Derive the asset ID from the digest. Make it a type-5 UUID + byte_t buf[UUID_Length]; + memcpy(buf, bin_buf, UUID_Length); + buf[6] &= 0x0f; // clear bits 4-7 + buf[6] |= 0x50; // set UUID version 'digest' + buf[8] &= 0x3f; // clear bits 6&7 + buf[8] |= 0x80; // set bit 7 + asset_id.Set(buf); +} + +//------------------------------------------------------------------------------------------ + + +AS_02::TimedText::Type5UUIDFilenameResolver::Type5UUIDFilenameResolver() {} +AS_02::TimedText::Type5UUIDFilenameResolver::~Type5UUIDFilenameResolver() {} + +const byte_t PNGMagic[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; +const byte_t OpenTypeMagic[5] = { 0x4f, 0x54, 0x54, 0x4f, 0x00 }; +const byte_t TrueTypeMagic[5] = { 0x00, 0x01, 0x00, 0x00, 0x00 }; + +// +Result_t +AS_02::TimedText::Type5UUIDFilenameResolver::OpenRead(const std::string& dirname) +{ + DirScannerEx dir_reader; + DirectoryEntryType_t ft; + std::string next_item; + std::string abs_dirname = PathMakeCanonical(dirname); + byte_t read_buffer[16]; + + if ( abs_dirname.empty() ) + { + abs_dirname = "."; + } + + if ( KM_SUCCESS(dir_reader.Open(abs_dirname.c_str())) ) + { + while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) ) + { + if ( next_item[0] == '.' ) continue; // no hidden files + std::string tmp_path = PathJoin(abs_dirname, next_item); + + if ( ft == DET_FILE ) + { + FileReader reader; + Result_t result = reader.OpenRead(tmp_path); + + if ( KM_SUCCESS(result) ) + { + result = reader.Read(read_buffer, 16); + } + + if ( KM_SUCCESS(result) ) + { + // is it PNG? + if ( memcmp(read_buffer, PNGMagic, sizeof(PNGMagic)) == 0 ) + { + UUID asset_id; + gen_png_name_id(next_item, asset_id); + m_ResourceMap.insert(ResourceMap::value_type(asset_id, next_item)); + } + } + } + } + } +} + +// +Result_t +AS_02::TimedText::Type5UUIDFilenameResolver::ResolveRID(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf) const +{ + Kumu::UUID tmp_id(uuid); + char buf[64]; + + ResourceMap::const_iterator i = m_ResourceMap.find(tmp_id); + + if ( i == m_ResourceMap.end() ) + { + return RESULT_NOT_FOUND; + } + + FileReader Reader; + + DefaultLogSink().Debug("Retrieving resource %s from file %s\n", tmp_id.EncodeHex(buf, 64), i->second.c_str()); + + Result_t result = Reader.OpenRead(i->second.c_str()); + + if ( KM_SUCCESS(result) ) + { + ui32_t read_count, read_size = Reader.Size(); + result = FrameBuf.Capacity(read_size); + + if ( KM_SUCCESS(result) ) + result = Reader.Read(FrameBuf.Data(), read_size, &read_count); + + if ( KM_SUCCESS(result) ) + FrameBuf.Size(read_count); + } + + return result; +} + //------------------------------------------------------------------------------------------ typedef std::map<Kumu::UUID, ASDCP::TimedText::MIMEType_t> ResourceTypeMap_t; @@ -56,7 +184,7 @@ public: std::string m_Filename; std::string m_XMLDoc; TimedTextDescriptor m_TDesc; - ASDCP::mem_ptr<ASDCP::TimedText::LocalFilenameResolver> m_DefaultResolver; + ASDCP::mem_ptr<ASDCP::TimedText::IResourceResolver> m_DefaultResolver; h__TextParser() : m_Root("**ParserRoot**") { @@ -69,7 +197,7 @@ public: { if ( m_DefaultResolver.empty() ) { - ASDCP::TimedText::LocalFilenameResolver *resolver = new ASDCP::TimedText::LocalFilenameResolver; + AS_02::TimedText::Type5UUIDFilenameResolver *resolver = new AS_02::TimedText::Type5UUIDFilenameResolver; resolver->OpenRead(PathDirname(m_Filename)); m_DefaultResolver = resolver; } @@ -79,7 +207,7 @@ public: Result_t OpenRead(const std::string& filename); Result_t OpenRead(const std::string& xml_doc, const std::string& filename); - Result_t ReadAncillaryResource(const UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf, + Result_t ReadAncillaryResource(const byte_t *uuid, ASDCP::TimedText::FrameBuffer& FrameBuf, const ASDCP::TimedText::IResourceResolver& Resolver) const; }; @@ -108,11 +236,63 @@ AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& } // +template <class VisitorType> +bool +apply_visitor(const XMLElement& element, VisitorType& visitor) +{ + const ElementList& l = element.GetChildren(); + ElementList::const_iterator i; + + for ( i = l.begin(); i != l.end(); ++i ) + { + if ( ! visitor.Element(**i) ) + { + return false; + } + + if ( ! apply_visitor(**i, visitor) ) + { + return false; + } + } + + return true; +} + +// +class AttributeVisitor +{ + std::string attr_name; + +public: + AttributeVisitor(const std::string& n) : attr_name(n) {} + std::set<std::string> value_list; + + bool Element(const XMLElement& e) + { + const AttributeList& l = e.GetAttributes(); + AttributeList::const_iterator i; + + for ( i = l.begin(); i != l.end(); ++i ) + { + if ( i->name == attr_name ) + { + value_list.insert(i->value); + } + } + + return true; + } +}; + +// Result_t AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead() { if ( ! m_Root.ParseString(m_XMLDoc.c_str()) ) - return RESULT_FORMAT; + { + return RESULT_FORMAT; + } m_TDesc.EncodingName = "UTF-8"; // the XML parser demands UTF-8 m_TDesc.ResourceList.clear(); @@ -129,9 +309,63 @@ AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead() m_TDesc.NamespaceName = ns->Name(); } + AttributeVisitor png_visitor("backgroundImage"); + apply_visitor(m_Root, png_visitor); + std::set<std::string>::const_iterator i; + + for ( i = png_visitor.value_list.begin(); i != png_visitor.value_list.end(); ++i ) + { + UUID asset_id; + gen_png_name_id(*i, asset_id); + TimedTextResourceDescriptor TmpResource; + memcpy(TmpResource.ResourceID, asset_id.Value(), UUIDlen); + TmpResource.Type = ASDCP::TimedText::MT_PNG; + m_TDesc.ResourceList.push_back(TmpResource); + m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(TmpResource.ResourceID), ASDCP::TimedText::MT_PNG)); + } + return RESULT_OK; } +// +Result_t +AS_02::TimedText::ST2052_TextParser::h__TextParser::ReadAncillaryResource(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf, + const ASDCP::TimedText::IResourceResolver& Resolver) const +{ + FrameBuf.AssetID(uuid); + UUID TmpID(uuid); + char buf[64]; + + ResourceTypeMap_t::const_iterator rmi = m_ResourceTypes.find(TmpID); + + if ( rmi == m_ResourceTypes.end() ) + { + DefaultLogSink().Error("Unknown ancillary resource id: %s\n", TmpID.EncodeHex(buf, 64)); + return RESULT_RANGE; + } + + Result_t result = Resolver.ResolveRID(uuid, FrameBuf); + + if ( KM_SUCCESS(result) ) + { + if ( (*rmi).second == ASDCP::TimedText::MT_PNG ) + { + FrameBuf.MIMEType("image/png"); + } + else if ( (*rmi).second == ASDCP::TimedText::MT_OPENTYPE ) + { + FrameBuf.MIMEType("application/x-font-opentype"); + } + else + { + FrameBuf.MIMEType("application/octet-stream"); + } + } + + return result; +} + + //------------------------------------------------------------------------------------------ @@ -199,7 +433,13 @@ ASDCP::Result_t AS_02::TimedText::ST2052_TextParser::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf, const ASDCP::TimedText::IResourceResolver* Resolver) const { - return RESULT_NOTIMPL; + if ( m_Parser.empty() ) + return RESULT_INIT; + + if ( Resolver == 0 ) + Resolver = m_Parser->GetDefaultResolver(); + + return m_Parser->ReadAncillaryResource(uuid.Value(), FrameBuf, *Resolver); } diff --git a/src/TimedText_Parser.cpp b/src/TimedText_Parser.cpp index e391694..7ecd093 100644 --- a/src/TimedText_Parser.cpp +++ b/src/TimedText_Parser.cpp @@ -45,6 +45,7 @@ const char* c_dcst_namespace_name = "http://www.smpte-ra.org/schemas/428-7/2007/ ASDCP::TimedText::LocalFilenameResolver::LocalFilenameResolver() {} +ASDCP::TimedText::LocalFilenameResolver::~LocalFilenameResolver() {} // Result_t diff --git a/src/Wav.cpp b/src/Wav.cpp index 20776d5..aad4b89 100755 --- a/src/Wav.cpp +++ b/src/Wav.cpp @@ -175,7 +175,7 @@ ASDCP::Wav::SimpleWaveHeader::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, { ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2; - if ( format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_EXTENSIBLE ) + if ( format != ASDCP_WAVE_FORMAT_PCM && format != ASDCP_WAVE_FORMAT_EXTENSIBLE ) { DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format); return RESULT_RAW_FORMAT; @@ -564,7 +564,7 @@ ASDCP::RF64::SimpleRF64Header::ReadFromBuffer(const byte_t* buf, ui32_t buf_len, { ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2; - if ( format != Wav::WAVE_FORMAT_PCM && format != Wav::WAVE_FORMAT_EXTENSIBLE ) + if ( format != Wav::ASDCP_WAVE_FORMAT_PCM && format != Wav::ASDCP_WAVE_FORMAT_EXTENSIBLE ) { DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format); return RESULT_RAW_FORMAT; @@ -89,8 +89,8 @@ namespace ASDCP const fourcc FCC_fmt_("fmt "); const fourcc FCC_data("data"); - const ui16_t WAVE_FORMAT_PCM = 1; - const ui16_t WAVE_FORMAT_EXTENSIBLE = 65534; + const ui16_t ASDCP_WAVE_FORMAT_PCM = 1; + const ui16_t ASDCP_WAVE_FORMAT_EXTENSIBLE = 65534; // class SimpleWaveHeader diff --git a/src/as-02-unwrap.cpp b/src/as-02-unwrap.cpp index 148946b..37b458b 100755 --- a/src/as-02-unwrap.cpp +++ b/src/as-02-unwrap.cpp @@ -86,7 +86,7 @@ USAGE: %s [-h|-help] [-V]\n\ %s [-1|-2] [-b <buffer-size>] [-d <duration>]\n\ [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <size>] [-v] [-W]\n\ [-w] <input-file> [<file-prefix>]\n\n", - PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME); + PROGRAM_NAME, PROGRAM_NAME); fprintf(stream, "\ Options:\n\ diff --git a/src/asdcp-util.cpp b/src/asdcp-util.cpp index 6be6c07..a3c891d 100755 --- a/src/asdcp-util.cpp +++ b/src/asdcp-util.cpp @@ -79,7 +79,7 @@ USAGE: %s [-h|-help] [-V]\n\ %s -d <input-file>\n\ \n\ %s -g | -u\n", - PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME); + PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME); fprintf(stream, "\ Major modes:\n\ diff --git a/src/asdcp-wrap.cpp b/src/asdcp-wrap.cpp index 9388d87..8d54772 100755 --- a/src/asdcp-wrap.cpp +++ b/src/asdcp-wrap.cpp @@ -1681,8 +1681,7 @@ main(int argc, const char** argv) case ESS_DCDATA_UNKNOWN: if ( ! Options.aux_data_coding.HasValue() ) { - fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n", - Options.filenames.front().c_str()); + fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n"); return 3; } else diff --git a/src/klvsplit.cpp b/src/klvsplit.cpp index 4f77dbb..5dcd8ed 100755 --- a/src/klvsplit.cpp +++ b/src/klvsplit.cpp @@ -292,7 +292,7 @@ main(int argc, const char** argv) snprintf(filename_buf, 1024, "%s%010qu%s", this_prefix.c_str(), item_counter, Options.suffix.c_str()); if ( Options.verbose_flag ) - fprintf(stderr, "%s (%d bytes)\n", filename_buf, packet.ValueLength()); + fprintf(stderr, "%s (%llu bytes)\n", filename_buf, packet.ValueLength()); Kumu::FileWriter writer; writer.OpenWrite(filename_buf); diff --git a/src/phdr-unwrap.cpp b/src/phdr-unwrap.cpp index a0ba7c7..b759cd7 100755 --- a/src/phdr-unwrap.cpp +++ b/src/phdr-unwrap.cpp @@ -77,7 +77,7 @@ USAGE: %s [-h|-help] [-V]\n\ %s [-b <buffer-size>] [-d <duration>]\n\ [-f <starting-frame>] [-m] [-R] [-s <size>] [-v] [-W]\n\ [-w] <input-file> [<file-prefix>]\n\n", - PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME); + PROGRAM_NAME, PROGRAM_NAME); fprintf(stream, "\ Options:\n\ |
