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\$
3
  • \$\begingroup\$ Do you know about cmder? \$\endgroup\$ Nov 16, 2017 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\$ Nov 16, 2017 at 19:06
  • \$\begingroup\$ They didn't run any advertising campaign. I don't think they care though. \$\endgroup\$ Nov 16, 2017 at 19:06

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.