C: SimpleIDE... Directory Listing of SD Card Files

I have been searching for hours..... Does anyone know of an an example for SimpleIDE, for recursively searching and/or listing SD card files and/or directories?
Basically, I want to list all the files and/or directories on an LCD display, so that a user can navigate this list, through the use of pushbuttons, to select a desired file.
Basically, I want to list all the files and/or directories on an LCD display, so that a user can navigate this list, through the use of pushbuttons, to select a desired file.
Comments
Just what I needed.
struct dirent { uint8_t name[11]; // filename uint8_t attr; // attributes uint8_t reserved; // reserved, must be 0 uint8_t crttimetenth; // create time, 10ths of a second (0-199) uint8_t crttime_0; // creation time byte 0 uint8_t crttime_1; // creation time byte 1 uint8_t crtdate_0; // creation date byte 0 uint8_t crtdate_1; // creation date byte 1 uint8_t lstaccdate_l; // last access date byte 0 uint8_t lstaccdate_h; // last access date byte 1 uint8_t startcluster_2; // first cluster, byte 2 (FAT32) uint8_t startcluster_3; // first cluster, byte 3 (FAT32) uint8_t wrttime_0; // last write time byte 0 uint8_t wrttime_1; // last write time byte 1 uint8_t wrtdate_0; // last write date byte 0 uint8_t wrtdate_1; // last write date byte 1 uint8_t startcluster_0; // first cluster, byte 0 uint8_t startcluster_1; // first cluster, byte 1 uint8_t filesize_0; // file size, byte 0 uint8_t filesize_1; // file size, byte 1 uint8_t filesize_2; // file size, byte 2 uint8_t filesize_3; // file size, byte 3 char d_name[13]; // filename in file.ext format };
I am surprised this example is not more publicized.
#include <propeller.h> #include <dirent.h> #include "simpletools.h" #define SD_DO 22 #define SD_CLK 23 #define SD_DI 24 #define SD_CS 25 int main() { DIR *sd_dir; struct dirent *dir_entry; sd_mount(SD_DO, SD_CLK, SD_DI, SD_CS); sd_dir = opendir("./"); if(sd_dir != NULL) { while((dir_entry = readdir(sd_dir))) { print("%s\n", dir_entry->d_name); } closedir(sd_dir); } else { print("Directory could not be opened"); } return 0; }
Yea, the title is definitely misleading, because I stated it incorrectly. Bsaically, I just wanted a directory listing, which could be searched further, with chdir, etc...
Sorry. I'm being a bit of a wind up. It's because writing a recursive directory search that will list all files in all directories and subdirectories from the the root downwards is an interesting exercise. Many C programmers, or indeed users of other languages never get their heads around such recursion.
Is it what you want to do? Is it practical on the Prop? No idea.
In the past, I have worked with recursive directory searching on two occassions, with both of them involving a Tree control. I cannot remember if I ever got it working correctly, but I think so. However, yes it is very interesting, without it, you would never find the word "scudattle", located in a file named "podunk.txt", buried deeply within a highly complex directory structure, of F: drive
Nah, as indicated, I used the word recursive without thinking about correct usage.
I suppose it could be useful, depending on the project of course. Perhaps famous quotations of various authors, musicians, and politicians, being indexed according to the category, and then by person.
That can lead to a C function that calls itself recursively. That is a big conceptual headache for many.
#include <stdio.h> #include <dirent.h> #ifdef __PROPELLER__ #include <propeller.h> #include "simpletools.h" #define SD_DO 22 #define SD_CLK 23 #define SD_DI 24 #define SD_CS 25 #endif void listfiles(char *dirname) { DIR *sd_dir = opendir(dirname); struct dirent *dir_entry; if(!sd_dir) return; // List files in this directory printf("\n%s:\n", dirname); while((dir_entry = readdir(sd_dir))) printf("%s\n", dir_entry->d_name); closedir(sd_dir); // List files in sub-directories sd_dir = opendir(dirname); while((dir_entry = readdir(sd_dir))) { if (strcmp(dir_entry->d_name, ".") && strcmp(dir_entry->d_name, "..")) listfiles(dir_entry->d_name); } closedir(sd_dir); } int main() { #ifdef __PROPELLER__ sd_mount(SD_DO, SD_CLK, SD_DI, SD_CS); #endif listfiles("./"); return 0; }
It is really not all that different, once you have a directory, simply iterate, open, and search the files. Following the directories is the hard part. And then as Dave mentions, you have to watch out for "." and ".."
Then the issue of the base case, as Dave demonstrates above.
Edit: No wait: the "." and ".." things are a looping within the recursive structure one is searching recursively...A kind of meta-recursion.
Headache time!
// FavTreeCtrl.cpp: // // wrapped CTreeCtrl to select and or display folders and files (optional ) // #include "stdafx.h" #include "FavTreeCtrl.h" #include "SortStringArray.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CFavTreeCtrl CFavTreeCtrl::CFavTreeCtrl() { m_strRoot = ""; bFirstExpand = TRUE; bFirstSelect = TRUE; bFirstExpandedSelect = TRUE; bFirstDisplay = TRUE; // AfxBeginThread(MonitorMyEmotesDir, GetSafeHwnd()); } CFavTreeCtrl::~CFavTreeCtrl() { m_imgList.Detach(); } BEGIN_MESSAGE_MAP(CFavTreeCtrl, CTreeCtrl) //{{AFX_MSG_MAP(CFavTreeCtrl) ON_NOTIFY_REFLECT(TVN_ITEMEXPANDED, OnItemexpanded) ON_NOTIFY_REFLECT(TVN_SELCHANGING, OnSelchanging) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// BOOL CFavTreeCtrl::DisplayTree(LPCTSTR strRoot, BOOL bFiles) { DWORD dwStyle = GetStyle(); // read the windowstyle if ( dwStyle & TVS_EDITLABELS ) { // Don't allow the user to edit ItemLabels ModifyStyle( TVS_EDITLABELS , 0 ); } // Display the DirTree with the Rootname e.g. C:\ // if Rootname == NULL then Display all Drives on this PC // First, we need the system-ImageList DeleteAllItems(); if ( !GetSysImgList() ) return FALSE; m_bFiles = bFiles; // if TRUE, Display Path- and Filenames if ( strRoot == NULL || strRoot[0] == '\0' ) { m_strRoot = ""; } else { m_strRoot = strRoot; if ( m_strRoot.Right(1) != '\\' ) m_strRoot += "\\"; HTREEITEM hParent = AddItem( TVI_ROOT, m_strRoot ); DisplayPath( hParent, strRoot ); Expand(hParent,TVE_EXPAND ); } return TRUE; } ///////////////////////////////////////////////// BOOL CFavTreeCtrl::GetSysImgList() ///////////////////////////////////////////////// { SHFILEINFO shFinfo; HIMAGELIST hImgList = NULL; if ( GetImageList( TVSIL_NORMAL ) ) m_imgList.Detach(); hImgList = (HIMAGELIST)SHGetFileInfo( "C:\\", 0, &shFinfo, sizeof( shFinfo ), SHGFI_SYSICONINDEX | SHGFI_SMALLICON ); if ( !hImgList ) { m_strError = "Cannot retrieve the Handle of SystemImageList!"; return FALSE; } m_imgList.m_hImageList = hImgList; SetImageList( &m_imgList, TVSIL_NORMAL ); return TRUE; // OK } void CFavTreeCtrl::DisplayPath(HTREEITEM hParent, LPCTSTR strPath) { // MessageBox(strPath, "TEST 1", MB_OK); // // Displaying the Path in the TreeCtrl // CString strTemp; CFileFind find; CString strPathFiles = strPath; BOOL bFind; CSortStringArray strDirArray; CSortStringArray strFileArray; if ( strPathFiles.Right(1) != "\\" ) { strPathFiles += "\\"; } strPathFiles += "*.*"; bFind = find.FindFile( strPathFiles ); while ( bFind ) { bFind = find.FindNextFile(); if ( find.IsDirectory() && !find.IsDots() ) { strDirArray.Add( find.GetFilePath() ); } if ( !find.IsDirectory() && m_bFiles ) { //NOTE: this is the new code ... strTemp = find.GetFileName(); if(strTemp.Right(3) == "url" || strTemp.Right(3) == "URL") { strFileArray.Add( find.GetFilePath() ); } } } strDirArray.Sort(); SetRedraw( FALSE ); CWaitCursor wait; for ( int i = 0; i < strDirArray.GetSize(); i++ ) { HTREEITEM hItem = AddItem( hParent, strDirArray.GetAt(i) ); if ( FindSubDir( strDirArray.GetAt(i) ) ) { InsertItem( "", 0, 0, hItem ); } } if ( m_bFiles ) { strFileArray.Sort(); for ( i = 0; i < strFileArray.GetSize(); i++ ) { HTREEITEM hItem = AddItem( hParent, strFileArray.GetAt(i) ); } } HTREEITEM hTreeParent = GetRootItem(); SetRedraw( TRUE ); SetItemText(hTreeParent, "My Emotes"); } HTREEITEM CFavTreeCtrl::AddItem(HTREEITEM hParent, LPCTSTR strPath) { // Adding the Item to the TreeCtrl with the current Icons SHFILEINFO shFinfo; int iIcon, iIconSel; CString strTemp = strPath; if ( strTemp.Right(1) != '\\' ) strTemp += "\\"; if ( !SHGetFileInfo( strTemp, 0, &shFinfo, sizeof( shFinfo ), SHGFI_ICON | SHGFI_SMALLICON ) ) { m_strError = "Error Gettting SystemFileInfo!"; return NULL; } iIcon = shFinfo.iIcon; // we only need the index from the system image list DestroyIcon( shFinfo.hIcon ); if ( !SHGetFileInfo( strTemp, 0, &shFinfo, sizeof( shFinfo ), SHGFI_ICON | SHGFI_OPENICON | SHGFI_SMALLICON ) ) { m_strError = "Error Gettting SystemFileInfo!"; return NULL; } iIconSel = shFinfo.iIcon; // we only need the index of the system image list DestroyIcon( shFinfo.hIcon ); if ( strTemp.Right(1) == "\\" ) strTemp.SetAt( strTemp.GetLength() - 1, '\0' ); if ( hParent == TVI_ROOT ) return InsertItem( strTemp, iIcon, iIconSel, hParent ); return InsertItem( GetSubPath( strTemp ), iIcon, iIconSel, hParent ); } LPCTSTR CFavTreeCtrl::GetSubPath(LPCTSTR strPath) { // // getting the last SubPath from a PathString // e.g. C:\temp\readme.txt // the result = readme.txt static CString strTemp; int iPos; strTemp = strPath; if ( strTemp.Right(1) == '\\' ) strTemp.SetAt( strTemp.GetLength() - 1, '\0' ); iPos = strTemp.ReverseFind( '\\' ); if ( iPos != -1 ) strTemp = strTemp.Mid( iPos + 1); return (LPCTSTR)strTemp; } BOOL CFavTreeCtrl::FindSubDir( LPCTSTR strPath) { // // Are there subDirs ? // CFileFind find; CString strTemp = strPath; BOOL bFind; if ( strTemp[strTemp.GetLength()-1] == '\\' ) strTemp += "*.*"; else strTemp += "\\*.*"; bFind = find.FindFile( strTemp ); while ( bFind ) { bFind = find.FindNextFile(); if ( find.IsDirectory() && !find.IsDots() ) { return TRUE; } if ( !find.IsDirectory() && m_bFiles && !find.IsHidden() ) return TRUE; } return FALSE; } void CFavTreeCtrl::OnSelchanging(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; // TODO: Add your control notification handler code here CString strExtension; strExtension = GetFullPath(pNMTreeView->itemNew.hItem); if (pNMTreeView->itemNew.state & TVIS_EXPANDED) { if(bFirstExpandedSelect == TRUE) { GetItemImage(pNMTreeView->itemNew.hItem, nExpandedSelectClosedFolder, nExpandedSelectOpenFolder); bFirstExpandedSelect = FALSE; } SetItemImage(pNMTreeView->itemNew.hItem, nExpandedSelectClosedFolder, nExpandedSelectOpenFolder); } else { if(bFirstSelect == TRUE) { GetItemImage(pNMTreeView->itemNew.hItem, nSelectClosedFolder, nSelectOpenFolder); bFirstSelect = FALSE; } if(strExtension.Right(3) == "url" || strExtension.Right(3) == "URL") { } else { SetItemImage(pNMTreeView->itemNew.hItem, nSelectClosedFolder, nSelectClosedFolder); } } *pResult = 0; } void CFavTreeCtrl::OnItemexpanded(NMHDR* pNMHDR, LRESULT* pResult) { NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; CString strPath; if(bFirstExpand == TRUE) { GetItemImage(pNMTreeView->itemNew.hItem, nExpandClosedFolder, nExpandOpenFolder); bFirstExpand = FALSE; } if ( pNMTreeView->itemNew.state & TVIS_EXPANDED ) { SetItemImage(pNMTreeView->itemNew.hItem, nExpandOpenFolder, nExpandOpenFolder); ExpandItem( pNMTreeView->itemNew.hItem, TVE_EXPAND ); } else { if (pNMTreeView->itemNew.state & TVIS_SELECTED) { SetItemImage(pNMTreeView->itemNew.hItem, nExpandClosedFolder, nExpandClosedFolder); } else { SetItemImage(pNMTreeView->itemNew.hItem, nExpandClosedFolder, nExpandOpenFolder); } // SetItemImage(pNMTreeView->itemNew.hItem, nExpandClosedFolder, nExpandOpenFolder); // // Delete the Items, but leave one there, for // expanding the item next time // HTREEITEM hChild = GetChildItem( pNMTreeView->itemNew.hItem ); while ( hChild ) { DeleteItem( hChild ); hChild = GetChildItem( pNMTreeView->itemNew.hItem ); } InsertItem( "", pNMTreeView->itemNew.hItem ); } *pResult = 0; } CString CFavTreeCtrl::GetFullPath(HTREEITEM hItem) { // get the Full Path of the item CString strReturn; CString strTemp; HTREEITEM hParent = hItem; strReturn = ""; while ( hParent ) { strTemp = GetItemText( hParent ); if(strTemp == "My Emotes") { strTemp = m_strRoot; strTemp.TrimRight( '\\' ); } strTemp += "\\"; strReturn = strTemp + strReturn; hParent = GetParentItem( hParent ); } strReturn.TrimRight( '\\' ); return strReturn; } BOOL CFavTreeCtrl::SetSelPath(LPCTSTR strPath) { // Setting the Selection in the Tree HTREEITEM hParent = TVI_ROOT; int iLen = strlen(strPath) + 2; char* pszPath = new char[iLen]; char* pPath = pszPath; BOOL bRet = FALSE; if ( !IsValidPath( strPath ) ) { delete [] pszPath; // this must be added 29.03.99 return FALSE; } strcpy( pszPath, strPath ); strupr( pszPath ); if ( pszPath[strlen(pszPath)-1] != '\\' ) strcat( pszPath, "\\" ); int iLen2 = strlen( pszPath ); for (WORD i = 0; i < iLen2; i++ ) { if ( pszPath[i] == '\\' ) { SetRedraw( FALSE ); pszPath[i] = '\0'; hParent = SearchSiblingItem( hParent, pPath ); if ( !hParent ) // Not found! break; else { // Info: // the notification OnItemExpanded // will not called every time // after the call Expand. // You must call Expand with TVE_COLLAPSE | TVE_COLLAPSERESET // to Reset the TVIS_EXPANDEDONCE Flag UINT uState; uState = GetItemState( hParent, TVIS_EXPANDEDONCE ); if ( uState ) { Expand( hParent, TVE_EXPAND ); Expand( hParent, TVE_COLLAPSE | TVE_COLLAPSERESET ); InsertItem("", hParent ); // insert a blank child-item Expand( hParent, TVE_EXPAND ); // now, expand send a notification } else Expand( hParent, TVE_EXPAND ); } pPath += strlen(pPath) + 1; } } delete [] pszPath; if ( hParent ) // Ok the last subpath was found { SelectItem( hParent ); // select the last expanded item bRet = TRUE; } else { bRet = FALSE; } SetRedraw( TRUE ); return bRet; } HTREEITEM CFavTreeCtrl::SearchSiblingItem( HTREEITEM hItem, LPCTSTR strText) { HTREEITEM hFound = GetChildItem( hItem ); CString strTemp; while ( hFound ) { strTemp = GetItemText( hFound ); strTemp.MakeUpper(); if ( strTemp == strText ) return hFound; hFound = GetNextItem( hFound, TVGN_NEXT ); } return NULL; } void CFavTreeCtrl::ExpandItem(HTREEITEM hItem, UINT nCode) { CString strPath; if ( nCode == TVE_EXPAND ) { HTREEITEM hChild = GetChildItem( hItem ); while ( hChild ) { DeleteItem( hChild ); hChild = GetChildItem( hItem ); } strPath = GetFullPath( hItem ); DisplayPath( hItem, strPath ); } } BOOL CFavTreeCtrl::IsValidPath(LPCTSTR strPath) { // This function check the Pathname HTREEITEM hChild; CString strItem; CString strTempPath = strPath; BOOL bFound = FALSE; CFileFind find; hChild = GetChildItem( TVI_ROOT ); strTempPath.MakeUpper(); strTempPath.TrimRight('\\'); while ( hChild ) { strItem = GetItemText( hChild ); strItem.MakeUpper(); if ( strItem == strTempPath.Mid( 0, strItem.GetLength() ) ) { bFound = TRUE; break; } hChild = GetNextItem( hChild, TVGN_NEXT ); } if ( !bFound ) return FALSE; strTempPath += "\\nul"; if ( find.FindFile( strTempPath ) ) return TRUE; return FALSE; }
Not that it is applicable, just you guys got me thinking about this stuff
If you get that tested on the Prop, please let us know the results. I would test it, but I only have three files on my SD card.
#include <stdio.h> #include <propeller.h> #include "simpletools.h" #include <dirent.h> #define SD_DO 22 #define SD_CLK 23 #define SD_DI 24 #define SD_CS 25 void listfiles(char *dirname) { DIR *sd_dir = opendir(dirname); struct dirent *dir_entry; char subdir[80]; int len = strlen(dirname); if(!sd_dir) return; // List files in this directory printf("\n%s:\n", dirname); while((dir_entry = readdir(sd_dir))) printf("%s\n", dir_entry->d_name); closedir(sd_dir); // List files in sub-directories sd_dir = opendir(dirname); while((dir_entry = readdir(sd_dir))) { if (strcmp(dir_entry->d_name, ".") && strcmp(dir_entry->d_name, "..")) { strcpy(subdir, dirname); if (dirname[len-1] != '/') strcat(subdir, "/"); strcat(subdir, dir_entry->d_name); listfiles(subdir); } } closedir(sd_dir); } int main() { waitcnt(CNT+CLKFREQ); sd_mount(SD_DO, SD_CLK, SD_DI, SD_CS); listfiles("/"); return 0; }
I do believe you have managed to sidetrack me a little. Please allow me to explain.
I have always prided myself with the ability to program and navigate files, folders, and disk paths, although in a Windows environment, where different functions were utilized. As it stands now, I feel like a duck out of water, with a completely new set of rules and functions, and I somehow feel obligated to learn these new rules and functions.
I was going to take the easy way out, and just store individual files within the root of the SD, but now that you have exposed me to various file and folder functions, I think I will create a directory tree and explore a little, just to acquire navigational skills. I don't know where this is leading, but hopefully I will be able to wrap my head around it fairly quickly.
That seems odd that it would only go one level deep in the root, but with a full path it would go further. Okay, time to go educate myself
Welcome to the freedom of the world outside Windows. Take a deep breath of the fresh air and dive in (Is that an odd mix of metaphors?)
It's a *****, you can spend years thinking you know how to program and every now and then be reminded you know nothing and all your assumptions may not be absolute truths. JavaScript gave me that jolt as it throws away some dearly held conceptions and introduces other subtle ways to program. Just now I'm playing with Erlang, again a language built around a totally different view of the programming problem.
Sorry, just rambling again...
Yes indeed.
LOL I have taken the leap.... As I swim about, it appears that it is not only direct C library functions that are being utilized. It is a llittle confusing, but muddling my way through it.
As far as I can determine.... remove, rename, chdir, rmdir, and mkdir all return 0 on success and -1 for failure. Is this correct?
And.... When making file paths, should a forwardslash or a backslash be used? I read that either can be used for SD. I would assume that the backslash is the norm for creating paths, with "\" being the delimiter, since that is the delimiter for print commands.
If you use anything other than "/" as a path separator I will have to come round your place and taunt you.
I wonder if they set errno when there is an error. Which can then be translated and printed as a string with perror() or just converted to a string with strerror().
I was there earlier, and their documentation is what I was basing my assumptions upon
That's good to know, since I assumed the "\".
If so, once again assuming... I would assume this would be a "last known error", which must be used immediately, before being replaced???
Since you are being kind enough to answer some of my lingering questions, let me ask you another.
Many of the functions have a parameter of "path", whereas "remove" and "rename" take file names. Do I have to be in the working directory to call these functions or can I also just supply a path to these functions?
#include <stdio.h>
int remove(const char *pathname);
I will just try it out and see what happens.
I am currently working on a file and folder functions wrapper for SD card access. I figure one file for all the necessary operations, just to make file access and manipulation a little easier.
Here is what I have so far, with the lower functions still needing to be defined. I have the mindless stuff done already
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <propeller.h> #include <dirent.h> #include "simpletools.h" #define FSLASH TEXT('/') int create_directory(const char *path) { int ret; ret = mkdir(path, 0); return ret; } int delete_directory(const char *path) { int ret; ret = rmdir(path); return ret; } int delete_file(char *filename) { int ret; ret = remove(filename); return ret; } get_current_directory set_current_directory list_directory_files path_get_root path_add_backslash path_append create_directory_path is_path_valid is_directory
However, in the past, I created something similar for Windows and found it to be extremely useful, instead of trying to remember all the various functions and includes. I would simply add one header file and one source file, and then I was ready to perform almost any file and folder operation that I wanted to do, with the header file being a handy quick reference at my disposal.
One include and you are good to go.