source: rtos_arduino/trunk/arduino_lib/libraries/SD/src/SD.cpp@ 136

Last change on this file since 136 was 136, checked in by ertl-honda, 8 years ago

ライブラリとOS及びベーシックなサンプルの追加.

File size: 14.6 KB
Line 
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
59bool 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
118boolean 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
233boolean 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
256boolean 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
280boolean 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
311boolean 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
319boolean 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
335boolean 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
351SdFile 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
407File 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/*
473File 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
518boolean 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
539boolean 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
550boolean 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
561boolean SDClass::remove(char *filepath) {
562 return walkPath(filepath, root, callback_remove);
563}
564
565
566// allows you to recurse into a directory
567File 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
611void File::rewindDirectory(void) {
612 if (isDirectory())
613 _file->rewind();
614}
615
616SDClass SD;
Note: See TracBrowser for help on using the repository browser.