From 65c102640dea51a709bd4c25ef6ca006edcbee71 Mon Sep 17 00:00:00 2001 From: jhurst Date: Fri, 2 Dec 2016 17:23:14 +0000 Subject: [PATCH] o Fixed AS-02 timed-text index partition . o Fixed contents of generic partiton EssenceContainers property in AS-DCP timed text files --- README | 21 +- configure.ac | 2 +- src/AS_02_TimedText.cpp | 101 +++-- src/AS_02_internal.h | 1 - src/AS_DCP_TimedText.cpp | 6 +- src/MXFTypes.cpp | 2 +- src/as-02-unwrap.cpp | 2 +- src/dirent_win.h | 931 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 1019 insertions(+), 47 deletions(-) create mode 100644 src/dirent_win.h diff --git a/README b/README index 24b3ae4..54ce524 100755 --- a/README +++ b/README @@ -152,26 +152,19 @@ command-line utilities all respond to -h. Change History -====== -NOTE: pre-release changes to be merged in final release version - -2016-11-22 - AS-02 Aux Data, 2.5.17b (pre-release) - - o Added optional Generic Partition to IMF Aux Data prototype, used to carry global metadata +2016-12-01 - bug fixes 2.6.17 + o Added PixelLayout to RGBAEssenceDescriptor (contributed by Schleich) + o Cleared up MXFGCP1FrameWrappedPictureElement / MXFGCP1FrameWrappedPictureElement + ambiguity. PHDR continues to use MXFGCP1FrameWrappedPictureElement. + MXFGCI1FrameWrappedPictureElement is supported for interlace. o Added support for 192, 200 and 240 fps images, includes 96, 100 and 120 fpe stereo - o Fixed raw essence detector for IMF Aux Data files o Added missing MCA properties to MCALabelSubDescriptor (contributed by Ruppel) o New MXF data type: LineMapPair o Added default 0,0 VideoLineMap value when wrapping CDCI with as-02-wrap o Added VideoLineMap property to GenericPictureEssenceDescriptor o Added timed text unwrap support to as-02-unwrap (contributed by Ruppel) - -2016-11-15 - AS-02 Aux Data, 2.5.17a (pre-release) - - o Added support for proposed Aux Data wrapping for AS-02/IMF - -====== - + o Added prototype PIDM dynamic metadata wrapping, config with --enable-phdr + (Not for production use, caveat emptor, not the droids, etc.) 2016-08-11 - bug fixes, 2.5.15 diff --git a/configure.ac b/configure.ac index eb8d19e..0a33166 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ AC_PREREQ([2.59]) # For example, if asdcplib version 1.0.0 were modified to accomodate changes # in file format, and if no changes were made to AS_DCP.h, the new version would be # 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1. -AC_INIT([asdcplib], [2.5.17b], [asdcplib@cinecert.com]) +AC_INIT([asdcplib], [2.6.17], [asdcplib@cinecert.com]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/KM_error.h]) diff --git a/src/AS_02_TimedText.cpp b/src/AS_02_TimedText.cpp index c404d9b..4965859 100644 --- a/src/AS_02_TimedText.cpp +++ b/src/AS_02_TimedText.cpp @@ -167,7 +167,9 @@ AS_02::TimedText::MXFReader::h__Reader::ReadTimedTextResource(ASDCP::TimedText:: AESDecContext* Ctx, HMACContext* HMAC) { if ( ! m_File.IsOpen() ) - return RESULT_INIT; + { + return RESULT_INIT; + } assert(m_Dict); Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC); @@ -437,6 +439,7 @@ public: TimedTextDescriptor m_TDesc; byte_t m_EssenceUL[SMPTE_UL_LENGTH]; ui32_t m_EssenceStreamID; + ASDCP::Rational m_EditRate; h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_EssenceStreamID(10) { @@ -522,19 +525,11 @@ AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT // 72 == sizeof K, L, instanceuid, uuid + sizeof int32 + tag/len * 4 m_HeaderSize += ( resourceSubdescriptor->MIMEMediaType.ArchiveLength() * 2 /*ArchiveLength is broken*/ ) + 72; } - } - - if ( KM_SUCCESS(result) ) - { - result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)), - "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_TimedTextEssence)), - TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate)); - } - - if ( KM_SUCCESS(result) ) - { - this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer); - } + } + + //Reset m_EssenceStreamID to 10 for later usage in WriteAncillaryResource + m_EssenceStreamID = 10; + assert(m_Dict); if ( KM_SUCCESS(result) ) { @@ -543,6 +538,14 @@ AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT result = m_State.Goto_READY(); } + if ( KM_SUCCESS(result) ) + { + m_EditRate = TDesc.EditRate; + result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)), + "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)), + TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate)); + } + return result; } @@ -551,6 +554,8 @@ ASDCP::Result_t AS_02::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc, ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC) { + ASDCP::FrameBuffer segment_buffer; + IndexTableSegment::IndexEntry index_entry; Result_t result = m_State.Goto_RUNNING(); if ( KM_SUCCESS(result) ) @@ -562,19 +567,67 @@ AS_02::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size); FrameBuf.Size(str_size); - - IndexTableSegment::IndexEntry Entry; - Entry.StreamOffset = m_StreamOffset; + index_entry.StreamOffset = m_StreamOffset; + result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, + m_StreamOffset, FrameBuf, m_EssenceUL, Ctx, HMAC); + } + + if ( KM_SUCCESS(result) ) + { + // encode the index table + IndexTableSegment::DeltaEntry nil_delta_entry; + IndexTableSegment segment(m_Dict); + segment.m_Lookup = &m_HeaderPart.m_Primer; + GenRandomValue(segment.InstanceUID); + + segment.DeltaEntryArray.push_back(nil_delta_entry); + segment.IndexEditRate = m_EditRate; + segment.IndexStartPosition = 0; + segment.IndexDuration = -1; + segment.IndexEntryArray.push_back(index_entry); + + result = segment_buffer.Capacity(MaxIndexSegmentSize); // segment-count * max-segment-size + if ( KM_SUCCESS(result) ) { - ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet - - result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, - m_StreamOffset, FrameBuf, m_EssenceUL, Ctx, HMAC); + result = segment.WriteToBuffer(segment_buffer); } } + if ( KM_SUCCESS(result) ) + { + // create an index partition header + Kumu::fpos_t here = m_File.Tell(); + assert(m_Dict); + + ASDCP::MXF::Partition partition(m_Dict); + partition.ThisPartition = here; + partition.BodySID = 0; + partition.IndexSID = 129; + partition.IndexByteCount = segment_buffer.Size(); + partition.PreviousPartition = m_RIP.PairArray.back().ByteOffset; + partition.OperationalPattern = m_HeaderPart.OperationalPattern; + + m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); + partition.EssenceContainers = m_HeaderPart.EssenceContainers; + UL TmpUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition)); + result = partition.WriteToFile(m_File, TmpUL); + } + + if ( KM_SUCCESS(result) ) + { + // write the encoded index table + ui32_t write_count = 0; + result = m_File.Write(segment_buffer.RoData(), segment_buffer.Size(), &write_count); + assert(write_count == segment_buffer.Size()); + } + + if ( KM_SUCCESS(result) ) + { + m_FramesWritten++; + } + return result; } @@ -603,14 +656,12 @@ AS_02::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::Time GSPart.OperationalPattern = m_HeaderPart.OperationalPattern; m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here)); - GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence))); + GSPart.EssenceContainers = m_HeaderPart.EssenceContainers; UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition)); Result_t result = GSPart.WriteToFile(m_File, TmpUL); if ( KM_SUCCESS(result) ) { - ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet - result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten, m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC); } @@ -629,7 +680,7 @@ AS_02::TimedText::MXFWriter::h__Writer::Finalize() return RESULT_STATE; } - m_IndexWriter.m_Duration = m_FramesWritten = m_TDesc.ContainerDuration; + m_FramesWritten = m_TDesc.ContainerDuration; Result_t result = m_State.Goto_FINAL(); diff --git a/src/AS_02_internal.h b/src/AS_02_internal.h index 2f96c25..76286b1 100644 --- a/src/AS_02_internal.h +++ b/src/AS_02_internal.h @@ -309,7 +309,6 @@ namespace AS_02 public: ui64_t m_ECStart; // offset of the first essence element ui64_t m_ClipStart; // state variable for clip-wrap-in-progress - // AS_02::MXF::AS02IndexWriterCBR m_IndexWriter; IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5 h__AS02WriterClip(const Dictionary&); diff --git a/src/AS_DCP_TimedText.cpp b/src/AS_DCP_TimedText.cpp index e6f3869..af5e976 100644 --- a/src/AS_DCP_TimedText.cpp +++ b/src/AS_DCP_TimedText.cpp @@ -634,9 +634,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string IndexTableSegment::IndexEntry Entry; Entry.StreamOffset = m_StreamOffset; - - if ( ASDCP_SUCCESS(result) ) - result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC); + result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC); if ( ASDCP_SUCCESS(result) ) { @@ -670,7 +668,7 @@ ASDCP::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::Time GSPart.OperationalPattern = m_HeaderPart.OperationalPattern; m_RIP.PairArray.push_back(RIP::PartitionPair(m_EssenceStreamID++, here)); - GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence))); + GSPart.EssenceContainers = m_HeaderPart.EssenceContainers; UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition)); Result_t result = GSPart.WriteToFile(m_File, TmpUL); diff --git a/src/MXFTypes.cpp b/src/MXFTypes.cpp index b2557f7..eda0783 100755 --- a/src/MXFTypes.cpp +++ b/src/MXFTypes.cpp @@ -561,7 +561,7 @@ ASDCP::MXF::TLVWriter::WriteTag(const MDDEntry& Entry) { if ( m_Lookup == 0 ) { - DefaultLogSink().Error("No Primer object available\n"); + DefaultLogSink().Error("No Primer object available.\n"); return RESULT_FAIL; } diff --git a/src/as-02-unwrap.cpp b/src/as-02-unwrap.cpp index 190204a..1104be0 100755 --- a/src/as-02-unwrap.cpp +++ b/src/as-02-unwrap.cpp @@ -709,7 +709,7 @@ main(int argc, const char** argv) break; default: - fprintf(stderr, "%s: Unknown file type, not AS-02 essence.\n", Options.input_filename); + fprintf(stderr, "%s: Unknown file type (%d), not AS-02 essence.\n", Options.input_filename, EssenceType); return 5; } } diff --git a/src/dirent_win.h b/src/dirent_win.h new file mode 100644 index 0000000..5191c81 --- /dev/null +++ b/src/dirent_win.h @@ -0,0 +1,931 @@ +#ifdef KM_WIN32 +/* + * Dirent interface for Microsoft Visual Studio + * Version 1.21 + * + * Copyright (C) 2006-2012 Toni Ronkko + * This file is part of dirent. Dirent may be freely distributed + * under the MIT license. For all details and documentation, see + * https://github.com/tronkko/dirent + */ +#ifndef DIRENT_H +#define DIRENT_H + +/* + * Include windows.h without Windows Sockets 1.1 to prevent conflicts with + * Windows Sockets 2.0. + */ +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Indicates that d_type field is available in dirent structure */ +#define _DIRENT_HAVE_D_TYPE + +/* Indicates that d_namlen field is available in dirent structure */ +#define _DIRENT_HAVE_D_NAMLEN + +/* Entries missing from MSVC 6.0 */ +#if !defined(FILE_ATTRIBUTE_DEVICE) +# define FILE_ATTRIBUTE_DEVICE 0x40 +#endif + +/* File type and permission flags for stat(), general mask */ +#if !defined(S_IFMT) +# define S_IFMT _S_IFMT +#endif + +/* Directory bit */ +#if !defined(S_IFDIR) +# define S_IFDIR _S_IFDIR +#endif + +/* Character device bit */ +#if !defined(S_IFCHR) +# define S_IFCHR _S_IFCHR +#endif + +/* Pipe bit */ +#if !defined(S_IFFIFO) +# define S_IFFIFO _S_IFFIFO +#endif + +/* Regular file bit */ +#if !defined(S_IFREG) +# define S_IFREG _S_IFREG +#endif + +/* Read permission */ +#if !defined(S_IREAD) +# define S_IREAD _S_IREAD +#endif + +/* Write permission */ +#if !defined(S_IWRITE) +# define S_IWRITE _S_IWRITE +#endif + +/* Execute permission */ +#if !defined(S_IEXEC) +# define S_IEXEC _S_IEXEC +#endif + +/* Pipe */ +#if !defined(S_IFIFO) +# define S_IFIFO _S_IFIFO +#endif + +/* Block device */ +#if !defined(S_IFBLK) +# define S_IFBLK 0 +#endif + +/* Link */ +#if !defined(S_IFLNK) +# define S_IFLNK 0 +#endif + +/* Socket */ +#if !defined(S_IFSOCK) +# define S_IFSOCK 0 +#endif + +/* Read user permission */ +#if !defined(S_IRUSR) +# define S_IRUSR S_IREAD +#endif + +/* Write user permission */ +#if !defined(S_IWUSR) +# define S_IWUSR S_IWRITE +#endif + +/* Execute user permission */ +#if !defined(S_IXUSR) +# define S_IXUSR 0 +#endif + +/* Read group permission */ +#if !defined(S_IRGRP) +# define S_IRGRP 0 +#endif + +/* Write group permission */ +#if !defined(S_IWGRP) +# define S_IWGRP 0 +#endif + +/* Execute group permission */ +#if !defined(S_IXGRP) +# define S_IXGRP 0 +#endif + +/* Read others permission */ +#if !defined(S_IROTH) +# define S_IROTH 0 +#endif + +/* Write others permission */ +#if !defined(S_IWOTH) +# define S_IWOTH 0 +#endif + +/* Execute others permission */ +#if !defined(S_IXOTH) +# define S_IXOTH 0 +#endif + +/* Maximum length of file name */ +#if !defined(PATH_MAX) +# define PATH_MAX MAX_PATH +#endif +#if !defined(FILENAME_MAX) +# define FILENAME_MAX MAX_PATH +#endif +#if !defined(NAME_MAX) +# define NAME_MAX FILENAME_MAX +#endif + +/* File type flags for d_type */ +#define DT_UNKNOWN 0 +#define DT_REG S_IFREG +#define DT_DIR S_IFDIR +#define DT_FIFO S_IFIFO +#define DT_SOCK S_IFSOCK +#define DT_CHR S_IFCHR +#define DT_BLK S_IFBLK +#define DT_LNK S_IFLNK + +/* Macros for converting between st_mode and d_type */ +#define IFTODT(mode) ((mode) & S_IFMT) +#define DTTOIF(type) (type) + +/* + * File type macros. Note that block devices, sockets and links cannot be + * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are + * only defined for compatibility. These macros should always return false + * on Windows. + */ +#if !defined(S_ISFIFO) +# define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISDIR) +# define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) +# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISLNK) +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) +# define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISCHR) +# define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISBLK) +# define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +/* Return the exact length of d_namlen without zero terminator */ +#define _D_EXACT_NAMLEN(p) ((p)->d_namlen) + +/* Return number of bytes needed to store d_namlen */ +#define _D_ALLOC_NAMLEN(p) (PATH_MAX) + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Wide-character version */ +struct _wdirent { + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + wchar_t d_name[PATH_MAX]; +}; +typedef struct _wdirent _wdirent; + +struct _WDIR { + /* Current directory entry */ + struct _wdirent ent; + + /* Private file data */ + WIN32_FIND_DATAW data; + + /* True if data is valid */ + int cached; + + /* Win32 search handle */ + HANDLE handle; + + /* Initial directory name */ + wchar_t *patt; +}; +typedef struct _WDIR _WDIR; + +static _WDIR *_wopendir (const wchar_t *dirname); +static struct _wdirent *_wreaddir (_WDIR *dirp); +static int _wclosedir (_WDIR *dirp); +static void _wrewinddir (_WDIR* dirp); + + +/* For compatibility with Symbian */ +#define wdirent _wdirent +#define WDIR _WDIR +#define wopendir _wopendir +#define wreaddir _wreaddir +#define wclosedir _wclosedir +#define wrewinddir _wrewinddir + + +/* Multi-byte character versions */ +struct dirent { + /* Always zero */ + long d_ino; + + /* Structure size */ + unsigned short d_reclen; + + /* Length of name without \0 */ + size_t d_namlen; + + /* File type */ + int d_type; + + /* File name */ + char d_name[PATH_MAX]; +}; +typedef struct dirent dirent; + +struct DIR { + struct dirent ent; + struct _WDIR *wdirp; +}; +typedef struct DIR DIR; + +static DIR *opendir (const char *dirname); +static struct dirent *readdir (DIR *dirp); +static int closedir (DIR *dirp); +static void rewinddir (DIR* dirp); + + +/* Internal utility functions */ +static WIN32_FIND_DATAW *dirent_first (_WDIR *dirp); +static WIN32_FIND_DATAW *dirent_next (_WDIR *dirp); + +static int dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count); + +static int dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, + const wchar_t *wcstr, + size_t count); + +static void dirent_set_errno (int error); + +/* + * Open directory stream DIRNAME for read and return a pointer to the + * internal working area that is used to retrieve individual directory + * entries. + */ +static _WDIR* +_wopendir( + const wchar_t *dirname) +{ + _WDIR *dirp = NULL; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); + if (dirp != NULL) { + DWORD n; + + /* Reset _WDIR structure */ + dirp->handle = INVALID_HANDLE_VALUE; + dirp->patt = NULL; + dirp->cached = 0; + + /* Compute the length of full path plus zero terminator + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + n = wcslen(dirname); +# else + n = GetFullPathNameW (dirname, 0, NULL, NULL); +# endif + + /* Allocate room for absolute directory name and search pattern */ + dirp->patt = (wchar_t*) malloc (sizeof (wchar_t) * n + 16); + if (dirp->patt) { + + /* + * Convert relative directory name to an absolute one. This + * allows rewinddir() to function correctly even when current + * working directory is changed between opendir() and rewinddir(). + * + * Note that on WinRT there's no way to convert relative paths + * into absolute paths, so just assume its an absolute path. + */ +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP) + wcsncpy_s(dirp->patt, n+1, dirname, n); +# else + n = GetFullPathNameW (dirname, n, dirp->patt, NULL); +# endif + if (n > 0) { + wchar_t *p; + + /* Append search pattern \* to the directory name */ + p = dirp->patt + n; + if (dirp->patt < p) { + switch (p[-1]) { + case '\\': + case '/': + case ':': + /* Directory ends in path separator, e.g. c:\temp\ */ + /*NOP*/; + break; + + default: + /* Directory name doesn't end in path separator */ + *p++ = '\\'; + } + } + *p++ = '*'; + *p = '\0'; + + /* Open directory stream and retrieve the first entry */ + if (dirent_first (dirp)) { + /* Directory stream opened successfully */ + error = 0; + } else { + /* Cannot retrieve first entry */ + error = 1; + dirent_set_errno (ENOENT); + } + + } else { + /* Cannot retrieve full path name */ + dirent_set_errno (ENOENT); + error = 1; + } + + } else { + /* Cannot allocate memory for search pattern */ + error = 1; + } + + } else { + /* Cannot allocate _WDIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + _wclosedir (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. The directory entry is returned in dirent + * structure in the d_name field. Individual directory entries returned by + * this function include regular files, sub-directories, pseudo-directories + * "." and ".." as well as volume labels, hidden files and system files. + */ +static struct _wdirent* +_wreaddir( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct _wdirent *entp; + + /* Read next directory entry */ + datap = dirent_next (dirp); + if (datap) { + size_t n; + DWORD attr; + + /* Pointer to directory entry to return */ + entp = &dirp->ent; + + /* + * Copy file name as wide-character string. If the file name is too + * long to fit in to the destination buffer, then truncate file name + * to PATH_MAX characters and zero-terminate the buffer. + */ + n = 0; + while (n + 1 < PATH_MAX && datap->cFileName[n] != 0) { + entp->d_name[n] = datap->cFileName[n]; + n++; + } + dirp->ent.d_name[n] = 0; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n; + + /* File type */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof (struct _wdirent); + + } else { + + /* Last directory entry read */ + entp = NULL; + + } + + return entp; +} + +/* + * Close directory stream opened by opendir() function. This invalidates the + * DIR structure as well as any directory entry read previously by + * _wreaddir(). + */ +static int +_wclosedir( + _WDIR *dirp) +{ + int ok; + if (dirp) { + + /* Release search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + } + + /* Release search pattern */ + if (dirp->patt) { + free (dirp->patt); + dirp->patt = NULL; + } + + /* Release directory structure */ + free (dirp); + ok = /*success*/0; + + } else { + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + } + return ok; +} + +/* + * Rewind directory stream such that _wreaddir() returns the very first + * file name again. + */ +static void +_wrewinddir( + _WDIR* dirp) +{ + if (dirp) { + /* Release existing search handle */ + if (dirp->handle != INVALID_HANDLE_VALUE) { + FindClose (dirp->handle); + } + + /* Open new search handle */ + dirent_first (dirp); + } +} + +/* Get first directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_first( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *datap; + + /* Open directory and retrieve the first entry */ + dirp->handle = FindFirstFileExW( + dirp->patt, FindExInfoStandard, &dirp->data, + FindExSearchNameMatch, NULL, 0); + if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* a directory entry is now waiting in memory */ + datap = &dirp->data; + dirp->cached = 1; + + } else { + + /* Failed to re-open directory: no directory entry in memory */ + dirp->cached = 0; + datap = NULL; + + } + return datap; +} + +/* Get next directory entry (internal) */ +static WIN32_FIND_DATAW* +dirent_next( + _WDIR *dirp) +{ + WIN32_FIND_DATAW *p; + + /* Get next directory entry */ + if (dirp->cached != 0) { + + /* A valid directory entry already in memory */ + p = &dirp->data; + dirp->cached = 0; + + } else if (dirp->handle != INVALID_HANDLE_VALUE) { + + /* Get the next directory entry from stream */ + if (FindNextFileW (dirp->handle, &dirp->data) != FALSE) { + /* Got a file */ + p = &dirp->data; + } else { + /* The very last entry has been processed or an error occured */ + FindClose (dirp->handle); + dirp->handle = INVALID_HANDLE_VALUE; + p = NULL; + } + + } else { + + /* End of directory stream reached */ + p = NULL; + + } + + return p; +} + +/* + * Open directory stream using plain old C-string. + */ +static DIR* +opendir( + const char *dirname) +{ + struct DIR *dirp; + int error; + + /* Must have directory name */ + if (dirname == NULL || dirname[0] == '\0') { + dirent_set_errno (ENOENT); + return NULL; + } + + /* Allocate memory for DIR structure */ + dirp = (DIR*) malloc (sizeof (struct DIR)); + if (dirp) { + wchar_t wname[PATH_MAX]; + size_t n; + + /* Convert directory name to wide-character string */ + error = dirent_mbstowcs_s (&n, wname, PATH_MAX, dirname, PATH_MAX); + if (!error) { + + /* Open directory stream using wide-character name */ + dirp->wdirp = _wopendir (wname); + if (dirp->wdirp) { + /* Directory stream opened */ + error = 0; + } else { + /* Failed to open directory stream */ + error = 1; + } + + } else { + /* + * Cannot convert file name to wide-character string. This + * occurs if the string contains invalid multi-byte sequences or + * the output buffer is too small to contain the resulting + * string. + */ + error = 1; + } + + } else { + /* Cannot allocate DIR structure */ + error = 1; + } + + /* Clean up in case of error */ + if (error && dirp) { + free (dirp); + dirp = NULL; + } + + return dirp; +} + +/* + * Read next directory entry. + * + * When working with text consoles, please note that file names returned by + * readdir() are represented in the default ANSI code page while any output to + * console is typically formatted on another code page. Thus, non-ASCII + * characters in file names will not usually display correctly on console. The + * problem can be fixed in two ways: (1) change the character set of console + * to 1252 using chcp utility and use Lucida Console font, or (2) use + * _cprintf function when writing to console. The _cprinf() will re-encode + * ANSI strings to the console code page so many non-ASCII characters will + * display correcly. + */ +static struct dirent* +readdir( + DIR *dirp) +{ + WIN32_FIND_DATAW *datap; + struct dirent *entp; + + /* Read next directory entry */ + datap = dirent_next (dirp->wdirp); + if (datap) { + size_t n; + int error; + + /* Attempt to convert file name to multi-byte string */ + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, datap->cFileName, PATH_MAX); + + /* + * If the file name cannot be represented by a multi-byte string, + * then attempt to use old 8+3 file name. This allows traditional + * Unix-code to access some file names despite of unicode + * characters, although file names may seem unfamiliar to the user. + * + * Be ware that the code below cannot come up with a short file + * name unless the file system provides one. At least + * VirtualBox shared folders fail to do this. + */ + if (error && datap->cAlternateFileName[0] != '\0') { + error = dirent_wcstombs_s( + &n, dirp->ent.d_name, PATH_MAX, + datap->cAlternateFileName, PATH_MAX); + } + + if (!error) { + DWORD attr; + + /* Initialize directory entry for return */ + entp = &dirp->ent; + + /* Length of file name excluding zero terminator */ + entp->d_namlen = n - 1; + + /* File attributes */ + attr = datap->dwFileAttributes; + if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { + entp->d_type = DT_CHR; + } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { + entp->d_type = DT_DIR; + } else { + entp->d_type = DT_REG; + } + + /* Reset dummy fields */ + entp->d_ino = 0; + entp->d_reclen = sizeof (struct dirent); + + } else { + /* + * Cannot convert file name to multi-byte string so construct + * an errornous directory entry and return that. Note that + * we cannot return NULL as that would stop the processing + * of directory entries completely. + */ + entp = &dirp->ent; + entp->d_name[0] = '?'; + entp->d_name[1] = '\0'; + entp->d_namlen = 1; + entp->d_type = DT_UNKNOWN; + entp->d_ino = 0; + entp->d_reclen = 0; + } + + } else { + /* No more directory entries */ + entp = NULL; + } + + return entp; +} + +/* + * Close directory stream. + */ +static int +closedir( + DIR *dirp) +{ + int ok; + if (dirp) { + + /* Close wide-character directory stream */ + ok = _wclosedir (dirp->wdirp); + dirp->wdirp = NULL; + + /* Release multi-byte character version */ + free (dirp); + + } else { + + /* Invalid directory stream */ + dirent_set_errno (EBADF); + ok = /*failure*/-1; + + } + return ok; +} + +/* + * Rewind directory stream to beginning. + */ +static void +rewinddir( + DIR* dirp) +{ + /* Rewind wide-character string directory stream */ + _wrewinddir (dirp->wdirp); +} + +/* Convert multi-byte string to wide character string */ +static int +dirent_mbstowcs_s( + size_t *pReturnValue, + wchar_t *wcstr, + size_t sizeInWords, + const char *mbstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = mbstowcs_s (pReturnValue, wcstr, sizeInWords, mbstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to wide-character string (or count characters) */ + n = mbstowcs (wcstr, mbstr, sizeInWords); + if (!wcstr || n < count) { + + /* Zero-terminate output buffer */ + if (wcstr && sizeInWords) { + if (n >= sizeInWords) { + n = sizeInWords - 1; + } + wcstr[n] = 0; + } + + /* Length of resuting multi-byte string WITH zero terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Could not convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Convert wide-character string to multi-byte string */ +static int +dirent_wcstombs_s( + size_t *pReturnValue, + char *mbstr, + size_t sizeInBytes, /* max size of mbstr */ + const wchar_t *wcstr, + size_t count) +{ + int error; + +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 or later */ + error = wcstombs_s (pReturnValue, mbstr, sizeInBytes, wcstr, count); + +#else + + /* Older Visual Studio or non-Microsoft compiler */ + size_t n; + + /* Convert to multi-byte string (or count the number of bytes needed) */ + n = wcstombs (mbstr, wcstr, sizeInBytes); + if (!mbstr || n < count) { + + /* Zero-terminate output buffer */ + if (mbstr && sizeInBytes) { + if (n >= sizeInBytes) { + n = sizeInBytes - 1; + } + mbstr[n] = '\0'; + } + + /* Length of resulting multi-bytes string WITH zero-terminator */ + if (pReturnValue) { + *pReturnValue = n + 1; + } + + /* Success */ + error = 0; + + } else { + + /* Cannot convert string */ + error = 1; + + } + +#endif + + return error; +} + +/* Set errno variable */ +static void +dirent_set_errno( + int error) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1400 + + /* Microsoft Visual Studio 2005 and later */ + _set_errno (error); + +#else + + /* Non-Microsoft compiler or older Microsoft compiler */ + errno = error; + +#endif +} + + +#ifdef __cplusplus +} +#endif +#endif /*DIRENT_H*/ + +#endif // KM_WIN32 -- 2.30.2