directory traverse c - c

I'm trying to traverse a directory and check for duplicate files.
void findDuplicates(){
char *dot[] = {".", 0};
FTS *ftsp, *temp_ftsp;
FTSENT *entry, *temp_entry;
int fts_options = FTS_LOGICAL;
ftsp = fts_open(dot, fts_options, NULL);
while((entry = fts_read(ftsp)) != NULL){
temp_ftsp = ftsp;
while((temp_entry = fts_read(temp_ftsp)) != NULL){
compareEntries(temp_ftsp, ftsp);
}
}
}
But it doesn't traverse the directory the way I wanted to. After the 2nd while loop, the
entry = fts_read(ftsp)
returns NULL. Is there an easy fix for this or I should use something else?

You need to re-structure your approach. The inner while is exhausting the list of files, so of course the outer will fail after succeeding, once.
A better approach is probably to store the files so you can just compare each new incoming file against the stored ones, or use a recursive approach. Both will require memory.

Related

Remove file from directory - ext-like file system implementation

I have an issue. I'm currently trying to implement an ext-ish file system. I've done the inode operations such as read and write. I've created a structure that represents both a regular file and a directory. I have a problem when trying to remove a certain file from the directory.
char
dirremove(struct dirent *dir, struct dirent *file)
{
dirent_t n = {.mode = NODDIR, .inumber = remdirnod,
.r = 0, .w = 0};
strcpy(n.nm, dir->nm);
dirent_t t;
dir->r = 0;
char r = 1;
while (!dirread(dir, &t))
{
int tt = dir->r;
dir->r = 0;
dirent_t ff[3];
filread(ff, dir, 3 * entrysiz);
dir->r = tt;
if (!strcmp(t.nm, ""))
return 1;
if (!(!strcmp(t.nm, file->nm) && !(r = 0)))
assert(!dirappend(&n, &t));
}
assert(n.w == dir->w - entrysiz);
dir->w = n.w;
dir->r = n.r;
copyinode(dir->inumber, remdirnod);
return r;
}
This is the function called from the rm command. It takes the directory object (where the file is stored) and the file object to be deleted. I know this solution is not the best in terms of speed and memory usage but I'm still a beginner in this area, so don't hate me a lot, please :).
The function is designed to do the following. It has to read all files and check if the current is the one to be deleted. If not, the file is added to a new directory (empty in the beginning) which will replace the old one at the end of the function. The "new" directory is an entry saved entirely for this purpose, so there isn't a chance that all inodes are already used.
The test that I've done is to create a file (works fine), then remove it, then create it again and remove it. Everything works perfectly except for the second execution of the dirremove function. The directory has its dot and dot-dot directories by default so it goes through them first. The result is that the first deletion is successful. Everything works perfectly. But the second time things go wrong somewhere.
int tt = dir->r;
dir->r = 0;
dirent_t ff[3];
filread(ff, dir, 3 * entrysize;
dir->r = tt;
I added the ff array that should read the whole content of the directory and this would help me figure out if the correct files are there. On the first and second iteration, all files (".", ".." and "some-other-file") are there but at the iteration which should hold the object of the file that's to be removed the third file suddenly goes all zeroes.
I've debugged this for several hours but it continues to fail the same way.
Probably I didn't explain the failure the best way, but there are a lot of things that I forgot to say, so if I missed something please don't ignore the question and just ask about it.

Recursive function : abort-condition

We need to create a binary tree which contains content of textfiles. The pointer selection_a and selection_b pointing to another textfile in the directory.
The structure of the textfiles is following:
line: Title
line: OptionA
line: OptionB
line: Text.
The first file is given as parameter while starting the program. All files should be saved at the beginning of the program. Then the text of the first file shows, and the user can input A or B to continue. Based on the selection, the text of File Option A/B is shown and the user can decide again.
The last file of a tree contains no Options: lines 2 and 3 are "-\n".
The problem is, this code only reads all the option A files of the first tree. It doesn't read in any B-Options. In the end, the program shows a memory access error.
I think the problem is that the readingRows function has no abort condition.
current->selection_a = readingRows(input_selection_a);
current->selection_b = readingRows(input_selection_b);
I know the code may be kind of chaotic, but we are beginners in programming. Hope anybody can help us to write an abort-condition.
The function should be aborted if the content of option A (line 3) is "-\n".
Here is the whole function:
struct story_file* readingRows(FILE *current_file)
{
char *buffer = fileSize(current_file);
char *delimiter = "\n";
char *lines = strtok(buffer, delimiter);
int line_counter = 0;
struct story_file *current = malloc(sizeof(struct story_file));
while(lines != NULL)
{
if(line_counter == 0)
{
current->title = lines;
}
else if(line_counter == 1)
{
char *filename_chapter_a = lines;
FILE *input_selection_a = fopen(filename_chapter_a, "r");
if(input_selection_a)
{
current->selection_a = readingRows(input_selection_a);
}
fclose(input_selection_a);
}
else if(line_counter == 2)
{
char *filename_chapter_b = lines;
FILE *input_selection_b = fopen(filename_chapter_b, "r");
if(input_selection_b)
{
current->selection_b = readingRows(input_selection_b);
}
fclose(input_selection_b);
}
else if (line_counter >= 3)
{
current->text = lines;
}
lines = strtok(NULL, delimiter);
line_counter++;
}
return current;
}
There are two items that define a terminating recursive function:
One or more base cases
Recursive calls that move toward a base case
Your code has one base case: while (lines!=NULL) {} return current;, it breaks the while loop when lines is NULL and returns current. In other words, within any particular call to your function, it only terminates when it reaches the end of a file.
Your code moves toward that base case as long as your files do not refer to each other in a loop. We know this because you always read a line, take an action according to your if-else block, and the read the next line. So you always move toward the end of each file you read.
But as you note, the issue is that you don't have a case to handle "no Options", being when lines 2 or 3 are "-\n". So right now, even though you move through files, you are always opening files in line 2. Unless a file is malformed and does not contain a line 2, your recursive call tree never ends. So you just need to add another base case that looks at whether the beginning of lines matches "-\n", and if it does, return before the recursive call. This will end that branch of your recursive tree.
Inside of your while loop, you will need code along the lines of:
if `line_counter` is `2` or `3`
if `lines` starts with your terminating sequence "-\n"
return current
else
`fopen` and make the recursive call
In the parent function that made the recursive call, it will move to the next line and continue as expected.
P.S. Make sure you use free for each malloc you do.

C Programming - fprintf and printf in while cicle doesn't work

I'm getting a strange problem with a while cicle inside of a function.
I have to look for the extreme vertices of a .ply model. All the data is stored in a linked list. When I'm done creating the list, I call the findExtremeVertex function, that modifies 6 global variables (leftVertex, rightVertex, downwardVertex, upwardVertex, backVertex and frontVertex).
To see if the values are right (the models I use are a bit too big to control every single line to find the maximum of every vertex) I decided to print every change in the max-min values but, when I try to print them in a file, the file is empty. Why is that? Also, when I saw that the file was empty, I tried to print something directly in the console but that didn't work either.
Here's the code of the funcion:
void findExtremeVertex(Vertex *vertex){
FILE *modelInfoFile;
int i = 0;
///Giving data to direction-vertices pointers
leftVertex = malloc(sizeof(Vertex));
rightVertex = malloc(sizeof(Vertex));
upwardVertex = malloc(sizeof(Vertex));
downwardVertex = malloc(sizeof(Vertex));
frontVertex = malloc(sizeof(Vertex));
backVertex = malloc(sizeof(Vertex));
///Giving the direction-vertices the values of the parameter
leftVertex = vertex;
rightVertex = vertex;
upwardVertex = vertex;
downwardVertex = vertex;
frontVertex = vertex;
backVertex = vertex;
///Opening file
modelInfoFile = fopen(us2, "w");
if(modelInfoFile == NULL){
printf("Error in file opening. Exiting.");
exit(EXIT_FAILURE);
}
///Scrolling the list
while(vertex->prev != NULL){
vertex = vertex->prev;
///If the given element of the list is more to the right than the global variable,
///I assign the values of the element to the global variable
if(vertex->vertexCoordinates.x > rightVertex->vertexCoordinates.x){
rightVertex = vertex;
}
/**
I'm omitting the other if constructs because are basically
the same, but the syntax is correct
**/
///Printing in file the cycle information
fprintf(modelInfoFile, "********** CYCLE %d **********\n\n", i);
fprintf(modelInfoFile, "Vertex sx\n");
fprintf(modelInfoFile, "%1.4f %1.4f %1.4f %1.4f %1.4f %1.4f\n\n", leftVertex->vertexCoordinates.x,
leftVertex->vertexCoordinates.y,
leftVertex->vertexCoordinates.z,
leftVertex->vertexNormals.x,
leftVertex->vertexNormals.y,
leftVertex->vertexNormals.z);
/**
Again, I'm omitting some repetitions but the syntax is correct
**/
}
}
I call this function in another function, but there's no segmentation fault signal, the compiler doesn't tell me anything, the program doesn't crash. I have no clue of the error, except from the fact that the file where I print the infos about the cycles is empty. What am I doing wrong?
There are many problems in your code.
You malloc() 6 variables and never use any of them, and you don't check if malloc() succeeded.
You never call fclose() or fflush() so maybe you are seeing the file before the data is flushed to the disk.
You reassign all the *Vertex (except for rightVertex) variables after they are malloc()ed to the same pointer vertex which means
You are causing a memory leak.
You are using 6 variables for a single pointer.
All the *Vertex variables are not declared inside the function which means that they are in the global scope, that is very likely a bad design choice. Given the code you posted it's not possible to tell whether or not global variables are the right choice, but 99% of the time they are a bad choice and there is a much more elegant and safe way to do things.
The bold point above is likely the reason why your program is behaving as it is.
The code
leftVertex = vertex;
rightVertex = vertex;
upwardVertex = vertex;
downwardVertex = vertex;
frontVertex = vertex;
backVertex = vertex;
sets the pointer value but not the actual value. You malloc space, get a pointer to that space, and then throw that pointer away setting it to the pointer of virtex.
Do you mean to use
*leftVertex = *vertex;
*rightVertex = *vertex;
*upwardVertex = *vertex;
*downwardVertex = *vertex;
*frontVertex = *vertex;
*backVertex = *vertex;
///Scrolling the list
while(vertex->prev != NULL){
vertex = vertex->prev;
And what happens if vertex is NULL after this?
You're checking if it's NULL, then changing it's value such that it can become NULL.
///Opening file
if(modelInfoFile == NULL){
printf("Error in file opening. Exiting.");
exit(EXIT_FAILURE);
}
I don't see you opening file.
if((modelInfoFile=fopen(filename,"w")) == NULL){
Should work.
EDIT
In you while loop you change -
vertex = vertex->prev;
But in fprintf you store in file in value of leftVertex->vertexCoordinates.x
So how do you expect to print inside file correctly.

Adding one Item to linked list creates two Items?

I'm trying to develop a device to copy files from one USB-drive to another, with both using the FAT-Filesystem. Therefor I use the "Vinculum II" microcontroller by FTDI. The Code is written in C.
To be able to copy all files, I need to know the names of the (sub-)directories on the drive because each of them has to be treated separately. There is a on-chip function to scan the current directory for files and sub-directories ('fat_dirTableFindFirst()' and 'fat_dirTableFindNext()').
I need to store the names of all directories (data type char *) which I received from the scan dynamically. I decided to use a linked-list. I use it like a stack (LIFO).
It's important for understanding the code, so I'll stress it again, that I have to scan each directory separately. So at first, I scan the root directory for its entries. Those ones that are further sub-directorys get pushed onto the stack.
After finishing the scan in the first directory, I grab the upper sub-directory off the stack (pop()). Then, I push the place marker "space" onto the stack, to be able to identify later, that I went into a deeper level/layer of that "directory-tree". If I don't find further directories during a scan, I move back to the last level and so on. Hence the scanning procedure should be similar to preorder traversing of a tree.
It works perfectly if there is max. one sub-directory in each directory. But if there are more than one, I get a confusing error: The first directory is pushed correctly, but all following entries appear twice on the stack! Because of that, the controller copies the same files again and again.
Single stepping through the program doesn't clearify why it happens. The code also writes the content of the stack before and after every push or pop into a .txt file with the same confusing results. It looks a bit like a push()-operation creates two Items, but only if it's called during that do...while loop.
Here's the interesting part of the code. vos_free() und vos_malloc() is equivalent to the usual free() an malloc() calls (ordner is the German word for directory or folder):
struct ordner {
char* data;
struct ordner* next;
};
void push(struct ordner** headRef, char* dirName)
{
struct ordner* newOrdner;
if (newOrdner = vos_malloc(sizeof(struct ordner)) != NULL)
{
newOrdner->data = dirName;
newOrdner->next = *headRef;
*headRef = newOrdner;
}
}
char* pop(struct ordner** headRef)
{
struct ordner* temp;
char* value = " ";
temp = *headRef;
value = *headRef->data; // "save" last element to return it
*headRef = temp->next;
vos_free(temp);
return (value);
}
while(1)
{
file_context_t fileToCopy; // File-Handle
struct ordner dummy;
struct ordner* head = &dummy;
dummy.next = NULL;
dummy.data = begin;
newScan: fat_dirTableFindFirst(fatContext1, &fileToCopy); if(firstRun == 0) // First filename in first scan is the name of the disk, and has to be ignored
{
fat_dirTableFindNext(fatContext1, &fileToCopy);
firstRun = 1;
}
do
{
// if the entry is a Directory, add it to the stack
if (fat_dirEntryIsDirectory(&fileToCopy) == 1)
{
strncpy(nextDir, (char*) &fileToCopy, 11);
push(&head, nextDir);
// The next if-statement usually cannot be true, because there can't be
// two files with the same name in one directory and the different levels/layers
// of sub-directories are separated by a place marker, but actually it becomes
// true (LEDs are flashing because of blink(3))
if (head->data == head->next->data) blink(3);
}
else
{
strncpy(nextFile, (char*) &fileToCopy, 11);
copyFile(fatContext1,fatContext2, nextFile); }
} while (fat_dirTableFindNext(fatContext1, &fileToCopy) == FAT_OK); // perform scan, until all items of the directory were scanned
// then the next (sub-)directory has to be opened to scan it
// there are two possibilities to proceed:
// (1) no directory found ("space" on stack) --> go back to last layer and open & scan the next directory there (if there is another one)
// (2) a new sub-directory was found --> open & scan it
change_layer: if (head != NULL)
{
nextDir = pop(&head); // get next Directory from stack
// Possibility (1)
if (nextDir == space)
{
// move back to last Directory
goto change_layer;
}
// Possibility (2): neue Unterordner gefunden
else
{
push(&head, space); // sign for entering next layer
//...
// open next directory
//...
goto newScan;
}
}
}
} // End while(1)
Can you tell me why it happens that one item appears twice on the stack? Is my Algorithm wrong?
After hours and hours of reasearching and coding I couldn't solve that problem.
Please forgive me my bad programming style with those assembler-like loops and my bad English (I'm from Germany :) )
Thanks in advance
Chris
Here is the declaration of a node for the linked list:
struct ordner {
char* data;
struct ordner* next;
};
So, the data has no storage associated with it. It is simply a pointer.
Then in your loop I do not see you call strdup() to allocate memory for a copy of the filename. You seem to be passing some buffer address directly to push() which saves a copy. This is a mistake.
I recommend that you change push() to call strdup() and save the filename. Then when you free an instance of ordner you must free data, the duplicate string, before you free the ordner instance.
Since in your design pop() also frees memory, you should change pop() so that the caller provides a buffer, and pop() copies the filename to the buffer before freeing the memory of the popped ordner instance.
You don't show where nextDir is declared, but at first glance, this seems likely:
You strncpy a directory name into nextDir. Then, you push this onto the stack. You now have for example an entry with data "dir1" on the stack.
If there is another directory within the same directory, you strncpy the next directory name into the same nextDir buffer, effectively overwriting it. You push it onto the stack. Its data pointer becomes the same nextDir buffer.
Now, both entries have the same data pointer, and the value is the value of the second entry, so the stack looks like "dir2","dir2".
If you want to have a string in each entry on the stack, you need to allocate the memory for each one (make sure you free it eventually though!)
I don't think you can declare variable like this within a while loop. The compiler might give you the same pointer over and over again.
while(1)
{
file_context_t fileToCopy; // File-Handle
struct ordner dummy;
struct ordner* head = &dummy;

Recursive CreateDirectory

I found many examples of CreatingDirectory recursively, but not the one I was looking for.
here is the spec
Given input
\\server\share\aa\bb\cc
c:\aa\bb\cc
USING helper API
CreateDirectory (char * path)
returns true, if successful
else
FALSE
Condition: There should not be any parsing to distinguish if the path is Local or Server share.
Write a routine in C, or C++
I think it's quite easier... here a version that works in every Windows version:
unsigned int pos = 0;
do
{
pos = path.find_first_of("\\/", pos + 1);
CreateDirectory(path.substr(0, pos).c_str(), NULL);
} while (pos != std::string::npos);
Unicode:
pos = path.find_first_of(L"\\/", pos + 1);
Regards,
This might be exactly what you want.
It doesn't try to do any parsing to distinguish if the path is Local or Server share.
bool TryCreateDirectory(char *path){
char *p;
bool b;
if(
!(b=CreateDirectory(path))
&&
!(b=NULL==(p=strrchr(path, '\\')))
){
size_t i;
(p=strncpy((char *)malloc(1+i), path, i=p-path))[i]='\0';
b=TryCreateDirectory(p);
free(p);
b=b?CreateDirectory(path):false;
}
return b;
}
The algorithm is quite simple, just pass the string of higher level directory recursively while creation of current level of directory fails until one success or there is no more higher level. When the inner call returns with succeed, create the current. This method do not parse to determ the local or server it self, it's according to the CreateDirectory.
In WINAPI, CreateDirectory will never allows you to create "c:" or "\" when the path reaches that level, the method soon falls in to calling it self with path="" and this fails, too. It's the reason why Microsoft defines file sharing naming rule like this, for compatibility of DOS path rule and simplify the coding effort.
Totally hackish and insecure and nothing you'd ever actually want to do in production code, but...
Warning: here be code that was typed in a browser:
int createDirectory(const char * path) {
char * buffer = malloc((strlen(path) + 10) * sizeof(char));
sprintf(buffer, "mkdir -p %s", path);
int result = system(buffer);
free(buffer);
return result;
}
How about using MakeSureDirectoryPathExists() ?
Just walk through each directory level in the path starting from the root, attempting to create the next level.
If any of the CreateDirectory calls fail then you can exit early, you're successful if you get to the end of the path without a failure.
This is assuming that calling CreateDirectory on a path that already exists has no ill effects.
The requirement of not parsing the pathname for server names is interesting, as it seems to concede that parsing for / is required.
Perhaps the idea is to avoid building in hackish expressions for potentially complex syntax for hosts and mount points, which can have on some systems elaborate credentials encoded.
If it's homework, I may be giving away the algorithm you are supposed to think up, but it occurs to me that one way to meet those requirements is to start trying by attempting to mkdir the full pathname. If it fails, trim off the last directory and try again, if that fails, trim off another and try again... Eventually you should reach a root directory without needing to understand the server syntax, and then you will need to start adding pathname components back and making the subdirs one by one.
std::pair<bool, unsigned long> CreateDirectory(std::basic_string<_TCHAR> path)
{
_ASSERT(!path.empty());
typedef std::basic_string<_TCHAR> tstring;
tstring::size_type pos = 0;
while ((pos = path.find_first_of(_T("\\/"), pos + 1)) != tstring::npos)
{
::CreateDirectory(path.substr(0, pos + 1).c_str(), nullptr);
}
if ((pos = path.find_first_of(_T("\\/"), path.length() - 1)) == tstring::npos)
{
path.append(_T("\\"));
}
::CreateDirectory(path.c_str(), nullptr);
return std::make_pair(
::GetFileAttributes(path.c_str()) != INVALID_FILE_ATTRIBUTES,
::GetLastError()
);
}
void createFolders(const std::string &s, char delim) {
std::stringstream ss(s);
std::string item;
char combinedName[50]={'\0'};
while (std::getline(ss, item, delim)) {
sprintf(combinedName,"%s%s%c",combinedName,item.c_str(),delim);
cout<<combinedName<<endl;
struct stat st = {0};
if (stat(combinedName,&st)==-1)
{
#if REDHAT
mkdir(combinedName,0777);
#else
CreateDirectory(combinedName,NULL);
#endif
}
}
}

Resources