4
\$\begingroup\$

I have written a C program to sort files (on Windows) into dynamically created subfolders based on information found in their data headers. I am a newb at C, especially memory management. The code works. I have tested it on a small sample of 6 files. The next step is to put it into "production" and have it sort a giant, 1,000+ files (18+ GB) folder of files (if it would help, please note that the files I am sorting start at 1MB and have no upper limit, but the average is between 50MB and 100MB). I have not done this yet because I do not have confidence in my C. I do not want to brick my computer or my drives. Any responses on memory/storage safety (and to a lesser extent, speed) would be greatly appreciated.

#include <stdio.h>
#include <stdlib.h>

#include <errno.h>
#include <limits.h>

#include <string.h>

#include <dirent.h>
#include <errno.h>

#include <sys/stat.h>
#include <sys/types.h>

//demo header info struct, written by me
//specifications here: https://developer.valvesoftware.com/wiki/DEM_Format
typedef struct dhead {
    char header[8];
    int demp;
    int netp;
    unsigned char servername[260];
    unsigned char clientname[260];
    unsigned char map[260];
    unsigned char gamedir[260];
    float playback;
    int ticks;
    int frames;
    int signon;
} dhead;

struct dhead alldemos[1000*sizeof(dhead)];

unsigned char buffer[1000];
FILE *ptr;
/* Everything above here I wrote */
/* BEGIN STUFF I STOLE */

char* replace_char(char* str, char find, char replace) {
    char *current_pos = strchr(str, find);
    for (char* p = current_pos; 
    (current_pos = strchr(str, find)) != NULL; 
    *current_pos = replace);
}

const char *get_filename_ext(const char *filename) {
    const char *dot = strrchr(filename, '.');
    if (!dot || dot == filename) return "";
    return dot+1;
}

// I found this online and made a few tweaks to make it a function
int move(char *in_, char *out_)
{
    size_t len = 0;
    char a[256]; 
    strcpy(a, in_);
    char b[256]; 
    strcpy(b, out_);
    strcat(b, "\\");
    strcat(b, a);
    char buffer[BUFSIZ] = {'\0'};
    printf("Moving %s -> %s\n", a, b);
    FILE* in = fopen(a, "rb");
    FILE* out = fopen(b, "wb");

    if (in == NULL || out == NULL)
    {
        printf("mve: %s, %s %s\n", in, out, out_);
        perror("Error while opening files in mv");
        in = out = 0;
    }
    else
    {
        while((len = fread(buffer, BUFSIZ, 1, in)) > 0)
        {
            fwrite(buffer, BUFSIZ, 1, out);
        }

        fclose(in);
        fclose(out);

        if (remove(a) == 0){printf("File moved\n");}else{
            printf("Error while removing file: \n");
            perror(NULL);
        }

    }

    return 0;
}

// https://gist.github.com/JonathonReinhart/8c0d90191c38af2dcadb102c4e202950
int mkdir_p(char *path)
{
    printf("Mkdir called: %s\n", path);
    /* Adapted from http://stackoverflow.com/a/2336245/119527 */
    const size_t len = strlen(path);
    char _path[PATH_MAX];
    char *p; 

    errno = 0;

    /* Copy string so its mutable */
    if (len > sizeof(_path)-1) {
        errno = ENAMETOOLONG;
        printf("ENAMETOOLONG\n");
        return -1; 
    }   
    strcpy(_path, path);

    /* Iterate the string */
    for (p = _path + 1; *p; p++) {
        if (*p == '\\') {
            /* Temporarily truncate */
            *p = '\0';

            if (mkdir(_path) != 0) {
                if (errno != 17){
                    printf("Err: %s\n", strerror(errno));
                    return -1; 
                }
            }

            *p = '\\';
        }
    }   

    if (mkdir(_path) != 0) {
        if (errno != 17){
            printf("Err: %s\n", strerror(errno));
            return -1; 
        }
    }   

    return 0;
}
/* END STUFF I STOLE */
/* Everything below here I wrote */
int list_len = 0;

