4
\$\begingroup\$

Hi guys how you doing??

I just finished creating an rsync helper program that reads its configuration file using a program that I also created and it deletes some cache directories that I included in the config file plus a few commands for cleaning the system, then it creates a directory on my hard drive with the date of today and starts backing up the system.

For this tool I used C. I know that it may sound strange choice for a wrapper program to be written in C, but the truth I like creating tools in C, so I don't know what about you guys but I think the results aren't bad.

So here is the code:

  1. errors.h
#ifndef ERRORS_H
#define ERRORS_H

void handle_files_error(const char *, char *);
int check_input_size(int, int);
void handle_general_error(const char *);
void handle_strstr_error(const char *);

#endif
  1. errors.c
/*
*This header is for handling errors 
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "errors.h"

/*
*This function will handle errors that occurres 
*while managing files and prints the right message.
*/
void handle_files_error(const char *message, char *file_path) {
    char full_message[300];

    if(check_input_size(sizeof(full_message), strlen(message)+strlen(file_path))) { //Check the final full_message size
        fprintf(stderr, "The size of the inserted string in errors.c -> handle_files_error is invalid.\n"); 
        exit(EXIT_FAILURE);
    }
    strcpy(full_message, message);
    strcat(full_message, file_path);
    perror(full_message);
    exit(EXIT_FAILURE);
}

/*
*This function will check if the size of the input is 
*valid and can fit in the array without overflowing it.
*0 for valid, 1 otherwise.
*/
int check_input_size(int valid_size, int input_size) {
    if(input_size < valid_size)
        return 0;
    else 
        return 1;
}

/*
*This function will handle general errors and display 
*the relevant message.
*/
void handle_general_error(const char *message) {
    fprintf(stderr, "%s\n", message);
    exit(EXIT_FAILURE);
}

/*
*This function will handle errors for the strstr() function 
*and it will display the write message if a string wasnt found.
*/
void handle_strstr_error(const char *serched_str) {
    fprintf(stderr, "Error occurred while serching for unit : %s.\n", serched_str);
}
  1. config_man.h
#ifndef CONFIG_MAN_H
#define CONFIG_MAN_H

int check_file_existence(char *);
void create_config_file(char *, const char*);
void write_config_unit(char *, const char *, const char *);
char *read_config_unit(char *, const char *);
int check_unit_existence(const char *, char *);
void read_reg_syntax(char *, char *, int);
int read_list_syntax(char *, char *, int);

#endif
  1. config_man.c
/*
*This header contains all required functions to manage configuration files.
*/ 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "errors.h" //Header for error handling
#include "config_man.h"

/*
*The function will check if the given file in the 
*file path exists. It retruns 1 for yes and 0 for no.
*/
int check_file_existence(char *file_path) {
    FILE *fp;

    fp = fopen(file_path, "r");
    if(fp == NULL) //if file doesnt exists 
        return 0;
    fclose(fp);
    return 1;
}

/*
*A function to create a configuration file with the given path.
*Then it'll write a comentted discription and instruction for this file.
*/ 
void create_config_file(char *file_path, const char *description) {
    FILE *fp;
    const char *config_instruct = {
        "################################################################\n"
        "#--------------------------------------------------------------#\n"
        "#                        [Instructions]                        #\n"
        "#--------------------------------------------------------------#\n"
        "################################################################\n"
        "# Please it's very important to make sure there are 3 charact- #\n"
        "# ers between the unit name and its configurations.            #\n"
        "# For example:                                                 #\n"
        "#                                                              #\n"
        "# UnitName = configurations                                    #\n"
        "#                                                              #\n"
        "# As you can see there are 3 characters between the unit name  #\n"
        "# and the configurations (2 spaces and an equal sign). Be awa- #\n"
        "# re that each new line terminates the unit's configurations.  #\n"
        "# If the unit's configurations are too long you can put it in- #\n"
        "# side a pair of braces and make it a list. For example:       #\n"
        "#                                                              #\n"
        "# VeryLongUnit = {                                             #\n"
        "#    config_1                                                  #\n"
        "#    config_2                                                  #\n"
        "#    config_3                                                  #\n"
        "#    config_4                                                  #\n"
        "# }  <- Don't forget to close the list                         #\n"
        "#                                                              #\n"
        "# If the list is group of commands you can add '&&' to the end #\n"
        "# of each line. If the command requires root privileges you    #\n"
        "# can just add sudo to the command. For example:               #\n"
        "#                                                              #\n"
        "# CommandsUnit = {                                             #\n"
        "#    command 1 &&                                              #\n"
        "#    sudo root_command 2 &&                                    #\n"
        "#    command 3 &&                                              #\n"
        "#    sudo root_command 3 &&                                    #\n"
        "#    command 4                                                 #\n"
        "# }                                                            #\n"
        "#                                                              #\n"
        "# Be aware that every new line in the list syntax is converted #\n"
        "# into a space, and '}' terminates the list.                   #\n"
        "# Note: Please if you want to use indention for the list, only #\n"
        "# use tabs, becuase they are ignored while reading a list.     #\n"
        "# Lastly as you can see hashes ('#') are ignored.              #\n"
        "# By the way, sorry for the hard syntax I really tried to make #\n"
        "# as easy as I can and that's the result. I hope you like it :)#\n"
        "################################################################\n"
    };

    fp = fopen(file_path, "w");
    if(fp == NULL) //Check if error occurred
        handle_files_error("An error occurred in create_config_file() while creating ", file_path);
    fprintf(fp, "%s\n", description);
    fprintf(fp, "%s\n", config_instruct);
    fclose(fp);
}

