3
\$\begingroup\$

There are essentially two options for Windows users accustomed to the *nix command-line functionality; you have to either download a framework like Cygwin/Msys or use the GnuWin32 packages (of which I am a huge fan). However, the GnuWin32 packages are developed using standard C and therefore do not take full advantage of the Windows Operating System interface.

My goal is to build a functional replica with a few more options specific to the Windows environment. Currently, the application successfully creates files and sets their hidden, read-only, and encrypted properties. While creating a new file using the context menu requires you to set a file extension (and then delete it, if you so wish), you are free to create extension-less files if you so wish. The application takes a variable number of parameters and will create as many files as passed in.

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include <chrono>
#include <iostream>
#include <ratio>
#include <string>
#include <utility>

#include <cstdlib>

void PrintProgramInfo() noexcept;
void PrintProgramHelp() noexcept;

int ValidateParameters(const int& numberOfParameters, char *parameters[]);


int main(const int argc, char **argv)
{
    if (ValidateParameters(argc,argv)) {
        PrintProgramHelp();

        return EXIT_FAILURE;
    }

    auto optionVerbose   = FALSE;
    auto optionAccess    = GENERIC_READ | GENERIC_WRITE;
    auto optionShare     = NULL;
    auto optionCreate    = CREATE_NEW;
    auto optionEncrypted = FALSE;
    auto optionHidden    = FALSE;

    auto filenamesStart = 1;

    if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)) {
        PrintProgramHelp();

        return EXIT_SUCCESS;
    }

    /** We need to make sure we catch the verbose option first, before anything else happens, so that we can
    *  give the user the information at each step of the process, including setting any other settings as we
    *  parse the command line input.
    *  
    */

    for (auto i = 1; i < argc; ++i) {
        if ((strcmp(argv[i], "--verbose") == 0) || (strcmp(argv[i], "-v") == 0)) {
            ++filenamesStart;
            optionVerbose = TRUE;
        }
    }

    for (auto i = 1; i < argc; ++i) {
        if ((strcmp(argv[i], "--desired-access") == 0) || (strcmp(argv[i], "-da") == 0)) {
            ++filenamesStart;

            if (strcmp(argv[i + 1], "read") == 0) {
                if (optionVerbose)
                    std::cout << "Setting access to read-only\n";

                ++filenamesStart;
                optionAccess = GENERIC_READ;
            }
            else if (strcmp(argv[i + 1], "write") == 0) {
                if (optionVerbose)
                    std::cout << "Setting access to write-only\n";

                ++filenamesStart;
                optionAccess = GENERIC_WRITE;
            }
            else if (strcmp(argv[i + 1], "both") == 0) {
                if (optionVerbose)
                    std::cout << "Setting access to read/write\n";

                ++filenamesStart;
                optionAccess = GENERIC_READ | GENERIC_WRITE;
            }
            else {
                std::cout << "[ERROR] Could not understand option passed in. Opt: [write,read,both]\n";

                return EXIT_FAILURE;
            }
        } else if ((strcmp(argv[i],"--share-mode") == 0) || (strcmp(argv[i],"-sm") == 0)) {
            ++filenamesStart;

            if (strcmp(argv[i+1],"read") == 0) {
                if (optionVerbose)
                    std::cout << "Setting share mode to 'read'\n";

                ++filenamesStart;
                optionShare = FILE_SHARE_READ;
            } else if (strcmp(argv[i + 1], "write") == 0) {
                if (optionVerbose)
                    std::cout << "Setting share mode to 'write'\n";

                ++filenamesStart;
                optionShare = FILE_SHARE_WRITE;
            } else if (strcmp(argv[i + 1], "delete") == 0) {
                if (optionVerbose)
                    std::cout << "Setting share mode to 'delete'\n";

                ++filenamesStart;
                optionShare = FILE_SHARE_DELETE;
            } else if (strcmp(argv[i + 1], "none") == 0) {
                if (optionVerbose)
                    std::cout << "Setting share mode to 'none'. (This was already the default)\n";

                ++filenamesStart;
                optionShare = NULL;
            } else {
                std::cout << "[ERROR] Are you sure you entered the correct settings for --share-mode?\n";
                std::cout << "\tOpts: [read,write,delete,none]\n\n";
            }
        } else if ((strcmp(argv[i],"--create") == 0) || (strcmp(argv[i],"-c") == 0)) {
            ++filenamesStart;

            if (strcmp(argv[i + 1], "a") == 0) {
                if (optionVerbose)
                    std::cout << "Setting create option to 'always'. THIS WILL OVERWRITE EXISTING FILES.\n";

                ++filenamesStart;
                optionCreate = CREATE_ALWAYS;
            } else if (strcmp(argv[i + 1], "n") == 0) {
                if (optionVerbose)
                    std::cout << "Setting create option to 'new'. This will not overwrite existing files with the same name. (This was already the default)\n";

                ++filenamesStart;
                optionCreate = CREATE_NEW;
            } else {
                std::cout << "[ERROR] Are you sure you entered the correct settings for create?\n";
                std::cout << "\tOpts: [a,n]\n\n";
            }
        } else if (strcmp(argv[i],"--hidden") == 0) {
            if (optionVerbose)
                std::cout << "Setting file option 'hidden' to TRUE\n";

            ++filenamesStart;
            optionHidden = TRUE;
        } else if ((strcmp(argv[i], "--encrypted") == 0) || (strcmp(argv[i], "-e") == 0)) {
            if (optionVerbose)
                std::cout << "Setting file option 'encrypted' to TRUE\n";

            ++filenamesStart;
            optionEncrypted = TRUE;
        }
    }

    for (auto i = filenamesStart; i < argc; ++i) {
        if (optionVerbose)
            std::cout << "Creating file " << i - filenamesStart + 1 << ": " << argv[i] << '\n';

        auto currentFile = CreateFile(argv[i], optionAccess, optionShare, nullptr, optionCreate, FILE_ATTRIBUTE_NORMAL, nullptr);

        if (optionAccess != (GENERIC_READ | GENERIC_WRITE)) {
            if (optionAccess == GENERIC_READ) {
                if (SetFileAttributes(argv[i], FILE_ATTRIBUTE_READONLY)) {
                    if (optionVerbose)
                        std::cout << "Successfully set file to read-only mode.\n";
                } else {
                        std::cout << "[ERROR] Could not make file readonly!\n";
                }
            }
        }

        if (optionHidden) {
            if (SetFileAttributes(argv[i], FILE_ATTRIBUTE_HIDDEN)) {
                if (optionVerbose)
                    std::cout << "Successfully set file to hidden mode.\n";
            } else {
                std::cout << "[ERROR] Could not make file hidden!\n";
            }
        }

        if (optionEncrypted) {
            if (SetFileAttributes(argv[i], FILE_ATTRIBUTE_ENCRYPTED)) {
                if (optionVerbose)
                    std::cout << "Successfully encrypted file!\n";
            } else {
                std::cout << "[ERROR] Could not encrypt file!\n";
            }
        }

    }
}


