/* * fileutil.c * * Alexander Occhipinti * Student ID: 29994705 * Created: 3 Sep 2020 * Last Modified: 10 Sep 2020 * * This is the main source file for the fileutil program. * fileutil is a program which is a utility for files. It combines the functionality of cat cp and mv. * This program allows you to copy the contents of a given file to stdout or to a another file. * It also allows you to delete the original (i.e. mv) if you please. * */ #include #include #include #include #include "fileutil.h" #include "iohelper.h" /* * Prints the contents of a given file (provided a path) to stdout. * Returns nothing. */ void print_file(char *read_path) { int read_fd; read_fd = open_file(read_path, O_RDONLY); file_into_file(read_fd, 1); close(read_fd); } /* * Copy a file from the read_path to the write_path, prints an error to stderr and quits if * there is an error with opening. Overwrites the target file only if bool overwrite is true, throws an error otherwise. * */ void copy_file(char *read_path, char *write_path, bool overwrite) { int read_fd, write_fd; int write_flags = O_WRONLY | O_CREAT | O_TRUNC; if (!overwrite) write_flags |= O_EXCL; // Add no-clobber flag if not allowed to overwrite // Open both files read_fd = open_file(read_path, O_RDONLY); write_fd = open_file(write_path, write_flags); file_into_file(read_fd, write_fd); // Write one file into the other // Close both files close(read_fd); close(write_fd); } /* * Provided a string of an absolute path, returns a pointer to the same string representing * a string of just the filename. */ char* get_filename(char *full_path){ char* filename = strrchr(full_path, '/'); // Find the string after the last occurence of a '/' if (!filename) return full_path; // If there are no slashes, the whole path is already a filename return ++filename; // Return the filename without a leading slash } /* * Returns true if the provided string is a UNIX style absolute path (starts with '/' or '~'), false otherwise */ bool is_a_path(char* string) { bool empty_string = strlen(string) == 0; return (empty_string) ? false : string[0] == '/' || string[0] == '~'; } /* * Copies a file given the path `file_path`, into the directory of path string `dir_path` * Moves the file rather than copies it (i.e. remove the original file) if `move` is true * Overwrites the target file if it exists if `overwrite` is true */ void copy_into_dir(char *file_path, char *dir_path, bool move, bool overwrite){ char* filename; char* new_path; size_t new_path_len; // Construct the absolute path of the new file in the directory specified, allocate memory filename = get_filename(file_path); new_path_len = strlen(dir_path) + strlen(filename) + 1; new_path = (char *) malloc(new_path_len); // Combine/concat the parts of the new filename strcpy(new_path, dir_path); strcat(new_path, "/"); strcat(new_path, filename); copy_file(file_path, new_path, overwrite); if (move) unlink(file_path); // Delete the original if `move` is specified to_stdout("Copy successful\n"); free(new_path); } /* * Given argc and argv (number of program arguments and the program arguments) provided from main, * create an `arguments_t` type struct containing all of the information about the arguments. * If the given arguments are invalid, the `.valid` attribute of the return value is false. */ arguments_t parse_arguments(int argc, char **argv){ // Create a fresh arguments_t struct arguments_t args = { .src_index = -1, .dir_index = -1, .valid = false, .move_flag = false, .force_flag = false, .dir_flag = false }; // Go through each argument, find the defined flags and store then in the struct for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-d") == 0) { args.dir_flag = true; args.dir_index = i+1; } else if (strcmp(argv[i], "-M") == 0) { args.move_flag = true; } else if (strcmp(argv[i], "-F") == 0) { args.force_flag = true; } } // Determine if the source path is either not provided or the first argument if (argc > 1 && is_a_path(argv[1])) args.src_index = 1; // Is a valid combination of flags provided? bool correct_flags = (args.move_flag || args.force_flag) ? args.dir_flag : true; // Is the -d tag followed by a path? bool correct_directory = false; if (args.dir_index < argc) { correct_directory = (args.dir_flag) ? is_a_path(argv[args.dir_index]) : true; } // Combine all conditions for a valid set of arguments args.valid = correct_flags && correct_directory && (argc >= MIN_NUM_OF_ARGS) && (argc <= MAX_NUM_OF_ARGS); return args; } /* * Given an arguments_t struct of the program's arguments, execute the correct suprogram * (i.e. move, copy or print to stdout) */ void execute_subprogram(arguments_t args, char *argv[]) { // Abort program if invalid arguments specified if (!args.valid) { to_stderr("Invalid arguments given.\n"); exit(1); } // Set the source_path to the default read path if it's not defined char *source_path = (args.src_index == -1 ) ? DEFAULT_READ_PATH : argv[args.src_index]; char *dir_path; if (args.dir_flag){ // Copy/move a file into a directory dir_path = argv[args.dir_index]; copy_into_dir(source_path, dir_path, args.move_flag, args.force_flag); } else { print_file(source_path); // Print a file } } // Main function of the whole program int main(int argc, char *argv[]) { arguments_t args = parse_arguments(argc, argv); execute_subprogram(args, argv); return 0; }