misc/libphysfs/lzma/CPP/7zip/UI/Common/EnumDirItems.cpp
author nemo
Mon, 10 Apr 2017 12:06:43 -0400
changeset 12218 bb5522e88ab2
permissions -rw-r--r--
bulk copy of latest physfs to our misc/libphysfs since this seems to fix an off-by-1 error reliably hit in readln read of 1 byte probably introduced in the addition of the buffered read. Whether this is excessive or whether libphysfs should even be maintained by us is another matter. But at least we shouldn't crash

// EnumDirItems.cpp

#include "StdAfx.h"

#include "Common/StringConvert.h"
#include "Common/Wildcard.h"
#include "Common/MyCom.h"

#include "EnumDirItems.h"

using namespace NWindows;
using namespace NFile;
using namespace NName;

void AddDirFileInfo(
    const UString &prefix,        // prefix for logical path
    const UString &fullPathName,  // path on disk: can be relative to some basePrefix
    const NFind::CFileInfoW &fileInfo, 
    CObjectVector<CDirItem> &dirItems)
{
  CDirItem item;
  item.Attributes = fileInfo.Attributes;
  item.Size = fileInfo.Size;
  item.CreationTime = fileInfo.CreationTime;
  item.LastAccessTime = fileInfo.LastAccessTime;
  item.LastWriteTime = fileInfo.LastWriteTime;
  item.Name = prefix + fileInfo.Name;
  item.FullPath = fullPathName;
  dirItems.Add(item);
}

static void EnumerateDirectory(
    const UString &baseFolderPrefix,  // base (disk) prefix for scanning  
    const UString &directory,         // additional disk prefix starting from baseFolderPrefix
    const UString &prefix,            // logical prefix
    CObjectVector<CDirItem> &dirItems,
    UStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  NFind::CEnumeratorW enumerator(baseFolderPrefix + directory + wchar_t(kAnyStringWildcard));
  for (;;)
  { 
    NFind::CFileInfoW fileInfo;
    bool found;
    if (!enumerator.Next(fileInfo, found))
    {
      errorCodes.Add(::GetLastError());
      errorPaths.Add(baseFolderPrefix + directory);
      return;
    }
    if (!found)
      break;
    AddDirFileInfo(prefix, directory + fileInfo.Name, fileInfo, dirItems);
    if (fileInfo.IsDirectory())
    {
      EnumerateDirectory(baseFolderPrefix, directory + fileInfo.Name + wchar_t(kDirDelimiter), 
          prefix + fileInfo.Name + wchar_t(kDirDelimiter), dirItems, errorPaths, errorCodes);
    }
  }
}

void EnumerateDirItems(
    const UString &baseFolderPrefix,   // base (disk) prefix for scanning  
    const UStringVector &fileNames,    // names relative to baseFolderPrefix
    const UString &archiveNamePrefix, 
    CObjectVector<CDirItem> &dirItems,
    UStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  for(int i = 0; i < fileNames.Size(); i++)
  {
    const UString &fileName = fileNames[i];
    NFind::CFileInfoW fileInfo;
    if (!NFind::FindFile(baseFolderPrefix + fileName, fileInfo))
    {
      errorCodes.Add(::GetLastError());
      errorPaths.Add(baseFolderPrefix + fileName);
      continue;
    }
    AddDirFileInfo(archiveNamePrefix, fileName, fileInfo, dirItems);
    if (fileInfo.IsDirectory())
    {
      EnumerateDirectory(baseFolderPrefix, fileName + wchar_t(kDirDelimiter), 
          archiveNamePrefix + fileInfo.Name +  wchar_t(kDirDelimiter), 
          dirItems, errorPaths, errorCodes);
    }
  }
}

