Support Wildcards with the 'ls' command #135

Closed
dlkeng opened this Issue Jan 27, 2013 · 1 comment

Projects

None yet

2 participants

@dlkeng

OpenLog v3.13

The following is a proposed way to add wildcard support ('?' '') back to the OpenLog firmware for the ' *ls** ' command. (i.e. " ls *.txt " or " ls test?.txt " or " ls config.* ")

It supports limited recursion into subdirectories (controlled by the existing FOLDER_TRACK_DEPTH #define - currently 2 levels deep) to save on RAM used at execution time due to recursion.

However, the proposed change results in a code space saving of about 280 bytes and uses 1 additional byte of RAM (as reported by the printRam() function) to maintain the current subdirectory recursion depth.

The following are the proposed code changes to the v3.13 version of the file OpenLog_v3.ino:

Replace: Lines 1152 to 1155

1152  //ERROR
1153  //      currentDirectory.ls(LS_SIZE, 0, &wildcmp, get_cmd_arg(1));
1154  currentDirectory.ls(LS_SIZE | LS_R);
1155  NewSerial.println(F("Wild card ls not yet supported"));

With: (new lines 1152 to 1165)

if (count_cmd_args() == 1)  // has no arguments
{
  // Don't use the 'ls()' method in the SdFat library as it does not
  // limit recursion into subdirectories.
  command_arg[0] = '*';       // use global wildcard
  command_arg[1] = '\0';
}
else      // has argument (and possible wildcards)
{
  command_arg = get_cmd_arg(1);
  strupr(command_arg);
}
// display listing with limited recursion into subdirectories
lsPrint(&currentDirectory, command_arg, LS_SIZE | LS_R, 0);

Also, add the following lines to the end of file OpenLog_v3.ino:

//------------------------------------------------------------------------------
// Listing functions
//  - Modeled loosely on the ls() methods in SdBaseFile.cpp from the SdFat library.

#define WAS_EOF             0
#define WAS_FILE            1
#define WAS_SUBDIR          2
#define FILENAME_EXT_SIZE   13
#define LS_FIELD_SIZE       (FILENAME_EXT_SIZE + 1)
#define SUBDIR_INDENT       2
#define DIR_SIZE            (sizeof(dir_t))

/*
 * NAME:
 *  void lsPrint(SdFile * theDir, char * cmdStr, byte flags, byte indent)
 *
 * PARAMETERS:
 *  SdFile * theDir = the directory to list (assumed opened)
 *  char * cmdStr = the command line file/directory string (with possible wildcards)
 *  byte flags = the inclusive OR of
 *                  LS_SIZE - print the file size
 *                  LS_R - recursively list subdirectories
 *  byte indent = amount of space before file name
 *                (used for recursive list to indicate subdirectory level)
 *
 * WHAT:
 *  List directory contents to serial port.
 *
 *  Wildcard support rules:
 *    Wildcard characters ('*', '?') only apply to the selection of filenames to
 *    list, not to the listing of directory or subdirectory names. All directory
 *    and subdirectory names are always listed.
 *
 * RETURN VALUES:
 *  None.
 *
 * SPECIAL CONSIDERATIONS:
 *  May be called recursively with limited recursion depth (see FOLDER_TRACK_DEPTH).
 *  Each recursion uses ~50 bytes of RAM.
 */
void lsPrint(SdFile * theDir, char * cmdStr, byte flags, byte indent)
{
  static byte depth = 0;      // current recursion depth
  byte status;
  uint16_t index;
  SdFile subdir;

  theDir->rewind();
  while ((status = lsPrintNext(theDir, cmdStr, flags, indent)))
  {
    if ((status == WAS_SUBDIR) && (flags & LS_R) && (depth < FOLDER_TRACK_DEPTH))
    {
      // was a subdirectory, recursion allowed, recursion depth OK

      index = (theDir->curPosition() / DIR_SIZE) - 1;  // determine current directory entry index
      if (subdir.open(theDir, index, O_READ))
      {
        ++depth;        // limit recursion
        lsPrint(&subdir, cmdStr, flags, indent + SUBDIR_INDENT); // recursively list subdirectory
        --depth;
        subdir.close();
      }
    }
  }
}

/*
 * NAME:
 *  void lsPrintNext(SdFile * theDir, char * cmdStr, byte flags, byte indent)
 *
 * PARAMETERS:
 *  SdFile * theDir = the directory to list (assumed opened)
 *  char * cmdStr = the command line file/directory string (with possible wildcards)
 *  byte flags = the inclusive OR of
 *                  LS_SIZE - print the file size
 *                  LS_R - recursively list subdirectories
 *  byte indent = amount of space before file name
 *                (used for recursive list to indicate subdirectory level)
 *
 * WHAT:
 *  List directory's next contents to serial port.
 *
 *  Wildcard support rules:
 *    Wildcard characters ('*', '?') only apply to the selection of filenames to
 *    list, not to the listing of directory or subdirectory names. All directory
 *    and subdirectory names are always listed.
 *
 * RETURN VALUES:
 *  byte = WAS_EOF - EOF, WAS_FILE - normal file, or WAS_SUBDIR - subdirectory
 *
 * SPECIAL CONSIDERATIONS:
 *  None.
 */
byte lsPrintNext(SdFile * theDir, char * cmdStr, byte flags, byte indent)
{
  byte pos = 0;           // output position
  byte open_stat;         // file open status
  byte status;            // return status
  SdFile tempFile;
  char fname[FILENAME_EXT_SIZE];

  // Find next available object to display in the specified directory
  while ((open_stat = tempFile.openNext(theDir, O_READ)))
  {
    if (tempFile.getFilename(fname)) // Get the filename of the object we're looking at
    {
      if (tempFile.isDir() || tempFile.isFile() || tempFile.isSubDir())
      {
        if (tempFile.isFile())        // wildcards only apply to files
        {
          if (wildcmp(cmdStr, fname)) // see if it matches the wildcard
          {
            status = WAS_FILE;
            break;      // is a matching file name, display it
          }
          // else skip it
        }
        else    // is a subdirectory
        {
          status = WAS_SUBDIR;
          break;        // display subdirectory name
        }
      }
    }
    tempFile.close();
  }
  if (!open_stat)
  {
    return WAS_EOF;     // nothing more in this (sub)directory
  }

  // output the file or directory name

  // indent for dir level
  for (byte i = 0; i < indent; i++)
  {
    NewSerial.write(' ');
  }

  // print name
  pos += NewSerial.print(fname);

  if (tempFile.isSubDir())
  {
    pos += NewSerial.write('/');    // subdirectory indicator
  }
  // print size if requested (files only)
  if (tempFile.isFile() && (flags & LS_SIZE))
  {
    while (pos++ < LS_FIELD_SIZE)
    {
      NewSerial.write(' ');
    }
    NewSerial.write(' ');           // ensure at least 1 separating space
    NewSerial.print(tempFile.fileSize());
  }
  NewSerial.writeln();
  tempFile.close();
  return status;
}
@nseidle nseidle added a commit that referenced this issue Apr 8, 2013
@nseidle nseidle Fixed rm issue #134. dlkeng is my new favorite person.
Fixed issue #135: When removing files and there is a directory, all
wildcard files are now removed correctly. Thank you dlkeng! Your patch
worked great!
1888d3d
@nseidle nseidle added a commit that referenced this issue Apr 8, 2013
@nseidle nseidle Added support for ls wildcards. Issue 135 fixed.
Fixed issue #135: ls with wildcard works again. Thank you dlkeng!
50ee63b
@nseidle
SparkFun Electronics member

Amazing work. Thank you very much. This worked like a charm.

I do believe that we should not be listing directory names that do not fit the wildcard search. For example

ls *.txt

returns

config.txt
july\

We you've offered is awesome and I can't thank you enough! If you ever decide to remove directories from the search results please let us know!

@nseidle nseidle closed this Apr 8, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment