|
@@ -24,76 +24,23 @@
|
|
|
*/
|
|
|
#define DBG_FILE "FatFileLFN.cpp"
|
|
|
#include "../common/DebugMacros.h"
|
|
|
+#include "../common/upcase.h"
|
|
|
+#include "../common/FsUtf.h"
|
|
|
#include "FatFile.h"
|
|
|
#include "FatVolume.h"
|
|
|
-//------------------------------------------------------------------------------
|
|
|
-//
|
|
|
-uint8_t FatFile::lfnChecksum(uint8_t* name) {
|
|
|
- uint8_t sum = 0;
|
|
|
- for (uint8_t i = 0; i < 11; i++) {
|
|
|
- sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + name[i];
|
|
|
- }
|
|
|
- return sum;
|
|
|
-}
|
|
|
#if USE_LONG_FILE_NAMES
|
|
|
//------------------------------------------------------------------------------
|
|
|
-// Saves about 90 bytes of flash on 328 over tolower().
|
|
|
-inline char lfnToLower(char c) {
|
|
|
- return 'A' <= c && c <= 'Z' ? c + 'a' - 'A' : c;
|
|
|
+static bool isLower(char c) {
|
|
|
+ return 'a' <= c && c <= 'z';
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-// Daniel Bernstein University of Illinois at Chicago.
|
|
|
-// Original had + instead of ^
|
|
|
-static uint16_t Bernstein(uint16_t hash, const char *str, size_t len) {
|
|
|
- for (size_t i = 0; i < len; i++) {
|
|
|
- // hash = hash * 33 ^ str[i];
|
|
|
- hash = ((hash << 5) + hash) ^ str[i];
|
|
|
- }
|
|
|
- return hash;
|
|
|
+static bool isUpper(char c) {
|
|
|
+ return 'A' <= c && c <= 'Z';
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-/**
|
|
|
- * Fetch a 16-bit long file name character.
|
|
|
- *
|
|
|
- * \param[in] ldir Pointer to long file name directory entry.
|
|
|
- * \param[in] i Index of character.
|
|
|
- * \return The 16-bit character.
|
|
|
- */
|
|
|
-static uint16_t lfnGetChar(DirLfn_t* ldir, uint8_t i) {
|
|
|
- if (i < 5) {
|
|
|
- return getLe16(ldir->unicode1 + 2*i);
|
|
|
- } else if (i < 11) {
|
|
|
- return getLe16(ldir->unicode2 + 2*i - 10);
|
|
|
- } else if (i < 13) {
|
|
|
- return getLe16(ldir->unicode3 + 2*i - 22);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-//------------------------------------------------------------------------------
|
|
|
-static size_t lfnGetName(DirLfn_t* ldir, char* name, size_t n) {
|
|
|
- uint8_t i;
|
|
|
- size_t k = 13*((ldir->order & 0X1F) - 1);
|
|
|
- for (i = 0; i < 13; i++) {
|
|
|
- uint16_t c = lfnGetChar(ldir, i);
|
|
|
- if (c == 0 || k >= (n - 1)) {
|
|
|
- break;
|
|
|
- }
|
|
|
- name[k++] = c >= 0X7F ? '?' : c;
|
|
|
- }
|
|
|
- // Terminate with zero byte.
|
|
|
- if (k >= n) {
|
|
|
- k = n - 1;
|
|
|
- }
|
|
|
- name[k] = '\0';
|
|
|
- return k;
|
|
|
-}
|
|
|
-//------------------------------------------------------------------------------
|
|
|
-inline bool lfnLegalChar(uint8_t c) {
|
|
|
- if (c == '/' || c == '\\' || c == '"' || c == '*' ||
|
|
|
- c == ':' || c == '<' || c == '>' || c == '?' || c == '|') {
|
|
|
- return false;
|
|
|
- }
|
|
|
- return 0X1F < c && c < 0X7F;
|
|
|
+// A bit smaller than toupper in AVR 328.
|
|
|
+inline char toUpper(char c) {
|
|
|
+ return isLower(c) ? c - 'a' + 'A' : c;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
/**
|
|
@@ -101,9 +48,9 @@ inline bool lfnLegalChar(uint8_t c) {
|
|
|
*
|
|
|
* \param[in] ldir Pointer to long file name directory entry.
|
|
|
* \param[in] i Index of character.
|
|
|
- * \param[in] c The 16-bit character.
|
|
|
+ * \param[in] c The 16-bit character.
|
|
|
*/
|
|
|
-static void lfnPutChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
|
|
|
+static void putLfnChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
|
|
|
if (i < 5) {
|
|
|
setLe16(ldir->unicode1 + 2*i, c);
|
|
|
} else if (i < 11) {
|
|
@@ -113,167 +60,152 @@ static void lfnPutChar(DirLfn_t* ldir, uint8_t i, uint16_t c) {
|
|
|
}
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-static void lfnPutName(DirLfn_t* ldir, const char* name, size_t n) {
|
|
|
- size_t k = 13*((ldir->order & 0X1F) - 1);
|
|
|
- for (uint8_t i = 0; i < 13; i++, k++) {
|
|
|
- uint16_t c = k < n ? name[k] : k == n ? 0 : 0XFFFF;
|
|
|
- lfnPutChar(ldir, i, c);
|
|
|
+// Daniel Bernstein University of Illinois at Chicago.
|
|
|
+// Original had + instead of ^
|
|
|
+__attribute__((unused))
|
|
|
+static uint16_t Bernstein(const char* bgn, const char* end, uint16_t hash) {
|
|
|
+ while (bgn < end) {
|
|
|
+ // hash = hash * 33 ^ str[i];
|
|
|
+ hash = ((hash << 5) + hash) ^ (*bgn++);
|
|
|
}
|
|
|
+ return hash;
|
|
|
}
|
|
|
//==============================================================================
|
|
|
-size_t FatFile::getName(char* name, size_t size) {
|
|
|
- size_t n = 0;
|
|
|
- FatFile dirFile;
|
|
|
+bool FatFile::cmpName(uint16_t index, FatName_t* fname, uint8_t lfnOrd) {
|
|
|
+ FatFile dir = *this;
|
|
|
DirLfn_t* ldir;
|
|
|
- if (!isOpen() || size < 13) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- if (!isLFN()) {
|
|
|
- return getSFN(name);
|
|
|
- }
|
|
|
- if (!dirFile.openCluster(this)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- for (uint8_t order = 1; order <= m_lfnOrd; order++) {
|
|
|
- if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
|
|
|
+ fname->reset();
|
|
|
+ for (uint8_t order = 1; order <= lfnOrd; order++) {
|
|
|
+ ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(index - order));
|
|
|
if (!ldir) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- if (ldir->attributes != FAT_ATTRIB_LONG_NAME) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- if (order != (ldir->order & 0X1F)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- n = lfnGetName(ldir, name, size);
|
|
|
- if (n == 0) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- if (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) {
|
|
|
- return n;
|
|
|
+ // These should be checked in caller.
|
|
|
+ DBG_HALT_IF(ldir->attributes != FAT_ATTRIB_LONG_NAME);
|
|
|
+ DBG_HALT_IF(order != (ldir->order & 0X1F));
|
|
|
+ for (uint8_t i = 0; i < 13; i++) {
|
|
|
+ uint16_t u = getLfnChar(ldir, i);
|
|
|
+ if (fname->atEnd()) {
|
|
|
+ return u == 0;
|
|
|
+ }
|
|
|
+#if USE_UTF8_LONG_NAMES
|
|
|
+ uint16_t cp = fname->get16();
|
|
|
+ // Make sure caller checked for valid UTF-8.
|
|
|
+ DBG_HALT_IF(cp == 0XFFFF);
|
|
|
+ if (toUpcase(u) != toUpcase(cp)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+#else // USE_UTF8_LONG_NAMES
|
|
|
+ if (u > 0X7F || toUpper(u) != toUpper(fname->getch())) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+#endif // USE_UTF8_LONG_NAMES
|
|
|
}
|
|
|
}
|
|
|
- // Fall into fail.
|
|
|
- DBG_FAIL_MACRO;
|
|
|
+ return true;
|
|
|
|
|
|
fail:
|
|
|
- name[0] = '\0';
|
|
|
- return 0;
|
|
|
+ return false;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-bool FatFile::openCluster(FatFile* file) {
|
|
|
- if (file->m_dirCluster == 0) {
|
|
|
- return openRoot(file->m_vol);
|
|
|
+bool FatFile::createLFN(uint16_t index, FatName_t* fname, uint8_t lfnOrd) {
|
|
|
+ FatFile dir = *this;
|
|
|
+ DirLfn_t* ldir;
|
|
|
+ uint8_t checksum = lfnChecksum(fname->sfn);
|
|
|
+ uint8_t fc = 0;
|
|
|
+ fname->reset();
|
|
|
+
|
|
|
+ for (uint8_t order = 1; order <= lfnOrd; order++) {
|
|
|
+ ldir = reinterpret_cast<DirLfn_t*>(dir.cacheDir(index - order));
|
|
|
+ if (!ldir) {
|
|
|
+ DBG_FAIL_MACRO;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ dir.m_vol->cacheDirty();
|
|
|
+ ldir->order = order == lfnOrd ? FAT_ORDER_LAST_LONG_ENTRY | order : order;
|
|
|
+ ldir->attributes = FAT_ATTRIB_LONG_NAME;
|
|
|
+ ldir->mustBeZero1 = 0;
|
|
|
+ ldir->checksum = checksum;
|
|
|
+ setLe16(ldir->mustBeZero2, 0);
|
|
|
+ for (uint8_t i = 0; i < 13; i++) {
|
|
|
+ uint16_t cp;
|
|
|
+ if (fname->atEnd()) {
|
|
|
+ cp = fc++ ? 0XFFFF : 0;
|
|
|
+ } else {
|
|
|
+ cp = fname->get16();
|
|
|
+ // Verify caller checked for valid UTF-8.
|
|
|
+ DBG_HALT_IF(cp == 0XFFFF);
|
|
|
+ }
|
|
|
+ putLfnChar(ldir, i, cp);
|
|
|
+ }
|
|
|
}
|
|
|
- memset(this, 0, sizeof(FatFile));
|
|
|
- m_attributes = FILE_ATTR_SUBDIR;
|
|
|
- m_flags = FILE_FLAG_READ;
|
|
|
- m_vol = file->m_vol;
|
|
|
- m_firstCluster = file->m_dirCluster;
|
|
|
return true;
|
|
|
+
|
|
|
+ fail:
|
|
|
+ return false;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-bool FatFile::parsePathName(const char* path,
|
|
|
- fname_t* fname, const char** ptr) {
|
|
|
- char c;
|
|
|
+bool FatFile::makeSFN(FatName_t* fname) {
|
|
|
bool is83;
|
|
|
+// char c;
|
|
|
+ uint8_t c;
|
|
|
uint8_t bit = FAT_CASE_LC_BASE;
|
|
|
uint8_t lc = 0;
|
|
|
uint8_t uc = 0;
|
|
|
uint8_t i = 0;
|
|
|
uint8_t in = 7;
|
|
|
- int end;
|
|
|
- int len = 0;
|
|
|
- int si;
|
|
|
- int dot;
|
|
|
+ const char* dot;
|
|
|
+ const char* end = fname->end;
|
|
|
+ const char* ptr = fname->begin;
|
|
|
|
|
|
- // Skip leading spaces.
|
|
|
- while (*path == ' ') {
|
|
|
- path++;
|
|
|
- }
|
|
|
- fname->lfn = path;
|
|
|
+ // Assume not zero length.
|
|
|
+ DBG_HALT_IF(end == ptr);
|
|
|
+ // Assume blanks removed from start and end.
|
|
|
+ DBG_HALT_IF(*ptr == ' ' || *(end - 1) == ' ' || *(end - 1) == '.');
|
|
|
|
|
|
- for (len = 0; ; len++) {
|
|
|
- c = path[len];
|
|
|
- if (c == 0 || isDirSeparator(c)) {
|
|
|
- break;
|
|
|
- }
|
|
|
- if (!lfnLegalChar(c)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- }
|
|
|
- // Advance to next path component.
|
|
|
- for (end = len; path[end] == ' ' || isDirSeparator(path[end]); end++) {}
|
|
|
- *ptr = &path[end];
|
|
|
-
|
|
|
- // Back over spaces and dots.
|
|
|
- while (len) {
|
|
|
- c = path[len - 1];
|
|
|
- if (c != '.' && c != ' ') {
|
|
|
- break;
|
|
|
- }
|
|
|
- len--;
|
|
|
- }
|
|
|
- // Max length of LFN is 255.
|
|
|
- if (len > 255) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- fname->len = len;
|
|
|
// Blank file short name.
|
|
|
for (uint8_t k = 0; k < 11; k++) {
|
|
|
fname->sfn[k] = ' ';
|
|
|
}
|
|
|
- // skip leading spaces and dots.
|
|
|
- for (si = 0; path[si] == '.' || path[si] == ' '; si++) {}
|
|
|
- // Not 8.3 if leading dot or space.
|
|
|
- is83 = !si;
|
|
|
+ // Not 8.3 if starts with dot.
|
|
|
+ is83 = *ptr == '.' ? false : true;
|
|
|
+ // Skip leading dots.
|
|
|
+ for (; *ptr == '.'; ptr++) {}
|
|
|
+ // Find last dot.
|
|
|
+ for (dot = end - 1; dot > ptr && *dot != '.'; dot--) {}
|
|
|
|
|
|
- // find last dot.
|
|
|
- for (dot = len - 1; dot >= 0 && path[dot] != '.'; dot--) {}
|
|
|
- for (; si < len; si++) {
|
|
|
- c = path[si];
|
|
|
- if (c == ' ' || (c == '.' && dot != si)) {
|
|
|
- is83 = false;
|
|
|
- continue;
|
|
|
- }
|
|
|
- if (!legal83Char(c) && si != dot) {
|
|
|
- is83 = false;
|
|
|
- c = '_';
|
|
|
- }
|
|
|
- if (si == dot || i > in) {
|
|
|
- if (in == 10) {
|
|
|
- // Done - extension longer than three characters.
|
|
|
- is83 = false;
|
|
|
- break;
|
|
|
- }
|
|
|
- if (si != dot) {
|
|
|
- is83 = false;
|
|
|
- }
|
|
|
- // Break if no dot and base-name is longer than eight characters.
|
|
|
- if (si > dot) {
|
|
|
- break;
|
|
|
- }
|
|
|
- si = dot;
|
|
|
+ for (; ptr < end; ptr++) {
|
|
|
+ c = *ptr;
|
|
|
+// Could skip UTF-8 units where (0XC0 & c) == 0X80
|
|
|
+
|
|
|
+ if (c == '.' && ptr == dot) {
|
|
|
in = 10; // Max index for full 8.3 name.
|
|
|
i = 8; // Place for extension.
|
|
|
bit = FAT_CASE_LC_EXT; // bit for extension.
|
|
|
} else {
|
|
|
- if ('a' <= c && c <= 'z') {
|
|
|
+ if (!legal83Char(c)) {
|
|
|
+ is83 = false;
|
|
|
+ // Skip UTF-8 trailing characters.
|
|
|
+ if ((c & 0XC0) == 0X80) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ c = '_';
|
|
|
+ }
|
|
|
+ if (i > in) {
|
|
|
+ is83 = false;
|
|
|
+ if (in == 10 || ptr > dot) {
|
|
|
+ // Done - extension longer than three characters or no extension.
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // Skip to dot.
|
|
|
+ ptr = dot - 1;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (isLower(c)) {
|
|
|
c += 'A' - 'a';
|
|
|
lc |= bit;
|
|
|
- } else if ('A' <= c && c <= 'Z') {
|
|
|
+ } else if (isUpper(c)) {
|
|
|
uc |= bit;
|
|
|
}
|
|
|
fname->sfn[i++] = c;
|
|
@@ -283,10 +215,9 @@ bool FatFile::parsePathName(const char* path,
|
|
|
}
|
|
|
}
|
|
|
if (fname->sfn[0] == ' ') {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
+ DBG_HALT_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
-
|
|
|
if (is83) {
|
|
|
fname->flags = lc & uc ? FNAME_FLAG_MIXED_CASE : lc;
|
|
|
} else {
|
|
@@ -300,7 +231,63 @@ bool FatFile::parsePathName(const char* path,
|
|
|
return false;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
+bool FatFile::makeUniqueSfn(FatName_t* fname) {
|
|
|
+ const uint8_t FIRST_HASH_SEQ = 2; // min value is 2
|
|
|
+ uint8_t pos = fname->seqPos;
|
|
|
+ DirFat_t* dir;
|
|
|
+ uint16_t hex;
|
|
|
+
|
|
|
+ DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
|
|
|
+ DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
|
|
|
+
|
|
|
+ for (uint8_t seq = FIRST_HASH_SEQ; seq < 100; seq++) {
|
|
|
+ DBG_WARN_IF(seq > FIRST_HASH_SEQ);
|
|
|
+#ifdef USE_LFN_HASH
|
|
|
+ hex = Bernstein(fname->begin, fname->end, seq);
|
|
|
+#else
|
|
|
+ hex = micros();
|
|
|
+#endif
|
|
|
+ if (pos > 3) {
|
|
|
+ // Make space in name for ~HHHH.
|
|
|
+ pos = 3;
|
|
|
+ }
|
|
|
+ for (uint8_t i = pos + 4 ; i > pos; i--) {
|
|
|
+ uint8_t h = hex & 0XF;
|
|
|
+ fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
|
|
|
+ hex >>= 4;
|
|
|
+ }
|
|
|
+ fname->sfn[pos] = '~';
|
|
|
+ rewind();
|
|
|
+ while (1) {
|
|
|
+ dir = readDirCache(true);
|
|
|
+ if (!dir) {
|
|
|
+ if (!getError()) {
|
|
|
+ // At EOF and name not found if no error.
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ DBG_FAIL_MACRO;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ if (dir->name[0] == FAT_NAME_FREE) {
|
|
|
+ goto done;
|
|
|
+ }
|
|
|
+ if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
|
|
|
+ // Name found - try another.
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // fall inti fail - too many tries.
|
|
|
+ DBG_FAIL_MACRO;
|
|
|
+
|
|
|
+ fail:
|
|
|
+ return false;
|
|
|
+
|
|
|
+ done:
|
|
|
+ return true;
|
|
|
+}
|
|
|
+//------------------------------------------------------------------------------
|
|
|
+bool FatFile::open(FatFile* dirFile, FatName_t* fname, oflag_t oflag) {
|
|
|
bool fnameFound = false;
|
|
|
uint8_t lfnOrd = 0;
|
|
|
uint8_t freeNeed;
|
|
@@ -308,25 +295,25 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
uint8_t order = 0;
|
|
|
uint8_t checksum = 0;
|
|
|
uint8_t ms10;
|
|
|
+ uint8_t nameOrd;
|
|
|
uint16_t freeIndex = 0;
|
|
|
uint16_t curIndex;
|
|
|
uint16_t date;
|
|
|
uint16_t time;
|
|
|
DirFat_t* dir;
|
|
|
DirLfn_t* ldir;
|
|
|
- size_t len = fname->len;
|
|
|
|
|
|
if (!dirFile->isDir() || isOpen()) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
// Number of directory entries needed.
|
|
|
- freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + (len + 12)/13 : 1;
|
|
|
-
|
|
|
+ nameOrd = (fname->len + 12)/13;
|
|
|
+ freeNeed = fname->flags & FNAME_FLAG_NEED_LFN ? 1 + nameOrd : 1;
|
|
|
dirFile->rewind();
|
|
|
while (1) {
|
|
|
curIndex = dirFile->m_curPosition/32;
|
|
|
- dir = dirFile->readDirCache(true);
|
|
|
+ dir = dirFile->readDirCache();
|
|
|
if (!dir) {
|
|
|
if (dirFile->getError()) {
|
|
|
DBG_FAIL_MACRO;
|
|
@@ -356,38 +343,20 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
} else if (isLongName(dir)) {
|
|
|
ldir = reinterpret_cast<DirLfn_t*>(dir);
|
|
|
if (!lfnOrd) {
|
|
|
- if ((ldir->order & FAT_ORDER_LAST_LONG_ENTRY) == 0) {
|
|
|
- continue;
|
|
|
- }
|
|
|
order = ldir->order & 0X1F;
|
|
|
- if (order != (freeNeed - 1)) {
|
|
|
+ if (order != nameOrd ||
|
|
|
+ (ldir->order & FAT_ORDER_LAST_LONG_ENTRY) == 0) {
|
|
|
continue;
|
|
|
}
|
|
|
- lfnOrd = order;
|
|
|
+ lfnOrd = nameOrd;
|
|
|
checksum = ldir->checksum;
|
|
|
} else if (ldir->order != --order || checksum != ldir->checksum) {
|
|
|
lfnOrd = 0;
|
|
|
continue;
|
|
|
}
|
|
|
- size_t k = 13*(order - 1);
|
|
|
- if (k >= len) {
|
|
|
- // Not found.
|
|
|
- lfnOrd = 0;
|
|
|
- continue;
|
|
|
- }
|
|
|
- for (uint8_t i = 0; i < 13; i++) {
|
|
|
- uint16_t u = lfnGetChar(ldir, i);
|
|
|
- if (k == len) {
|
|
|
- if (u != 0) {
|
|
|
- // Not found.
|
|
|
- lfnOrd = 0;
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- if (u > 255 || lfnToLower(u) != lfnToLower(fname->lfn[k++])) {
|
|
|
- // Not found.
|
|
|
+ if (order == 1) {
|
|
|
+ if (!dirFile->cmpName(curIndex + 1, fname, lfnOrd)) {
|
|
|
lfnOrd = 0;
|
|
|
- break;
|
|
|
}
|
|
|
}
|
|
|
} else if (isFileOrSubdir(dir)) {
|
|
@@ -420,10 +389,10 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
create:
|
|
|
// don't create unless O_CREAT and write mode
|
|
|
if (!(oflag & O_CREAT) || !isWriteMode(oflag)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
+ DBG_WARN_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- // If at EOF start in next cluster.
|
|
|
+ // Keep found entries or start at current index if no free entries found.
|
|
|
if (freeFound == 0) {
|
|
|
freeIndex = curIndex;
|
|
|
}
|
|
@@ -446,38 +415,19 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- // Done if more than one sector per cluster. Max freeNeed is 21.
|
|
|
- if (dirFile->m_vol->sectorsPerCluster() > 1) {
|
|
|
- break;
|
|
|
- }
|
|
|
- freeFound += 16;
|
|
|
+ freeFound += dirFile->m_vol->sectorsPerCluster();
|
|
|
}
|
|
|
if (fnameFound) {
|
|
|
- if (!dirFile->lfnUniqueSfn(fname)) {
|
|
|
+ if (!dirFile->makeUniqueSfn(fname)) {
|
|
|
goto fail;
|
|
|
}
|
|
|
}
|
|
|
- if (!dirFile->seekSet(32UL*freeIndex)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
lfnOrd = freeNeed - 1;
|
|
|
- for (order = lfnOrd ; order ; order--) {
|
|
|
- ldir = reinterpret_cast<DirLfn_t*>(dirFile->readDirCache());
|
|
|
- if (!ldir) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- dirFile->m_vol->cacheDirty();
|
|
|
- ldir->order = order == lfnOrd ? FAT_ORDER_LAST_LONG_ENTRY | order : order;
|
|
|
- ldir->attributes = FAT_ATTRIB_LONG_NAME;
|
|
|
- ldir->mustBeZero1 = 0;
|
|
|
- ldir->checksum = lfnChecksum(fname->sfn);
|
|
|
- setLe16(ldir->mustBeZero2, 0);
|
|
|
- lfnPutName(ldir, fname->lfn, len);
|
|
|
+ curIndex = freeIndex + lfnOrd;
|
|
|
+ if (!dirFile->createLFN(curIndex, fname, lfnOrd)) {
|
|
|
+ goto fail;
|
|
|
}
|
|
|
- curIndex = dirFile->m_curPosition/32;
|
|
|
- dir = dirFile->readDirCache();
|
|
|
+ dir = dirFile->cacheDir(curIndex);
|
|
|
if (!dir) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
@@ -487,7 +437,7 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
memcpy(dir->name, fname->sfn, 11);
|
|
|
|
|
|
// Set base-name and extension lower case bits.
|
|
|
- dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags;
|
|
|
+ dir->caseFlags = (FAT_CASE_LC_BASE | FAT_CASE_LC_EXT) & fname->flags;
|
|
|
|
|
|
// Set timestamps.
|
|
|
if (FsDateTime::callback) {
|
|
@@ -520,51 +470,53 @@ bool FatFile::open(FatFile* dirFile, fname_t* fname, oflag_t oflag) {
|
|
|
return false;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
-size_t FatFile::printName(print_t* pr) {
|
|
|
- FatFile dirFile;
|
|
|
- DirLfn_t* ldir;
|
|
|
- size_t n = 0;
|
|
|
- uint16_t u;
|
|
|
- uint8_t buf[13];
|
|
|
- uint8_t i;
|
|
|
-
|
|
|
- if (!isLFN()) {
|
|
|
- return printSFN(pr);
|
|
|
- }
|
|
|
- if (!dirFile.openCluster(this)) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
+bool FatFile::parsePathName(const char* path,
|
|
|
+ FatName_t* fname, const char** ptr) {
|
|
|
+ size_t len = 0;
|
|
|
+ // Skip leading spaces.
|
|
|
+ while (*path == ' ') {
|
|
|
+ path++;
|
|
|
}
|
|
|
- for (uint8_t order = 1; order <= m_lfnOrd; order++) {
|
|
|
- if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
|
|
|
+ fname->begin = path;
|
|
|
+ while (*path && !isDirSeparator(*path)) {
|
|
|
+#if USE_UTF8_LONG_NAMES
|
|
|
+ uint32_t cp;
|
|
|
+ // Allow end = path + 4 since path is zero terminated.
|
|
|
+ path = FsUtf::mbToCp(path, path + 4, &cp);
|
|
|
+ if (!path) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
|
|
|
- if (!ldir) {
|
|
|
+ len += cp <= 0XFFFF ? 1 : 2;
|
|
|
+ if (cp < 0X80 && lfnReservedChar(cp)) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- if (ldir->attributes != FAT_ATTRIB_LONG_NAME ||
|
|
|
- order != (ldir->order & 0X1F)) {
|
|
|
+#else // USE_UTF8_LONG_NAMES
|
|
|
+ uint8_t cp = *path++;
|
|
|
+ if (cp >= 0X80 || lfnReservedChar(cp)) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
|
}
|
|
|
- for (i = 0; i < 13; i++) {
|
|
|
- u = lfnGetChar(ldir, i);
|
|
|
- if (u == 0) {
|
|
|
- // End of name.
|
|
|
- break;
|
|
|
- }
|
|
|
- buf[i] = u < 0X7F ? u : '?';
|
|
|
- n++;
|
|
|
+ len++;
|
|
|
+#endif // USE_UTF8_LONG_NAMES
|
|
|
+ if (cp != '.' && cp != ' ') {
|
|
|
+ // Need to trim trailing dots spaces.
|
|
|
+ fname->len = len;
|
|
|
+ fname->end = path;
|
|
|
}
|
|
|
- pr->write(buf, i);
|
|
|
}
|
|
|
- return n;
|
|
|
+ if (!fname->len || fname->len > FAT_MAX_LFN_LENGTH) {
|
|
|
+ DBG_FAIL_MACRO;
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ // Advance to next path component.
|
|
|
+ for (; *path == ' ' || isDirSeparator(*path); path++) {}
|
|
|
+ *ptr = path;
|
|
|
+ return makeSFN(fname);
|
|
|
|
|
|
fail:
|
|
|
- return 0;
|
|
|
+ return false;
|
|
|
}
|
|
|
//------------------------------------------------------------------------------
|
|
|
bool FatFile::remove() {
|
|
@@ -613,11 +565,7 @@ bool FatFile::remove() {
|
|
|
goto fail;
|
|
|
}
|
|
|
for (uint8_t order = 1; order <= m_lfnOrd; order++) {
|
|
|
- if (!dirFile.seekSet(32UL*(m_dirIndex - order))) {
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- ldir = reinterpret_cast<DirLfn_t*>(dirFile.readDirCache());
|
|
|
+ ldir = reinterpret_cast<DirLfn_t*>(dirFile.cacheDir(m_dirIndex - order));
|
|
|
if (!ldir) {
|
|
|
DBG_FAIL_MACRO;
|
|
|
goto fail;
|
|
@@ -645,60 +593,4 @@ bool FatFile::remove() {
|
|
|
fail:
|
|
|
return false;
|
|
|
}
|
|
|
-//------------------------------------------------------------------------------
|
|
|
-bool FatFile::lfnUniqueSfn(fname_t* fname) {
|
|
|
- const uint8_t FIRST_HASH_SEQ = 2; // min value is 2
|
|
|
- uint8_t pos = fname->seqPos;
|
|
|
- DirFat_t* dir;
|
|
|
- uint16_t hex;
|
|
|
-
|
|
|
- DBG_HALT_IF(!(fname->flags & FNAME_FLAG_LOST_CHARS));
|
|
|
- DBG_HALT_IF(fname->sfn[pos] != '~' && fname->sfn[pos + 1] != '1');
|
|
|
-
|
|
|
- for (uint8_t seq = 2; seq < 100; seq++) {
|
|
|
- if (seq < FIRST_HASH_SEQ) {
|
|
|
- fname->sfn[pos + 1] = '0' + seq;
|
|
|
- } else {
|
|
|
- DBG_PRINT_IF(seq > FIRST_HASH_SEQ);
|
|
|
- hex = Bernstein(seq + fname->len, fname->lfn, fname->len);
|
|
|
- if (pos > 3) {
|
|
|
- // Make space in name for ~HHHH.
|
|
|
- pos = 3;
|
|
|
- }
|
|
|
- for (uint8_t i = pos + 4 ; i > pos; i--) {
|
|
|
- uint8_t h = hex & 0XF;
|
|
|
- fname->sfn[i] = h < 10 ? h + '0' : h + 'A' - 10;
|
|
|
- hex >>= 4;
|
|
|
- }
|
|
|
- }
|
|
|
- fname->sfn[pos] = '~';
|
|
|
- rewind();
|
|
|
- while (1) {
|
|
|
- dir = readDirCache(true);
|
|
|
- if (!dir) {
|
|
|
- if (!getError()) {
|
|
|
- // At EOF and name not found if no error.
|
|
|
- goto done;
|
|
|
- }
|
|
|
- DBG_FAIL_MACRO;
|
|
|
- goto fail;
|
|
|
- }
|
|
|
- if (dir->name[0] == FAT_NAME_FREE) {
|
|
|
- goto done;
|
|
|
- }
|
|
|
- if (isFileOrSubdir(dir) && !memcmp(fname->sfn, dir->name, 11)) {
|
|
|
- // Name found - try another.
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // fall inti fail - too many tries.
|
|
|
- DBG_FAIL_MACRO;
|
|
|
-
|
|
|
- fail:
|
|
|
- return false;
|
|
|
-
|
|
|
- done:
|
|
|
- return true;
|
|
|
-}
|
|
|
#endif // #if USE_LONG_FILE_NAMES
|