/* ///////////////////////////////////////////////////////////////////////////// * File: recls.d * * Purpose: D mapping for the recls library. recls is a platform-independent * recursive search library. It is mapped to several languages, * including D. recls was written by Matthew Wilson, as the first * exemplar for his "Positive Integration" column in C/C++ User's * Journal. * * Created 10th Octover 2003 * Updated: 27th November 2003 * * Author: Matthew Wilson * * License: (Licensed under the Synesis Software Standard Source License) * * Copyright (C) 2002-2003, Synesis Software Pty Ltd. * * All rights reserved. * * www: http://www.recls.org/ * http://www.synesis.com.au/software * http://www.synsoft.org/ * * email: admin@recls.org * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * (i) Redistributions of source code must retain the above * copyright notice and contact information, this list of * conditions and the following disclaimer. * * (ii) Any derived versions of this software (howsoever modified) * remain the sole property of Synesis Software. * * (iii) Any derived versions of this software (howsoever modified) * remain subject to all these conditions. * * (iv) Neither the name of Synesis Software nor the names of any * subdivisions, employees or agents of Synesis Software, nor the * names of any other contributors to this software may be used to * endorse or promote products derived from this software without * specific prior written permission. * * This source code is provided by Synesis Software "as is" and any * warranties, whether expressed or implied, including, but not * limited to, the implied warranties of merchantability and * fitness for a particular purpose are disclaimed. In no event * shall the Synesis Software be liable for any direct, indirect, * incidental, special, exemplary, or consequential damages * (including, but not limited to, procurement of substitute goods * or services; loss of use, data, or profits; or business * interruption) however caused and on any theory of liability, * whether in contract, strict liability, or tort (including * negligence or otherwise) arising in any way out of the use of * this software, even if advised of the possibility of such * damage. * * ////////////////////////////////////////////////////////////////////////// */ //////////////////////////////////////////////////////////////////////////////// // Module module std.recls; //////////////////////////////////////////////////////////////////////////////// // Imports import std.string; version (linux) { private import std.c.time; private import std.c.linux.linux; } //////////////////////////////////////////////////////////////////////////////// // Public types private alias int recls_sint32_t; /// Unsigned 32-bit integer, used for flags public alias uint recls_uint32_t; /// boolean type, used in the recls API functions public typedef int recls_bool_t; /// boolean type, used in the recls class mappings public alias recls_bool_t boolean; version(Windows) { /// Win32 time type public struct recls_time_t { uint dwLowDateTime; uint dwHighDateTime; }; /// Win32 file size type alias ulong recls_filesize_t; } else version(linux) { /// UNIX time type typedef time_t recls_time_t; /// UNIX file size type typedef off_t recls_filesize_t; } /// The recls search handle type. public typedef void *hrecls_t; /// The recls entry handle type. public typedef void *recls_info_t; /// The recls entry process callback function parameter type. */ public typedef void *recls_process_fn_param_t; /// The return code of the recls API public typedef recls_sint32_t recls_rc_t; /// Returns non-zero if the given return code represents a failure condition. public recls_bool_t RECLS_FAILED(recls_rc_t rc) { return cast(recls_bool_t)(rc < 0); } /// Returns non-zero if the given return code represents a success condition. public recls_bool_t RECLS_SUCCEEDED(recls_rc_t rc) { return cast(recls_bool_t)!RECLS_FAILED(rc); } //////////////////////////////////////////////////////////////////////////////// // Values /** General success code */ public const recls_rc_t RECLS_RC_OK = cast(recls_rc_t)(0); /** Return code that indicates that there is no more data available from an otherwise valid search. */ public const recls_rc_t RECLS_RC_NO_MORE_DATA = cast(recls_rc_t)(-1004); /// The flags used to moderate the recls search behaviour public enum RECLS_FLAG { RECLS_F_FILES = 0x00000001 /*!< Include files in search. Included by default if none specified */ , RECLS_F_DIRECTORIES = 0x00000002 /*!< Include directories in search. Not currently supported. */ , RECLS_F_LINKS = 0x00000004 /*!< Include links in search. Ignored in Win32. */ , RECLS_F_DEVICES = 0x00000008 /*!< Include devices in search. Not currently supported. */ , RECLS_F_TYPEMASK = 0x00000FFF , RECLS_F_RECURSIVE = 0x00010000 /*!< Searches given directory and all sub-directories */ , RECLS_F_NO_FOLLOW_LINKS = 0x00020000 /*!< Does not expand links */ , RECLS_F_DIRECTORY_PARTS = 0x00040000 /*!< Fills out the directory parts. Supported from version 1.1.1 onwards. */ , RECLS_F_DETAILS_LATER = 0x00080000 /*!< Does not fill out anything other than the path. Not currently supported. */ }; //////////////////////////////////////////////////////////////////////////////// // Private recls API declarations extern (Windows) { private recls_rc_t Recls_Search( char *searchRoot , char *pattern , recls_uint32_t flags , hrecls_t *phSrch); typedef int (*hrecls_process_fn_t)(recls_info_t info, recls_process_fn_param_t param); private recls_rc_t Recls_SearchProcess( char *searchRoot , char *pattern , recls_uint32_t flags , hrecls_process_fn_t pfn , recls_process_fn_param_t param); private void Recls_SearchClose(in hrecls_t hSrch); private recls_rc_t Recls_GetNext(in hrecls_t hSrch); private recls_rc_t Recls_GetDetails(in hrecls_t hSrch, out recls_info_t pinfo); private recls_rc_t Recls_GetNextDetails(in hrecls_t hSrch, out recls_info_t pinfo); private void Recls_CloseDetails(in recls_info_t fileInfo); private recls_rc_t Recls_CopyDetails(in recls_info_t fileInfo, in recls_info_t *pinfo); private recls_rc_t Recls_OutstandingDetails(in hrecls_t hSrch, out recls_uint32_t count); private recls_rc_t Recls_GetLastError(in hrecls_t hSrch); private int Recls_GetErrorString(in recls_rc_t rc, in char *buffer, in uint cchBuffer); private int Recls_GetLastErrorString(in hrecls_t hSrch, in char *buffer, in uint cchBuffer); private uint Recls_GetPathProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); version(Windows) { private void Recls_GetDriveProperty(in recls_info_t fileInfo, out char chDrive); } private uint Recls_GetDirectoryProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetDirectoryPathProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetFileProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetShortFileProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetFileNameProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetFileExtProperty(in recls_info_t fileInfo, in char *buffer, in uint cchBuffer); private uint Recls_GetDirectoryPartProperty(in recls_info_t fileInfo, in int part, in char *buffer, in uint cchBuffer); private recls_bool_t Recls_IsFileReadOnly(in recls_info_t fileInfo); private recls_bool_t Recls_IsFileDirectory(in recls_info_t fileInfo); private recls_bool_t Recls_IsFileLink(in recls_info_t fileInfo); private void Recls_GetSizeProperty(in recls_info_t fileInfo, recls_filesize_t *size); private recls_time_t Recls_GetCreationTime(in recls_info_t fileInfo); private recls_time_t Recls_GetModificationTime(in recls_info_t fileInfo); private recls_time_t Recls_GetLastAccessTime(in recls_info_t fileInfo); private recls_time_t Recls_GetLastStatusChangeTime(in recls_info_t fileInfo); } //////////////////////////////////////////////////////////////////////////////// // Public functions /// Creates a search /// /// \param searchRoot /// \param pattern /// \param flags /// \param hSrch /// \return /// \retval public recls_rc_t Search_Create(in char[] searchRoot, in char[] pattern, in int flags, out hrecls_t hSrch) { return Recls_Search(toStringz(searchRoot), toStringz(pattern), flags, &hSrch); } /+ private extern(Windows) int process_fn(recls_info_t entry, recls_process_fn_param_t p) { return dg(Entry._make_Entry(entry), p); } public recls_rc_t Search_Process( in char[] searchRoot , in char[] pattern , in int flags , int delegate(in Entry entry, recls_process_fn_param_t param) dg , recls_process_fn_param_t param) { /* extern(Windows) int process_fn(recls_info_t entry, recls_process_fn_param_t p) { return dg(Entry._make_Entry(entry), p); } */ return Recls_SearchProcess(searchRoot, pattern, flags, process_fn, param); } +/ /// Advances the given search to the next position /// /// \param hSrch handle identifying the search /// \return return code indicating status of the operation /// \return RECLS_ public recls_rc_t Search_GetNext(in hrecls_t hSrch) { return Recls_GetNext(hSrch); } /// Closes the given search /// /// \param hSrch handle identifying the search public void Search_Close(inout hrecls_t hSrch) { Recls_SearchClose(hSrch); hSrch = null; } public recls_rc_t Search_GetEntry(in hrecls_t hSrch, out recls_info_t entry) { return Recls_GetDetails(hSrch, entry); } public recls_rc_t Search_GetNextEntry(in hrecls_t hSrch, out recls_info_t entry) { return Recls_GetNextDetails(hSrch, entry); } public void Search_CloseEntry(inout recls_info_t entry) { Recls_CloseDetails(entry); entry = null; } public recls_info_t Search_CopyEntry(in recls_info_t entry) { recls_info_t copy; if(RECLS_FAILED(Recls_CopyDetails(entry, ©))) { copy = null; } return copy; } public recls_rc_t Search_OutstandingDetails(in hrecls_t hSrch, out recls_uint32_t count) { return Recls_OutstandingDetails(hSrch, count); } public recls_rc_t Search_GetLastError(in hrecls_t hSrch) { return Recls_GetLastError(hSrch); } public char[] Search_GetErrorString(in recls_rc_t rc) { uint cch = Recls_GetErrorString(rc, null, 0); char[] err = new char[cch]; cch = Recls_GetErrorString(rc, err, err.length); assert(cch <= err.length); return err; } public char[] Search_GetEntryPath(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetPathProperty(entry, null, 0); char[] path = new char[cch]; cch = Recls_GetPathProperty(entry, path, path.length); assert(cch <= path.length); return path; } version(Windows) { public char Search_GetEntryDrive(in recls_info_t entry) in { assert(null !== entry); } body { char chDrive; return (Recls_GetDriveProperty(entry, chDrive), chDrive); } } public char[] Search_GetEntryDirectory(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetDirectoryProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetDirectoryProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[] Search_GetEntryDirectoryPath(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetDirectoryPathProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetDirectoryPathProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[] Search_GetEntryFile(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetFileProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetFileProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[] Search_GetEntryShortFile(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetShortFileProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetShortFileProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[] Search_GetEntryFileName(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetFileNameProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetFileNameProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[] Search_GetEntryFileExt(in recls_info_t entry) in { assert(null !== entry); } body { uint cch = Recls_GetFileExtProperty(entry, null, 0); char[] str = new char[cch]; cch = Recls_GetFileExtProperty(entry, str, str.length); assert(cch <= str.length); return str; } public char[][] Search_GetEntryDirectoryParts(in recls_info_t entry) in { assert(null !== entry); } body { uint cParts = Recls_GetDirectoryPartProperty(entry, -1, null, 0); char[][] parts = new char[][cParts]; for(int i = 0; i < cParts; ++i) { uint cch = Recls_GetDirectoryPartProperty(entry, i, null, 0); char[] str = new char[cch]; cch = Recls_GetDirectoryPartProperty(entry, i, str, str.length); assert(cch <= str.length); parts[i] = str; } return parts; } public boolean Search_IsEntryReadOnly(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_IsFileReadOnly(entry); } public boolean Search_IsEntryDirectory(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_IsFileDirectory(entry); } public boolean Search_IsEntryLink(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_IsFileLink(entry); } public recls_filesize_t Search_GetEntrySize(in recls_info_t entry) in { assert(null !== entry); } body { recls_filesize_t size; return (Recls_GetSizeProperty(entry, &size), size); } public recls_time_t Search_GetEntryCreationTime(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_GetCreationTime(entry); } public recls_time_t Search_GetEntryModificationTime(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_GetModificationTime(entry); } public recls_time_t Search_GetEntryLastAccessTime(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_GetLastAccessTime(entry); } public recls_time_t Search_GetEntryLastStatusChangeTime(in recls_info_t entry) in { assert(null !== entry); } body { return Recls_GetLastStatusChangeTime(entry); } //////////////////////////////////////////////////////////////////////////////// // Classes /// Represents a search public class Search { /// \name Construction public: /// Create a search object with the given searchRoot, pattern and flags /// /// \param searchRoot The root directory of the search. If null, or the empty string, the current directory is assumed /// \param pattern The search pattern. If null, or the empty string, all entries are returned /// \param flags The flags with moderate the search this(in char[] searchRoot, in char[] pattern, in uint flags) { m_searchRoot = searchRoot; m_pattern = pattern; m_flags = flags; } /// \name Types public: class Enumerator { private: this(hrecls_t hSrch, recls_rc_t lastError) { m_hSrch = hSrch; m_lastError = lastError; } public: ~this() { if(null != m_hSrch) { Search_Close(m_hSrch); } } public: boolean HasEntry() { return RECLS_SUCCEEDED(m_lastError); } Entry CurrentEntry() in { assert(null !== m_hSrch); } body { recls_info_t entry; recls_rc_t rc = Search_GetEntry(m_hSrch, entry); m_lastError = rc; try { return Entry._make_Entry(entry); } finally { Search_CloseEntry(entry); } } recls_rc_t LastError() { return m_lastError; } public: boolean GetNextEntry() in { assert(null != m_hSrch); } body { recls_rc_t rc = Search_GetNext(m_hSrch); m_lastError = rc; if(RECLS_FAILED(rc)) { Search_Close(m_hSrch); if(RECLS_RC_NO_MORE_DATA != rc) { // throw new ReclsException("Search continuation failed", rc); } } return RECLS_SUCCEEDED(rc); } /// Members private: hrecls_t m_hSrch; // NOTE THAT D DOES STRONG TYPEDEFS (see true-typedefs) recls_rc_t m_lastError; } /// Operations public: Enumerator Enumerate() { hrecls_t hSrch; recls_rc_t rc = Search_Create(m_searchRoot, m_pattern, m_flags, hSrch); try { return new Enumerator(hSrch, rc); } catch(Exception x) { Search_Close(hSrch); throw x; } } public: int opApply(int delegate(inout Entry entry) dg) { int result = 0; hrecls_t hSrch; recls_rc_t rc = Search_Create(m_searchRoot, m_pattern, m_flags, hSrch); recls_info_t entry; do { if(RECLS_FAILED(rc)) { if(RECLS_RC_NO_MORE_DATA != rc) { // throw new ReclsException("Search continuation failed", rc); } result = 1; } else { rc = Search_GetEntry(hSrch, entry); if(RECLS_FAILED(rc)) { if(RECLS_RC_NO_MORE_DATA != rc) { // throw new ReclsException("Search continuation failed", rc); } result = 1; } else { try { Entry e = Entry._make_Entry(entry); result = dg(e); } finally { Search_CloseEntry(entry); } } rc = Search_GetNextEntry(hSrch, entry); } } while(result == 0); return result; } /// Members private: char[] m_searchRoot; char[] m_pattern; uint m_flags; } /* public class Boolean { public: this(boolean value) { m_value = value; } op() { return m_value != 0; } private: boolean m_value; } */ /// Represents a search entry public class Entry { invariant { if(null != m_entry) { // Now do all the checks to verify that the various components of the path are valid // Since we cannot call member functions (as that would end up in recursion) // the only thing we can do is to "test" the validity of the entry handle, so // we just add a reference, and then release it recls_info_t entry = Search_CopyEntry(m_entry); assert(null !== entry); Recls_CloseDetails(entry); } } private: /// This is necessary, because DMD 0.73 generates code /// that goes into an infinite loop when creating an /// Entry instance with a non-null entry static Entry _make_Entry(recls_info_t entry) { recls_info_t copy = Search_CopyEntry(entry); Entry e = null; try { e = new Entry(null); e.m_entry = entry; } catch(Exception x) { Search_CloseEntry(copy); throw x; } return e; } this(recls_info_t entry) { m_entry = entry; } ~this() { if(null !== m_entry) { Search_CloseEntry(m_entry); } } public: /// The full path of the entry /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "/usr/include/recls/recls_assert.h" char[] GetPath() in { assert(null !== m_entry); } body { return Search_GetEntryPath(m_entry); } /// The full path of the entry /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "/usr/include/recls/recls_assert.h" char[] Path() in { assert(null !== m_entry); } body { return Search_GetEntryPath(m_entry); } version(Windows) { /// The drive component of the entry's path /// /// \note For "H:\Dev\include\recls\recls_assert.h" this would yield 'H' char Drive() in { assert(null !== m_entry); } body { return Search_GetEntryDrive(m_entry); } } // version(Windows) /// The directory component of the entry's path /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "/usr/include/recls/" char[] Directory() in { assert(null !== m_entry); } body { return Search_GetEntryDirectory(m_entry); } /// The full location component of the entry's path. /// /// \note This is everything before the filename+fileext. On Win32 systems for "H:\Dev\include\recls\recls_assert.h" this would yield "H:\Dev\include\recls\" char[] DirectoryPath() in { assert(null !== m_entry); } body { return Search_GetEntryDirectoryPath(m_entry); } /// An array of strings representing the parts of the Directory property /// /// \note For "/usr/include/recls/recls_assert.h" this would yield [ "/", "usr/", "include/", "recls/"] char[][] DirectoryParts() in { assert(null !== m_entry); } body { return Search_GetEntryDirectoryParts(m_entry); } /// The file component of the entry's path /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "recls_assert.h" char[] File() in { assert(null !== m_entry); } body { return Search_GetEntryFile(m_entry); } /// The short equivalent of the entry's File property /// /// \note On Win32 systems, this is the 8.3 form, e.g. "recls_~1.h". On other systems this is identical to the File property char[] ShortFile() in { assert(null !== m_entry); } body { return Search_GetEntryShortFile(m_entry); } /// The file name component of the entry's path /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "recls_assert" char[] FileName() in { assert(null !== m_entry); } body { return Search_GetEntryFileName(m_entry); } /// The file extension component of the entry's path /// /// \note For "/usr/include/recls/recls_assert.h" this would yield "h" char[] FileExt() in { assert(null !== m_entry); } body { return Search_GetEntryFileExt(m_entry); } /// The time the entry was created recls_time_t CreationTime() in { assert(null !== m_entry); } body { return Search_GetEntryCreationTime(m_entry); } /// The time the entry was last modified recls_time_t ModificationTime() in { assert(null !== m_entry); } body { return Search_GetEntryModificationTime(m_entry); } /// The time the entry was last accessed recls_time_t LastAccessTime() in { assert(null !== m_entry); } body { return Search_GetEntryLastAccessTime(m_entry); } /// The time the entry's last status changed recls_time_t LastStatusChangeTime() in { assert(null !== m_entry); } body { return Search_GetEntryLastStatusChangeTime(m_entry); } /// The size of the entry recls_filesize_t Size() in { assert(null !== m_entry); } body { return Search_GetEntrySize(m_entry); } /// Indicates whether the entry is read-only boolean IsReadOnly() in { assert(null !== m_entry); } body { return Search_IsEntryReadOnly(m_entry); } /// Indicates whether the entry is a directory boolean IsDirectory() in { assert(null !== m_entry); } body { return Search_IsEntryDirectory(m_entry); } /// Indicates whether the entry is a link boolean IsLink() in { assert(null !== m_entry); } body { return Search_IsEntryLink(m_entry); } /// Members private: recls_info_t m_entry; } //////////////////////////////////////////////////////////////////////////////// unittest { Search search = new Search(".", "*.*", RECLS_FLAG.RECLS_F_RECURSIVE); foreach(Entry entry; search) { entry.Path(); version(Windows) { entry.Drive(); } // version(Windows) entry.Directory(); entry.DirectoryPath(); entry.DirectoryParts(); entry.File(); entry.ShortFile(); entry.FileName(); entry.FileExt(); entry.CreationTime(); entry.ModificationTime(); entry.LastAccessTime(); entry.LastStatusChangeTime(); entry.Size(); entry.IsReadOnly(); entry.IsDirectory(); entry.IsLink(); } } ////////////////////////////////////////////////////////////////////////////////