/* * Copyright (c) 2018-2020 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 #include "fusee_ini.hpp" #include "fusee_malloc.hpp" #include "fs/fusee_fs_api.hpp" namespace ams::nxboot { namespace { constexpr s64 IniFileSizeMax = 64_KB; constexpr bool IsWhiteSpace(char c) { return c == ' ' || c == '\r'; } } ParseIniResult ParseIniFile(IniSectionList &out_sections, const char *ini_path) { /* Open the ini file. */ fs::FileHandle file; if (R_FAILED(fs::OpenFile(std::addressof(file), ini_path, fs::OpenMode_Read))) { return ParseIniResult_NoFile; } ON_SCOPE_EXIT { fs::CloseFile(file); }; /* Get file size. */ s64 file_size; if (R_FAILED(fs::GetFileSize(std::addressof(file_size), file))) { return ParseIniResult_NoFile; } /* Cap file size. */ file_size = std::min(IniFileSizeMax, file_size); /* Allocate memory for file. */ char *buffer = static_cast(AllocateAligned(util::AlignUp(file_size + 1, 0x10), 0x10)); buffer[file_size] = '\x00'; /* Read file. */ if (R_FAILED(fs::ReadFile(file, 0, buffer, file_size))) { return ParseIniResult_NoFile; } /* Parse the file. */ enum class State { Newline, Comment, SectionName, Key, KvSpace, KvSpace2, Value, TrailingSpace, }; char *sec_start, *key_start, *val_start, *val_end; IniSection *cur_sec = nullptr; State state = State::Newline; for (int i = 0; i < file_size; ++i) { const char c = buffer[i]; switch (state) { case State::Newline: if (c == '[') { sec_start = buffer + i + 1; state = State::SectionName; } else if (c == ';' || c == '#') { state = State::Comment; } else if (IsWhiteSpace(c) || c == '\n') { state = State::Newline; } else if (cur_sec != nullptr) { key_start = buffer + i; state = State::Key; } else { return ParseIniResult_InvalidFormat; } break; case State::Comment: if (c == '\n') { state = State::Newline; } break; case State::SectionName: if (c == '\n') { return ParseIniResult_InvalidFormat; } else if (c == ']') { cur_sec = AllocateObject(); cur_sec->name = sec_start; buffer[i] = '\x00'; out_sections.push_back(*cur_sec); state = State::TrailingSpace; } break; case State::Key: if (c == '\n') { return ParseIniResult_InvalidFormat; } else if (IsWhiteSpace(c)) { buffer[i] = '\x00'; state = State::KvSpace; } else if (c == '=') { buffer[i] = '\x00'; state = State::KvSpace2; } break; case State::KvSpace: if (c == '=') { state = State::KvSpace2; } else if (!IsWhiteSpace(c)) { return ParseIniResult_InvalidFormat; } break; case State::KvSpace2: if (c == '\n') { buffer[i] = '\x00'; auto *entry = AllocateObject(); entry->key = key_start; entry->value = buffer + i; cur_sec->kv_list.push_back(*entry); state = State::Newline; } else if (!IsWhiteSpace(c)) { val_start = buffer + i; val_end = buffer + i + 1; state = State::Value; } break; case State::Value: if (c == '\r' || c == '\n') { buffer[i] = '\x00'; *val_end = '\x00'; auto *entry = AllocateObject(); entry->key = key_start; entry->value = val_start; cur_sec->kv_list.push_back(*entry); state = (c == '\n') ? State::Newline : State::TrailingSpace; } else if (c != ' ') { val_end = buffer + i + 1; } break; case State::TrailingSpace: if (c == '\n') { state = State::Newline; } else if (!IsWhiteSpace(c)) { return ParseIniResult_InvalidFormat; } break; } } /* Accept value-state. */ if (state == State::Value) { auto *entry = AllocateObject(); entry->key = key_start; entry->value = val_start; cur_sec->kv_list.push_back(*entry); return ParseIniResult_Success; } else if (state == State::TrailingSpace || state == State::Comment || state == State::Newline) { return ParseIniResult_Success; } else { return ParseIniResult_InvalidFormat; } } }