2017-02-20 18:55:16 +00:00
|
|
|
#include <Arduino.h>
|
|
|
|
#include <wchar.h>
|
|
|
|
#include "teensy-filemanager.h"
|
|
|
|
#include <string.h> // strcpy
|
2020-07-04 12:04:59 +00:00
|
|
|
#include "teensy-println.h"
|
2017-02-20 18:55:16 +00:00
|
|
|
|
2020-08-03 00:34:48 +00:00
|
|
|
/* FIXME globals */
|
|
|
|
static SdFat sd;
|
2020-12-29 02:48:00 +00:00
|
|
|
static FsFile cacheFile;
|
|
|
|
static FsFile outerDir;
|
2020-08-03 00:34:48 +00:00
|
|
|
|
2021-01-10 02:32:40 +00:00
|
|
|
#include "iocompat.h"
|
2020-08-03 00:34:48 +00:00
|
|
|
|
2017-02-20 18:55:16 +00:00
|
|
|
TeensyFileManager::TeensyFileManager()
|
|
|
|
{
|
|
|
|
numCached = 0;
|
2018-01-07 10:08:29 +00:00
|
|
|
|
2020-07-03 02:03:29 +00:00
|
|
|
// FIXME: used to have 'enabled = sd.begin()' here, but we weren't
|
|
|
|
// using the enabled flag, so I've removed it to save the RAM for
|
|
|
|
// now; but eventually we need better error handling here
|
2020-08-03 00:34:48 +00:00
|
|
|
sd.begin(SdioConfig(FIFO_SDIO));
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TeensyFileManager::~TeensyFileManager()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
int8_t TeensyFileManager::openFile(const char *name)
|
|
|
|
{
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cacheFd != -1) {
|
|
|
|
cacheFile.close();
|
|
|
|
cacheFd = -1;
|
2017-12-29 20:35:47 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 18:55:16 +00:00
|
|
|
// See if there's a hole to re-use...
|
|
|
|
for (int i=0; i<numCached; i++) {
|
|
|
|
if (cachedNames[i][0] == '\0') {
|
|
|
|
strncpy(cachedNames[i], name, MAXPATH-1);
|
|
|
|
cachedNames[i][MAXPATH-1] = '\0'; // safety: ensure string terminator
|
|
|
|
fileSeekPositions[i] = 0;
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for too many open files
|
|
|
|
if (numCached >= MAXFILES)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
|
|
// No, so we'll add it to the end
|
|
|
|
strncpy(cachedNames[numCached], name, MAXPATH-1);
|
|
|
|
cachedNames[numCached][MAXPATH-1] = '\0'; // safety: ensure string terminator
|
|
|
|
fileSeekPositions[numCached] = 0;
|
|
|
|
|
|
|
|
numCached++;
|
|
|
|
return numCached-1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TeensyFileManager::closeFile(int8_t fd)
|
|
|
|
{
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cacheFd != -1) {
|
|
|
|
cacheFile.close();
|
|
|
|
cacheFd = -1;
|
2017-12-29 20:35:47 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 18:55:16 +00:00
|
|
|
// invalid fd provided?
|
|
|
|
if (fd < 0 || fd >= numCached)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// clear the name
|
|
|
|
cachedNames[fd][0] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *TeensyFileManager::fileName(int8_t fd)
|
|
|
|
{
|
|
|
|
if (fd < 0 || fd >= numCached)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return cachedNames[fd];
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:39:56 +00:00
|
|
|
void TeensyFileManager::closeDir()
|
|
|
|
{
|
|
|
|
// FIXME: this should Threads::Scope lock, but it's being called
|
|
|
|
// from readDir, so that would block
|
|
|
|
if (outerDir) {
|
|
|
|
outerDir.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-20 18:55:16 +00:00
|
|
|
// suffix may be comma-separated
|
2020-07-11 11:39:56 +00:00
|
|
|
int16_t TeensyFileManager::readDir(const char *where, const char *suffix, char *outputFN, int16_t startIdx, uint16_t maxlen)
|
2017-02-20 18:55:16 +00:00
|
|
|
{
|
2020-07-11 11:39:56 +00:00
|
|
|
// First entry is always "../" if we're in a subdir of the root
|
|
|
|
if (startIdx == 0 || !outerDir) {
|
|
|
|
if (outerDir)
|
|
|
|
closeDir();
|
2020-08-03 00:34:48 +00:00
|
|
|
char buf[255];
|
|
|
|
strcpy(buf, where);
|
|
|
|
if (strlen(buf) > 1 && buf[strlen(buf)-1] == '/') {
|
|
|
|
buf[strlen(buf)-1] = 0;
|
|
|
|
}
|
|
|
|
if (!outerDir.open(buf, O_RDONLY)) {
|
2020-07-11 11:39:56 +00:00
|
|
|
return -1;
|
2020-08-03 00:34:48 +00:00
|
|
|
}
|
2020-07-11 11:39:56 +00:00
|
|
|
if (strcmp(where, "/")) { // FIXME: is this correct for the root?
|
2017-02-20 18:55:16 +00:00
|
|
|
strcpy(outputFN, "../");
|
|
|
|
return 0;
|
2020-07-11 11:39:56 +00:00
|
|
|
}
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
|
|
|
|
2020-07-11 11:39:56 +00:00
|
|
|
outputFN[0] = '\0';
|
2017-02-20 18:55:16 +00:00
|
|
|
|
2020-12-29 02:48:00 +00:00
|
|
|
FsFile e;
|
2020-08-03 00:34:48 +00:00
|
|
|
while (e.openNext(&outerDir, O_RDONLY)) {
|
2017-02-20 18:55:16 +00:00
|
|
|
|
2018-01-07 10:08:29 +00:00
|
|
|
// Skip MAC fork files
|
2020-07-06 11:04:22 +00:00
|
|
|
// FIXME: strncpy
|
|
|
|
strcpy(outputFN, e.name()); // and we need maxlen-1 for trailing '/' on directories
|
2020-08-03 00:34:48 +00:00
|
|
|
e.getName(outputFN, maxlen-1); // -1 for trailing '/' on directories
|
|
|
|
|
2018-01-07 10:08:29 +00:00
|
|
|
if (outputFN[0] == '.') {
|
|
|
|
e.close();
|
2017-02-20 18:55:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-01-07 10:08:29 +00:00
|
|
|
// skip anything that has the wrong suffix and isn't a directory
|
|
|
|
if (suffix && !e.isDirectory() && strlen(outputFN) >= 3) {
|
|
|
|
const char *fsuff = &outputFN[strlen(outputFN)-3];
|
2017-02-20 18:55:16 +00:00
|
|
|
if (strstr(suffix, ",")) {
|
2018-01-07 10:08:29 +00:00
|
|
|
// multiple suffixes to check - all must be 3 chars long, FIXME
|
2017-02-20 18:55:16 +00:00
|
|
|
bool matchesAny = false;
|
|
|
|
const char *p = suffix;
|
2020-07-06 20:46:14 +00:00
|
|
|
while (p && *p && strlen(p)) {
|
2017-02-20 18:55:16 +00:00
|
|
|
if (!strncasecmp(fsuff, p, 3)) {
|
|
|
|
matchesAny = true;
|
|
|
|
break;
|
|
|
|
}
|
2020-07-06 20:46:14 +00:00
|
|
|
p = strstr(p, ",");
|
|
|
|
if (p) p++;
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
2018-01-07 10:08:29 +00:00
|
|
|
if (!matchesAny) {
|
|
|
|
e.close();
|
2017-02-20 18:55:16 +00:00
|
|
|
continue;
|
2018-01-07 10:08:29 +00:00
|
|
|
}
|
2017-02-20 18:55:16 +00:00
|
|
|
} else {
|
|
|
|
// one suffix to check
|
2018-01-07 10:08:29 +00:00
|
|
|
if (strcasecmp(fsuff, suffix)) {
|
|
|
|
e.close();
|
2017-02-20 18:55:16 +00:00
|
|
|
continue;
|
2018-01-07 10:08:29 +00:00
|
|
|
}
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-11 11:39:56 +00:00
|
|
|
if (e.isDirectory()) {
|
|
|
|
strcat(outputFN, "/");
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
2020-07-11 11:39:56 +00:00
|
|
|
e.close();
|
|
|
|
|
|
|
|
return startIdx;
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 00:34:48 +00:00
|
|
|
/* Reached the end of the files */
|
|
|
|
|
|
|
|
closeDir();
|
|
|
|
return -1;
|
2017-02-20 18:55:16 +00:00
|
|
|
}
|
|
|
|
|
2017-12-29 20:35:47 +00:00
|
|
|
bool TeensyFileManager::_prepCache(int8_t fd)
|
|
|
|
{
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cacheFd == -1 ||
|
|
|
|
cacheFd != fd) {
|
2017-12-29 20:35:47 +00:00
|
|
|
|
|
|
|
// Not our cached file, or we have no cached file
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cacheFd != -1) {
|
2017-12-29 20:35:47 +00:00
|
|
|
// Close the old one if we had one
|
2020-07-03 02:03:29 +00:00
|
|
|
cacheFile.close();
|
|
|
|
cacheFd = -1;
|
2017-12-29 20:35:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Open the new one
|
2021-01-10 02:32:40 +00:00
|
|
|
cacheFile.open(cachedNames[fd], O_CREAT|O_RDWR);
|
2020-07-06 11:04:22 +00:00
|
|
|
if (!cacheFile) {
|
2021-01-10 02:32:40 +00:00
|
|
|
printf("failed to open cacheFile\n");
|
2017-12-29 20:35:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-07-03 02:03:29 +00:00
|
|
|
cacheFd = fd; // cache is live
|
2017-12-29 20:35:47 +00:00
|
|
|
}
|
|
|
|
|
2021-01-10 02:32:40 +00:00
|
|
|
return true;
|
2017-12-29 20:35:47 +00:00
|
|
|
}
|
|
|
|
|
2018-02-07 15:20:26 +00:00
|
|
|
void TeensyFileManager::getRootPath(char *toWhere, int8_t maxLen)
|
|
|
|
{
|
|
|
|
strcpy(toWhere, "/A2DISKS/");
|
|
|
|
// strncpy(toWhere, "/A2DISKS/", maxLen);
|
|
|
|
}
|
2019-02-21 03:06:55 +00:00
|
|
|
|
2020-07-04 12:04:59 +00:00
|
|
|
// FIXME: this should be private
|
2019-02-21 03:06:55 +00:00
|
|
|
bool TeensyFileManager::setSeekPosition(int8_t fd, uint32_t pos)
|
|
|
|
{
|
|
|
|
seekToEnd(fd);
|
|
|
|
uint32_t endPos = getSeekPosition(fd);
|
|
|
|
if (pos >= endPos) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fileSeekPositions[fd] = pos;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-07-04 12:04:59 +00:00
|
|
|
// FIXME: this should be private
|
2019-02-21 03:06:55 +00:00
|
|
|
void TeensyFileManager::seekToEnd(int8_t fd)
|
|
|
|
{
|
2020-12-29 02:48:00 +00:00
|
|
|
FsFile f;
|
2020-08-03 00:34:48 +00:00
|
|
|
f.open(cachedNames[fd], O_RDONLY);
|
2020-07-06 11:04:22 +00:00
|
|
|
if (!f) {
|
2019-02-21 03:06:55 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-07-06 11:04:22 +00:00
|
|
|
fileSeekPositions[fd] = f.size();
|
2019-02-21 03:06:55 +00:00
|
|
|
f.close();
|
|
|
|
}
|
|
|
|
|
2020-06-28 13:21:27 +00:00
|
|
|
int TeensyFileManager::write(int8_t fd, const void *buf, int nbyte)
|
|
|
|
{
|
|
|
|
// open, seek, write, close.
|
|
|
|
if (fd < 0 || fd >= numCached) {
|
2021-01-10 02:32:40 +00:00
|
|
|
printf("no fd\n");
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cachedNames[fd][0] == 0) {
|
2021-01-10 02:32:40 +00:00
|
|
|
printf("no name\n");
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
_prepCache(fd);
|
|
|
|
|
|
|
|
uint32_t pos = fileSeekPositions[fd];
|
|
|
|
|
2020-07-06 11:04:22 +00:00
|
|
|
if (!cacheFile.seek(pos)) {
|
2021-01-10 02:32:40 +00:00
|
|
|
printf("can't seek to %d\n", pos);
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-07-06 11:04:22 +00:00
|
|
|
if (cacheFile.write((const uint8_t *)buf, (size_t)nbyte) != (size_t)nbyte) {
|
2021-01-10 02:32:40 +00:00
|
|
|
printf("can't write\n");
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
fileSeekPositions[fd] += nbyte;
|
|
|
|
return nbyte;
|
|
|
|
};
|
|
|
|
|
|
|
|
int TeensyFileManager::read(int8_t fd, void *buf, int nbyte)
|
|
|
|
{
|
|
|
|
// open, seek, read, close.
|
2020-07-03 02:03:29 +00:00
|
|
|
if (fd < 0 || fd >= numCached) {
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
2020-07-03 02:03:29 +00:00
|
|
|
}
|
2020-06-28 13:21:27 +00:00
|
|
|
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cachedNames[fd][0] == 0) {
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
2020-07-03 02:03:29 +00:00
|
|
|
}
|
2020-06-28 13:21:27 +00:00
|
|
|
|
|
|
|
_prepCache(fd);
|
|
|
|
|
|
|
|
uint32_t pos = fileSeekPositions[fd];
|
2020-07-06 11:04:22 +00:00
|
|
|
if (!cacheFile.seek(pos)) {
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2021-01-10 02:32:40 +00:00
|
|
|
|
2020-07-03 02:03:29 +00:00
|
|
|
fileSeekPositions[fd] += nbyte;
|
2020-06-28 13:21:27 +00:00
|
|
|
|
2020-07-03 02:03:29 +00:00
|
|
|
if (cacheFile.read(buf, nbyte) != nbyte) {
|
2020-06-28 13:21:27 +00:00
|
|
|
return -1;
|
2020-07-03 02:03:29 +00:00
|
|
|
}
|
2020-06-28 13:21:27 +00:00
|
|
|
|
|
|
|
return nbyte;
|
|
|
|
};
|
|
|
|
|
|
|
|
int TeensyFileManager::lseek(int8_t fd, int offset, int whence)
|
|
|
|
{
|
|
|
|
if (whence == SEEK_CUR && offset == 0) {
|
|
|
|
return fileSeekPositions[fd];
|
|
|
|
}
|
|
|
|
if (whence == SEEK_SET) {
|
|
|
|
if (!setSeekPosition(fd, offset))
|
|
|
|
return -1;
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
// Other cases not supported yet
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
|