static HRESULT EnumerateDirItems(
    const NWildcard::CCensorNode &curNode, 
    const UString &diskPrefix,        // full disk path prefix 
    const UString &archivePrefix,     // prefix from root
    const UStringVector &addArchivePrefix,  // prefix from curNode
    CObjectVector<CDirItem> &dirItems, 
    bool enterToSubFolders,
    IEnumDirItemCallback *callback,
    UStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  if (!enterToSubFolders)
    if (curNode.NeedCheckSubDirs())
      enterToSubFolders = true;
  if (callback)
    RINOK(callback->CheckBreak());

  // try direct_names case at first
  if (addArchivePrefix.IsEmpty() && !enterToSubFolders)
  {
    // check that all names are direct
    int i;
    for (i = 0; i < curNode.IncludeItems.Size(); i++)
    {
      const NWildcard::CItem &item = curNode.IncludeItems[i];
      if (item.Recursive || item.PathParts.Size() != 1)
        break;
      const UString &name = item.PathParts.Front();
      if (name.IsEmpty() || DoesNameContainWildCard(name))
        break;
    }
    if (i == curNode.IncludeItems.Size())
    {
      // all names are direct (no wildcards)
      // so we don't need file_system's dir enumerator
      CRecordVector<bool> needEnterVector;
      for (i = 0; i < curNode.IncludeItems.Size(); i++)
      {
        const NWildcard::CItem &item = curNode.IncludeItems[i];
        const UString &name = item.PathParts.Front();
        const UString fullPath = diskPrefix + name;
        NFind::CFileInfoW fileInfo;
        if (!NFind::FindFile(fullPath, fileInfo))
        {
          errorCodes.Add(::GetLastError());
          errorPaths.Add(fullPath);
          continue;
        }
        bool isDir = fileInfo.IsDirectory();
        if (isDir && !item.ForDir || !isDir && !item.ForFile)
        {
          errorCodes.Add((DWORD)E_FAIL);
          errorPaths.Add(fullPath);
          continue;
        }
        const UString realName = fileInfo.Name;
        const UString realDiskPath = diskPrefix + realName;
        {
          UStringVector pathParts;
          pathParts.Add(fileInfo.Name);
          if (curNode.CheckPathToRoot(false, pathParts, !isDir))
            continue;
        }
        AddDirFileInfo(archivePrefix, realDiskPath, fileInfo, dirItems);
        if (!isDir)
          continue;
        
        UStringVector addArchivePrefixNew;
        const NWildcard::CCensorNode *nextNode = 0;
        int index = curNode.FindSubNode(name);
        if (index >= 0)
        {
          for (int t = needEnterVector.Size(); t <= index; t++)
            needEnterVector.Add(true);
          needEnterVector[index] = false;
          nextNode = &curNode.SubNodes[index];
        }
        else
        {
          nextNode = &curNode;
          addArchivePrefixNew.Add(name); // don't change it to realName. It's for shortnames support
        }
        RINOK(EnumerateDirItems(*nextNode,   
            realDiskPath + wchar_t(kDirDelimiter), 
            archivePrefix + realName + wchar_t(kDirDelimiter), 
            addArchivePrefixNew, dirItems, true, callback, errorPaths, errorCodes));
      }
      for (i = 0; i < curNode.SubNodes.Size(); i++)
      {
        if (i < needEnterVector.Size())
          if (!needEnterVector[i])
            continue;
        const NWildcard::CCensorNode &nextNode = curNode.SubNodes[i];
        const UString fullPath = diskPrefix + nextNode.Name;
        NFind::CFileInfoW fileInfo;
        if (!NFind::FindFile(fullPath, fileInfo))
        {
          if (!nextNode.AreThereIncludeItems())
            continue;
          errorCodes.Add(::GetLastError());
          errorPaths.Add(fullPath);
          continue;
        }
        if (!fileInfo.IsDirectory())
        {
          errorCodes.Add((DWORD)E_FAIL);
          errorPaths.Add(fullPath);
          continue;
        }
        RINOK(EnumerateDirItems(nextNode, 
            diskPrefix + fileInfo.Name + wchar_t(kDirDelimiter), 
            archivePrefix + fileInfo.Name + wchar_t(kDirDelimiter), 
            UStringVector(), dirItems, false, callback, errorPaths, errorCodes));
      }
      return S_OK;
    }
  }


  NFind::CEnumeratorW enumerator(diskPrefix + wchar_t(kAnyStringWildcard));
  for (;;)
  {
    NFind::CFileInfoW fileInfo;
    bool found;
    if (!enumerator.Next(fileInfo, found))
    {
      errorCodes.Add(::GetLastError());
      errorPaths.Add(diskPrefix);
      break;
    }
    if (!found)
      break;

    if (callback)
      RINOK(callback->CheckBreak());
    const UString &name = fileInfo.Name;
    bool enterToSubFolders2 = enterToSubFolders;
    UStringVector addArchivePrefixNew = addArchivePrefix;
    addArchivePrefixNew.Add(name);
    {
      UStringVector addArchivePrefixNewTemp(addArchivePrefixNew);
      if (curNode.CheckPathToRoot(false, addArchivePrefixNewTemp, !fileInfo.IsDirectory()))
        continue;
    }
    if (curNode.CheckPathToRoot(true, addArchivePrefixNew, !fileInfo.IsDirectory()))
    {
      AddDirFileInfo(archivePrefix, diskPrefix + name, fileInfo, dirItems);
      if (fileInfo.IsDirectory())
        enterToSubFolders2 = true;
    }
    if (!fileInfo.IsDirectory())
      continue;

    const NWildcard::CCensorNode *nextNode = 0;
    if (addArchivePrefix.IsEmpty())
    {
      int index = curNode.FindSubNode(name);
      if (index >= 0)
        nextNode = &curNode.SubNodes[index];
    }
    if (!enterToSubFolders2 && nextNode == 0)
      continue;

    addArchivePrefixNew = addArchivePrefix;
    if (nextNode == 0)
    {
      nextNode = &curNode;
      addArchivePrefixNew.Add(name);
    }
    RINOK(EnumerateDirItems(*nextNode,   
        diskPrefix + name + wchar_t(kDirDelimiter), 
        archivePrefix + name + wchar_t(kDirDelimiter), 
        addArchivePrefixNew, dirItems, enterToSubFolders2, callback, errorPaths, errorCodes));
  }
  return S_OK;
}

HRESULT EnumerateItems(
    const NWildcard::CCensor &censor, 
    CObjectVector<CDirItem> &dirItems, 
    IEnumDirItemCallback *callback,
    UStringVector &errorPaths,
    CRecordVector<DWORD> &errorCodes)
{
  for (int i = 0; i < censor.Pairs.Size(); i++)
  {
    const NWildcard::CPair &pair = censor.Pairs[i];
    RINOK(EnumerateDirItems(pair.Head, pair.Prefix, L"", UStringVector(), dirItems, false, 
        callback, errorPaths, errorCodes));
  }
  return S_OK;
}