void PrintProgramInfo() noexcept {
    std::cout << "\n\n";
    std::cout << "\t\tTouch - File Creation Utility Version 0.5.4\n";
    std::cout << "\t\t\tAuthor: Jose Fernando Lopez Fernandez\n";
    std::cout << "\t\t\tDate:        16-November-2017\n\n\n";
}

void PrintProgramHelp() noexcept {
    PrintProgramInfo();

    std::cout << "Expected format: touch <options> <filenames>\n\n";

    std::cout << "Options: \n";
    std::cout << "--help          or -h                   Display this help screen\n";
    std::cout << "--verbose       or -v                   Display verbose program output (must be passed as the first argument)\n";
    std::cout << "--desired-acess or -da <opt>            File access mode\n";
    std::cout << "                                        read   - GENERIC_READ\n";
    std::cout << "                                        write  - GENERIC_WRITE\n";
    std::cout << "                                        both   - both (default)\n";
    std::cout << "--share-mode    or -sm <opt>            File share mode\n";
    std::cout << "                                        none   - prevents other processes from opening file if they \n";
    std::cout << "                                                 request delete/read/write access\n";
    std::cout << "                                        delete - enables subsequent open operations on a file\n";
    std::cout << "                                                 to request delete access\n";
    std::cout << "                                        read   - enables subsequent open operations on a file\n";
    std::cout << "                                                 to request read access (default)\n";
    std::cout << "                                        write  - enables subsequent open operations on a file\n";
    std::cout << "                                                 to request write access\n";
    std::cout << "--create        or -c  <opt>            action to take on file that does or does not exist\n";
    std::cout << "                                        a      - if a file by a passed-in name already exists,\n";
    std::cout << "                                                 it will be overwritten\n";
    std::cout << "                                        n      - only create new file if it doesn not already exist.\n";
    std::cout << "                                                 This will not overwrite any files. (default)\n";
    std::cout << "--encrypted     or -e                   file should be encrypted\n";
    std::cout << "--hidden        or -h                   file should be hidden\n";
    std::cout << "\n\n";
    std::cout << "Examples: \n";
    std::cout << "touch --help                            Displays this help menu\n";
    std::cout << "touch text1.txt text2.txt text3.txt     Creates three regular text files.\n";
    std::cout << "touch -v -da both -c n main.c           Creates a file main.c with read and write access only if\n";
    std::cout << "                                        another file named main.c does not already exist\n";
    std::cout << "                                        Additionally, the program will output additional information\n";
    std::cout << "                                        as it processes the request.\n";
    std::cout << "\n\n";
}

int ValidateParameters(const int& numberOfParameters, char *parameters[]) {
    if (numberOfParameters < 2)
        return EXIT_FAILURE;

    return EXIT_SUCCESS;
}
\$\endgroup\$
  • \$\begingroup\$ Do you know about cmder? \$\endgroup\$ – Incomputable Nov 16 '17 at 18:58
  • \$\begingroup\$ I didn't know about it, no, it looks really cool though, I wonder how I hadn't heard about it \$\endgroup\$ – Jose Fernando Lopez Fernandez Nov 16 '17 at 19:06
  • \$\begingroup\$ They didn't run any advertising campaign. I don't think they care though. \$\endgroup\$ – Incomputable Nov 16 '17 at 19:06

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.