/*
*This function takes a unit name and a description then it'll 
*write the description to it so youll know how to configure it.
*/
void write_config_unit(char *file_path, const char *unit_name, const char *unit_desc) {
    FILE *fp;

    fp = fopen(file_path, "a");
    if(fp == NULL) //Check if error occurred
        handle_files_error("An error occurred in write_config_unit() while opening ", file_path);
    fprintf(fp, "%s", unit_name);
    fprintf(fp, "%s", " = ");
    fprintf(fp, "%s\n", unit_desc);
    fclose(fp); 
}

/*
*This function will read all the config file, it'll
*ignore every commented line (with '#'), it will search 
*for the desired unit's confgis. If the unit exists it will 
*return a pointer to it, otherwise it will return NULL.
*/
char *read_config_unit(char *file_path, const char *unit_name) {
    FILE *fp;
    char unit_buffer[400]; //Buffer for the unit name and its configs 
    char *configs_beginning; //Pointer to the targeted unit's configs beginning
    static char read_configs[500]; //Array for the read configs
    unsigned int config_status = 0; //1 = list, 0 = one line configurations

    fp = fopen(file_path, "r");
    if(fp == NULL) //Check if error occurred
        handle_files_error("An error occurred in read_config_file() while reading ", file_path);
    memset(read_configs, '\0', sizeof(read_configs)); //Make sure the static array is empty 
    //Loop in the file's content 
    while(fgets(unit_buffer, 400, fp) != NULL) { 
        //If the beginning of a line is commented or empty
        if(*unit_buffer=='#' || *unit_buffer=='\n' || *unit_buffer=='\0') 
            continue; //Ignore and read next line
        if(config_status) { //If list was found
            if(read_list_syntax(unit_buffer, read_configs, sizeof(read_configs))) //If reading list
                continue; //read the next line of the list
            fclose(fp);
            return read_configs;
        }
        else {
            if(check_unit_existence(unit_name, unit_buffer))  //If unit wasnt found in line
                continue; //Read the next one 
            /*The address of the beginning of the unit's configurations =
            beginning of the line + len of the unit name + 3 bytes (2 spaces and '=' sign)*/
            configs_beginning = unit_buffer + strlen(unit_name) + 3;
            if(*(configs_beginning) == '{') { //If unit configs is beginning of a list
                config_status = 1;
                continue; 
            }
            else {
                read_reg_syntax(configs_beginning, read_configs, sizeof(read_configs));
                fclose(fp);
                return read_configs;
            }
        }
    }

    fclose(fp);
    return NULL;
}

/*
*This function reads the regular configurations syntax,
*Then passes it to the config_buffer.
*/
void read_reg_syntax(char *config_beginning, char *configs_buffer, int buffer_size) {
    int i = 0;

    while(1) 
        switch(config_beginning[i]) {
            case '\n':
                configs_buffer[i] = '\0'; //teminate line
                return; //exit
            case '\0': //Line is terminated
                return; //exit
            default:
                //Check for overflow
                if(check_input_size(buffer_size, i+1)) {
                    fprintf(stderr, "The size of the inserted string in config_man.c -> read_reg_syntax is invalid.\n");
                    exit(EXIT_FAILURE);
                }
                //insert char into the configs_buffer
                configs_buffer[i] = config_beginning[i];
                i++;
                break;
            }
}

