[136] | 1 | /*
|
---|
| 2 |
|
---|
| 3 | SD - a slightly more friendly wrapper for sdfatlib
|
---|
| 4 |
|
---|
| 5 | This library aims to expose a subset of SD card functionality
|
---|
| 6 | in the form of a higher level "wrapper" object.
|
---|
| 7 |
|
---|
| 8 | License: GNU General Public License V3
|
---|
| 9 | (Because sdfatlib is licensed with this.)
|
---|
| 10 |
|
---|
| 11 | (C) Copyright 2010 SparkFun Electronics
|
---|
| 12 |
|
---|
| 13 |
|
---|
| 14 | This library provides four key benefits:
|
---|
| 15 |
|
---|
| 16 | * Including `SD.h` automatically creates a global
|
---|
| 17 | `SD` object which can be interacted with in a similar
|
---|
| 18 | manner to other standard global objects like `Serial` and `Ethernet`.
|
---|
| 19 |
|
---|
| 20 | * Boilerplate initialisation code is contained in one method named
|
---|
| 21 | `begin` and no further objects need to be created in order to access
|
---|
| 22 | the SD card.
|
---|
| 23 |
|
---|
| 24 | * Calls to `open` can supply a full path name including parent
|
---|
| 25 | directories which simplifies interacting with files in subdirectories.
|
---|
| 26 |
|
---|
| 27 | * Utility methods are provided to determine whether a file exists
|
---|
| 28 | and to create a directory heirarchy.
|
---|
| 29 |
|
---|
| 30 |
|
---|
| 31 | Note however that not all functionality provided by the underlying
|
---|
| 32 | sdfatlib library is exposed.
|
---|
| 33 |
|
---|
| 34 | */
|
---|
| 35 |
|
---|
| 36 | /*
|
---|
| 37 |
|
---|
| 38 | Implementation Notes
|
---|
| 39 |
|
---|
| 40 | In order to handle multi-directory path traversal, functionality that
|
---|
| 41 | requires this ability is implemented as callback functions.
|
---|
| 42 |
|
---|
| 43 | Individual methods call the `walkPath` function which performs the actual
|
---|
| 44 | directory traversal (swapping between two different directory/file handles
|
---|
| 45 | along the way) and at each level calls the supplied callback function.
|
---|
| 46 |
|
---|
| 47 | Some types of functionality will take an action at each level (e.g. exists
|
---|
| 48 | or make directory) which others will only take an action at the bottom
|
---|
| 49 | level (e.g. open).
|
---|
| 50 |
|
---|
| 51 | */
|
---|
| 52 |
|
---|
| 53 | #include "SD.h"
|
---|
| 54 |
|
---|
| 55 | // Used by `getNextPathComponent`
|
---|
| 56 | #define MAX_COMPONENT_LEN 12 // What is max length?
|
---|
| 57 | #define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
|
---|
| 58 |
|
---|
| 59 | bool getNextPathComponent(char *path, unsigned int *p_offset,
|
---|
| 60 | char *buffer) {
|
---|
| 61 | /*
|
---|
| 62 |
|
---|
| 63 | Parse individual path components from a path.
|
---|
| 64 |
|
---|
| 65 | e.g. after repeated calls '/foo/bar/baz' will be split
|
---|
| 66 | into 'foo', 'bar', 'baz'.
|
---|
| 67 |
|
---|
| 68 | This is similar to `strtok()` but copies the component into the
|
---|
| 69 | supplied buffer rather than modifying the original string.
|
---|
| 70 |
|
---|
| 71 |
|
---|
| 72 | `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
|
---|
| 73 |
|
---|
| 74 | `p_offset` needs to point to an integer of the offset at
|
---|
| 75 | which the previous path component finished.
|
---|
| 76 |
|
---|
| 77 | Returns `true` if more components remain.
|
---|
| 78 |
|
---|
| 79 | Returns `false` if this is the last component.
|
---|
| 80 | (This means path ended with 'foo' or 'foo/'.)
|
---|
| 81 |
|
---|
| 82 | */
|
---|
| 83 |
|
---|
| 84 | // TODO: Have buffer local to this function, so we know it's the
|
---|
| 85 | // correct length?
|
---|
| 86 |
|
---|
| 87 | int bufferOffset = 0;
|
---|
| 88 |
|
---|
| 89 | int offset = *p_offset;
|
---|
| 90 |
|
---|
| 91 | // Skip root or other separator
|
---|
| 92 | if (path[offset] == '/') {
|
---|
| 93 | offset++;
|
---|
| 94 | }
|
---|
| 95 |
|
---|
| 96 | // Copy the next next path segment
|
---|
| 97 | while (bufferOffset < MAX_COMPONENT_LEN
|
---|
| 98 | && (path[offset] != '/')
|
---|
| 99 | && (path[offset] != '\0')) {
|
---|
| 100 | buffer[bufferOffset++] = path[offset++];
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | buffer[bufferOffset] = '\0';
|
---|
| 104 |
|
---|
| 105 | // Skip trailing separator so we can determine if this
|
---|
| 106 | // is the last component in the path or not.
|
---|
| 107 | if (path[offset] == '/') {
|
---|
| 108 | offset++;
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | *p_offset = offset;
|
---|
| 112 |
|
---|
| 113 | return (path[offset] != '\0');
|
---|
| 114 | }
|
---|
| 115 |
|
---|
| 116 |
|
---|
| 117 |
|
---|
| 118 | boolean walkPath(char *filepath, SdFile& parentDir,
|
---|
| 119 | boolean (*callback)(SdFile& parentDir,
|
---|
| 120 | char *filePathComponent,
|
---|
| 121 | boolean isLastComponent,
|
---|
| 122 | void *object),
|
---|
| 123 | void *object = NULL) {
|
---|
| 124 | /*
|
---|
| 125 |
|
---|
| 126 | When given a file path (and parent directory--normally root),
|
---|
| 127 | this function traverses the directories in the path and at each
|
---|
| 128 | level calls the supplied callback function while also providing
|
---|
| 129 | the supplied object for context if required.
|
---|
| 130 |
|
---|
| 131 | e.g. given the path '/foo/bar/baz'
|
---|
| 132 | the callback would be called at the equivalent of
|
---|
| 133 | '/foo', '/foo/bar' and '/foo/bar/baz'.
|
---|
| 134 |
|
---|
| 135 | The implementation swaps between two different directory/file
|
---|
| 136 | handles as it traverses the directories and does not use recursion
|
---|
| 137 | in an attempt to use memory efficiently.
|
---|
| 138 |
|
---|
| 139 | If a callback wishes to stop the directory traversal it should
|
---|
| 140 | return false--in this case the function will stop the traversal,
|
---|
| 141 | tidy up and return false.
|
---|
| 142 |
|
---|
| 143 | If a directory path doesn't exist at some point this function will
|
---|
| 144 | also return false and not subsequently call the callback.
|
---|
| 145 |
|
---|
| 146 | If a directory path specified is complete, valid and the callback
|
---|
| 147 | did not indicate the traversal should be interrupted then this
|
---|
| 148 | function will return true.
|
---|
| 149 |
|
---|
| 150 | */
|
---|
| 151 |
|
---|
| 152 |
|
---|
| 153 | SdFile subfile1;
|
---|
| 154 | SdFile subfile2;
|
---|
| 155 |
|
---|
| 156 | char buffer[PATH_COMPONENT_BUFFER_LEN];
|
---|
| 157 |
|
---|
| 158 | unsigned int offset = 0;
|
---|
| 159 |
|
---|
| 160 | SdFile *p_parent;
|
---|
| 161 | SdFile *p_child;
|
---|
| 162 |
|
---|
| 163 | SdFile *p_tmp_sdfile;
|
---|
| 164 |
|
---|
| 165 | p_child = &subfile1;
|
---|
| 166 |
|
---|
| 167 | p_parent = &parentDir;
|
---|
| 168 |
|
---|
| 169 | while (true) {
|
---|
| 170 |
|
---|
| 171 | boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
|
---|
| 172 |
|
---|
| 173 | boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
|
---|
| 174 |
|
---|
| 175 | if (!shouldContinue) {
|
---|
| 176 | // TODO: Don't repeat this code?
|
---|
| 177 | // If it's one we've created then we
|
---|
| 178 | // don't need the parent handle anymore.
|
---|
| 179 | if (p_parent != &parentDir) {
|
---|
| 180 | (*p_parent).close();
|
---|
| 181 | }
|
---|
| 182 | return false;
|
---|
| 183 | }
|
---|
| 184 |
|
---|
| 185 | if (!moreComponents) {
|
---|
| 186 | break;
|
---|
| 187 | }
|
---|
| 188 |
|
---|
| 189 | boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
|
---|
| 190 |
|
---|
| 191 | // If it's one we've created then we
|
---|
| 192 | // don't need the parent handle anymore.
|
---|
| 193 | if (p_parent != &parentDir) {
|
---|
| 194 | (*p_parent).close();
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | // Handle case when it doesn't exist and we can't continue...
|
---|
| 198 | if (exists) {
|
---|
| 199 | // We alternate between two file handles as we go down
|
---|
| 200 | // the path.
|
---|
| 201 | if (p_parent == &parentDir) {
|
---|
| 202 | p_parent = &subfile2;
|
---|
| 203 | }
|
---|
| 204 |
|
---|
| 205 | p_tmp_sdfile = p_parent;
|
---|
| 206 | p_parent = p_child;
|
---|
| 207 | p_child = p_tmp_sdfile;
|
---|
| 208 | } else {
|
---|
| 209 | return false;
|
---|
| 210 | }
|
---|
| 211 | }
|
---|
| 212 |
|
---|
| 213 | if (p_parent != &parentDir) {
|
---|
| 214 | (*p_parent).close(); // TODO: Return/ handle different?
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | return true;
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 |
|
---|
| 221 |
|
---|
| 222 | /*
|
---|
| 223 |
|
---|
| 224 | The callbacks used to implement various functionality follow.
|
---|
| 225 |
|
---|
| 226 | Each callback is supplied with a parent directory handle,
|
---|
| 227 | character string with the name of the current file path component,
|
---|
| 228 | a flag indicating if this component is the last in the path and
|
---|
| 229 | a pointer to an arbitrary object used for context.
|
---|
| 230 |
|
---|
| 231 | */
|
---|
| 232 |
|
---|
| 233 | boolean callback_pathExists(SdFile& parentDir, char *filePathComponent,
|
---|
| 234 | boolean isLastComponent, void *object) {
|
---|
| 235 | /*
|
---|
| 236 |
|
---|
| 237 | Callback used to determine if a file/directory exists in parent
|
---|
| 238 | directory.
|
---|
| 239 |
|
---|
| 240 | Returns true if file path exists.
|
---|
| 241 |
|
---|
| 242 | */
|
---|
| 243 | SdFile child;
|
---|
| 244 |
|
---|
| 245 | boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
|
---|
| 246 |
|
---|
| 247 | if (exists) {
|
---|
| 248 | child.close();
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | return exists;
|
---|
| 252 | }
|
---|
| 253 |
|
---|
| 254 |
|
---|
| 255 |
|
---|
| 256 | boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent,
|
---|
| 257 | boolean isLastComponent, void *object) {
|
---|
| 258 | /*
|
---|
| 259 |
|
---|
| 260 | Callback used to create a directory in the parent directory if
|
---|
| 261 | it does not already exist.
|
---|
| 262 |
|
---|
| 263 | Returns true if a directory was created or it already existed.
|
---|
| 264 |
|
---|
| 265 | */
|
---|
| 266 | boolean result = false;
|
---|
| 267 | SdFile child;
|
---|
| 268 |
|
---|
| 269 | result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
|
---|
| 270 | if (!result) {
|
---|
| 271 | result = child.makeDir(parentDir, filePathComponent);
|
---|
| 272 | }
|
---|
| 273 |
|
---|
| 274 | return result;
|
---|
| 275 | }
|
---|
| 276 |
|
---|
| 277 |
|
---|
| 278 | /*
|
---|
| 279 |
|
---|
| 280 | boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
|
---|
| 281 | boolean isLastComponent, void *object) {
|
---|
| 282 |
|
---|
| 283 | Callback used to open a file specified by a filepath that may
|
---|
| 284 | specify one or more directories above it.
|
---|
| 285 |
|
---|
| 286 | Expects the context object to be an instance of `SDClass` and
|
---|
| 287 | will use the `file` property of the instance to open the requested
|
---|
| 288 | file/directory with the associated file open mode property.
|
---|
| 289 |
|
---|
| 290 | Always returns true if the directory traversal hasn't reached the
|
---|
| 291 | bottom of the directory heirarchy.
|
---|
| 292 |
|
---|
| 293 | Returns false once the file has been opened--to prevent the traversal
|
---|
| 294 | from descending further. (This may be unnecessary.)
|
---|
| 295 |
|
---|
| 296 | if (isLastComponent) {
|
---|
| 297 | SDClass *p_SD = static_cast<SDClass*>(object);
|
---|
| 298 | p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
|
---|
| 299 | if (p_SD->fileOpenMode == FILE_WRITE) {
|
---|
| 300 | p_SD->file.seekSet(p_SD->file.fileSize());
|
---|
| 301 | }
|
---|
| 302 | // TODO: Return file open result?
|
---|
| 303 | return false;
|
---|
| 304 | }
|
---|
| 305 | return true;
|
---|
| 306 | }
|
---|
| 307 | */
|
---|
| 308 |
|
---|
| 309 |
|
---|
| 310 |
|
---|
| 311 | boolean callback_remove(SdFile& parentDir, char *filePathComponent,
|
---|
| 312 | boolean isLastComponent, void *object) {
|
---|
| 313 | if (isLastComponent) {
|
---|
| 314 | return SdFile::remove(parentDir, filePathComponent);
|
---|
| 315 | }
|
---|
| 316 | return true;
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | boolean callback_rmdir(SdFile& parentDir, char *filePathComponent,
|
---|
| 320 | boolean isLastComponent, void *object) {
|
---|
| 321 | if (isLastComponent) {
|
---|
| 322 | SdFile f;
|
---|
| 323 | if (!f.open(parentDir, filePathComponent, O_READ)) return false;
|
---|
| 324 | return f.rmDir();
|
---|
| 325 | }
|
---|
| 326 | return true;
|
---|
| 327 | }
|
---|
| 328 |
|
---|
| 329 |
|
---|
| 330 |
|
---|
| 331 | /* Implementation of class used to create `SDCard` object. */
|
---|
| 332 |
|
---|
| 333 |
|
---|
| 334 |
|
---|
| 335 | boolean SDClass::begin(uint8_t csPin) {
|
---|
| 336 | /*
|
---|
| 337 |
|
---|
| 338 | Performs the initialisation required by the sdfatlib library.
|
---|
| 339 |
|
---|
| 340 | Return true if initialization succeeds, false otherwise.
|
---|
| 341 |
|
---|
| 342 | */
|
---|
| 343 | return card.init(SPI_HALF_SPEED, csPin) &&
|
---|
| 344 | volume.init(card) &&
|
---|
| 345 | root.openRoot(volume);
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 |
|
---|
| 349 |
|
---|
| 350 | // this little helper is used to traverse paths
|
---|
| 351 | SdFile SDClass::getParentDir(const char *filepath, int *index) {
|
---|
| 352 | // get parent directory
|
---|
| 353 | SdFile d1 = root; // start with the mostparent, root!
|
---|
| 354 | SdFile d2;
|
---|
| 355 |
|
---|
| 356 | // we'll use the pointers to swap between the two objects
|
---|
| 357 | SdFile *parent = &d1;
|
---|
| 358 | SdFile *subdir = &d2;
|
---|
| 359 |
|
---|
| 360 | const char *origpath = filepath;
|
---|
| 361 |
|
---|
| 362 | while (strchr(filepath, '/')) {
|
---|
| 363 |
|
---|
| 364 | // get rid of leading /'s
|
---|
| 365 | if (filepath[0] == '/') {
|
---|
| 366 | filepath++;
|
---|
| 367 | continue;
|
---|
| 368 | }
|
---|
| 369 |
|
---|
| 370 | if (! strchr(filepath, '/')) {
|
---|
| 371 | // it was in the root directory, so leave now
|
---|
| 372 | break;
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | // extract just the name of the next subdirectory
|
---|
| 376 | uint8_t idx = strchr(filepath, '/') - filepath;
|
---|
| 377 | if (idx > 12)
|
---|
| 378 | idx = 12; // dont let them specify long names
|
---|
| 379 | char subdirname[13];
|
---|
| 380 | strncpy(subdirname, filepath, idx);
|
---|
| 381 | subdirname[idx] = 0;
|
---|
| 382 |
|
---|
| 383 | // close the subdir (we reuse them) if open
|
---|
| 384 | subdir->close();
|
---|
| 385 | if (! subdir->open(parent, subdirname, O_READ)) {
|
---|
| 386 | // failed to open one of the subdirectories
|
---|
| 387 | return SdFile();
|
---|
| 388 | }
|
---|
| 389 | // move forward to the next subdirectory
|
---|
| 390 | filepath += idx;
|
---|
| 391 |
|
---|
| 392 | // we reuse the objects, close it.
|
---|
| 393 | parent->close();
|
---|
| 394 |
|
---|
| 395 | // swap the pointers
|
---|
| 396 | SdFile *t = parent;
|
---|
| 397 | parent = subdir;
|
---|
| 398 | subdir = t;
|
---|
| 399 | }
|
---|
| 400 |
|
---|
| 401 | *index = (int)(filepath - origpath);
|
---|
| 402 | // parent is now the parent diretory of the file!
|
---|
| 403 | return *parent;
|
---|
| 404 | }
|
---|
| 405 |
|
---|
| 406 |
|
---|
| 407 | File SDClass::open(const char *filepath, uint8_t mode) {
|
---|
| 408 | /*
|
---|
| 409 |
|
---|
| 410 | Open the supplied file path for reading or writing.
|
---|
| 411 |
|
---|
| 412 | The file content can be accessed via the `file` property of
|
---|
| 413 | the `SDClass` object--this property is currently
|
---|
| 414 | a standard `SdFile` object from `sdfatlib`.
|
---|
| 415 |
|
---|
| 416 | Defaults to read only.
|
---|
| 417 |
|
---|
| 418 | If `write` is true, default action (when `append` is true) is to
|
---|
| 419 | append data to the end of the file.
|
---|
| 420 |
|
---|
| 421 | If `append` is false then the file will be truncated first.
|
---|
| 422 |
|
---|
| 423 | If the file does not exist and it is opened for writing the file
|
---|
| 424 | will be created.
|
---|
| 425 |
|
---|
| 426 | An attempt to open a file for reading that does not exist is an
|
---|
| 427 | error.
|
---|
| 428 |
|
---|
| 429 | */
|
---|
| 430 |
|
---|
| 431 | int pathidx;
|
---|
| 432 |
|
---|
| 433 | // do the interative search
|
---|
| 434 | SdFile parentdir = getParentDir(filepath, &pathidx);
|
---|
| 435 | // no more subdirs!
|
---|
| 436 |
|
---|
| 437 | filepath += pathidx;
|
---|
| 438 |
|
---|
| 439 | if (! filepath[0]) {
|
---|
| 440 | // it was the directory itself!
|
---|
| 441 | return File(parentdir, "/");
|
---|
| 442 | }
|
---|
| 443 |
|
---|
| 444 | // Open the file itself
|
---|
| 445 | SdFile file;
|
---|
| 446 |
|
---|
| 447 | // failed to open a subdir!
|
---|
| 448 | if (!parentdir.isOpen())
|
---|
| 449 | return File();
|
---|
| 450 |
|
---|
| 451 | // there is a special case for the Root directory since its a static dir
|
---|
| 452 | if (parentdir.isRoot()) {
|
---|
| 453 | if ( ! file.open(SD.root, filepath, mode)) {
|
---|
| 454 | // failed to open the file :(
|
---|
| 455 | return File();
|
---|
| 456 | }
|
---|
| 457 | // dont close the root!
|
---|
| 458 | } else {
|
---|
| 459 | if ( ! file.open(parentdir, filepath, mode)) {
|
---|
| 460 | return File();
|
---|
| 461 | }
|
---|
| 462 | // close the parent
|
---|
| 463 | parentdir.close();
|
---|
| 464 | }
|
---|
| 465 |
|
---|
| 466 | if (mode & (O_APPEND | O_WRITE))
|
---|
| 467 | file.seekSet(file.fileSize());
|
---|
| 468 | return File(file, filepath);
|
---|
| 469 | }
|
---|
| 470 |
|
---|
| 471 |
|
---|
| 472 | /*
|
---|
| 473 | File SDClass::open(char *filepath, uint8_t mode) {
|
---|
| 474 | //
|
---|
| 475 |
|
---|
| 476 | Open the supplied file path for reading or writing.
|
---|
| 477 |
|
---|
| 478 | The file content can be accessed via the `file` property of
|
---|
| 479 | the `SDClass` object--this property is currently
|
---|
| 480 | a standard `SdFile` object from `sdfatlib`.
|
---|
| 481 |
|
---|
| 482 | Defaults to read only.
|
---|
| 483 |
|
---|
| 484 | If `write` is true, default action (when `append` is true) is to
|
---|
| 485 | append data to the end of the file.
|
---|
| 486 |
|
---|
| 487 | If `append` is false then the file will be truncated first.
|
---|
| 488 |
|
---|
| 489 | If the file does not exist and it is opened for writing the file
|
---|
| 490 | will be created.
|
---|
| 491 |
|
---|
| 492 | An attempt to open a file for reading that does not exist is an
|
---|
| 493 | error.
|
---|
| 494 |
|
---|
| 495 | //
|
---|
| 496 |
|
---|
| 497 | // TODO: Allow for read&write? (Possibly not, as it requires seek.)
|
---|
| 498 |
|
---|
| 499 | fileOpenMode = mode;
|
---|
| 500 | walkPath(filepath, root, callback_openPath, this);
|
---|
| 501 |
|
---|
| 502 | return File();
|
---|
| 503 |
|
---|
| 504 | }
|
---|
| 505 | */
|
---|
| 506 |
|
---|
| 507 |
|
---|
| 508 | //boolean SDClass::close() {
|
---|
| 509 | // /*
|
---|
| 510 | //
|
---|
| 511 | // Closes the file opened by the `open` method.
|
---|
| 512 | //
|
---|
| 513 | // */
|
---|
| 514 | // file.close();
|
---|
| 515 | //}
|
---|
| 516 |
|
---|
| 517 |
|
---|
| 518 | boolean SDClass::exists(char *filepath) {
|
---|
| 519 | /*
|
---|
| 520 |
|
---|
| 521 | Returns true if the supplied file path exists.
|
---|
| 522 |
|
---|
| 523 | */
|
---|
| 524 | return walkPath(filepath, root, callback_pathExists);
|
---|
| 525 | }
|
---|
| 526 |
|
---|
| 527 |
|
---|
| 528 | //boolean SDClass::exists(char *filepath, SdFile& parentDir) {
|
---|
| 529 | // /*
|
---|
| 530 | //
|
---|
| 531 | // Returns true if the supplied file path rooted at `parentDir`
|
---|
| 532 | // exists.
|
---|
| 533 | //
|
---|
| 534 | // */
|
---|
| 535 | // return walkPath(filepath, parentDir, callback_pathExists);
|
---|
| 536 | //}
|
---|
| 537 |
|
---|
| 538 |
|
---|
| 539 | boolean SDClass::mkdir(char *filepath) {
|
---|
| 540 | /*
|
---|
| 541 |
|
---|
| 542 | Makes a single directory or a heirarchy of directories.
|
---|
| 543 |
|
---|
| 544 | A rough equivalent to `mkdir -p`.
|
---|
| 545 |
|
---|
| 546 | */
|
---|
| 547 | return walkPath(filepath, root, callback_makeDirPath);
|
---|
| 548 | }
|
---|
| 549 |
|
---|
| 550 | boolean SDClass::rmdir(char *filepath) {
|
---|
| 551 | /*
|
---|
| 552 |
|
---|
| 553 | Remove a single directory or a heirarchy of directories.
|
---|
| 554 |
|
---|
| 555 | A rough equivalent to `rm -rf`.
|
---|
| 556 |
|
---|
| 557 | */
|
---|
| 558 | return walkPath(filepath, root, callback_rmdir);
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | boolean SDClass::remove(char *filepath) {
|
---|
| 562 | return walkPath(filepath, root, callback_remove);
|
---|
| 563 | }
|
---|
| 564 |
|
---|
| 565 |
|
---|
| 566 | // allows you to recurse into a directory
|
---|
| 567 | File File::openNextFile(uint8_t mode) {
|
---|
| 568 | dir_t p;
|
---|
| 569 |
|
---|
| 570 | //Serial.print("\t\treading dir...");
|
---|
| 571 | while (_file->readDir(&p) > 0) {
|
---|
| 572 |
|
---|
| 573 | // done if past last used entry
|
---|
| 574 | if (p.name[0] == DIR_NAME_FREE) {
|
---|
| 575 | //Serial.println("end");
|
---|
| 576 | return File();
|
---|
| 577 | }
|
---|
| 578 |
|
---|
| 579 | // skip deleted entry and entries for . and ..
|
---|
| 580 | if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
|
---|
| 581 | //Serial.println("dots");
|
---|
| 582 | continue;
|
---|
| 583 | }
|
---|
| 584 |
|
---|
| 585 | // only list subdirectories and files
|
---|
| 586 | if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
|
---|
| 587 | //Serial.println("notafile");
|
---|
| 588 | continue;
|
---|
| 589 | }
|
---|
| 590 |
|
---|
| 591 | // print file name with possible blank fill
|
---|
| 592 | SdFile f;
|
---|
| 593 | char name[13];
|
---|
| 594 | _file->dirName(p, name);
|
---|
| 595 | //Serial.print("try to open file ");
|
---|
| 596 | //Serial.println(name);
|
---|
| 597 |
|
---|
| 598 | if (f.open(_file, name, mode)) {
|
---|
| 599 | //Serial.println("OK!");
|
---|
| 600 | return File(f, name);
|
---|
| 601 | } else {
|
---|
| 602 | //Serial.println("ugh");
|
---|
| 603 | return File();
|
---|
| 604 | }
|
---|
| 605 | }
|
---|
| 606 |
|
---|
| 607 | //Serial.println("nothing");
|
---|
| 608 | return File();
|
---|
| 609 | }
|
---|
| 610 |
|
---|
| 611 | void File::rewindDirectory(void) {
|
---|
| 612 | if (isDirectory())
|
---|
| 613 | _file->rewind();
|
---|
| 614 | }
|
---|
| 615 |
|
---|
| 616 | SDClass SD;
|
---|