|
|
@ -261,7 +261,14 @@ void CardReader::selectByName(SdFile dir, const char * const match) { |
|
|
|
} |
|
|
|
|
|
|
|
//
|
|
|
|
// Recursive method to list all files within a folder
|
|
|
|
// Recursive method to print all files within a folder in flat
|
|
|
|
// DOS 8.3 format. This style of listing is the most compatible
|
|
|
|
// with legacy hosts.
|
|
|
|
//
|
|
|
|
// This method recurses to unlimited depth and lists every
|
|
|
|
// G-code file within the given parent. If the hierarchy is
|
|
|
|
// very deep this can blow up the stack, so a 'depth' parameter
|
|
|
|
// (as with printListingJSON) would be a good addition.
|
|
|
|
//
|
|
|
|
void CardReader::printListing(SdFile parent, const char * const prepend/*=nullptr*/) { |
|
|
|
dir_t p; |
|
|
@ -288,17 +295,17 @@ void CardReader::printListing(SdFile parent, const char * const prepend/*=nullpt |
|
|
|
|
|
|
|
// Get a new directory object using the full path
|
|
|
|
// and dive recursively into it.
|
|
|
|
SdFile child; |
|
|
|
if (!child.open(&parent, dosFilename, O_READ)) |
|
|
|
SdFile child; // child.close() in destructor
|
|
|
|
if (child.open(&parent, dosFilename, O_READ)) |
|
|
|
printListing(child, path); |
|
|
|
else { |
|
|
|
SERIAL_ECHO_MSG(STR_SD_CANT_OPEN_SUBDIR, dosFilename); |
|
|
|
|
|
|
|
printListing(child, path); |
|
|
|
// close() is done automatically by destructor of SdFile
|
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
else if (is_dir_or_gcode(p)) { |
|
|
|
createFilename(filename, p); |
|
|
|
if (prepend) SERIAL_ECHO(prepend); |
|
|
|
SERIAL_ECHO(filename); |
|
|
|
SERIAL_ECHO(createFilename(filename, p)); |
|
|
|
SERIAL_CHAR(' '); |
|
|
|
SERIAL_ECHOLN(p.fileSize); |
|
|
|
} |
|
|
@ -342,7 +349,7 @@ void CardReader::ls() { |
|
|
|
// Go to the next segment
|
|
|
|
while (path[++i]) { } |
|
|
|
|
|
|
|
// SERIAL_ECHOPGM("Looking for segment: "); SERIAL_ECHOLN(segment);
|
|
|
|
//SERIAL_ECHOLNPAIR("Looking for segment: ", segment);
|
|
|
|
|
|
|
|
// Find the item, setting the long filename
|
|
|
|
diveDir.rewind(); |
|
|
@ -720,14 +727,14 @@ void CardReader::removeFile(const char * const name) { |
|
|
|
|
|
|
|
//abortFilePrintNow();
|
|
|
|
|
|
|
|
SdFile *curDir; |
|
|
|
const char * const fname = diveToFile(false, curDir, name); |
|
|
|
SdFile *itsDirPtr; |
|
|
|
const char * const fname = diveToFile(false, itsDirPtr, name); |
|
|
|
if (!fname) return; |
|
|
|
|
|
|
|
#if ENABLED(SDCARD_READONLY) |
|
|
|
SERIAL_ECHOLNPAIR("Deletion failed (read-only), File: ", fname, "."); |
|
|
|
#else |
|
|
|
if (file.remove(curDir, fname)) { |
|
|
|
if (file.remove(itsDirPtr, fname)) { |
|
|
|
SERIAL_ECHOLNPAIR("File deleted:", fname); |
|
|
|
sdpos = 0; |
|
|
|
TERN_(SDCARD_SORT_ALPHA, presort()); |
|
|
@ -870,98 +877,101 @@ uint16_t CardReader::countFilesInWorkDir() { |
|
|
|
* - If update_cwd was 'true' the workDir now points to the file's directory. |
|
|
|
* |
|
|
|
* Returns a pointer to the last segment (filename) of the given DOS 8.3 path. |
|
|
|
* On exit, inDirPtr contains an SdFile reference to the file's directory. |
|
|
|
* |
|
|
|
* A nullptr result indicates an unrecoverable error. |
|
|
|
* |
|
|
|
* NOTE: End the path with a slash to dive to a folder. In this case the |
|
|
|
* returned filename will be blank (points to the end of the path). |
|
|
|
*/ |
|
|
|
const char* CardReader::diveToFile(const bool update_cwd, SdFile* &diveDir, const char * const path, const bool echo/*=false*/) { |
|
|
|
const char* CardReader::diveToFile(const bool update_cwd, SdFile* &inDirPtr, const char * const path, const bool echo/*=false*/) { |
|
|
|
DEBUG_SECTION(est, "diveToFile", true); |
|
|
|
|
|
|
|
// Track both parent and subfolder
|
|
|
|
static SdFile newDir1, newDir2; |
|
|
|
SdFile *sub = &newDir1, *startDir; |
|
|
|
SdFile *sub = &newDir1, *startDirPtr; |
|
|
|
|
|
|
|
// Parsing the path string
|
|
|
|
const char *item_name_adr = path; |
|
|
|
const char *atom_ptr = path; |
|
|
|
|
|
|
|
DEBUG_ECHOLNPAIR(" path = '", path, "'"); |
|
|
|
|
|
|
|
if (path[0] == '/') { // Starting at the root directory?
|
|
|
|
diveDir = &root; |
|
|
|
item_name_adr++; |
|
|
|
DEBUG_ECHOLNPAIR(" CWD to root: ", hex_address((void*)diveDir)); |
|
|
|
inDirPtr = &root; |
|
|
|
atom_ptr++; |
|
|
|
DEBUG_ECHOLNPAIR(" CWD to root: ", hex_address((void*)inDirPtr)); |
|
|
|
if (update_cwd) workDirDepth = 0; // The cwd can be updated for the benefit of sub-programs
|
|
|
|
} |
|
|
|
else |
|
|
|
diveDir = &workDir; // Dive from workDir (as set by the UI)
|
|
|
|
inDirPtr = &workDir; // Dive from workDir (as set by the UI)
|
|
|
|
|
|
|
|
startDir = diveDir; |
|
|
|
startDirPtr = inDirPtr; |
|
|
|
|
|
|
|
DEBUG_ECHOLNPAIR(" startDir = ", hex_address((void*)startDir)); |
|
|
|
DEBUG_ECHOLNPAIR(" startDirPtr = ", hex_address((void*)startDirPtr)); |
|
|
|
|
|
|
|
while (item_name_adr) { |
|
|
|
while (atom_ptr) { |
|
|
|
// Find next subdirectory delimiter
|
|
|
|
char * const name_end = strchr(item_name_adr, '/'); |
|
|
|
char * const name_end = strchr(atom_ptr, '/'); |
|
|
|
|
|
|
|
// Last atom in the path? Item found.
|
|
|
|
if (name_end <= item_name_adr) break; |
|
|
|
if (name_end <= atom_ptr) break; |
|
|
|
|
|
|
|
// Set subDirName
|
|
|
|
const uint8_t len = name_end - item_name_adr; |
|
|
|
// Isolate the next subitem name
|
|
|
|
const uint8_t len = name_end - atom_ptr; |
|
|
|
char dosSubdirname[len + 1]; |
|
|
|
strncpy(dosSubdirname, item_name_adr, len); |
|
|
|
strncpy(dosSubdirname, atom_ptr, len); |
|
|
|
dosSubdirname[len] = 0; |
|
|
|
|
|
|
|
if (echo) SERIAL_ECHOLN(dosSubdirname); |
|
|
|
|
|
|
|
DEBUG_ECHOLNPAIR(" sub = ", hex_address((void*)sub)); |
|
|
|
|
|
|
|
// Open diveDir (closing first)
|
|
|
|
// Open inDirPtr (closing first)
|
|
|
|
sub->close(); |
|
|
|
if (!sub->open(diveDir, dosSubdirname, O_READ)) { |
|
|
|
if (!sub->open(inDirPtr, dosSubdirname, O_READ)) { |
|
|
|
openFailed(dosSubdirname); |
|
|
|
item_name_adr = nullptr; |
|
|
|
atom_ptr = nullptr; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
// Close diveDir if not at starting-point
|
|
|
|
if (diveDir != startDir) { |
|
|
|
DEBUG_ECHOLNPAIR(" closing diveDir: ", hex_address((void*)diveDir)); |
|
|
|
diveDir->close(); |
|
|
|
// Close inDirPtr if not at starting-point
|
|
|
|
if (inDirPtr != startDirPtr) { |
|
|
|
DEBUG_ECHOLNPAIR(" closing inDirPtr: ", hex_address((void*)inDirPtr)); |
|
|
|
inDirPtr->close(); |
|
|
|
} |
|
|
|
|
|
|
|
// diveDir now subDir
|
|
|
|
diveDir = sub; |
|
|
|
DEBUG_ECHOLNPAIR(" diveDir = sub: ", hex_address((void*)diveDir)); |
|
|
|
// inDirPtr now subDir
|
|
|
|
inDirPtr = sub; |
|
|
|
DEBUG_ECHOLNPAIR(" inDirPtr = sub: ", hex_address((void*)inDirPtr)); |
|
|
|
|
|
|
|
// Update workDirParents and workDirDepth
|
|
|
|
if (update_cwd) { |
|
|
|
DEBUG_ECHOLNPAIR(" update_cwd"); |
|
|
|
if (workDirDepth < MAX_DIR_DEPTH) |
|
|
|
workDirParents[workDirDepth++] = *diveDir; |
|
|
|
workDirParents[workDirDepth++] = *inDirPtr; |
|
|
|
} |
|
|
|
|
|
|
|
// Point sub at the other scratch object
|
|
|
|
sub = (diveDir != &newDir1) ? &newDir1 : &newDir2; |
|
|
|
sub = (inDirPtr != &newDir1) ? &newDir1 : &newDir2; |
|
|
|
DEBUG_ECHOLNPAIR(" swapping sub = ", hex_address((void*)sub)); |
|
|
|
|
|
|
|
// Next path atom address
|
|
|
|
item_name_adr = name_end + 1; |
|
|
|
atom_ptr = name_end + 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (update_cwd) { |
|
|
|
workDir = *diveDir; |
|
|
|
DEBUG_ECHOLNPAIR(" final workDir = ", hex_address((void*)diveDir)); |
|
|
|
workDir = *inDirPtr; |
|
|
|
DEBUG_ECHOLNPAIR(" final workDir = ", hex_address((void*)inDirPtr)); |
|
|
|
flag.workDirIsRoot = (workDirDepth == 0); |
|
|
|
TERN_(SDCARD_SORT_ALPHA, presort()); |
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_ECHOLNPAIR(" returning string ", item_name_adr ?: "nullptr"); |
|
|
|
return item_name_adr; |
|
|
|
DEBUG_ECHOLNPAIR(" returning string ", atom_ptr ?: "nullptr"); |
|
|
|
return atom_ptr; |
|
|
|
} |
|
|
|
|
|
|
|
void CardReader::cd(const char * relpath) { |
|
|
|
SdFile newDir; |
|
|
|
SdFile *parent = workDir.isOpen() ? &workDir : &root; |
|
|
|
SdFile newDir, *parent = &getWorkDir(); |
|
|
|
|
|
|
|
if (newDir.open(parent, relpath, O_READ)) { |
|
|
|
workDir = newDir; |
|
|
|