/*
*This function will read the syntax of a list, if finished reading itll 
*return 0, if still reading it will return 1 to read the next line.
*/
int read_list_syntax(char *line_beginning, char *configs_buffer, int buffer_size) {
    int char_cnt, i;

    char_cnt = strlen(configs_buffer); //number of characters in configs_buffer
    i = 0;  
    while(1) { //Loop and read line until reaching '\n' or ';'
        switch(line_beginning[i]) {
            case '}': //If end of list
                configs_buffer[char_cnt] = '\0'; //terminate line
                return 0; //Finished reading list
            case '\t': //Ignore indention 
                break;
            case '\n': //Convert to one space 
                //Make sure there is no overflow
                if(check_input_size(buffer_size, char_cnt+1)) {
                    fprintf(stderr, "The size of the inserted string in config_man.c -> read_list_syntax is invalid.\n");
                    exit(EXIT_FAILURE);
                }
                configs_buffer[char_cnt] = ' ';
                return 1; //Read next line
            default:
                //Make sure there is no overflow
                if(check_input_size(buffer_size, char_cnt+1)) {
                    fprintf(stderr, "The size of the inserted string in config_man.c -> read_list_syntax is invalid.\n");
                    exit(EXIT_FAILURE);
                }
                configs_buffer[char_cnt] = line_beginning[i];
                char_cnt++;
                break;
        }
        i++;
    }
}

/*
*This function takes a unit name and pointer to the beginning
*of the unit's line and it then it finds the unit's name which is
*the first word then it checks if the founded unit and the passed 
*one are equal and the same.
*0 = equal, 1 = not equal
*/
int check_unit_existence(const char *unit_name, char *line_begin) {
    unsigned int i;
    char unit_to_check[50]; //Array for the unit name that needs to be checked

    //Get unit name
    for(i=0; line_begin[i]!='\n' && line_begin[i]!='\t' && line_begin[i]!=' ' && line_begin[i]!='\0'; i++) {
        //Make sure there is no overflow
        if(check_input_size(sizeof(unit_to_check), i+1)) {
                    fprintf(stderr, "The size of the inserted string in config_man.c -> check_unit_existence is invalid.\n");
                    exit(EXIT_FAILURE);
                } 
        unit_to_check[i] = line_begin[i];
    }
    if(strcmp(unit_name, unit_to_check) == 0) //If equal 
        return 0;
    else //Not equal
        return 1;
}
  1. sys_backup.h
#ifndef SYS_BACKUP_H
#define SYS_BACKUP_H

void delete_dirs(char *);
void get_date(int *);
char *make_backup_dir(char *, int *);
void backup_sys(const char *, char *);

#endif
  1. sys_backup.c
/*
*This program will make some cleaning that you regularly do 
*before the full system backup. And then itll create new dir 
*in your storage device with date, to make the system backup in
*it useng rsync. The program will use system() to connect all the
*command line tools together and automate this process. Lastly 
*its good to note that tho program reads all the commands and 
*your customaized cleaning process from a config file with the 
*following path: ~/.config/sys_backup
*/
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "config_man.h" //For config files managment 
#include "errors.h" //Error handling header
#include "sys_backup.h"

