/* * Copyright (c) Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include namespace ams::fs::RomPathTool { Result PathParser::Initialize(const RomPathChar *path) { AMS_ASSERT(path != nullptr); /* Require paths start with a separator, and skip repeated separators. */ R_UNLESS(RomPathTool::IsSeparator(path[0]), fs::ResultDbmInvalidPathFormat()); while (RomPathTool::IsSeparator(path[1])) { ++path; } m_prev_path_start = path; m_prev_path_end = path; m_next_path = path + 1; R_SUCCEED(); } void PathParser::Finalize() { m_prev_path_start = nullptr; m_prev_path_end = nullptr; m_next_path = nullptr; m_finished = false; } bool PathParser::IsParseFinished() const { return m_finished; } bool PathParser::IsDirectoryPath() const { AMS_ASSERT(m_next_path != nullptr); if (RomPathTool::IsNullTerminator(m_next_path[0]) && RomPathTool::IsSeparator(m_next_path[-1])) { return true; } if (RomPathTool::IsCurrentDirectory(m_next_path)) { return true; } return RomPathTool::IsParentDirectory(m_next_path); } Result PathParser::GetNextDirectoryName(RomEntryName *out) { AMS_ASSERT(m_prev_path_start != nullptr); AMS_ASSERT(m_prev_path_end != nullptr); AMS_ASSERT(m_next_path != nullptr); AMS_ASSERT(out != nullptr); /* Get as directory name. */ R_TRY(this->GetAsDirectoryName(out)); /* Parse the next path. */ const RomPathChar *cur = m_next_path; size_t name_len; for (name_len = 0; !RomPathTool::IsSeparator(cur[name_len]); ++name_len) { if (RomPathTool::IsNullTerminator(cur[name_len])) { m_finished = true; m_prev_path_start = m_next_path; m_next_path = cur + name_len; m_prev_path_end = cur + name_len; R_SUCCEED(); } } /* Advance past separators. */ m_prev_path_start = m_next_path; m_prev_path_end = cur + name_len; for (m_next_path = m_prev_path_end + 1; RomPathTool::IsSeparator(m_next_path[0]); ++m_next_path) { /* ... */ } /* Check if we're finished. */ if (RomPathTool::IsNullTerminator(m_next_path[0])) { m_finished = true; } R_SUCCEED(); } Result PathParser::GetAsDirectoryName(RomEntryName *out) const { AMS_ASSERT(out != nullptr); AMS_ASSERT(m_prev_path_start != nullptr); AMS_ASSERT(m_prev_path_end != nullptr); AMS_ASSERT(m_next_path != nullptr); AMS_ASSERT(m_prev_path_start <= m_prev_path_end); const size_t len = m_prev_path_end - m_prev_path_start; R_UNLESS(len <= MaxPathLength, fs::ResultDbmDirectoryNameTooLong()); out->Initialize(m_prev_path_start, len); R_SUCCEED(); } Result PathParser::GetAsFileName(RomEntryName *out) const { AMS_ASSERT(out != nullptr); AMS_ASSERT(m_prev_path_start != nullptr); AMS_ASSERT(m_prev_path_end != nullptr); AMS_ASSERT(m_next_path != nullptr); AMS_ASSERT(m_prev_path_start <= m_prev_path_end); const size_t len = m_prev_path_end - m_prev_path_start; R_UNLESS(len <= MaxPathLength, fs::ResultDbmFileNameTooLong()); out->Initialize(m_prev_path_start, len); R_SUCCEED(); } Result GetParentDirectoryName(RomEntryName *out, const RomEntryName &cur, const RomPathChar *p) { AMS_ASSERT(out != nullptr); AMS_ASSERT(p != nullptr); const RomPathChar *start = cur.begin(); const RomPathChar *end = cur.end() - 1; s32 depth = 1; if (cur.IsParentDirectory()) { ++depth; } if (start > p) { size_t len = 0; for (const RomPathChar *head = start - 1; head >= p; --head) { if (RomPathTool::IsSeparator(*head)) { if (IsCurrentDirectory(head + 1, len)) { ++depth; } if (IsParentDirectory(head + 1, len)) { depth += 2; } if (depth == 0) { start = head + 1; break; } do { --head; } while (head > p && RomPathTool::IsSeparator(*head)); end = head; len = 0; --depth; } ++len; } R_UNLESS(depth == 0, fs::ResultDirectoryUnobtainable()); } if (end <= p) { out->Initialize(p, 0); } else { out->Initialize(start, end - start + 1); } R_SUCCEED(); } }