D:/DRISSI/arduino-0022/arduino-0022/libraries/SD/SD.cpp
00001 /*
00002 
00003  SD - a slightly more friendly wrapper for sdfatlib
00004 
00005  This library aims to expose a subset of SD card functionality
00006  in the form of a higher level "wrapper" object.
00007 
00008  License: GNU General Public License V3
00009           (Because sdfatlib is licensed with this.)
00010 
00011  (C) Copyright 2010 SparkFun Electronics
00012 
00013 
00014  This library provides four key benefits:
00015 
00016    * Including `SD.h` automatically creates a global
00017      `SD` object which can be interacted with in a similar
00018      manner to other standard global objects like `Serial` and `Ethernet`.
00019 
00020    * Boilerplate initialisation code is contained in one method named 
00021      `begin` and no further objects need to be created in order to access
00022      the SD card.
00023 
00024    * Calls to `open` can supply a full path name including parent 
00025      directories which simplifies interacting with files in subdirectories.
00026 
00027    * Utility methods are provided to determine whether a file exists
00028      and to create a directory heirarchy.
00029 
00030 
00031   Note however that not all functionality provided by the underlying
00032   sdfatlib library is exposed.
00033 
00034  */
00035 
00036 /*
00037 
00038   Implementation Notes
00039 
00040   In order to handle multi-directory path traversal, functionality that 
00041   requires this ability is implemented as callback functions.
00042 
00043   Individual methods call the `walkPath` function which performs the actual
00044   directory traversal (swapping between two different directory/file handles
00045   along the way) and at each level calls the supplied callback function.
00046 
00047   Some types of functionality will take an action at each level (e.g. exists
00048   or make directory) which others will only take an action at the bottom
00049   level (e.g. open).
00050 
00051  */
00052 
00053 #include "SD.h"
00054 
00055 // Used by `getNextPathComponent`
00056 #define MAX_COMPONENT_LEN 12 // What is max length?
00057 #define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
00058 
00059 bool getNextPathComponent(char *path, unsigned int *p_offset,
00060                           char *buffer) {
00061   /*
00062 
00063     Parse individual path components from a path.
00064 
00065       e.g. after repeated calls '/foo/bar/baz' will be split
00066            into 'foo', 'bar', 'baz'.
00067 
00068     This is similar to `strtok()` but copies the component into the
00069     supplied buffer rather than modifying the original string.
00070 
00071 
00072     `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
00073 
00074     `p_offset` needs to point to an integer of the offset at
00075     which the previous path component finished.
00076 
00077     Returns `true` if more components remain.
00078 
00079     Returns `false` if this is the last component.
00080       (This means path ended with 'foo' or 'foo/'.)
00081 
00082    */
00083 
00084   // TODO: Have buffer local to this function, so we know it's the
00085   //       correct length?
00086 
00087   int bufferOffset = 0;
00088 
00089   int offset = *p_offset;
00090 
00091   // Skip root or other separator
00092   if (path[offset] == '/') {
00093     offset++;
00094   }
00095   
00096   // Copy the next next path segment
00097   while (bufferOffset < MAX_COMPONENT_LEN
00098          && (path[offset] != '/')
00099          && (path[offset] != '\0')) {
00100     buffer[bufferOffset++] = path[offset++];
00101   }
00102 
00103   buffer[bufferOffset] = '\0';
00104 
00105   // Skip trailing separator so we can determine if this
00106   // is the last component in the path or not.
00107   if (path[offset] == '/') {
00108     offset++;
00109   }
00110 
00111   *p_offset = offset;
00112 
00113   return (path[offset] != '\0');
00114 }
00115 
00116 
00117 
00118 boolean walkPath(char *filepath, SdFile& parentDir,
00119                  boolean (*callback)(SdFile& parentDir,
00120                                      char *filePathComponent,
00121                                      boolean isLastComponent,
00122                                      void *object),
00123                  void *object = NULL) {
00124   /*
00125      
00126      When given a file path (and parent directory--normally root),
00127      this function traverses the directories in the path and at each
00128      level calls the supplied callback function while also providing
00129      the supplied object for context if required.
00130 
00131        e.g. given the path '/foo/bar/baz'
00132             the callback would be called at the equivalent of
00133             '/foo', '/foo/bar' and '/foo/bar/baz'.
00134 
00135      The implementation swaps between two different directory/file
00136      handles as it traverses the directories and does not use recursion
00137      in an attempt to use memory efficiently.
00138 
00139      If a callback wishes to stop the directory traversal it should
00140      return false--in this case the function will stop the traversal,
00141      tidy up and return false.
00142 
00143      If a directory path doesn't exist at some point this function will
00144      also return false and not subsequently call the callback.
00145 
00146      If a directory path specified is complete, valid and the callback
00147      did not indicate the traversal should be interrupted then this
00148      function will return true.
00149 
00150    */
00151 
00152 
00153   SdFile subfile1;
00154   SdFile subfile2;
00155 
00156   char buffer[PATH_COMPONENT_BUFFER_LEN]; 
00157 
00158   unsigned int offset = 0;
00159 
00160   SdFile *p_parent;
00161   SdFile *p_child;
00162 
00163   SdFile *p_tmp_sdfile;  
00164   
00165   p_child = &subfile1;
00166   
00167   p_parent = &parentDir;
00168 
00169   while (true) {
00170 
00171     boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
00172 
00173     boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
00174 
00175     if (!shouldContinue) {
00176       // TODO: Don't repeat this code?
00177       // If it's one we've created then we
00178       // don't need the parent handle anymore.
00179       if (p_parent != &parentDir) {
00180         (*p_parent).close();
00181       }
00182       return false;
00183     }
00184     
00185     if (!moreComponents) {
00186       break;
00187     }
00188     
00189     boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
00190 
00191     // If it's one we've created then we
00192     // don't need the parent handle anymore.
00193     if (p_parent != &parentDir) {
00194       (*p_parent).close();
00195     }
00196     
00197     // Handle case when it doesn't exist and we can't continue...
00198     if (exists) {
00199       // We alternate between two file handles as we go down
00200       // the path.
00201       if (p_parent == &parentDir) {
00202         p_parent = &subfile2;
00203       }
00204 
00205       p_tmp_sdfile = p_parent;
00206       p_parent = p_child;
00207       p_child = p_tmp_sdfile;
00208     } else {
00209       return false;
00210     }
00211   }
00212   
00213   if (p_parent != &parentDir) {
00214     (*p_parent).close(); // TODO: Return/ handle different?
00215   }
00216 
00217   return true;
00218 }
00219 
00220 
00221 
00222 /*
00223 
00224    The callbacks used to implement various functionality follow.
00225 
00226    Each callback is supplied with a parent directory handle,
00227    character string with the name of the current file path component,
00228    a flag indicating if this component is the last in the path and
00229    a pointer to an arbitrary object used for context.
00230 
00231  */
00232 
00233 boolean callback_pathExists(SdFile& parentDir, char *filePathComponent, 
00234                             boolean isLastComponent, void *object) {
00235   /*
00236 
00237     Callback used to determine if a file/directory exists in parent
00238     directory.
00239 
00240     Returns true if file path exists.
00241 
00242   */
00243   SdFile child;
00244 
00245   boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
00246   
00247   if (exists) {
00248      child.close(); 
00249   }
00250   
00251   return exists;
00252 }
00253 
00254 
00255 
00256 boolean callback_makeDirPath(SdFile& parentDir, char *filePathComponent, 
00257                              boolean isLastComponent, void *object) {
00258   /*
00259 
00260     Callback used to create a directory in the parent directory if
00261     it does not already exist.
00262 
00263     Returns true if a directory was created or it already existed.
00264 
00265   */
00266   boolean result = false;
00267   SdFile child;
00268   
00269   result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
00270   if (!result) {
00271     result = child.makeDir(parentDir, filePathComponent);
00272   } 
00273   
00274   return result;
00275 }
00276 
00277 
00278 
00279 boolean callback_openPath(SdFile& parentDir, char *filePathComponent, 
00280                           boolean isLastComponent, void *object) {
00281   /*
00282 
00283     Callback used to open a file specified by a filepath that may
00284     specify one or more directories above it.
00285 
00286     Expects the context object to be an instance of `SDClass` and
00287     will use the `file` property of the instance to open the requested
00288     file/directory with the associated file open mode property.
00289 
00290     Always returns true if the directory traversal hasn't reached the
00291     bottom of the directory heirarchy.
00292 
00293     Returns false once the file has been opened--to prevent the traversal
00294     from descending further. (This may be unnecessary.)
00295 
00296   */
00297   if (isLastComponent) {
00298     SDClass *p_SD = static_cast<SDClass*>(object);
00299     p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
00300     if (p_SD->fileOpenMode == FILE_WRITE) {
00301       p_SD->file.seekSet(p_SD->file.fileSize());
00302     }
00303     // TODO: Return file open result?
00304     return false;
00305   }
00306   return true;
00307 }
00308 
00309 
00310 boolean callback_remove(SdFile& parentDir, char *filePathComponent, 
00311                         boolean isLastComponent, void *object) {
00312   if (isLastComponent) {
00313     return SdFile::remove(parentDir, filePathComponent);
00314   }
00315   return true;
00316 }
00317 
00318 boolean callback_rmdir(SdFile& parentDir, char *filePathComponent, 
00319                         boolean isLastComponent, void *object) {
00320   if (isLastComponent) {
00321     SdFile f;
00322     if (!f.open(parentDir, filePathComponent, O_READ)) return false;
00323     return f.rmDir();
00324   }
00325   return true;
00326 }
00327 
00328 
00329 
00330 /* Implementation of class used to create `SDCard` object. */
00331 
00332 
00333 
00334 boolean SDClass::begin(uint8_t csPin) {
00335   /*
00336 
00337     Performs the initialisation required by the sdfatlib library.
00338 
00339     Return true if initialization succeeds, false otherwise.
00340 
00341    */
00342   return card.init(SPI_HALF_SPEED, csPin) &&
00343          volume.init(card) &&
00344          root.openRoot(volume);
00345 }
00346 
00347 
00348 File SDClass::open(char *filepath, uint8_t mode) {
00349   /*
00350 
00351      Open the supplied file path for reading or writing.
00352 
00353      The file content can be accessed via the `file` property of
00354      the `SDClass` object--this property is currently
00355      a standard `SdFile` object from `sdfatlib`.
00356 
00357      Defaults to read only.
00358 
00359      If `write` is true, default action (when `append` is true) is to
00360      append data to the end of the file.
00361 
00362      If `append` is false then the file will be truncated first.
00363 
00364      If the file does not exist and it is opened for writing the file
00365      will be created.
00366 
00367      An attempt to open a file for reading that does not exist is an
00368      error.
00369 
00370    */
00371 
00372   // TODO: Allow for read&write? (Possibly not, as it requires seek.)
00373 
00374   fileOpenMode = mode;
00375   walkPath(filepath, root, callback_openPath, this);
00376 
00377   return File();
00378 
00379 }
00380 
00381 
00382 //boolean SDClass::close() {
00383 //  /*
00384 //
00385 //    Closes the file opened by the `open` method.
00386 //
00387 //   */
00388 //  file.close();
00389 //}
00390 
00391 
00392 boolean SDClass::exists(char *filepath) {
00393   /*
00394 
00395      Returns true if the supplied file path exists.
00396 
00397    */
00398   return walkPath(filepath, root, callback_pathExists);
00399 }
00400 
00401 
00402 //boolean SDClass::exists(char *filepath, SdFile& parentDir) {
00403 //  /*
00404 //
00405 //     Returns true if the supplied file path rooted at `parentDir`
00406 //     exists.
00407 //
00408 //   */
00409 //  return walkPath(filepath, parentDir, callback_pathExists);
00410 //}
00411 
00412 
00413 boolean SDClass::mkdir(char *filepath) {
00414   /*
00415   
00416     Makes a single directory or a heirarchy of directories.
00417 
00418     A rough equivalent to `mkdir -p`.
00419   
00420    */
00421   return walkPath(filepath, root, callback_makeDirPath);
00422 }
00423 
00424 boolean SDClass::rmdir(char *filepath) {
00425   /*
00426   
00427     Makes a single directory or a heirarchy of directories.
00428 
00429     A rough equivalent to `mkdir -p`.
00430   
00431    */
00432   return walkPath(filepath, root, callback_rmdir);
00433 }
00434 
00435 boolean SDClass::remove(char *filepath) {
00436   return walkPath(filepath, root, callback_remove);
00437 }
00438 
00439 SDClass SD;