int main() {
    const char *config_desc = {
        "################################################################\n"
        "# This configuration file purposes are to provide to the       #\n"
        "# sys_backup.c program the right cleaning process and storage  #\n"
        "# device path, customized to your own needs and preferences.   #\n"
        "################################################################"
    };
    const char *units_list[] = {
        "DirsToClean", "CleaningCommands", "DevicePath", 
        "RsyncCommand", "\0"
    };
    const char *units_desc[] = {
        "Dirs path you regularly clean, like some cache dirs",
        "{\n"
        "\tCommands for cleaning your sysem, like:\n"
        "\tsudo pacman -Sc (for deleting uninstalled packages\n"
        "\tfrom the cache in arch based distros)\n"
        "}", 
        "Your storage device path hdd, usb or whatever you use",  
        "sudo rsync -aAXHv --exclude={\"/dev/*\",\"/proc/*\",\"/sys/*\""
        ",\"/tmp/*\",\"/run/*\",\"/mnt/*\",\"/media/*\",\"/lost+found\"} / ", 
        "\0"
    };
    char *config_path = "/.config/sys_backup";
    char config_full_path[60];
    char *home, *configurations, *backup_path;
    int date[3]; //Array for the date of today
    unsigned int i;

    /*Get configuration's file path*/
    home = getenv("HOME"); //Get home path 
    //Check for overflow 
    if(check_input_size(sizeof(config_full_path), strlen(home)+strlen(config_path))) {
        fprintf(stderr, "The size of the inserted string in sys_backup.c -> main() is invalid.\n");
        exit(EXIT_FAILURE);
    }
    strcpy(config_full_path, home);
    strcat(config_full_path, config_path);
    
    if(check_file_existence(config_full_path) == 0) { //Check if config file exists
        create_config_file(config_full_path, config_desc);
        for(i=0; *units_desc[i]!='\0'; i++)
            write_config_unit(config_full_path, *(units_list+i), *(units_desc+i));
        printf("Please configure the prorgram's config file in the following path: %s.\n", config_full_path);
        return 0;
    }
    /*Read the configured units*/
    if((configurations=read_config_unit(config_full_path, units_list[0])) == NULL) { //Unit wasnt found
        handle_strstr_error(units_list[0]);
        exit(EXIT_FAILURE);
    }
    delete_dirs(configurations);
    
    if((configurations=read_config_unit(config_full_path, units_list[1])) == NULL) { //Unit wasnt found
        handle_strstr_error(units_list[1]);
        exit(EXIT_FAILURE);
    }
    system(configurations);
    
    if((configurations=read_config_unit(config_full_path, units_list[2])) == NULL) { //Unit wasnt found
        handle_strstr_error(units_list[2]);
        exit(EXIT_FAILURE);
    }
    get_date(date); //Get the date of today and pass it to the date array
    backup_path = make_backup_dir(configurations, date); //Create backup dir and get its path
    
    if((configurations=read_config_unit(config_full_path, units_list[3])) == NULL) { //Unit wasnt found
        handle_strstr_error(units_list[3]);
        exit(EXIT_FAILURE);
    }
    backup_sys(configurations, backup_path); //Backup system in the created dir
    return 0;
}

/*
*This function will delete the given argument dirs.
*/
void delete_dirs(char *dirs_path) {
    char full_command[700];
    const char *command = "rm -rf ";
    const char *danger_message = {
        "Nice try, but we wont let you destroy your system ;)\n"
        "Please make sure that DirsToClean unit is configured properly "
        "and there is no sign of standalone root tree (\"/\")."
    };
    unsigned int i;

    //Loop in dirs_path yo make sure no one removing the root tree by mistake
    for(i=0; i<strlen(dirs_path); i++)
        if(dirs_path[i]==' ' && dirs_path[i+1]=='/' && dirs_path[i+2]==' ') {
            fprintf(stderr, "%s\n", danger_message);
            exit(EXIT_FAILURE);
        }
    //Check for overflow
    if(check_input_size(sizeof(full_command), strlen(dirs_path)+strlen(command))) {
        fprintf(stderr, "The size of the inserted string in sys_backup.c -> delete_dirs is invalid.\n");
        exit(EXIT_FAILURE);
    }
    //Copy command to buffer (rm -rf dirs_path) to delete dirs.
    strcpy(full_command, command);
    strcat(full_command, dirs_path);

    system(full_command); //Execute command
}

/*
*This function will get the date of today, and it'll insert it to the passed date array.
*/
void get_date(int *date) {
    long int sec_since_epoch;
    struct tm current_time, *time_ptr;

    sec_since_epoch = time(0); 
    time_ptr = &current_time; //Set time pointer to the current_time struct
    localtime_r(&sec_since_epoch, time_ptr);

    //Pass today's date to the array 
    *date = time_ptr->tm_mday;
    *(date+1) = time_ptr->tm_mon + 1; //+1 because months range from 0 - 11
    *(date+2) =  time_ptr->tm_year - 100; //-100 because tm_year is number of passed years since 1900
}

