0

for private use and learning purpose I want to write a little program that helps me to detect if a directory changed (like files deleted/added and stuff) on windows.

Problem-related part of code:

typedef struct
{
    char *fileName;
    char *sPath;
    unsigned long long byteSize;
    unsigned long lastWrite;
} FileItem;

// handling files > 2^32 bytes
unsigned long long int constructByteSize(const unsigned long fileSizeLow, const unsigned long fileSizeHigh)
{
    return (unsigned long long int)(fileSizeLow + ((unsigned long long int)fileSizeHigh << 32));
}

void traverse(const char * const inputPath, HANDLE * fHandle, WIN32_FIND_DATA * fData, FileItem (*fileItems)[], unsigned long * const fileCount, unsigned long * const dirCount,
                unsigned long long * const totalSize, unsigned long long * const arraySize)
{
    char sPath[256];
    int index;
    
    // specify file mask
    sprintf(sPath, "%s\\*.*", inputPath);

    if( (fHandle = FindFirstFile(sPath, fData)) == INVALID_HANDLE_VALUE )
    {
        fprintf(stderr, "%s is not a valid directory, aborting..\n", inputPath);
        return;
    }   

    do
    {
        // FindFirstFile always returns . and .. first
        if( strcmp(fData->cFileName, ".") != 0 && strcmp(fData->cFileName, "..") != 0 )
        {
            // concatenate filepath
            sprintf(sPath, "%s\\%s", inputPath, fData->cFileName); 
            
            // check if inputPath is directory or file
            if( fData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
            {       
                // output directory
                printf("Directory: %s\n", sPath);
                ++(*dirCount);
                
                // recursion
                traverse(sPath, fHandle, fData, fileItems, fileCount, dirCount, totalSize, arraySize);
            }
            else
            {
                // output file
                printf("File: %s\n", sPath, strlen(sPath));
                
                // calculate how much memory we need
                unsigned long requestBytes;
                requestBytes = (strlen(fData->cFileName) + 1) + (strlen(sPath) + 1) + sizeof(unsigned long long) + sizeof(unsigned long);
                *arraySize += requestBytes;

                // allocate memory
                fileItems = (FileItem (*)[]) realloc(fileItems, *arraySize);    
                while( *arraySize != 0 && fileItems == NULL)
                {
                    fprintf(stderr, "Memory reallocation failed\n");
                    fileItems = (FileItem (*)[]) realloc(fileItems, *arraySize);
                }
    
                // add structure to our array
                // check if file > 2^32 bytes then create new file struct
                if( fData->nFileSizeHigh != 0 )
                    (*fileItems)[(*fileCount)] = (FileItem) { fData->cFileName, sPath, constructByteSize(fData->nFileSizeLow, fData->nFileSizeHigh), constructWriteTime(fData->ftLastWriteTime) };          
                else
                    (*fileItems)[(*fileCount)] = (FileItem) { fData->cFileName, sPath, fData->nFileSizeLow, constructWriteTime(fData->ftLastWriteTime) };

                DEBUG_LOG((*fileItems)[(*fileCount)].fileName);
                // add bytesize to sum
                *totalSize += (*fileItems)[(*fileCount)].byteSize;
                ++*fileCount;
                
                
            }
        }
    } while( FindNextFile(fHandle, fData) );
}

void printArray(FileItem (*fileItems)[], unsigned long * const size);

void startTraverse(const char * const root, HANDLE * fHandle, WIN32_FIND_DATA * fData, unsigned long * const fileCount, unsigned long * const dirCount)
{
    // list of files
    FileItem (*fileItems)[] = NULL;
    // store meta-information (total bytesize, arraysize)
    unsigned long long totalSize = 0, arraySize = 0;
    
    // start directory traverse
    printf("Traversing %s..\n", root);
    traverse(root, fHandle, fData, fileItems, fileCount, dirCount, &totalSize, &arraySize);

    // output stats
    printf("\nFile Count: %d\n", *fileCount);
    printf("Directory Count: %d\n\n", *dirCount);
    printf("Total Size: %d Bytes\n", totalSize);
    printf("Total Size: %d KBytes\n", totalSize / 1024 + totalSize % 1024);
    printf("Total Size: %d MBytes\n", totalSize / (1024 * 1024));
    printf("Total Size: %.2f GBytes\n\n", (float) totalSize / (1024 * 1024 * 1024));
    
    printArray(fileItems, fileCount);
    
    //cleanup
    free(fileItems);
}

void printArray(FileItem (*fileItems)[], unsigned long * const size)
{
    for(int i = 0; i < (*size); ++i)
    {
        printf("FileItem #%d: %s\n", i + 1, (*fileItems)[i].fileName);
    }
}

int main(int argc, char **argv)
{
    WIN32_FIND_DATA fData;
    HANDLE fHandle = NULL;
    
    unsigned long fileCount = 0, dirCount = 0;
    
    printf("#############################\n");
    printf("##  File Change Detection  ##\n");
    printf("#############################\n\n");
    
    // parsing arguments
    if( argc == 1 )
    {
        printf("need more arguments, aborting..\n");
    }
    // parse files without comparing
    else if( argc == 2 )
    {
        startTraverse(argv[1], &fHandle, &fData, &fileCount, &dirCount);
        //writeToDisk(fileItems, &fileCount);
    }
    /*
    // parse files with comparing
    else if( argc == 3 )
    {
        FileItem (*fileItemsIn)[] = malloc(sizeof(FileItem));
        
        readFromDisk(argv[2], fileItemsIn);
        startTraverse(argv[1], &fHandle, &fData, &fileCount, &dirCount);
        //compare(fileItems, fileItemsIn, &fileCount);
    
        free(fileItemsIn);
    }
    */
    // too many arguments
    else
        printf("Too many arguments, aborting..\n");


    // cleanup
    FindClose(fHandle);
    

    return EXIT_SUCCESS;
}

Parsing the files and directories and outputting their names (inside the traverse-function!) works fine (as far as I see), printing out the total size works fine, but the program crashes when my function printArray is called (or to be more specific, when the first iteration of the for-loop is executed). By testing purpose, I made the ptr to the array of FileItems static. This time, the program didnt crash, but every item of this array seems to have the name of the last parsed file (e.g. when given a directory with 958 files, and name of last parsed file was "playerout.bik", printArray prints FileItem#1: playerout.bik FileItem#2: playerout.bik . . . FileItem#957: playerout.bik FileItem#958: playerout.bik ).

This made me think that this problem may be scope-related, but I can't see what I'm doing wrong here.

  • `void printArray(FileItem (*fileItems)[], unsigned long * const)` problem is that you declared `fileItems` as an array of pointers to function. One way of doing this the way you need is use `FileItem**`, as the system does for every C program building an array of `argc` pointers `char** argv` – arfneto Sep 18 '20 at 22:36
  • You're using the wrong basic type. Use plain pointer to `FileItem` and use the address-of operand `&` to pass this pointer to the functions where you need to emulate pass by reference. I.e. `FileItems *fileItems = NULL; ...; traverse(..., &fileItems, ...);` Modify your functions as needed. And you [don't need to cast the result of `malloc` or related functions](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc). – Some programmer dude Sep 18 '20 at 22:37
  • Oh, and never assign back to the pointer you pass to `realloc`, use a temporary pointer. If `realloc` fail you will loose the original (and otherwise still valid) pointer otherwise. – Some programmer dude Sep 18 '20 at 22:40

0 Answers0