I'm developing some code which reads file names from an sd-card (using FatFs) and displays them to the screen. Here's a snipet of what I have working, this prints out the files on the card as expected -
FRESULT result;
char *path = '/'; //look in root of sd card
result = f_opendir(&directory, path); //open directory
if(result==FR_OK){
for(;;){
result = f_readdir(&directory, &fileInfo); //read directory
if(result==FR_OK){
if(fileInfo.fname[0]==0){ //end of dir reached
//LCD_UsrLog("End of directory.\n");
break;
}
if(fileInfo.fname[0]=='.')continue; //ignore '.' files
TCHAR *fn_ptr; //file name, why a pointer?
fn_ptr=&fileInfo.fname; //get file name
LCD_UsrLog("%s\n",fn_ptr);
for(delay=0;delay<0x0FFFFF;delay++){ShortDelay();} //delay to display
}//end result==fr_ok
}//end for
}//end result==fr_ok
Where
typedef char TCHAR
and
typedef struct {
DWORD fsize; /* File size */
WORD fdate; /* Last modified date */
WORD ftime; /* Last modified time */
BYTE fattrib; /* Attribute */
TCHAR fname[13]; /* Short file name (8.3 format) */
} FILINFO;
I need to copy the names of the files into an array for processing however I've tried a few ways but can't seem to get the array working. I have tried creating an arbitrarily large array of TCHARs and dereferencing the file name pointer but this prints garbage.
FRESULT result;
char *path = '/'; //look in root of sd card
TCHAR fileList[50];
u32 index=0;
result = f_opendir(&directory, path); //open directory
if(result==FR_OK){
for(;;){
result = f_readdir(&directory, &fileInfo); //read directory
if(result==FR_OK){
if(fileInfo.fname[0]==0){ //end of dir reached
//LCD_UsrLog("End of directory.\n");
break;
}
if(fileInfo.fname[0]=='.')continue; //ignore '.' files
TCHAR *fn_ptr; //file name, why a pointer?
fn_ptr=&fileInfo.fname; //get file name
fileList[index]=*fn_ptr;
LCD_UsrLog("%s\n",fileList[index]);
for(delay=0;delay<0x0FFFFF;delay++){ShortDelay();} //delay to display
index++;
}//end result==fr_ok
}//end for
}//end result==fr_ok
I suspect this is a simple mistake regarding pointers or the proper usage of an array of chars but it has been 4+ years since I've last touched C and I'm lost!
Any help would be greatly appreciated.
First problem: currently your file list is an array of chars, while it should be an array of strings. So declare it as
TCHAR* fileList[50];
then allocate the strings of proper length for each filename (not forgetting the extra space for the terminating 0 char). You also need to explicitly copy the filenames into your name list, because the contents of fileInfo get overwritten in each loop cycle, so simply storing the pointers would result in your list containing the name of the last file 50 times.
All in all, you need something like this:
if(fileInfo.fname[0]=='.')continue; //ignore '.' files
fileList[index] = malloc(strlen(fileInfo.fname) + 1);
strcpy(fileList[index], fileInfo.fname);
LCD_UsrLog("%s\n",fileList[index]);
(Disclaimer: No guarantee that this works as it is, I have no chance to test it, but hopefully this gives you the idea).
Alternatively, if you know the upper limit of the filename length, you can declare an array of filenames with fixed length, and get rid of dynamic allocation. But then you should use strncpy instead of strcpy to be on the safe side, to prevent buffer overflows. And this also requires the terminating 0 character to be appended, again to be on the safe side:
TCHAR fileList[50][MAX_FILENAME_LENGTH + 1];
...
strncpy(fileList[index], fileInfo.fname, strlen(fileInfo.fname));
fileList[index][MAX_FILENAME_LENGTH] = '\0';
You must use next array definition
TCHAR fileList[50][13];
...
if(fileInfo.fname[0]=='.')continue; //ignore '.' files
strncpy(fileList[index], sizeof(fileList[index]), fileInfo.fname);
LCD_UsrLog("%s\n",fileList[index]);
or for dynamic memory. Don't forget to free memory!
TCHAR* fileList[50];
...
if(fileInfo.fname[0]=='.')continue; //ignore '.' files
fileList[index]=strdup(fileInfo.fname);
LCD_UsrLog("%s\n",fileList[index]);
PS:
Related
I am using the FatFs library to create and manage a file system on an SD card. The goal is to identify a file containing the firmware for the bootloader to update (or not) the device.
The procedure im following is the follwing:
DIR directory;
FILINFO filinfo;
f_findfirst(&directory, &filinfo, "", "firmware_");
To my understanding, the first file that starts with "firmware_" should be identified and the information stored in filinfo . This does work, but when I try to extract the name from filinfo.fname the name that has been extracted is the follwing: "\0IRMWA~1.BIN".
I don't know the reason why the naming of the file is extracted in this format. The idea would be to later take the second part of the file: "firmware_01_01_01" and perform a char to int conversion to process the version number, but I cannot extract the full string of the file name.
I had a similar problem. Here is my code. Hope you find this helpful
#define FW_FOLDER "fw"
#define FW_PATH "0:/" FW_FOLDER
#define FW_BIN "*.bin"
FRESULT res;
DIR dir;
FILINFO fno;
static char fw_path[128];
// Find first item
res = f_findfirst(&dir, &fno, FW_PATH, FW_BIN);
// Create directory (optional)
if (res == FR_NO_PATH)
{
printf("Creating %s directory!\n", FW_FOLDER);
f_mkdir(FW_FOLDER);
}
// Repeat until proper item is found
while (res == FR_OK)
{
// Make sure file name is valid
if (fno.fname[0] == 0)
break;
// Debugging
printf("Scanning: %s\n", fno.fname);
// Make sure it is not a directory
if (!(fno.fattrib & AM_DIR))
{
// Success!
printf("Found: %s\n", fno.fname);
// Construct file path
sprintf(fw_path, "%s/%s", FW_PATH, fno.fname);
break;
}
// Move to the next one
res = f_findnext(&dir, &fno);
}
// Close directory
f_closedir(&dir);
And yes, you need to enable FF_USE_FIND == 1 in fatfs library. And if you decide to use 2 for FF_USE_FIND you have to check altname as part of fno.
Also, I had to update file validation eventually to this snippet
// Make sure file name is alpha numeric
if (!isalnum(fat_fno.fname[0]))
break;
Always read the documentation.
Description
After the directory specified by path could be opened, it starts to
search the directory for items with the name specified by pattern. If
the first item is found, the information about the object is stored
into the file information structure fno.
The matching pattern can contain wildcard characters (? and *). A ?
matches an any character and an * matches an any string in length of
zero or longer. When support of long file name is enabled, only
fname[] is tested at FF_USE_FIND == 1 and also altname[] is tested at
FF_USE_FIND == 2. In this revision, there are some differences listed
below between FatFs and standard systems in matching condition.
- "*.*" never matches any name without extension while it matches any name with or without extension at the standard systems.
- Any pattern terminated with a period never matches any name while it matches the name without extensiton at the standard systems.
- DBCS extended characters are compared in case-sensitive at LFN with ANSI/OEM API.
>
QuickInfo
This is a wrapper function of f_opendir and f_readdir function.
Available when FF_USE_FIND >= 1 and FF_FS_MINIMIZE <= 1. Examples
/* Search a directory for objects and display it */
void find_image_file (void) {
FRESULT fr; /* Return value */
DIR dj; /* Directory search object */
FILINFO fno; /* File information */
fr = f_findfirst(&dj, &fno, "", "dsc*.jpg"); /* Start to search for photo files */
while (fr == FR_OK && fno.fname[0]) { /* Repeat while an item is found */
printf("%s\n", fno.fname); /* Display the object name */
fr = f_findnext(&dj, &fno); /* Search for next item */
}
f_closedir(&dj);
}
Then you will learn that you need to use:
wildcards
you need to check the function return value
if the first char of the filename is zero - it means that the no file was found
I was using Long File Name (LFN) which had been activated in the ffconf.h file (the options file that comes with FatFs). The problem was that I was using filinfo.fname which refers to the 8.3 file name structure (short naming scheme).
If LFN is activated, the fields lfname and lfsize within the FILINFO struct are active. These two fields are a pointer to a char array as well as the size. Both fields must be initialized before using them like follows:
static char lfn[_MAX_LFN + 1]
//MAX_LFN is defined in ffconf.h and is 255, maximum value for LFN
filinfo.lfname = lfn;
filinfo.lfsize = sizeof lfn;
f_findfirst(&directory, &filinfo, "", "firmware_*.bin");
(...)
The documentation that also shows an example can be read here.
When LFN feature is enabled, lfname and lfsize in the file information structure must be initialized with valid value prior to use the f_readdir function. The lfname is a pointer to the string buffer to return the long file name. The lfsize is the size of the string buffer in unit of TCHAR.
lfname and lfsize would work the same for f_findfirst as with f_readdir.
This is a wrapper function of f_opendir and f_readdir
When trying to open a file with fopen(path, "2"); i get NULL on an existing path
iv'e tried to enter only the file name and it works but i want the program to write the file in the path...
Yes, i write the path with double backslashes "\\" when it's necesary.
Yes the path without doubt exists.
FILE* log;
char directory_path[PATH_LEN] = { 0 };
char directory_file[PATH_LEN] = { 0 };
//directory_path is the directory, entered by the user
//LOG_NAME is the files name without the path - "log.txt"
//#define PATH_LEN 100
printf("Folder to scan: ");
fgets(directory_path, PATH_LEN, stdin);
directory_path[strlen(directory_path) - 1] = 0;
//this section connects the path with the file name.
strcpy(directory_file, directory_path);
strcat(directory_file, "\\");
strcat(directory_file, LOG_NAME);
if ((log = fopen(directory_file, "w")) == NULL)
{
printf("Error");
}
My program worked until i tried to write into a file in order to create a log file. This means that the path is correct without doubt.
Can anyone tell me the problem here?
You have several issues in your code:
For one, fopen(path, "2"); is not valid.
The mode argument needs to include one of a, r, and w and can optionally include b or +.
As another thing, directory_path[strlen(directory_path) - 1] = 0; may truncate the end of your path (if it's over PATH_LEN characters long).
There also may be a possible issue with buffer overflow due to the fact that you copy a string to a buffer of the same size and then concatenate two other strings to it. Therefore, you should change this line:
char directory_file[PATH_LEN] = { 0 };
to this:
char directory_file[PATH_LEN+sizeof(LOG_NAME)+1] = { 0 };
To debug this issue, you should print the string entered and ask for confirmation before using it (wrap this in #ifdef DEBUG).
I create a code in C to find all file names in a directory, this code is recursive so if it finds other directories within this will review the others directories. The search in the directory and the search of files works well,, because i printed on screen what the program read and i realized that find all filenames and does not repeat any filename. The problem i have is that i saves into an array of strings of characters the file name found, and when you find all the files i will have in the main the arrangement of strings with all the file names, but when i printed on screen that arrangement there is not all the files in it, but a few repeated files names. The ultimate goal of this program is not print all file names in screen, the purpose is to save in the arrangement all the file names .I can not see the error, if someone can tell me something that I'm doing wrong I'll be grateful.
void findfiles(char *root,char *p[],int *tam){
DIR * dir;
struct dirent *entrada;
struct stat stt;
dir = opendir(root);
char *aux;
char nombre[BUFFER_TAM];
char buf[30];
if (dir == NULL) {
printf("hola4\n");
return;
}
while ((entrada = readdir(dir)) != NULL) {
if (strcmp(entrada->d_name,".")==0 || strcmp(entrada->d_name,"..")==0);
else {
if (entrada->d_type == DT_DIR){
strcpy(nombre,root);
strcat(nombre,"/");
strcat(nombre,entrada->d_name);
findfiles(nombre,p,tam);
}
else {
strcpy(nombre,root);
strcat(nombre,"/");
strcat(nombre,entrada->d_name);
p[*tam]=malloc(strlen(nombre)+1);
p[*tam]=nombre;
*tam = *tam +1;
}
}
}
}
void main(){
char *archivos[BUFFER_TAM];
char root[BUFFER_TAM]="/home/jesusmolina/Documentos";
int i=0,tam=0;
findfiles(root,archivos,&tam);
for (i;i<tam;i++)
printf("%s\n",archivos[i]);
}
p[*tam]=malloc(strlen(nombre)+1);
p[*tam]=nombre;
You allocate a chunk of memory, then immediately lose the pointer to that memory and leak it. You probably wanted:
p[*tam]=malloc(strlen(nombre)+1);
strcpy(p[*tam], nombre);
I am reading data from a number of files, each containing a list of words. I am trying to display the number of words in each file, but I am running into issues. For example, when I run my code, I receive the output as shown below.
Almost every amount is correctly displayed with the exception of two files, each containing word counts in the thousands. Every other file only has three digits worth of words, and they seem just fine.
I can only guess what this problem could be (not enough space allocated somewhere?) and I do not know how to solve it. I apologize if this is all poorly worded. My brain is fried and I am struggling. Any help would be appreciated.
I've tried to keep my example code as brief as possible. I've cut out a lot of error checking and other tasks related to the full program. I've also added comments where I can. Thanks.
StopWords.c
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <stddef.h>
#include <string.h>
typedef struct
{
char stopwords[2000][60];
int wordcount;
} LangData;
typedef struct
{
int languageCount;
LangData languages[];
} AllData;
main(int argc, char **argv)
{
//Initialize data structures and open path directory
int langCount = 0;
DIR *d;
struct dirent *ep;
d = opendir(argv[1]);
//Count the number of language files in the directory
while(readdir(d))
langCount++;
//Account for "." and ".." in directory
//langCount = langCount - 2 THIS MAKES SENSE RIGHT?
langCount = langCount + 1; //The program crashes if I don't do this, which doesn't make sense to me.
//Allocate space in AllData for languageCount
AllData *data = malloc(sizeof(AllData) + sizeof(LangData)*langCount); //Unsure? Seems to work.
//Reset the directory in preparation for reading data
rewinddir(d);
//Copy all words into respective arrays.
char word[60];
int i = 0;
int k = 0;
int j = 0;
while((ep = readdir(d)) != NULL) //Probably could've used for loops to make this cleaner. Oh well.
{
if (!strcmp(ep->d_name, ".") || !strcmp(ep->d_name, ".."))
{
//Filtering "." and ".."
}
else
{
FILE *entry;
//Get string for path (i should make this a function)
char fullpath[100];
strcpy(fullpath, path);
strcat(fullpath, "\\");
strcat(fullpath, ep->d_name);
entry = fopen(fullpath, "r");
//Read all words from file
while(fgets(word, 60, entry) != NULL)
{
j = 0;
//Store each word one character at a time (better way?)
while(word[j] != '\0') //Check for end of word
{
data->languages[i].stopwords[k][j] = word[j];
j++; //Move onto next character
}
k++; //Move onto next word
data->languages[i].wordcount++;
}
//Display number of words in file
printf("%d\n", data->languages[i].wordcount);
i++; Increment index in preparation for next language file.
fclose(entry);
}
}
}
Output
256 //czech.txt: Correct
101 //danish.txt: Correct
101 //dutch.txt: Correct
547 //english.txt: Correct
1835363006 //finnish.txt: Should be 1337. Of course it's 1337.
436 //french.txt: Correct
576 //german.txt: Correct
737 //hungarian.txt: Correct
683853 //icelandic.txt: Should be 1000.
399 //italian.txt: Correct
172 //norwegian.txt: Correct
269 //polish.txt: Correct
437 //portugese.txt: Correct
282 //romanian.txt: Correct
472 //spanish.txt: Correct
386 //swedish.txt: Correct
209 //turkish.txt: Correct
Do the files have more than 2000 words? You have only allocated space for 2000 words so once your program tries to copy over word 2001 it will be doing it outside of the memory allocated for that array, possibly into the space allocated for "wordcount".
Also I want to point out that fgets returns a string to the end of the line or at most n characters (60 in your case), whichever comes first. This will work find if there is only one word per line in the files you are reading from, otherwise will have to locate spaces within the string and count words from there.
If you are simply trying to get a word count, then there is no need to store all the words in an array in the first place. Assuming one word per line, the following should work just as well:
char word[60];
while(fgets(word, 60, entry) != NULL)
{
data->languages[i].wordcount++;
}
fgets reference- http://www.cplusplus.com/reference/cstdio/
Update
I took another look and you might want to try allocating data as follows:
typedef struct
{
char stopwords[2000][60];
int wordcount;
} LangData;
typedef struct
{
int languageCount;
LangData *languages;
} AllData;
AllData *data = malloc(sizeof(AllData));
data->languages = malloc(sizeof(LangData)*langCount);
This way memory is being specifically allocated for the languages array.
I agree that langCount = langCount - 2 makes sense. What error are you getting?
I am having a problem regarding FOPEN in C.
I have this code which reads a particular file from a directory
FILE *ifp ;
char directoryname[50];
char result[100];
char *rpath = "/home/kamal/samples/pipe26/divpipe0.f00001";
char *mode = "r";
ifp = fopen("director.in",mode); %director file contains path of directory
while (fscanf(ifp, "%s", directoname) != EOF)
{
strcpy(result,directoname); /* Path of diretory /home/kamal/samples/pipe26 */
strcat(result,"/"); /* front slash for path */
strcat(result,name); /* name of the file divpipe0.f00001*/
}
Till this point my code works perfectly creating a string which looks " /home/kamal/samples/pipe26/divpipe0.f00001 ".
The problem arises when I try to use the 'result' to open a file, It gives me error. Instead if I use 'rpath' it works fine even though both strings contain same information.
if (!(fp=fopen(rpath,"rb"))) /* This one works fine */
{
printf(fopen failure2!\n");
return;
}
if (!(fp=fopen(result,"rb"))) /* This does not work */
{
printf(fopen failure2!\n");
return;
}
Could some one please tell why I am getting this error ?
I think you mean char result[100];; i.e. without the asterisk. (Ditto for directoryname.)
You're currently stack-allocating an array of 100 pointers. This will not end well.
Note that rpath and mode point to read-only memory. Really you should use const char* for those two literals.
The error is the array 'char* result[100]', here you are allocating an array of 100 pointers to strings, not 100 bytes / characters, which was your intent.