Allow lsDive to recurse with minimal stack usage

This commit is contained in:
Scott Lahteine 2015-05-15 03:19:07 -07:00
parent 8b2cd930c3
commit 98e91f5253
2 changed files with 63 additions and 54 deletions

View File

@ -1114,50 +1114,38 @@ int8_t SdBaseFile::readDir(dir_t* dir, char* longFilename) {
if (!isDir() || (0X1F & curPosition_)) return -1; if (!isDir() || (0X1F & curPosition_)) return -1;
//If we have a longFilename buffer, mark it as invalid. If we find a long filename it will be filled automaticly. //If we have a longFilename buffer, mark it as invalid. If we find a long filename it will be filled automaticly.
if (longFilename != NULL) if (longFilename != NULL) longFilename[0] = '\0';
{
longFilename[0] = '\0';
}
while (1) { while (1) {
n = read(dir, sizeof(dir_t)); n = read(dir, sizeof(dir_t));
if (n != sizeof(dir_t)) return n == 0 ? 0 : -1; if (n != sizeof(dir_t)) return n == 0 ? 0 : -1;
// last entry if DIR_NAME_FREE // last entry if DIR_NAME_FREE
if (dir->name[0] == DIR_NAME_FREE) return 0; if (dir->name[0] == DIR_NAME_FREE) return 0;
// skip empty entries and entry for . and .. // skip empty entries and entry for . and ..
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue; if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') continue;
//Fill the long filename if we have a long filename entry,
// long filename entries are stored before the actual filename. // Fill the long filename if we have a long filename entry.
if (DIR_IS_LONG_NAME(dir) && longFilename != NULL) // Long filename entries are stored before the short filename.
{ if (longFilename != NULL && DIR_IS_LONG_NAME(dir)) {
vfat_t *VFAT = (vfat_t*)dir; vfat_t *VFAT = (vfat_t*)dir;
//Sanity check the VFAT entry. The first cluster is always set to zero. And th esequence number should be higher then 0 // Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
if (VFAT->firstClusterLow == 0 && (VFAT->sequenceNumber & 0x1F) > 0 && (VFAT->sequenceNumber & 0x1F) <= MAX_VFAT_ENTRIES) if (VFAT->firstClusterLow == 0 && (VFAT->sequenceNumber & 0x1F) > 0 && (VFAT->sequenceNumber & 0x1F) <= MAX_VFAT_ENTRIES) {
{
// TODO: Store the filename checksum to verify if a none-long filename aware system modified the file table. // TODO: Store the filename checksum to verify if a none-long filename aware system modified the file table.
n = ((VFAT->sequenceNumber & 0x1F) - 1) * FILENAME_LENGTH; n = ((VFAT->sequenceNumber & 0x1F) - 1) * FILENAME_LENGTH;
longFilename[n+0] = VFAT->name1[0]; for (uint8_t i=0; i<FILENAME_LENGTH; i++)
longFilename[n+1] = VFAT->name1[1]; longFilename[n+i] = (i < 5) ? VFAT->name1[i] : (i < 11) ? VFAT->name2[i-5] : VFAT->name3[i-11];
longFilename[n+2] = VFAT->name1[2];
longFilename[n+3] = VFAT->name1[3];
longFilename[n+4] = VFAT->name1[4];
longFilename[n+5] = VFAT->name2[0];
longFilename[n+6] = VFAT->name2[1];
longFilename[n+7] = VFAT->name2[2];
longFilename[n+8] = VFAT->name2[3];
longFilename[n+9] = VFAT->name2[4];
longFilename[n+10] = VFAT->name2[5];
longFilename[n+11] = VFAT->name3[0];
longFilename[n+12] = VFAT->name3[1];
// If this VFAT entry is the last one, add a NUL terminator at the end of the string // If this VFAT entry is the last one, add a NUL terminator at the end of the string
if (VFAT->sequenceNumber & 0x40) if (VFAT->sequenceNumber & 0x40) longFilename[n+FILENAME_LENGTH] = '\0';
longFilename[n+FILENAME_LENGTH] = '\0';
} }
} }
// return if normal file or subdirectory // Return if normal file or subdirectory
if (DIR_IS_FILE_OR_SUBDIR(dir)) return n; if (DIR_IS_FILE_OR_SUBDIR(dir)) return n;
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Read next directory entry into the cache // Read next directory entry into the cache
// Assumes file is correctly positioned // Assumes file is correctly positioned

View File

@ -40,24 +40,43 @@ char *createFilename(char *buffer, const dir_t &p) { //buffer > 12characters
return buffer; return buffer;
} }
/**
* Dive into a folder and recurse depth-first to perform a pre-set operation lsAction:
* LS_Count - Add +1 to nrFiles for every file within the parent
* LS_GetFilename - Get the filename of the file indexed by nrFiles
* LS_SerialPrint - Print the full path of each file to serial output
*/
void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) { void CardReader::lsDive(const char *prepend, SdFile parent, const char * const match/*=NULL*/) {
dir_t p; dir_t p;
uint8_t cnt = 0; uint8_t cnt = 0;
// Read the next entry from a directory
while (parent.readDir(p, longFilename) > 0) { while (parent.readDir(p, longFilename) > 0) {
if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) { // hence LS_SerialPrint
char path[FILENAME_LENGTH*2]; // If the entry is a directory and the action is LS_SerialPrint
if (DIR_IS_SUBDIR(&p) && lsAction != LS_Count && lsAction != LS_GetFilename) {
// Allocate enough stack space for the full path to a folder
int len = strlen(prepend) + FILENAME_LENGTH + 1;
char path[len];
// Get the short name for the item, which we know is a folder
char lfilename[FILENAME_LENGTH]; char lfilename[FILENAME_LENGTH];
createFilename(lfilename, p); createFilename(lfilename, p);
path[0] = 0; // Append the FOLDERNAME12/ to the passed string.
if (prepend[0] == 0) strcat(path, "/"); //avoid leading / if already in prepend // It contains the full path to the "parent" argument.
// We now have the full path to the item in this folder.
path[0] = '\0';
if (prepend[0] == '\0') strcat(path, "/"); // a root slash if prepend is empty
strcat(path, prepend); strcat(path, prepend);
strcat(path, lfilename); strcat(path, lfilename);
strcat(path, "/"); strcat(path, "/");
// Serial.print(path); // Serial.print(path);
// Get a new directory object using the full path
// and dive recursively into it.
SdFile dir; SdFile dir;
if (!dir.open(parent, lfilename, O_READ)) { if (!dir.open(parent, lfilename, O_READ)) {
if (lsAction == LS_SerialPrint) { if (lsAction == LS_SerialPrint) {
@ -67,14 +86,13 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
} }
} }
lsDive(path, dir); lsDive(path, dir);
//close done automatically by destructor of SdFile // close() is done automatically by destructor of SdFile
} }
else { else {
char pn0 = p.name[0]; char pn0 = p.name[0];
if (pn0 == DIR_NAME_FREE) break; if (pn0 == DIR_NAME_FREE) break;
if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue; if (pn0 == DIR_NAME_DELETED || pn0 == '.') continue;
char lf0 = longFilename[0]; if (longFilename[0] == '.') continue;
if (lf0 == '.') continue;
if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue; if (!DIR_IS_FILE_OR_SUBDIR(&p)) continue;
@ -82,24 +100,27 @@ void CardReader::lsDive(const char *prepend, SdFile parent, const char * const m
if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue; if (!filenameIsDir && (p.name[8] != 'G' || p.name[9] == '~')) continue;
//if (cnt++ != nr) continue; switch (lsAction) {
case LS_Count:
nrFiles++;
break;
case LS_SerialPrint:
createFilename(filename, p); createFilename(filename, p);
if (lsAction == LS_SerialPrint) {
SERIAL_PROTOCOL(prepend); SERIAL_PROTOCOL(prepend);
SERIAL_PROTOCOLLN(filename); SERIAL_PROTOCOLLN(filename);
} break;
else if (lsAction == LS_Count) { case LS_GetFilename:
nrFiles++; createFilename(filename, p);
}
else if (lsAction == LS_GetFilename) {
if (match != NULL) { if (match != NULL) {
if (strcasecmp(match, filename) == 0) return; if (strcasecmp(match, filename) == 0) return;
} }
else if (cnt == nrFiles) return; else if (cnt == nrFiles) return;
cnt++; cnt++;
break;
} }
} }
} } // while readDir
} }
void CardReader::ls() { void CardReader::ls() {