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;
}