/*
*A function that gets pointer to int array that contains the
*date of today and create a backup dir in the passed path 
*passed date. Then it will return the full path of the created dir. 
*/
char *make_backup_dir(char *device_path, int *date_array) {
    char dir_name[10], full_command[200];
    const char *command = "mkdir ";
    static char backup_path[150]; //The returned full backup path 

    //Convert the date_array to a string so will use it to name the dir in the device path
    sprintf(dir_name, "%02d", *date_array);
    sprintf((dir_name+3), "%02d", *(date_array+1));
    sprintf(dir_name, "%02d", *date_array);
    sprintf(dir_name+6, "%d", *(date_array+2));
    dir_name[2] = dir_name[5] = '-';
    //Check for overflow
    if(check_input_size(sizeof(backup_path), strlen(device_path)+strlen(dir_name))) {
        fprintf(stderr, "The size of the inserted string in sys_backup.c -> make_backup_dir is invalid.\n");
        exit(EXIT_FAILURE);
    }
    strcpy(backup_path, device_path);
    strcat(backup_path, dir_name); //Complete the full dir path 
    //Check for overflow
    if(check_input_size(sizeof(full_command), strlen(command)+strlen(backup_path))) {
        fprintf(stderr, "The size of the inserted string in sys_backup.c -> make_backup_dir is invalid.\n");
        exit(EXIT_FAILURE);
    }
    strcpy(full_command, command); //Insert command to the full_command
    strcat(full_command, backup_path); //Complete the command
    
    system(full_command); //Execute the command 
    return backup_path;
}

/*
*This function will make the full system backup using rsync to the passed dir path.
*/
void backup_sys(const char *command, char *backup_path) {
    char full_command[500];

    /*Prepare command*/
    //Check for overflow
    if(check_input_size(sizeof(full_command), strlen(command)+strlen(backup_path)+sizeof("/"))) {
        fprintf(stderr, "The size of the inserted string in sys_backup.c -> backup_sys is invalid.\n");
        exit(EXIT_FAILURE);
    }
    strcpy(full_command, command);
    strcat(full_command, backup_path);
    strcat(full_command, "/");
    
    system(full_command); //Execute the command 
}
  1. Here is a config example
################################################################
# This configuration file purposes are to provide to the       #
# sys_backup.c program the right cleaning process and storage  #
# device path, customized to your own needs and preferences.   #
################################################################
################################################################
#--------------------------------------------------------------#
#                        [Instructions]                        #
#--------------------------------------------------------------#
################################################################
# Please it's very important to make sure there are 3 charact- #
# ers between the unit name and its configurations.            #
# For example:                                                 #
#                                                              #
# UnitName = configurations                                    #
#                                                              #
# As you can see there are 3 characters between the unit name  #
# and the configurations (2 spaces and an equal sign). Be awa- #
# re that each new line terminates the unit's configurations.  #
# If the unit's configurations are too long you can put it in- #
# side a pair of braces and make it a list. For example:       #
#                                                              #
# VeryLongUnit = {                                             #
#    config_1                                                  #
#    config_2                                                  #
#    config_3                                                  #
#    config_4                                                  #
# }  <- Don't forget to close the list                         #
#                                                              #
# If the list is group of commands you can add '&&' to the end #
# of each line. If the command requires root privileges you    #
# can just add sudo to the command. For example:               #
#                                                              #
# CommandsUnit = {                                             #
#    command 1 &&                                              #
#    sudo root_command 2 &&                                    #
#    command 3 &&                                              #
#    sudo root_command 3 &&                                    #
#    command 4                                                 #
# }                                                            #
#                                                              #
# Be aware that every new line in the list syntax is converted #
# into a space, and '}' terminates the list.                   #
# Note: Please if you want to use indention for the list, only #
# use tabs, becuase they are ignored while reading a list.     #
# Lastly as you can see hashes ('#') are ignored.              #
# By the way, sorry for the hard syntax I really tried to make #
# as easy as I can and that's the result. I hope you like it :)#
################################################################

