diff --git a/include/pros/misc.h b/include/pros/misc.h index d0ad6764..56a2d8c1 100644 --- a/include/pros/misc.h +++ b/include/pros/misc.h @@ -723,6 +723,55 @@ double battery_get_capacity(void); */ int32_t usd_is_installed(void); +/** + * Lists the files in a directory specified by the path + * Puts the list of file names (NOT DIRECTORIES) into the buffer seperated by newlines + * + * This function uses the following values of errno when an error state is + * reached: + * + * EIO - Hard error occured in the low level disk I/O layer + * EINVAL - file or directory is invalid, or length is invalid + * EBUSY - THe physical drinve cannot work + * ENOENT - cannot find the path or file + * EINVAL - the path name format is invalid + * EACCES - Access denied or directory full + * EEXIST - Access denied + * EROFS - SD card is write protected + * ENXIO - drive number is invalid or not a FAT32 drive + * ENOBUFS - drive has no work area + * ETIMEDOUT - Operation timed out while accessing the volume + * ENFILE - too many open files + * + * \param path + * The path to the directory to list files in + * \param buffer + * The buffer to put the file names into + * \param len + * The length of the buffer + * + * \note use a path of "\" to list the files in the main directory NOT "/usd/" + * DO NOT PREPEND YOUR PATHS WITH "/usd/" + * + * \return 1 on success or PROS_ERR on failure setting errno + * + * \b Example + * \code + * void opcontrol() { + * char* test = (char*) malloc(128); + * pros::c::usd_list_files_raw("/", test, 128); + * pros::delay(200); + * printf("%s\n", test); //Prints the file names in the root directory seperated by newlines + * pros::delay(100); + * pros::c::usd_list_files_raw("/test", test, 128); + * pros::delay(200); + * printf("%s\n", test); //Prints the names of files in the folder named test seperated by newlines + * pros::delay(100); + * } + * \endcode +*/ +int32_t usd_list_files_raw(const char* path, char* buffer, int32_t len); + /******************************************************************************/ /** Date and Time **/ /******************************************************************************/ diff --git a/include/pros/misc.hpp b/include/pros/misc.hpp index 8ade055a..c40849e2 100644 --- a/include/pros/misc.hpp +++ b/include/pros/misc.hpp @@ -26,6 +26,7 @@ #include #include +#include namespace pros { inline namespace v5 { @@ -525,6 +526,106 @@ namespace usd { * \endcode */ std::int32_t is_installed(void); + +/** + * Lists the files in a directory specified by the path + * Puts the list of file names (NOT DIRECTORIES) into the buffer seperated by newlines + * + * This function uses the following values of errno when an error state is + * reached: + * + * EIO - Hard error occured in the low level disk I/O layer + * EINVAL - file or directory is invalid, or length is invalid + * EBUSY - THe physical drinve cannot work + * ENOENT - cannot find the path or file + * EINVAL - the path name format is invalid + * EACCES - Access denied or directory full + * EEXIST - Access denied + * EROFS - SD card is write protected + * ENXIO - drive number is invalid or not a FAT32 drive + * ENOBUFS - drive has no work area + * ENFILE - too many open files + * + * \param path + * The path to the directory to list files in + * \param buffer + * The buffer to put the file names into + * \param len + * The length of the buffer + * + * \note use a path of "\" to list the files in the main directory NOT "/usd/" + * DO NOT PREPEND YOUR PATHS WITH "/usd/" + * + * \return 1 on success or PROS_ERR on failure, setting errno + * + * \b Example + * \code + * void opcontrol() { + * char* test = (char*) malloc(128); + * pros::usd::list_files_raw("/", test, 128); + * pros::delay(200); + * printf("%s\n", test); //Prints the file names in the root directory seperated by newlines + * pros::delay(100); + * pros::list_files_raw("/test", test, 128); + * pros::delay(200); + * printf("%s\n", test); //Prints the names of files in the folder named test seperated by newlines + * pros::delay(100); + * } + * \endcode +*/ +std::int32_t list_files_raw(const char* path, char* buffer, int32_t len); + +/** + * Lists the files in a directory specified by the path + * Puts the list of file paths (NOT DIRECTORIES) into a vector of std::string + * + * This function uses the following values of errno when an error state is + * reached: + * + * EIO - Hard error occured in the low level disk I/O layer + * EINVAL - file or directory is invalid, or length is invalid + * EBUSY - THe physical drinve cannot work + * ENOENT - cannot find the path or file + * EINVAL - the path name format is invalid + * EACCES - Access denied or directory full + * EEXIST - Access denied + * EROFS - SD card is write protected + * ENXIO - drive number is invalid or not a FAT32 drive + * ENOBUFS - drive has no work area + * ETIMEDOUT - Operation timed out while accessing the volume + * ENFILE - too many open files + * + * \param path + * The path to the directory to list files in + * + * \return vector of std::string of file names, if error occurs, returns vector containing + * two elements, first element is "ERROR" and second element is the error message + * + * \b Example + * \code + * void opcontrol() { + * // Will return vector containing file paths of files in root directory + * std::vector files = pros::usd::list_files("/test"); + * pros::delay(200); + * // Given vector of std::string file paths, print each file path + * // Note that if there is an error, the vector will contain two elements, + * // first element is "ERROR" and second element is the error message + * + * // Check if error occurred + * if (file_list.front().start_with("ERROR")) { + * // deal with error + * } + * else { + * // file list returned is valid + * // Print each file + * for (std::string& file : files) { + * std::cout << file << std::endl; + * } + * } + * } + * \endcode +*/ +std::vector list_files(const char* path); } // namespace usd } // namespace pros diff --git a/src/devices/vdml_usd.c b/src/devices/vdml_usd.c index 879f6525..c5355880 100644 --- a/src/devices/vdml_usd.c +++ b/src/devices/vdml_usd.c @@ -17,3 +17,15 @@ int32_t usd_is_installed(void) { return vexFileDriveStatus(0); } +static const int FRESULTMAP[] = {0, EIO, EINVAL, EBUSY, ENOENT, ENOENT, EINVAL, EACCES, // FR_DENIED + EEXIST, EINVAL, EROFS, ENXIO, ENOBUFS, ENXIO, EIO, EACCES, // FR_LOCKED + ENOBUFS, ENFILE, EINVAL}; + +int32_t usd_list_files_raw(const char* path, char* buffer, int32_t len) { + FRESULT result = vexFileDirectoryGet(path, buffer, len); + if (result != F_OK) { + errno = FRESULTMAP[result]; + return PROS_ERR; + } + return PROS_SUCCESS; +} diff --git a/src/devices/vdml_usd.cpp b/src/devices/vdml_usd.cpp index 71318463..8de9c5eb 100644 --- a/src/devices/vdml_usd.cpp +++ b/src/devices/vdml_usd.cpp @@ -21,5 +21,90 @@ std::int32_t is_installed(void) { return usd_is_installed(); } +std::int32_t list_files_raw(const char* path, char* buffer, int32_t len) { + return usd_list_files_raw(path, buffer, len); +} + +std::vector list_files(const char* path) { + std::vector files = {}; + // malloc buffer to store file names + size_t buffer_size = 10000; + char *buffer = (char *) malloc(buffer_size); + if (buffer == NULL) { + // try again smaller buffer to see if that works + buffer_size = 500; + buffer = (char *) malloc(buffer_size); + if (buffer == NULL) { + // if still fails, return vector containing error state + // set errno to ENOMEM + errno = ENOMEM; + files.push_back("ERROR"); + files.push_back("not enough memory to get file names"); + return files; + } + } + + // Check path user passed in + std::string_view path_sv(path); + constexpr std::string_view usd_prefix {"usd"}; + const size_t usd_prefix_idx = path_sv.find(usd_prefix); + if (usd_prefix_idx == 0 || usd_prefix_idx == 1) { + // Deal with when user prepends path with usd + // as either "usd/..." or "/usd/..." + path_sv.remove_prefix(usd_prefix.length() + usd_prefix_idx); + } + + // set path to path_sv.data() + path = path_sv.data(); + + // Call the C function + int32_t success = usd_list_files_raw(path, buffer, buffer_size); + // Check if call successful, if error return vector containing error state + if (success == PROS_ERR) { + files.push_back("ERROR"); + // Check errno to see which error state occurred + // push back error state to files vector as std::string + if (errno == EINVAL || errno == ENOENT) { + // errors related to path not found + files.push_back("path not found"); + } else { + // other error stats related to file io + files.push_back("file i/o error"); + } + return files; + } + + // Parse buffer given call successful, split by '/n' + std::string_view str(buffer); + + // delimter_pos is the position of the delimiter '\n' + // index of which character to start substr from + // file_name used to store each file name + size_t delimiter_pos = 0; + size_t index = 0; + std::string file_name; + + // Loop until delimiter '\n' can not be found anymore + while ((delimiter_pos = str.find('\n', index)) != std::string::npos) { + // file_name is the string from the beginning of str to the first '\n', excluding '\n' + file_name = std::string(str.data() + index, delimiter_pos - index); + + files.push_back(file_name); + // Increment index to start substr from + index = delimiter_pos + 1; + + // If index is greater than or equal to str length, break + if (index >= str.length()) { + break; + } + } + + // Free buffer + free(buffer); + + // Return vector of file names + return files; +} + } // namespace usd } // namespace pros