diff --git a/apps/apps_container.cpp b/apps/apps_container.cpp index 1eee9ce075e..c9a38c89502 100644 --- a/apps/apps_container.cpp +++ b/apps/apps_container.cpp @@ -92,9 +92,14 @@ App::Snapshot * AppsContainer::usbConnectedAppSnapshot() { return &m_usbConnectedSnapshot; } -void AppsContainer::reset() { - // Empty storage (delete functions, variables, python scripts) - Ion::Storage::sharedStorage()->destroyAllRecords(); +void AppsContainer::reset(bool storage) { + if (storage) { + // Empty storage (delete functions, variables, python scripts) + Ion::Storage::sharedStorage()->destroyAllRecords(); + } else { + // Activate Quarantine + Ion::Storage::sharedStorage()->activateQuarantine(); + } // Empty clipboard Clipboard::sharedClipboard()->reset(); for (int i = 0; i < numberOfApps(); i++) { @@ -411,7 +416,7 @@ void AppsContainer::redrawWindow(bool force) { void AppsContainer::activateExamMode(GlobalPreferences::ExamMode examMode) { assert(examMode != GlobalPreferences::ExamMode::Off && examMode != GlobalPreferences::ExamMode::Unknown); - reset(); + reset(examMode != GlobalPreferences::ExamMode::Standard); Ion::LED::setColor(KDColorRed); /* The Dutch exam mode LED is supposed to be orange but we can only make * blink "pure" colors: with RGB leds on or off (as the PWM is used for diff --git a/apps/apps_container.h b/apps/apps_container.h index f52d50b6c50..9f020777dc9 100644 --- a/apps/apps_container.h +++ b/apps/apps_container.h @@ -32,7 +32,7 @@ class AppsContainer : public Container, ExamPopUpControllerDelegate, Ion::Storag App::Snapshot * hardwareTestAppSnapshot(); App::Snapshot * onBoardingAppSnapshot(); App::Snapshot * usbConnectedAppSnapshot(); - void reset(); + void reset(bool storage = true); Poincare::Context * globalContext(); MathToolbox * mathToolbox(); MathVariableBoxController * variableBoxController(); diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index 75a998565ff..f72084b585e 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -17,6 +17,7 @@ ExamPopUpController::ExamPopUpController(ExamPopUpControllerDelegate * delegate) GlobalPreferences::sharedGlobalPreferences()->setExamMode(mode); AppsContainer * container = AppsContainer::sharedAppsContainer(); if (mode == GlobalPreferences::ExamMode::Off) { + Ion::Storage::sharedStorage()->deactivateQuarantine(); Ion::LED::setColor(KDColorBlack); Ion::LED::updateColorWithPlugAndCharge(); } else { diff --git a/apps/settings/base.fr.i18n b/apps/settings/base.fr.i18n index b44969e73b3..70537539d9c 100644 --- a/apps/settings/base.fr.i18n +++ b/apps/settings/base.fr.i18n @@ -74,7 +74,7 @@ USBExplanation1= "La protection USB protège votre" USBExplanation2= "calculatrice contre un verrouillage" USBExplanation3= "non-intentionnel" USBProtection= "Protection USB" -USBProtectionLevel = "Mise à jour acceptées" +USBProtectionLevel = "Mises à jour acceptées" USBDefaultLevel = "Basées sur Upsilon" USBLowLevel = "Basées sur Omega" USBParanoidLevel = "Aucune" diff --git a/apps/settings/sub_menu/exam_mode_controller.cpp b/apps/settings/sub_menu/exam_mode_controller.cpp index 33a853eaab1..2c1df1a7ec5 100644 --- a/apps/settings/sub_menu/exam_mode_controller.cpp +++ b/apps/settings/sub_menu/exam_mode_controller.cpp @@ -7,6 +7,7 @@ #include #include #include +#include using namespace Poincare; using namespace Shared; @@ -112,7 +113,11 @@ GlobalPreferences::ExamMode ExamModeController::examMode() { GlobalPreferences::ExamMode mode = GlobalPreferences::sharedGlobalPreferences()->tempExamMode(); if (GlobalPreferences::sharedGlobalPreferences()->isInExamMode()) { // If the exam mode is already on, this re-activate the same exam mode + // #if ION_SIMULATOR_FILES + // mode = GlobalPreferences::ExamMode::Off; + // #else mode = GlobalPreferences::sharedGlobalPreferences()->examMode(); + // #endif } return mode; } diff --git a/apps/shared/function.h b/apps/shared/function.h index a5daa9c5917..1dcea372d3c 100644 --- a/apps/shared/function.h +++ b/apps/shared/function.h @@ -78,6 +78,7 @@ class Function : public ExpressionModelHandle { } bool isActive() const { return m_active; } void setActive(bool active) { m_active = active; } + void setGraphColor(KDColor color) { m_color = color; } private: #if __EMSCRIPTEN__ /* For emscripten memory representation, loads and stores must be aligned; diff --git a/apps/shared/sequence.cpp b/apps/shared/sequence.cpp index 20ffb0ec62c..c2e963f5cc5 100644 --- a/apps/shared/sequence.cpp +++ b/apps/shared/sequence.cpp @@ -14,6 +14,7 @@ #include #include #include +#include using namespace Poincare; @@ -315,7 +316,14 @@ void Sequence::rangeForDisplay(float * xMin, float * xMax, float * yMin, float * Sequence::RecordDataBuffer * Sequence::recordData() const { assert(!isNull()); Ion::Storage::Record::Data d = value(); - return reinterpret_cast(const_cast(d.buffer)); + Sequence::RecordDataBuffer * buffer = reinterpret_cast(const_cast(d.buffer)); + for (int i = 0; i < Shared::MaxNumberOfSequences; i++) { + if (Ion::Storage::strstr(fullName(), SequenceStore::k_sequenceNames[i])) { + buffer->setGraphColor(Palette::DataColor[i]); + break; + } + } + return buffer; } /* Sequence Model */ diff --git a/ion/include/ion/storage.h b/ion/include/ion/storage.h index 14d70900463..2e34a820716 100644 --- a/ion/include/ion/storage.h +++ b/ion/include/ion/storage.h @@ -3,7 +3,6 @@ #include #include - namespace Ion { /* Storage : | Magic | Record1 | Record2 | ... | Magic | @@ -17,7 +16,9 @@ class Storage { public: typedef uint16_t record_size_t; - constexpr static size_t k_storageSize = 64000; + constexpr static size_t k_storageSize = 63900; + constexpr static size_t k_fullNameMaxSize = 220 + 5; + constexpr static size_t k_extensionMaxSize = 10; static_assert(UINT16_MAX >= k_storageSize, "record_size_t not big enough"); static Storage * sharedStorage(); @@ -27,6 +28,22 @@ class Storage { static constexpr char expExtension[] = "exp"; static constexpr char funcExtension[] = "func"; static constexpr char seqExtension[] = "seq"; + static constexpr char examPrefix[] = "exam"; + + static constexpr uint8_t eqMaxRecords = 6; + static constexpr uint8_t expMaxRecords = -1; + static constexpr uint8_t funcMaxRecords = -1; + static constexpr uint8_t seqMaxRecords = 3; + + static constexpr uint8_t noMaxRecords = -1; + + + /* TODO + * Replace k_sequenceNames from sequence_store.h with this one + * */ + static constexpr const char * k_sequenceNames[seqMaxRecords] = { + "u", "v", "w" + }; class Record { /* A Record is identified by the CRC32 on its fullName because: @@ -64,13 +81,13 @@ class Storage { return m_fullNameCRC32 == 0; } const char * fullName() const { - return Storage::sharedStorage()->fullNameOfRecord(*this); + return Storage::sharedStorage()->fullNameOfRecord(*this, false); } ErrorStatus setBaseNameWithExtension(const char * baseName, const char * extension) { - return Storage::sharedStorage()->setBaseNameWithExtensionOfRecord(*this, baseName, extension); + return Storage::sharedStorage()->setBaseNameWithExtensionOfRecord(*this, baseName, extension, false); } ErrorStatus setName(const char * fullName) { - return Storage::sharedStorage()->setFullNameOfRecord(*this, fullName); + return Storage::sharedStorage()->setFullNameOfRecord(*this, fullName, false); } Data value() const { return Storage::sharedStorage()->valueOfRecord(*this); @@ -88,6 +105,7 @@ class Storage { #if ION_STORAGE_LOG void log(); + void logMessage(const char * message); #endif size_t availableSize(); @@ -100,7 +118,7 @@ class Storage { void notifyChangeToDelegate(const Record r = Record()) const; Record::ErrorStatus notifyFullnessToDelegate() const; - int numberOfRecordsWithExtension(const char * extension); + int numberOfRecordsWithExtension(const char * extension, bool system = false); static bool FullNameHasExtension(const char * fullName, const char * extension, size_t extensionLength); // Record creation @@ -120,11 +138,19 @@ class Storage { void destroyRecordWithBaseNameAndExtension(const char * baseName, const char * extension); void destroyRecordsWithExtension(const char * extension); + // Exam Mode + void activateQuarantine(); + void deactivateQuarantine(); + void setFullNameBufferWithPrefix(const char * prefix, const char * name); + static bool fullNameAuthorized(const char * fullname); + void deleteRecordByExtensionIfNeeded(const char * extension); + // Useful - static bool FullNameCompliant(const char * name); - + static bool FullNameCompliant(const char * name, bool withoutExtension = false); + static bool strstr(const char * first, const char * second); + // User by Python OS module - int numberOfRecords(); + int numberOfRecords(bool system = false); Record recordAtIndex(int index); private: @@ -134,9 +160,9 @@ class Storage { Storage(); /* Getters/Setters on recordID */ - const char * fullNameOfRecord(const Record record); - Record::ErrorStatus setFullNameOfRecord(const Record record, const char * fullName); - Record::ErrorStatus setBaseNameWithExtensionOfRecord(const Record record, const char * baseName, const char * extension); + const char * fullNameOfRecord(const Record record, bool system = false); + Record::ErrorStatus setFullNameOfRecord(const Record record, const char *fullName, bool system = false); + Record::ErrorStatus setBaseNameWithExtensionOfRecord(const Record record, const char *baseName, const char *extension, bool system = false); Record::Data valueOfRecord(const Record record); Record::ErrorStatus setValueOfRecord(const Record record, Record::Data data); void destroyRecord(const Record record); @@ -186,6 +212,9 @@ class Storage { StorageDelegate * m_delegate; mutable Record m_lastRecordRetrieved; mutable char * m_lastRecordRetrievedPointer; + bool m_quarantine; + int m_examNumber; + char m_fullNameBuffer[k_fullNameMaxSize]; }; /* Some apps memoize records and need to be notified when a record might have diff --git a/ion/src/device/shared/usb/dfu_interface.cpp b/ion/src/device/shared/usb/dfu_interface.cpp index a473e142294..0a343eb6cf4 100644 --- a/ion/src/device/shared/usb/dfu_interface.cpp +++ b/ion/src/device/shared/usb/dfu_interface.cpp @@ -152,7 +152,6 @@ bool DFUInterface::processUploadRequest(SetupPacket *request, uint8_t *transferB } else { /* We decided to never protect Read operation. Else we would have to check * here it is not protected before reading. */ - // Compute the reading address uint32_t readAddress = (request->wValue() - 2) * Endpoint0::MaxTransferSize + m_addressPointer; // Copy the requested memory zone into the transfer buffer. @@ -217,7 +216,7 @@ void DFUInterface::eraseMemoryIfNeeded() { if (m_erasePage < 0) { return; } - + willErase(); #if 0 // We don't erase now the flash memory to avoid crash if writing is refused diff --git a/ion/src/shared/storage.cpp b/ion/src/shared/storage.cpp index 3419e3d8c53..0637bb2ba84 100644 --- a/ion/src/shared/storage.cpp +++ b/ion/src/shared/storage.cpp @@ -2,8 +2,14 @@ #include #include #include +#include "ion/storage.h" +#include +#include +#include + #if ION_STORAGE_LOG -#include +#include +#include #endif namespace Ion { @@ -26,6 +32,16 @@ constexpr char Storage::expExtension[]; constexpr char Storage::funcExtension[]; constexpr char Storage::seqExtension[]; constexpr char Storage::eqExtension[]; +constexpr char Storage::examPrefix[]; + +constexpr uint8_t Storage::expMaxRecords; +constexpr uint8_t Storage::funcMaxRecords; +constexpr uint8_t Storage::seqMaxRecords; +constexpr uint8_t Storage::eqMaxRecords; + +constexpr uint8_t Storage::noMaxRecords; + +constexpr const char * Storage::k_sequenceNames[seqMaxRecords]; Storage * Storage::sharedStorage() { static Storage * storage = new (staticStorageArea) Storage(); @@ -94,6 +110,10 @@ void Storage::log() { Record(currentName).log(); } } + +void Storage::logMessage(const char * message) { + std::cout << message << std::endl; +} #endif size_t Storage::availableSize() { @@ -146,11 +166,18 @@ Storage::Record::ErrorStatus Storage::notifyFullnessToDelegate() const { } Storage::Record::ErrorStatus Storage::createRecordWithFullName(const char * fullName, const void * data, size_t size) { + setFullNameBufferWithPrefix(examPrefix, fullName); + if (!fullNameAuthorized(fullName)) { + return Record::ErrorStatus::NameTaken; + } + if (m_quarantine) { + fullName = m_fullNameBuffer; + } size_t recordSize = sizeOfRecordWithFullName(fullName, size); if (recordSize >= k_maxRecordSize || recordSize > availableSize()) { return notifyFullnessToDelegate(); } - if (isFullNameTaken(fullName)) { + if (isFullNameTaken(fullName) || (!m_quarantine && !fullNameAuthorized(fullName))) { return Record::ErrorStatus::NameTaken; } // Find the end of data @@ -168,17 +195,35 @@ Storage::Record::ErrorStatus Storage::createRecordWithFullName(const char * full notifyChangeToDelegate(r); m_lastRecordRetrieved = r; m_lastRecordRetrievedPointer = newRecordAddress; + #if ION_STORAGE_LOG + logMessage("createRecordWithFullName start"); + log(); + r.log(); + logMessage("createRecordWithFullName end"); + #endif return Record::ErrorStatus::None; } Storage::Record::ErrorStatus Storage::createRecordWithExtension(const char * baseName, const char * extension, const void * data, size_t size) { + setFullNameBufferWithPrefix(examPrefix, baseName); + if (!fullNameAuthorized(baseName)) { + return Record::ErrorStatus::NameTaken; + } + #if ION_STORAGE_LOG + logMessage("createRecordWithExtension start"); + logMessage(baseName); + #endif + if (m_quarantine) { + baseName = m_fullNameBuffer; + } size_t recordSize = sizeOfRecordWithBaseNameAndExtension(baseName, extension, size); if (recordSize >= k_maxRecordSize || recordSize > availableSize()) { return notifyFullnessToDelegate(); } - if (isBaseNameWithExtensionTaken(baseName, extension)) { + if (isBaseNameWithExtensionTaken(baseName, extension) || (!m_quarantine && !fullNameAuthorized(baseName))) { return Record::ErrorStatus::NameTaken; } + deleteRecordByExtensionIfNeeded(extension); // Find the end of data char * newRecordAddress = endBuffer(); char * newRecord = newRecordAddress; @@ -194,27 +239,58 @@ Storage::Record::ErrorStatus Storage::createRecordWithExtension(const char * bas notifyChangeToDelegate(r); m_lastRecordRetrieved = r; m_lastRecordRetrievedPointer = newRecordAddress; + #if ION_STORAGE_LOG + log(); + r.log(); + logMessage("createRecordWithExtension end"); + #endif return Record::ErrorStatus::None; } -int Storage::numberOfRecordsWithExtension(const char * extension) { +int Storage::numberOfRecordsWithExtension(const char * extension, bool system) { int count = 0; size_t extensionLength = strlen(extension); + #if ION_STORAGE_LOG + logMessage("numberOfRecordsWithExtension start"); + #endif for (char * p : *this) { const char * name = fullNameOfRecordStarting(p); - if (FullNameHasExtension(name, extension, extensionLength)) { + #if ION_STORAGE_LOG + logMessage(name); + #endif + if (FullNameHasExtension(name, extension, extensionLength) && !(m_quarantine && !strstr(name, examPrefix) && !system)) { count++; + #if ION_STORAGE_LOG + logMessage("Counted !"); + #endif } } + #if ION_STORAGE_LOG + logMessage("numberOfRecordsWithExtension end"); + #endif return count; } -int Storage::numberOfRecords() { +int Storage::numberOfRecords(bool system) { int count = 0; + #if ION_STORAGE_LOG + logMessage("numberOfRecords start"); + #endif for (char * p : *this) { const char * name = fullNameOfRecordStarting(p); - count++; + #if ION_STORAGE_LOG + logMessage(name); + #endif + if(!(m_quarantine && !strstr(name, examPrefix)) || system){ + count++; + #if ION_STORAGE_LOG + logMessage("counted !"); + #endif + } } + #if ION_STORAGE_LOG + logMessage("numberOfRecords end"); + #endif return count; } @@ -222,15 +298,33 @@ Storage::Record Storage::recordAtIndex(int index) { int currentIndex = -1; const char * name = nullptr; char * recordAddress = nullptr; + #if ION_STORAGE_LOG + logMessage("recordAtIndex start"); + #endif for (char * p : *this) { const char * currentName = fullNameOfRecordStarting(p); + #if ION_STORAGE_LOG + logMessage(currentName); + #endif + if (m_quarantine && !strstr(currentName, examPrefix)) { + continue; + } currentIndex++; + #if ION_STORAGE_LOG + logMessage("currentIndex ++"); + #endif if (currentIndex == index) { recordAddress = p; name = currentName; + #if ION_STORAGE_LOG + logMessage("found !"); + #endif break; } } + #if ION_STORAGE_LOG + logMessage("recordAtIndex end"); + #endif if (name == nullptr) { return Record(); } @@ -245,17 +339,35 @@ Storage::Record Storage::recordWithExtensionAtIndex(const char * extension, int const char * name = nullptr; size_t extensionLength = strlen(extension); char * recordAddress = nullptr; + #if ION_STORAGE_LOG + logMessage("recordWithExtensionAtIndex start"); + #endif for (char * p : *this) { const char * currentName = fullNameOfRecordStarting(p); + #if ION_STORAGE_LOG + logMessage(currentName); + #endif + if (m_quarantine && !strstr(currentName, examPrefix)) { + continue; + } if (FullNameHasExtension(currentName, extension, extensionLength)) { + #if ION_STORAGE_LOG + logMessage("currentIndex ++"); + #endif currentIndex++; } if (currentIndex == index) { + #if ION_STORAGE_LOG + logMessage("found !"); + #endif recordAddress = p; name = currentName; break; } } + #if ION_STORAGE_LOG + logMessage("recordWithExtensionAtIndex end"); + #endif if (name == nullptr) { return Record(); } @@ -269,11 +381,36 @@ Storage::Record Storage::recordNamed(const char * fullName) { if (fullName == nullptr) { return Record(); } + #if ION_STORAGE_LOG + logMessage("recordNamed start"); + logMessage(fullName); + #endif Record r = Record(fullName); char * p = pointerOfRecord(r); - if (p != nullptr) { + if (p != nullptr && (!m_quarantine || strstr(fullName, examPrefix))) { + #if ION_STORAGE_LOG + logMessage("First return"); + logMessage("recordNamed end"); + #endif return r; + }else if (p == nullptr && m_quarantine) { + setFullNameBufferWithPrefix(examPrefix, fullName); + r = Record(m_fullNameBuffer); + p = pointerOfRecord(r); + #if ION_STORAGE_LOG + logMessage("Second if"); + #endif + if (p != nullptr) { + #if ION_STORAGE_LOG + logMessage("Second return"); + logMessage("recordNamed end"); + #endif + return r; + } } + #if ION_STORAGE_LOG + logMessage("recordNamed end"); + #endif return Record(); } @@ -328,7 +465,10 @@ Storage::Storage() : m_magicFooter(Magic), m_delegate(nullptr), m_lastRecordRetrieved(nullptr), - m_lastRecordRetrievedPointer(nullptr) + m_lastRecordRetrievedPointer(nullptr), + m_quarantine(false), + m_examNumber(0), + m_fullNameBuffer() { assert(m_magicHeader == Magic); assert(m_magicFooter == Magic); @@ -336,21 +476,49 @@ Storage::Storage() : overrideSizeAtPosition(m_buffer, 0); } -const char * Storage::fullNameOfRecord(const Record record) { +const char * Storage::fullNameOfRecord(const Record record, bool system) { char * p = pointerOfRecord(record); if (p != nullptr) { - return fullNameOfRecordStarting(p); + const char * name = fullNameOfRecordStarting(p); + return m_quarantine && !system && Storage::strstr(name, examPrefix) ? name + strlen(examPrefix): name; } return nullptr; } -Storage::Record::ErrorStatus Storage::setFullNameOfRecord(const Record record, const char * fullName) { - if (!FullNameCompliant(fullName)) { +Storage::Record::ErrorStatus Storage::setFullNameOfRecord(const Record record, const char *fullName, bool system) { + #if ION_STORAGE_LOG + logMessage("setFullNameOfRecord start"); + logMessage(fullName); + #endif + if (m_quarantine && !system) { + if (!fullNameAuthorized(fullName)) { + #if ION_STORAGE_LOG + logMessage("NonCompliant return"); + logMessage("setFullNameOfRecord end"); + #endif + return Record::ErrorStatus::NonCompliantName; + } + setFullNameBufferWithPrefix(examPrefix, fullName); + fullName = m_fullNameBuffer; + } + if (!FullNameCompliant(fullName) || (!m_quarantine && !fullNameAuthorized(fullName))) { + #if ION_STORAGE_LOG + logMessage("NonCompliant return"); + logMessage("setFullNameOfRecord end"); + #endif return Record::ErrorStatus::NonCompliantName; } if (isFullNameTaken(fullName, &record)) { + #if ION_STORAGE_LOG + logMessage("Taken return"); + logMessage("setFullNameOfRecord end"); + #endif return Record::ErrorStatus::NameTaken; } + #if ION_STORAGE_LOG + logMessage("After conditions"); + logMessage(fullName); + #endif size_t nameSize = strlen(fullName) + 1; char * p = pointerOfRecord(record); if (p != nullptr) { @@ -358,6 +526,10 @@ Storage::Record::ErrorStatus Storage::setFullNameOfRecord(const Record record, c record_size_t previousRecordSize = sizeOfRecordStarting(p); size_t newRecordSize = previousRecordSize-previousNameSize+nameSize; if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+sizeof(record_size_t)+previousNameSize, nameSize-previousNameSize)) { + #if ION_STORAGE_LOG + logMessage("notify return"); + logMessage("setFullNameOfRecord end"); + #endif return notifyFullnessToDelegate(); } overrideSizeAtPosition(p, newRecordSize); @@ -365,15 +537,47 @@ Storage::Record::ErrorStatus Storage::setFullNameOfRecord(const Record record, c notifyChangeToDelegate(record); m_lastRecordRetrieved = record; m_lastRecordRetrievedPointer = p; + #if ION_STORAGE_LOG + logMessage(record.fullName()); + logMessage("return None"); + logMessage("setFullNameOfRecord end"); + #endif return Record::ErrorStatus::None; } + #if ION_STORAGE_LOG + logMessage("return NotExist"); + logMessage("setFullNameOfRecord end"); + #endif return Record::ErrorStatus::RecordDoesNotExist; } -Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record record, const char * baseName, const char * extension) { - if (isBaseNameWithExtensionTaken(baseName, extension, &record)) { +Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record record, const char *baseName, const char *extension, bool system) { + #if ION_STORAGE_LOG + logMessage("setBaseNameWithExtensionOfRecord start"); + logMessage(baseName); + #endif + if (m_quarantine && !system) { + if (!fullNameAuthorized(baseName)) { + #if ION_STORAGE_LOG + logMessage("Taken return"); + logMessage("setBaseNameWithExtensionOfRecord end"); + #endif + return Record::ErrorStatus::NameTaken; + } + setFullNameBufferWithPrefix(examPrefix, baseName); + baseName = m_fullNameBuffer; + } + if (isBaseNameWithExtensionTaken(baseName, extension, &record) || (!m_quarantine && !fullNameAuthorized(baseName))) { + #if ION_STORAGE_LOG + logMessage("Taken return"); + logMessage("setBaseNameWithExtensionOfRecord end"); + #endif return Record::ErrorStatus::NameTaken; } + #if ION_STORAGE_LOG + logMessage("After conditions"); + logMessage(baseName); + #endif size_t nameSize = sizeOfBaseNameAndExtension(baseName, extension); char * p = pointerOfRecord(record); if (p != nullptr) { @@ -381,6 +585,10 @@ Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record re record_size_t previousRecordSize = sizeOfRecordStarting(p); size_t newRecordSize = previousRecordSize-previousNameSize+nameSize; if (newRecordSize >= k_maxRecordSize || !slideBuffer(p+sizeof(record_size_t)+previousNameSize, nameSize-previousNameSize)) { + #if ION_STORAGE_LOG + logMessage("notify return"); + logMessage("setBaseNameWithExtensionOfRecord end"); + #endif return notifyFullnessToDelegate(); } overrideSizeAtPosition(p, newRecordSize); @@ -391,8 +599,16 @@ Storage::Record::ErrorStatus Storage::setBaseNameWithExtensionOfRecord(Record re notifyChangeToDelegate(record); m_lastRecordRetrieved = record; m_lastRecordRetrievedPointer = p; + #if ION_STORAGE_LOG + logMessage("return None"); + logMessage("setBaseNameWithExtensionOfRecord end"); + #endif return Record::ErrorStatus::None; } + #if ION_STORAGE_LOG + logMessage("return NotExist"); + logMessage("setBaseNameWithExtensionOfRecord end"); + #endif return Record::ErrorStatus::RecordDoesNotExist; } @@ -527,7 +743,7 @@ bool Storage::isNameOfRecordTaken(Record r, const Record * recordToExclude) { return false; } -bool Storage::FullNameCompliant(const char * fullName) { +bool Storage::FullNameCompliant(const char * fullName, bool withoutExtension) { // We check that there is one dot and one dot only. const char * dotChar = UTF8Helper::CodePointSearch(fullName, k_dotChar); if (*dotChar == 0) { @@ -584,6 +800,10 @@ bool Storage::slideBuffer(char * position, int delta) { } Storage::Record Storage::privateRecordAndExtensionOfRecordBaseNamedWithExtensions(const char * baseName, const char * const extensions[], size_t numberOfExtensions, const char * * extensionResult, int baseNameLength) { + setFullNameBufferWithPrefix(examPrefix, baseName); + if (m_quarantine && !strstr(baseName, examPrefix)) { + baseName = m_fullNameBuffer; + } size_t nameLength = baseNameLength < 0 ? strlen(baseName) : baseNameLength; { const char * lastRetrievedRecordFullName = fullNameOfRecordStarting(m_lastRecordRetrievedPointer); @@ -619,6 +839,187 @@ Storage::Record Storage::privateRecordAndExtensionOfRecordBaseNamedWithExtension return Record(); } +void Storage::activateQuarantine() { + if (m_quarantine) { + deactivateQuarantine(); + } + m_quarantine = true; + // TODO maybe do: check if there is enough space for the exam +} + +bool Storage::strstr(const char * first, const char * second) { + size_t fSize = strlen(first); + size_t sSize = strlen(second); + if (fSize == sSize) { + return strcmp(first, second) == 0; + } + if (sSize > fSize) { + const char * buf = first; + first = second; + second = buf; + fSize = strlen(first); + sSize = strlen(second); + } + bool result = false; + int index = 0; + for (int i = 0; (size_t)i < fSize; i++) { + if (first[i] == second[index]) { + index++; + result = (size_t)index >= sSize; + } else if (fSize - 1 - (size_t)i < sSize - 1 - (size_t)index) { + break; + } else { + index = 0; + } + if (result) break; + } + return result; +} + +void Storage::deactivateQuarantine() { + if (!m_quarantine) { + return; + } + int count = numberOfRecords(); + #ifdef ION_STORAGE_LOG + logMessage("deactivateQuarantine start"); + #endif + for (int i = 0; i < count; i++) { + for (char * p : *this) { + const char * name = fullNameOfRecordStarting(p); + if (strstr(name, examPrefix)) { + #ifdef ION_STORAGE_LOG + logMessage(name); + #endif + Record r = Record(name); + const char * originalNameP = fullNameOfRecord(r, false); + if (!isFullNameTaken(originalNameP)) { + memset(m_fullNameBuffer, 0, k_fullNameMaxSize); + strlcpy(m_fullNameBuffer, originalNameP, k_fullNameMaxSize); + setFullNameOfRecord(r, m_fullNameBuffer, true); + break; + } + const char * dotChar = UTF8Helper::CodePointSearch(originalNameP, k_dotChar); + char originalName[k_fullNameMaxSize]; + char extension[k_extensionMaxSize]; + size_t originalSize; + if (*dotChar != 0) { + originalSize = strlen(originalNameP) - strlen(originalNameP + strlen(originalNameP) - strlen(dotChar)); + strlcpy(originalName, originalNameP, originalSize + 1); + originalName[originalSize + 1] = '\0'; + strlcpy(extension, originalNameP + originalSize, k_extensionMaxSize); + if (strcmp(originalNameP + originalSize + 1, seqExtension) == 0) { + continue; + } + } else { + originalSize = strlen(originalNameP); + strlcpy(originalName, originalNameP, originalSize); + } + #ifdef ION_STORAGE_LOG + logMessage(originalNameP); + logMessage(originalName); + logMessage(extension); + #endif + int current = 0; + char converted[3]; + Poincare::Integer(current).serialize(&converted[0], 3); + setFullNameBufferWithPrefix(originalName, converted); + if (*dotChar != 0) { + strlcat(m_fullNameBuffer, extension, k_fullNameMaxSize); + #ifdef ION_STORAGE_LOG + logMessage("--"); + logMessage(m_fullNameBuffer); + logMessage("--"); + #endif + } + while (isFullNameTaken(m_fullNameBuffer)) { + current ++; + converted[3]; + Poincare::Integer(current).serialize(&converted[0], 3); + setFullNameBufferWithPrefix(originalName, converted); + if (*dotChar != 0) { + strlcat(m_fullNameBuffer, extension, k_fullNameMaxSize); + } + } + setFullNameOfRecord(r, m_fullNameBuffer, true); + break; + } + } + } + int currentIndex = 0; + for (int i = 0; i < numberOfRecordsWithExtension(seqExtension, true); i ++) { + for (char *p : *this) { + const char * name = fullNameOfRecordStarting(p); + if (strstr(name, seqExtension)) { + if (currentIndex < i) { + currentIndex ++; + continue; + } + Record r = Record(name); + const char * originalNameP = fullNameOfRecord(r, false); + const char * dotChar = UTF8Helper::CodePointSearch(originalNameP, k_dotChar); + char extension[k_extensionMaxSize]; + size_t originalSize; + originalSize = strlen(originalNameP) - strlen(originalNameP + strlen(originalNameP) - strlen(dotChar)); + strlcpy(extension, originalNameP + originalSize, k_extensionMaxSize); + setFullNameBufferWithPrefix(&k_sequenceNames[i][0], extension); + setFullNameOfRecord(r, m_fullNameBuffer, true); + currentIndex = 0; + break; + } + } + } + #ifdef ION_STORAGE_LOG + logMessage("deactivateQuarantine end"); + #endif + m_quarantine = false; +} + +void Storage::setFullNameBufferWithPrefix(const char *prefix, const char *name) { + assert(sizeof(prefix) + sizeof(name) <= k_fullNameMaxSize); + memset(m_fullNameBuffer, 0, k_fullNameMaxSize); + strlcat(m_fullNameBuffer, prefix, k_fullNameMaxSize); + strlcat(m_fullNameBuffer, name, k_fullNameMaxSize); + #ifdef ION_STORAGE_LOG + logMessage("setFullNameBufferWithPrefix start"); + logMessage(name); + logMessage(prefix); + logMessage(m_fullNameBuffer); + logMessage("setFullNameBufferWithPrefix end"); + #endif +} + +void Storage::deleteRecordByExtensionIfNeeded(const char * extension) { + const uint8_t * limit; + if (strcmp(extension, eqExtension) == 0) { + limit = &eqMaxRecords; + } else if (strcmp(extension, seqExtension) == 0) { + limit = &seqMaxRecords; + } else { + limit = &noMaxRecords; + } + if (*limit == -1 || !m_quarantine) { + return; + } + int currentNumber = numberOfRecordsWithExtension(extension, true); + if (currentNumber + 1 <= *limit) { + return; + } + for (char * p : *this) { + const char * name = fullNameOfRecordStarting(p); + if (FullNameHasExtension(name, extension, strlen(extension)) && !strstr(name, examPrefix)) { + Record r = Record(name); + destroyRecord(r); + break; + } + } + +} + +bool Storage::fullNameAuthorized(const char *fullname) { + return !Storage::strstr(fullname, examPrefix); +} + Storage::RecordIterator & Storage::RecordIterator::operator++() { assert(m_recordStart); record_size_t size = StorageHelper::unalignedShort(m_recordStart);