/* I wrote this function to search a directory and compile a list of demo files and put demo information into a struct*/
int find_demos(char* directory_location, char** list)
{
    struct dirent *de;
    DIR *directory = opendir(directory_location);
    list_len = 0;
    while ((de = readdir(directory)) != NULL)
    {
        if (strcmp(get_filename_ext(de->d_name),"dem")) //if file not dem
        {
            continue;
        }
        list[list_len] = strdup(de->d_name);
        ptr = fopen(de->d_name, "rb");
        fread(&alldemos[list_len], sizeof(dhead), 1, ptr);
        list_len++;
        fclose(ptr);
    }
    closedir(directory);
    perror(NULL);
    printf("Closed dir\n");
    return 0;
}

/* I wrote this function to call mkdir_p to create directories for each demo then move them there. */
int create_dirs(char** list)
{
    for (int iter = 0; iter < list_len; iter++) //for file
    {
            printf("\n\n");
            //establish path and demo
            char pth[256];
            dhead dem = alldemos[iter];
            //can't have folders with : in windows      
            replace_char(dem.servername, ':', '@');
            //establish truncated date
            char date[8];
            strncpy(date, list[iter], 7);
            date[7] = '\0';
            //create path 
            snprintf(pth, 256, "%s\\%s\\%s", dem.map, date, dem.servername);
            printf("File: %s | Path: %s\n", list[iter], pth);
            //create path
            if (mkdir_p(pth) != 0)
            {
                printf("Makedir error %s\n", strerror(errno));
            }
            //move demo
            move(list[iter], pth);
    }
    return 0;
}
/* I also wrote this. I initialize an array of arrays to hold all relevant file names */
int main(int argc, char *argv[]) {
    char **list;                                        //the two * means this is a pointer to a pointer
    //const int datacount = 2100;                       //I have like 2100 files to search through, although only near 1,000 +- 50 will be entered into the array.
    list = malloc(260*210);                             //A quick google search shows that max filename length is 259 so this should work.
    if (!list) {                                        //if can't allocate space
        perror("Error allocating memory");
        abort();
    }

    for (int i = 0; i < 2000; ++i) {
        list[i] = (char *)malloc(261);      //each string is (probably) no longer than 260 bytes + a null terminator (in practice, a majority of those bytes will be termintors but I'm being safe)
    }

    find_demos(".", list);
    create_dirs(list);

    return 0;
}

```
\$\endgroup\$
12
  • \$\begingroup\$ Please add comments before and after the parts you wrote, so we know what to review. \$\endgroup\$ – pacmaninbw May 12 '20 at 22:23
  • \$\begingroup\$ Alright, I added a few more comments that I hope will clarify. \$\endgroup\$ – Zidgel May 12 '20 at 22:50
  • 1
    \$\begingroup\$ Is there any specific reason why you chose C as the programming language? If you had chosen Python instead, the code would have been much shorter, and you wouldn't have to care about memory management. String processing is a lot easier in Python than in C. \$\endgroup\$ – Roland Illig May 13 '20 at 6:12
  • \$\begingroup\$ @RolandIllig Well I thought learning C would be fun (I was wrong), but also I was attracted to the ease of compiling binaries with no need to install C or dependencies for end users. I originally wanted to write this in Lua, which I am fluent in, but I struggled to find a program that could link a LuaJIT compiler with dependencies for use on another computer with little work. Python did not cross my mind, I do not know the process of creating bins for it. Having bins has since moved to the back of my mind, but I'd revisit, were I to bulk up the program with more useful features. \$\endgroup\$ – Zidgel May 13 '20 at 7:08
  • 1
    \$\begingroup\$ @pacmaninbw Yeah, might actually take some time to properly review this. There's a lot more to be said, though, and I only tend to commit to writing up a review if I'm going to cover the full code \$\endgroup\$ – Elias Van Ootegem May 13 '20 at 14:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.