DirsToClean = {
    ~/.cache/spotify
    ~/.cache/mozilla/firefox/9jizeht4.default-release/thumbnails
    ~/.cache/tracker3
    ~/.cache/mozilla/firefox/9jizeht4.default-release/cache2
    ~/.cache/zoom
    ~/.local/share/Trash/files/*
    ~/.cache/torbrowser/download/*
    ~/.cache/yarn
}

CleaningCommands = {
    sudo pacman -Sc &&
    sudo paccache -rk2 &&
    sudo journalctl --vacuum-time=1weeks
}

DevicePath = /run/media/yan/HDD/

#This is the default command which backups all the necessary directories.
#You can of course change it to your needs.
RsyncCommand = sudo rsync -aAXHv --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/mnt/*","/media/*","/lost+found"} / 

I would like honest reviews and feedback that would help me to improve the code and more important my coding skills. Here is a link to the program's repo if its easier for you to read it there: https://github.com/yankh764/full-system-backup

Thanks for any review.

\$\endgroup\$
2
  • \$\begingroup\$ This is not by any stretch of the imagination functional programming. \$\endgroup\$ – Reinderien Mar 16 at 15:40
  • \$\begingroup\$ You may have meant procedural programming, but there's no tag for that, and anyway that's implied enough by the c tag. \$\endgroup\$ – Reinderien Mar 16 at 15:43
3
\$\begingroup\$

Language

I know that it may sound strange choice for a wrapper program to be written in C

Yeah, about that. For practice, fine; but overall C isn't the right tool for this job. A more abstract scripting language such as Python will buy you shorter code with more memory safety, more operating system portability, and built-in support for better handling of alternate character encodings, locales, etc., etc., etc.

Typo

erros.h -> errors.h

serching -> searching

prorgram -> program

Constant parameters

void handle_files_error(const char *message, char *file_path) {

should have const before file_path as well.

Truncation instead of failure

handle_files_error is a nasty consequence of using non-memory-managed language, and would be comparatively trivial in a memory-managed language. Even if you were to stick to C, aborting on a message that's too long is the least friendly of your options. Instead, truncate to the length of your buffer; or better yet don't have a buffer at all and simply fprintf to stderr.

Utility functions

check_input_size should not exist and makes the code less legible than simply doing the length comparison in place.

Default configuration

This whole default-config-if-missing mechanism:

    if(check_file_existence(config_full_path) == 0) { //Check if config file exists
        create_config_file(config_full_path, config_desc);
        for(i=0; *units_desc[i]!='\0'; i++)
            write_config_unit(config_full_path, *(units_list+i), *(units_desc+i));
        printf("Please configure the prorgram's config file in the following path: %s.\n", config_full_path);
        return 0;
    }

can go away. The typical thing for Linux programs to do is require that a configuration file exist, and if it doesn't, fail outright; or (somewhat more complex) adopt a set of in-memory defaults. Writing a default configuration file if none exists is surprising behaviour.

Implicit array-to-pointer

This syntax is a little spooky:

const char *danger_message = {
    "Nice try, but we wont let you destroy your system ;)\n"
    "Please make sure that DirsToClean unit is configured properly "
    "and there is no sign of standalone root tree (\"/\")."
};

You're using an array literal but then dumping the array information and only keeping a pointer. For that reason, drop your braces - the string literal on its own is enough.

Dates by reference

get_date is problematic. You're assuming that int *date is actually an array of three integers. Why not just pass three separate pointers? The intent is clearer and more explicit that way.

Even if you kept your current pointer style - which you shouldn't - your array assignments

*date = time_ptr->tm_mday;
*(date+1) = time_ptr->tm_mon + 1; //+1 because months range from 0 - 11
*(date+2) =  time_ptr->tm_year - 100; //-100 because tm_year is number of passed years since 1900

should turn into

date[0] = time_ptr->tm_mday;
date[1] = time_ptr->tm_mon + 1; //+1 because months range from 0 - 11
date[2] = time_ptr->tm_year - 100; //-100 because tm_year is number of passed years since 1900

But again, this will be a one-liner in Python.

Repeated sprintf

You need to avoid your manual offsets here:

sprintf(dir_name, "%02d", *date_array);
sprintf((dir_name+3), "%02d", *(date_array+1));
sprintf(dir_name, "%02d", *date_array);
sprintf(dir_name+6, "%d", *(date_array+2));));
dir_name[2] = dir_name[5] = '-';

Instead,

sprintf(dir_name, "%02d-%02d-%d", date_array[0], date_array[1], date_array[2]);

Security

There is ample opportunity for this program to be abused. You're forming command line strings using a fragile manual-memory-offset style in fixed-size buffers and then passing that off to system. Read this for flavour. This is a recipe for disaster.

Rather than shelling into mkdir for instance, just call it from C.

\$\endgroup\$
2
  • \$\begingroup\$ Thank you for your honest feedback and this full review I really appreciate it @Reinderien and im sorry that I don't know all these things but im kind of beginner so im trying to learn from books, documents, reviews and feedbacks from you guys. So thank you for trying to help, ill of course modify the code, improve it and try to make it as best as i can and even maybe ill do onother version of it in python or bash script \$\endgroup\$ – yan_kh Mar 16 at 18:05
  • 1
    \$\begingroup\$ Great! If you do implement the above improvements, submit a second question with your new code. Thanks! \$\endgroup\$ – Reinderien Mar 16 at 20:48

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.