SDel is an open-source command-line utility for secure file deletion. (For me personally, it is also an attempt to refresh my knowledge of modern C++ after a long period of using C.)SDel Alpha requires a C++14 standard-compliant toolchain and the Boost Libraries to be installed for Boost.IOStreams, Boost.System and Boost.Filesystem.Note that the two help printing functions print_help() and print_help2() are generated by the gen_print_help utility from print_help.txt and print_regex_help.txt respectively.Aside from code style concerns, I am also worried that using memory-mapped files is not suitable for repeatedly overwriting files with data (i.e. the point of secure deletion), especially when no flush() function is available.Posted below is the source file sdel.cpp from Revision 1://// Copyright (c) 2016 Andrei Bondor//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the Software), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.////// TODO: switch from `boost::filesystem` to `std::filesystem` when C++17//  support becomes available//#include <algorithm>#include <cassert>#include <cstddef>#include <cstdlib>#include <fstream>#include <functional>#include <iostream>#include <limits>#include <new>#include <random>#include <regex>#include <set>#include <stdexcept>#include <string>#include <boost/filesystem.hpp>#include <boost/iostreams/device/mapped_file.hpp>using namespace std::literals::string_literals;namespace fs = boost::filesystem;namespace io = boost::iostreams;using filename_list = std::set<std::string>;using path_list     = std::set<fs::path>;////// @brief Program version number./// @details The version can be thought of as a specially written integer.///  Thus, for example, the version following 1.0.2.9 is 1.0.3.0.///  The version shall never be something like 1.0.3210.4.///#define SDEL_VERSION    1.0.0.0namespace{////// @brief Prints basic help information./// @note This function was generated by the `gen_print_help` utility and as///  such it should not be edited directly.///void print_help() noexcept{    std::cout <<        \n ===================================================================        ===========\n Secure Deletion Utility                                                version  SDEL_VERSION \n ====================================        ==========================================\n\n The syntax is:\n\n    s        del [option <arg> [...]] path1 [path2 [...]]\n    sdel --regex|-R rege        x1 [regex2 [...]]\n\n The following options are supported:\n\n    LONG         NAME       SHORT   ARGUMENT        MEANING\n    ---------------------        ------------------------------------------------------\n    --help                  -h      (none)          print this help information\n    --help2                 -h2     (none)          print help for ECMAScript regex\n            --version       -V      (none)          print version information\n            --verbose       -v      (none)          print debug information\n            --random        -r      (none)          write random data instead of z        eros\n    --regex         -R      (none)          expect ECMAScript re        gex filenames\n    --passes        -p      number > 0      how many ti        mes to overwrite files\n    ------------------------------------------        ---------------------------------\n\n Examples:\n\n    sdel ugly.jpg\n            sdel -v garbage_folder/\n    sdel --random --passes 5 naughty.mov        \n    sdel -r -p 10 horny/*.mp4\n    sdel --regex \[^a-dA-D].+_2001-0        2-\\d\\d\\.jpe?g\\n;}////// @brief Prints usage help for the ECMAScript regex syntax./// @note This function was generated by the `gen_print_help` utility and as///  such it should not be edited directly.///void print_help2() noexcept{    std::cout <<        \n ===================================================================        ===========\n Secure Deletion Utility                                                version  SDEL_VERSION \n ====================================        ==========================================\n\n Short intro\n ---------        --\n ECMAScript regex mode is enabled by using the \--regex\ or \-R        \ options.\n Without using the above options, SDel expects traditiona        l wildcard filenames.\n Wildcards are the asterisk (*) and the questio        n mark (?) symbols, meaning:\n\n    * = any number of any character\n                Example: *.txt matches \readme.txt\, \tutorial.txt\, etc.\n        \n    ? = exactly one of any character\n        Example: ???.txt match        es \abc.txt\ but not \a.txt\\n\n    Mixed example:\n    *_photo??.        jpg matches \samantha_photo01.jpg\ but not \joe_photograph.jpg\\n        \n In cases where the above functionality is not fine-grained enough,         regex mode\n can be enabled, and SDel treats all filenames as regexes.         (So be careful what\n you type!)\n\n ECMAScript regex syntax\n ------        -----------------\n This section is a quick-and-dirty tutorial, and no        t an in-depth explanation\n of all that ECMAScript regex syntax is and         can do. (Additional links will be\n found at the end of this help mes        sage.)\n\n Note that regex filenames only work for files in the curren        t directory.\n\n Note also that you must quote regexes to prevente you        r shell from\n expanding them (you can use double or single quotes):\n        \n  sdel -R \regex\\n\n ============================================        ==================================\n\n    SYNTAX              MEANING        \n    ----------------------------------------------------------------        -----------\n    .                   any character except newline\n            ---------------------------------------------------------------------        ------\n\n As such if you have dots (.) in your filename, you must esc        ape them with the\n backslash character (\\) in your regex.\n\n Exampl        e:\n\n    .txt                matches \atxt\\n    .\\.txt                      matches \a.txt\\n\n Note that other characters must also be escap        ed, if they're part of the\n filename you're searching for:\n\n    ^ $         \\ . * + ? ( ) [ ] { } |\n\n ========================================        ======================================\n\n    SYNTAX              MEAN        ING\n    -------------------------------------------------------------        --------------\n    *                   the previous char, group, or c        lass, 0 or more times\n    +                   same as above, but at l        east once: 1 or more times\n    {N}                 same as above, but         exactly N times\n    {MIN,MAX}           same as above, but at least         MIN, at most MAX times\n    ------------------------------------------        ---------------------------------\n\n Example:\n\n    .*\\.txt                     matches \.txt\, \a.txt\, \readme.txt\\n    .+\\.txt                     matches \a.txt\, \readme.txt\\n    .{5}\\.txt           match        es \abcde.txt\ but NOT \abc.txt\\n\n =============================        =================================================\n\n    SYNTAX                      MEANING\n    --------------------------------------------------        -------------------------\n    \\d                  digit (0, 1, 2, ..        ., 9)\n    \\D                  NOT digit\n    \\s                  wh        itespace (space, tab, newline)\n    \\S                  NOT whitespac        e\n    \\w                  word (alphanumerical + underscore: a-z, A-        Z, 0-9, _)\n    \\W                  NOT word\n    -------------------        --------------------------------------------------------\n\n Example:        \n\n    \\d+\\.jpg            matches \0123.jpg\, \4.jpg\ but NOT         \ab50.jpg\\n    \\w+_\\D+.png         matches \ab923_whx.png\ but         NOT \ab923_100.png\\n\n ============================================        ==================================\n\n    SYNTAX              MEANING        \n    ----------------------------------------------------------------        -----------\n    (group)             group (all the characters inside         the parentheses)\n    ------------------------------------------------        ---------------------------\n\n Example:\n\n    (Abc)*\\.txt         m        atches \AbcAbcAbc.txt\\n\n =========================================        =====================================\n\n    SYNTAX              MEANI        NG\n    --------------------------------------------------------------        -------------\n    [class]             class (any of the characters in        side the brackets)\n    [^class]            NOT class (negated class)        \n    ----------------------------------------------------------------        -----------\n\n Example:\n\n    [abc]\\.txt          matches \a.txt\        , \b.txt\, \c.txt\ but NOT \d.txt\\n    [0-5]+              matc        hes \001\, \53\ but NOT \93\\n    [^a-d]+             matches \        egz\, \gh\, \zw\ but NOT \ba\\n\n =============================        =================================================\n\n More regex info         online\n ----------------------\n The following websites explain the E        CMAScript regex syntax in more detail:\n\n    http://www.cplusplus.com        /reference/regex/ECMAScript/\n    http://en.cppreference.com/w/cpp/reg        ex/ecmascript\n    http://ecma-international.org/ecma-262/5.1/#sec-15.        10\n;}////// @brief Prints version information./// @note This function uses the following string macros if they are defined:///  `SDEL_VERSION`                     e.g. 1.0.0.6///  `SDEL_BUILD_TYPE`                  e.g. 32-bit (i686)/// @remarks `SDEL_VERSION` is mandatory.///void print_version() noexcept{    std::cout << SDel  << SDEL_VERSION;#ifdef SDEL_BUILD_TYPE    if (SDEL_BUILD_TYPE != s)        std::cout << ' ' << SDEL_BUILD_TYPE;#endif    std::cout << std::endl;}////// @brief Overwrites a file with data for a given number of passes./// @pre `fs::is_regular_file(fpath) == true`/// @param [in] fpath               File path of file to be overwritten./// @param [in] passes              How many times to repeat the overwriting./// @param [in] random              Whether or not to use random data instead///                                 of zeros.///void overwrite_file(const fs::path &fpath, std::size_t passes, bool random)try{    assert(fs::is_regular_file(fpath) == true);    io::mapped_file_sink file(fpath.string());    if (!file.is_open())        throw std::runtime_error(could not open  + fpath.string() +             for writing);    char * const begin  = file.data();    char * const end    = file.data() + file.size();    for (std::size_t p=0; p < passes; ++p)    {        if (random) // fill stream with random data        {            std::random_device rd;            std::uniform_int_distribution<char> rand_char(                std::numeric_limits<char>::min(),                std::numeric_limits<char>::max());            std::generate(begin, end, [&rd, &rand_char]() -> char                {                    return rand_char(rd);                });        }        else // fill stream with zeros        {            std::fill(begin, end, char(0));        }    }}catch (const std::runtime_error &e){    std::cerr << `std::runtime_error` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw;}catch (...){    std::cerr << unknown exception in ` << __func__ << '`' << std::endl;    throw;}////// @brief Helper structure to hold the options and filenames given by the user///  from the command-line.///struct program_options{public:    bool help       = false;    ///< Print help information.    bool help2      = false;    ///< Print regex help information.    bool version    = false;    ///< Print version information.    bool verbose    = false;    ///< Print debug information.    bool random     = false;    ///< Fill files with random data.    bool regex      = false;    ///< Expect ECMAScript regex syntax.    /// How many time a file will be overwritten.    std::size_t     passes = 1;    /// Raw Filenames of files to be deleted (raw, to be resolved).    filename_list   rawfnames;};////// @brief Parses the command-line arguments into program options./// @param [in] argc                Number of arguments./// @param [in] argv                Argument strings./// @returns The program options structure.///program_options parse_args(int argc, char *argv[])try{    program_options opts;    for (int i=1; i < argc; ++i)    {        if (argv[i] == --helps ||            argv[i] == -hs)        {            opts.help = true;        }        else        if (argv[i] == --help2s ||            argv[i] == -h2s)        {            opts.help2 = true;        }        else        if (argv[i] == --versions ||            argv[i] == -Vs)        {            opts.version = true;        }        else        if (argv[i] == --verboses ||            argv[i] == -vs)        {            opts.verbose = true;        }        else        if (argv[i] == --randoms ||            argv[i] == -rs)        {            opts.random = true;        }        else        if (argv[i] == --regexs ||            argv[i] == -Rs)        {            opts.regex = true;        }        else        if (argv[i] == --passess ||            argv[i] == -ps)        {            if (++i < argc)                opts.passes = std::stoul(argv[i]);        }        else // consider `argv[i]` to be a raw filename        {            opts.rawfnames.insert(argv[i]);        }    }    return opts;}catch (const std::invalid_argument &e){    std::cerr << `std::invalid_argument` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw std::runtime_error(runtime error in `s + __func__ + '`');}catch (const std::out_of_range &e){    std::cerr << `std::out_of_range` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw std::runtime_error(runtime error in `s + __func__ + '`');}catch (...){    std::cerr << unknown exception in ` << __func__ << '`' << std::endl;    throw;}////// @brief Resolves raw filenames into full paths./// @param [in] rawfnames           Raw filenames to be resolved./// @param [in] regex               Whether or not to use all raw filenames as///                                 ECMAScript regexes./// @returns List of resolved filenames.///path_list resolve_fnames(const filename_list &rawfnames, bool regex)try{    path_list rpaths; // Resolved Paths    if (regex) // treat raw filenames as regexes    {        for (const fs::path &p: fs::directory_iterator(.))        {            for (const auto &rfn: rawfnames)            {                const std::string   subj(p.filename().string()); // Subject                const std::regex    patt(rfn); // Pattern                if (std::regex_match(subj, patt))                {                    rpaths.insert(fs::canonical(p));                    break;                }            }        }    }    else // check which raw filenames actually exist    {        for (const auto &rfn: rawfnames)        {            if (!fs::exists(rfn))                continue;            if (fs::is_directory(rfn)) // recurse directories            {                // also add the directory, for cleaning up by `fs::remove_all()`                rpaths.insert(fs::canonical(rfn));                for (const fs::path &p: fs::recursive_directory_iterator(rfn))                {                    if (fs::is_regular_file(p))                        rpaths.insert(fs::canonical(p));                }            }            else            if (fs::is_regular_file(rfn))                rpaths.insert(fs::canonical(rfn));        }    }    return rpaths;}catch (const std::bad_alloc &e){    std::cerr << `std::bad_alloc` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw;}catch (const fs::filesystem_error &e){    std::cerr << `fs::filesystem_error` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw;}catch (...){    std::cerr << unknown exception in ` << __func__ << '`' << std::endl;    throw;}////// @brief Prints program options./// @param [in] opts                Options to print.///void print_options(const program_options &opts) noexcept{    std::cout << \n Program Options\n ---------------\n;    std::cout <<  overwriting mode:   ;    std::cout << (opts.random ? random data : zeros) << '\n';    std::cout <<  matching mode:      ;    std::cout << (opts.regex ? ECMAScript regex : plain and wildcards);    std::cout << \n\n;    if (!opts.rawfnames.empty())    {        std::cout << ' ' << opts.rawfnames.size();        std::cout <<  filename(s) to be resolved:\n;        for (const auto &fn: opts.rawfnames)            std::cout <<     << fn << '\n';    }    else        std::cout <<  no filenames to be resolved\n;    std::cout <<  --------------- << std::endl;}////// @brief Prints resolved paths./// @param [in] rp                  List of resolved paths.///void print_paths(const path_list &rp) noexcept{    std::cout << \n Resolved Paths\n --------------\n;    std::cout <<  total:  << rp.size() << '\n';    for (const auto &fn: rp)        std::cout <<     << fn << '\n';    std::cout <<  -------------- << std::endl;}} // unnamed namespace////// @brief Enters the program./// @param [in] argc                Number of arguments./// @param [in] argv                Argument strings./// @returns Whether or not the operation was successful./// @retval EXIT_SUCCESS            Operation success./// @retval EXIT_FAILURE            Operation failure.///int main(int argc, char *argv[])try{    const program_options opts = parse_args(argc, argv);    if (argc <= 1 || opts.help)    {        print_help();        return EXIT_SUCCESS;    }    if (opts.help2)    {        print_help2();        return EXIT_SUCCESS;    }    if (opts.version)    {        print_version();        return EXIT_SUCCESS;    }    // Resolved Paths    const path_list rpaths = resolve_fnames(opts.rawfnames, opts.regex);    if (opts.verbose)    {        print_options(opts);        print_paths(rpaths);    }    std::function<void (const fs::path &)> verbose_maybe =        [&opts](const fs::path &p) -> void        {            if (!opts.verbose)                return;            std::cout <<     << p.string() <<  ( << fs::file_size(p);            std::cout <<  bytes) << std::endl;        };    path_list empty_dirs; // Empty Directories    if (opts.verbose && !rpaths.empty())        std::cout << overwriting and deleting files\n;    for (const fs::path &p: rpaths)    {        if (fs::is_directory(p))        {            empty_dirs.insert(p);            continue;        }        verbose_maybe(p);        overwrite_file(p, opts.passes, opts.random);        fs::remove(p);    }    if (opts.verbose && !empty_dirs.empty())        std::cout << cleanup of empty directories\n;    for (const fs::path &p: empty_dirs)    {        if (opts.verbose)            std::cout <<     << p.string() << std::endl;        fs::remove_all(p);    }    return EXIT_SUCCESS;}catch (const std::bad_alloc &e){    std::cerr << `std::bad_alloc` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    throw;}catch (const fs::filesystem_error &e){    std::cerr << `fs::filesystem_error` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    return EXIT_FAILURE;}catch (const std::runtime_error &e){    std::cerr << `std::runtime_error` exception in ` << __func__ << `: ;    std::cerr << e.what() << std::endl;    return EXIT_FAILURE;}catch (...){    std::cerr << unknown exception in ` << __func__ << '`' << std::endl;    return EXIT_FAILURE;}
SDel file deletion utility
c++;security;c++14;boost