]> git.armaanb.net Git - gen-shell.git/commitdiff
Major restructuring main
authorArmaan Bhojwani <me@armaanb.net>
Sat, 15 May 2021 16:21:53 +0000 (12:21 -0400)
committerArmaan Bhojwani <me@armaanb.net>
Sat, 15 May 2021 16:51:09 +0000 (12:51 -0400)
Fix build system and repository layout, remove unused options, improve
code style. My programming skills have improved a lot since first
writing this program, and this commit fixes most of the rookie
mistakes that I made.

16 files changed:
.build.yml [deleted file]
.gitignore
.gitmodules
CMakeLists.txt [deleted file]
Makefile [new file with mode: 0644]
README.md
Sarge [deleted submodule]
branding/logo.png [deleted file]
branding/logo.xcf [deleted file]
cmake.h.in [deleted file]
cmake/CXXSniffer.cmake [deleted file]
cmake/Modules/FindReadline.cmake [deleted file]
src/CMakeLists.txt [deleted file]
src/main.cpp
src/sarge.cpp [new file with mode: 0644]
src/sarge.h [new file with mode: 0644]

diff --git a/.build.yml b/.build.yml
deleted file mode 100644 (file)
index fb8f236..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-image: archlinux
-packages:
-  - readline
-  - cmake
-  - gcc
-sources:
-  - https://git.sr.ht/~armaan/gen-shell
-tasks:
-  - cmake: |
-      cd gen-shell
-      cmake .
-      make
-artifacts:
-  - gen-shell/src/gen-shell
index e64a8f60f3975800830456ba8a0d7904c9b16077..33f3ef963fa7b695ab63db9a485736818c91161e 100644 (file)
@@ -1,39 +1,2 @@
-*.d
-*.slo
-*.lo
-*.o
-*.obj
-*.gch
-*.pch
-*.so
-*.dylib
-*.dll
-*.a
-*.mod
-*.smod
-*.exe
-*.out
-*.app
 gen-shell
-CMakeLists.txt.user
-CMakeCache.txt
-CMakeFiles
-CMakeScripts
-Testing
-Makefile
-cmake_install.cmake
-install_manifest.txt
-compile_commands.json
-CTestTestfile.cmake
-_deps
-CPackConfig.cmake
-CPackSourceConfig.cmake
-commit.h
-commit.h.in
-*-prefix/
-*~
-.fuse_hidden*
-.directory
-.Trash-*
-.nfs*
-cmake.h
+*.o
index e89502215851342a1e6e589a60476d5e3445e525..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,3 +0,0 @@
-[submodule "Sarge"]
-       path = Sarge
-       url = https://github.com/MayaPosch/Sarge
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644 (file)
index 31151de..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-cmake_minimum_required (VERSION 2.8)
-set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake")
-set (HAVE_CMAKE true)
-
-project (gen-shell)
-include (CXXSniffer)
-
-include (CheckFunctionExists)
-include (CheckStructHasMember)
-include (CheckCXXCompilerFlag)
-
-# include the readline library finder module
-set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules")
-
-# find readline
-message ("-- Looking for GNU Readline")
-find_package (Readline REQUIRED)
-if (READLINE_FOUND)
-  set (HAVE_READLINE true)
-  set (GEN-SHELL_INCLUDE_DIRS ${GEN-SHELL_INCLUDE_DIRS} ${READLINE_INCLUDE_DIR})
-  set (GEN-SHELL_LIBRARIES    ${GEN-SHELL_LIBRARIES}    ${READLINE_LIBRARIES})
-endif (READLINE_FOUND)
-
-message ("-- Configuring cmake.h")
-configure_file (
-  ${CMAKE_SOURCE_DIR}/cmake.h.in
-  ${CMAKE_SOURCE_DIR}/cmake.h)
-
-add_subdirectory (src)
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..26bddec
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,10 @@
+all:
+       ${CC} -o gen-shell src/*.cpp ${CFLAGS} -lstdc++ -lreadline ${LDFLAGS}
+
+install: all
+       cp gen-shell /usr/local/bin/gen-shell
+
+uninstall:
+       rm /usr/local/bin/gen-shell
+
+.PHONY: all
index 516bbcb600ef1ca1249a61ba2ffc52f33b505ac7..5b0e8356c34e5000384987693a1cd20d433bc4f2 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,27 +1,18 @@
 # gen-shell
-[![builds.sr.ht status](https://builds.sr.ht/~armaan/gen-shell/commits/.build.yml.svg)](https://builds.sr.ht/~armaan/gen-shell/commits/.build.yml?)
-
-A simple way to turn any command into a shell with arrow key/history support (if compiled with libreadline). Forked from [taskshell](https://github.com/GothenburgBitFactory/taskshell).
+A simple way to turn any command into a REPL with arrow key/history support.
 
 ## Usage
 See `gen-shell --help`
 
 ## Installation
-### From a binary
-Binaries can be downloaded from [here](https://builds.sr.ht/~armaan/gen-shell/commits/.build.yml). NB: these binaries are compiled against libreadline version 8
-
-### From source
 Dependencies:  
-  - cmake (2.8 or higher)
-  - make
-  - g++ (sometimes packaged as gcc-c++ or gcc-g++)
-  - libreadline development files (7 or 8 is fine, 5 is untested) (optional, but highly recommended)
+  - POSIX make
+  - a c++ compiler and standard library
+  - libreadline development files (7 or 8 is fine, 5 is untested)
 
 ```bash
-git clone https://git.sr.ht/~armaan/gen-shell
-cd gen-shell
-cmake .
-sudo make install
+make
+make install
 ```
 ## License
 Following suit from taskshell, gen-shell is MIT licensed by Armaan Bhojwani, 2021. Gen-shell is forked from taskshell, which was developed by [these people](https://github.com/GothenburgBitFactory/taskshell/blob/master/AUTHORS).
diff --git a/Sarge b/Sarge
deleted file mode 160000 (submodule)
index 5c0b2a7..0000000
--- a/Sarge
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5c0b2a714aca97de16bb0a1b2f723681f69f3307
diff --git a/branding/logo.png b/branding/logo.png
deleted file mode 100644 (file)
index 4824efd..0000000
Binary files a/branding/logo.png and /dev/null differ
diff --git a/branding/logo.xcf b/branding/logo.xcf
deleted file mode 100644 (file)
index 106ebf7..0000000
Binary files a/branding/logo.xcf and /dev/null differ
diff --git a/cmake.h.in b/cmake.h.in
deleted file mode 100644 (file)
index e905598..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* cmake.h.in. Creates cmake.h during a cmake run */
-
-/* Product identification */
-#define PRODUCT_TASKSH 1
-
-/* Package information */
-#define PACKAGE           "${PACKAGE}"
-#define VERSION           "${VERSION}"
-#define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}"
-#define PACKAGE_NAME      "${PACKAGE_NAME}"
-#define PACKAGE_TARNAME   "${PACKAGE_TARNAME}"
-#define PACKAGE_VERSION   "${PACKAGE_VERSION}"
-#define PACKAGE_STRING    "${PACKAGE_STRING}"
-
-#define CMAKE_BUILD_TYPE  "${CMAKE_BUILD_TYPE}"
-
-/* Localization */
-#define PACKAGE_LANGUAGE ${PACKAGE_LANGUAGE}
-#define LANGUAGE_ENG_USA ${LANGUAGE_ENG_USA}
-
-/* git information */
-#cmakedefine HAVE_COMMIT
-
-/* cmake information */
-#cmakedefine HAVE_CMAKE
-#define CMAKE_VERSION "${CMAKE_VERSION}"
-
-/* Compiling platform */
-#cmakedefine LINUX
-#cmakedefine DARWIN
-#cmakedefine CYGWIN
-#cmakedefine FREEBSD
-#cmakedefine OPENBSD
-#cmakedefine NETBSD
-#cmakedefine HAIKU
-#cmakedefine SOLARIS
-#cmakedefine KFREEBSD
-#cmakedefine GNUHURD
-#cmakedefine UNKNOWN
-
-/* Found the Readline library */
-#cmakedefine HAVE_READLINE
-
-/* Found the pthread library */
-#cmakedefine HAVE_LIBPTHREAD
-
-/* Found wordexp.h */
-#cmakedefine HAVE_WORDEXP
-
-/* Found tm.tm_gmtoff struct member */
-#cmakedefine HAVE_TM_GMTOFF
-
-/* Found st.st_birthtime struct member */
-#cmakedefine HAVE_ST_BIRTHTIME
-
-/* Functions */
-#cmakedefine HAVE_GET_CURRENT_DIR_NAME
-#cmakedefine HAVE_TIMEGM
-#cmakedefine HAVE_UUID_UNPARSE_LOWER
-
diff --git a/cmake/CXXSniffer.cmake b/cmake/CXXSniffer.cmake
deleted file mode 100644 (file)
index cf6482a..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-message ("-- Configuring C++11")
-message ("-- System: ${CMAKE_SYSTEM_NAME}")
-
-include (CheckCXXCompilerFlag)
-
-# NOTE: Phase out -std=gnu++0x and --std=c++0x as soon as realistically possible.
-CHECK_CXX_COMPILER_FLAG("-std=c++11"   _HAS_CXX11)
-CHECK_CXX_COMPILER_FLAG("-std=c++0x"   _HAS_CXX0X)
-CHECK_CXX_COMPILER_FLAG("-std=gnu++0x" _HAS_GNU0X)
-
-if (_HAS_CXX11)
-  set (_CXX11_FLAGS "-std=c++11")
-elseif (_HAS_CXX0X)
-  message (WARNING "Enabling -std=c++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option.  Consider upgrading.")
-  set (_CXX11_FLAGS "-std=c++0x")
-elseif (_HAS_GNU0X)
-  message (WARNING "Enabling -std=gnu++0x draft compile flag. Your compiler does not support the standard '-std=c++11' option. Consider upgrading.")
-  set (_CXX11_FLAGS "-std=gnu++0x")
-else (_HAS_CXX11)
- message (FATAL_ERROR "C++11 support missing. Try upgrading your C++ compiler.")
-endif (_HAS_CXX11)
-
-if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-  set (LINUX true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-  set (DARWIN true)
-  set (_CXX11_FLAGS "${_CXX11_FLAGS} -stdlib=libc++")
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "kFreeBSD")
-  set (KFREEBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
-  set (FREEBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
-  set (OPENBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
-  set (NETBSD true)
-elseif (${CMAKE_SYSTEM_NAME} MATCHES "SunOS")
-  set (SOLARIS true)
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "GNU")
-  set (GNUHURD true)
-elseif (${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN")
-  set (CYGWIN true)
-  # NOTE: Not setting -std=gnu++0x leads to compile errors even with
-  #       GCC 4.8.3, and debugging those leads to insanity. Adding this
-  #       workaround instead of fixing Cygwin.
-  set (_CXX11_FLAGS "-std=gnu++0x")
-else (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-  set (UNKNOWN true)
-endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-
-set (CMAKE_CXX_FLAGS "${_CXX11_FLAGS} ${CMAKE_CXX_FLAGS}")
-set (CMAKE_CXX_FLAGS "-Wall -Wextra -Wsign-compare -Wreturn-type ${CMAKE_CXX_FLAGS}")
diff --git a/cmake/Modules/FindReadline.cmake b/cmake/Modules/FindReadline.cmake
deleted file mode 100644 (file)
index 8fdaec1..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-# - Find the readline library
-# This module defines
-#  READLINE_INCLUDE_DIR, path to readline/readline.h, etc.
-#  READLINE_LIBRARIES, the libraries required to use READLINE.
-#  READLINE_FOUND, If false, do not try to use READLINE.
-# also defined, but not for general use are
-# READLINE_readline_LIBRARY, where to find the READLINE library.
-# READLINE_ncurses_LIBRARY, where to find the ncurses library [might not be defined]
-
-# Apple readline does not support readline hooks
-# So we look for another one by default
-IF (APPLE OR FREEBSD)
-  FIND_PATH (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS
-    /usr/include/
-    /sw/include
-    /opt/local/include
-    /opt/include
-    /usr/local/include
-    NO_DEFAULT_PATH
-    )
-ENDIF (APPLE OR FREEBSD)
-FIND_PATH (READLINE_INCLUDE_DIR NAMES readline/readline.h)
-
-
-# Apple readline does not support readline hooks
-# So we look for another one by default
-IF (APPLE OR FREEBSD)
-  FIND_LIBRARY (READLINE_readline_LIBRARY NAMES readline PATHS
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/lib
-    /usr/local/lib
-    NO_DEFAULT_PATH
-    )
-ENDIF (APPLE OR FREEBSD)
-FIND_LIBRARY (READLINE_readline_LIBRARY NAMES readline)
-
-# Sometimes readline really needs ncurses
-IF (APPLE OR FREEBSD)
-  FIND_LIBRARY (READLINE_ncurses_LIBRARY NAMES ncurses PATHS
-    /usr/lib
-    /sw/lib
-    /opt/local/lib
-    /opt/lib
-    /usr/local/lib
-    /usr/lib
-    NO_DEFAULT_PATH
-    )
-ENDIF (APPLE OR FREEBSD)
-FIND_LIBRARY (READLINE_ncurses_LIBRARY NAMES ncurses)
-
-MARK_AS_ADVANCED (
-  READLINE_INCLUDE_DIR
-  READLINE_readline_LIBRARY
-  READLINE_ncurses_LIBRARY
-  )
-
-SET (READLINE_FOUND "NO" )
-IF (READLINE_INCLUDE_DIR)
-  IF (READLINE_readline_LIBRARY)
-    SET (READLINE_FOUND "YES" )
-    SET (READLINE_LIBRARIES
-      ${READLINE_readline_LIBRARY} 
-      )
-
-    # some readline libraries depend on ncurses
-    IF (READLINE_ncurses_LIBRARY)
-      SET (READLINE_LIBRARIES ${READLINE_LIBRARIES} ${READLINE_ncurses_LIBRARY})
-    ENDIF (READLINE_ncurses_LIBRARY)
-
-  ENDIF (READLINE_readline_LIBRARY)
-ENDIF (READLINE_INCLUDE_DIR)
-
-IF (READLINE_FOUND)
-  MESSAGE (STATUS "Found readline library")
-ELSE (READLINE_FOUND)
-  IF (READLINE_FIND_REQUIRED)
-    MESSAGE (FATAL_ERROR "Could not find readline -- please give some paths to CMake")
-  ENDIF (READLINE_FIND_REQUIRED)
-ENDIF (READLINE_FOUND)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644 (file)
index ecd29b1..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-cmake_minimum_required (VERSION 2.8)
-include_directories (${CMAKE_SOURCE_DIR}
-                     ${CMAKE_SOURCE_DIR}/src
-                     ${GEN-SHELL_INCLUDE_DIRS})
-
-set (gen-shell_SRCS ../Sarge/src/sarge.cpp)
-
-add_library (gen-shell STATIC ${gen-shell_SRCS})
-
-add_executable (gen-shell_executable main.cpp)
-
-target_link_libraries (gen-shell_executable gen-shell ${GEN-SHELL_LIBRARIES})
-
-set_property (TARGET gen-shell_executable PROPERTY OUTPUT_NAME "gen-shell")
-
-install (TARGETS gen-shell_executable DESTINATION /usr/bin/)
index 516f22396109081ec12a451262bf87c3d5d99e01..ae69ebb6f09f1e6ea52e4d6a5a6a9eb1ff542b97 100644 (file)
@@ -1,74 +1,59 @@
-// gen-shell - the generic shell
+// gen-shell - the generic REPL
 // Copyright (c) 2021, Armaan Bhojwani <me@armaanb.net>
 
-#include <cmake.h>
 #include <iostream>
-#include <../Sarge/src/sarge.h>
+#include "sarge.h"
+
 #include <vector>
 #include <string>
 
-#ifdef HAVE_READLINE
 #include <readline/readline.h>
 #include <readline/history.h>
-#endif
 
 ////////////////////////////////////////////////////////////////////////////////
 
 using namespace std;
 
-const std::string getResponse(const std::string & prompt) {
+const std::string
+getResponse(const std::string & prompt) {
   std::string response {
     ""
   };
 
   // Display prompt, get input
-#ifdef HAVE_READLINE
   char * line_read = readline(prompt.c_str());
   if (!line_read) {
     std::cout << "\n";
     response = "<EOF>";
   } else {
-    // Save history
-    if ( * line_read)
+    if (*line_read) {
       add_history(line_read);
+    }
 
     response = std::string(line_read);
     free(line_read);
   }
-#else
-  std::cout << prompt;
-  std::getline(std::cin, response);
-  if (std::cin.eof() == 1) {
-    std::cout << "\n";
-    response = "<EOF>";
-  }
-#endif
 
   return response;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-int main(int argc, char** argv)
+int
+main(int argc, char** argv)
 {
-
   // Command line arguments
   Sarge sarge;
 
-  sarge.setArgument("a", "after",
-      "Command to execute before leaving the shell", true);
-  sarge.setArgument("b", "before",
-      "Command to execute before entering the shell", true);
-  sarge.setArgument("c", "command", "Command to convert to shell", true);
-  sarge.setArgument("h", "help", "Get help.", false);
+  sarge.setArgument("c", "command", "Command to convert to REPL", true);
+  sarge.setArgument("h", "help", "Show this message.", false);
   sarge.setArgument("p", "prompt", "Define a custom prompt", true);
-  sarge.setArgument("", "no-space",
-      "Dont automatically add spaces after custom prompt and command", true);
-  sarge.setDescription("Make a shell from any executable");
+  sarge.setArgument("q", "quotes", "Treat whole input as argv[1]", false);
+  sarge.setDescription("Make a REPL from any executable");
   sarge.setUsage("gen-shell <options>");
 
   if (!sarge.parseArguments(argc, argv)) {
-    std::cerr << "Couldn't parse arguments..." << std::endl;
+    std::cerr << "Could not parse command line arguments" << std::endl;
     return 1;
   }
 
@@ -77,47 +62,29 @@ int main(int argc, char** argv)
     return 0;
   }
 
-  bool space = true;
-  if (sarge.exists("no-space")) {
-    space = false;
-  }
-
-  // Define input command
   string arg_cmd;
   sarge.getFlag("command", arg_cmd);
-  if ( space )
-    arg_cmd += " ";
 
-  // Define prompt
   string prompt = "";
   sarge.getFlag("prompt", prompt);
-  if ( prompt == "" )
-  {
+  if (prompt == "") {
     prompt = "% ";
-  } else if ( space ) {
-    prompt += " ";
   }
 
-  // Execute before-command
-  string before_command;
-  sarge.getFlag("before", before_command);
-  system (before_command.c_str ());
-
-  // Execute after-command
-  string after_command;
-  sarge.getFlag("after", after_command);
-
   // Do the stuffs!
   while (true) {
     auto command = getResponse(prompt);
 
-    if (command == "<EOF>" || command == "exit" || command == "quit" )
-    {
-      system (after_command.c_str ());
+    if (command == "<EOF>" || command == "exit" || command == "quit" ) {
       return 0;
     } else {
-      string whole_command = arg_cmd + command;
-      system (whole_command.c_str ());
+      string whole_command = arg_cmd + " ";
+      if (sarge.exists("quotes")) {
+        whole_command = whole_command + "\"" + command + "\"";
+      } else {
+        whole_command += command;
+      }
+      system(whole_command.c_str());
     }
   }
 }
diff --git a/src/sarge.cpp b/src/sarge.cpp
new file mode 100644 (file)
index 0000000..eb7d14f
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+       sarge.cpp - Implementation file for the Sarge command line argument parser project.
+       
+       Revision 0
+       
+       Features:
+                       - 
+       
+       Notes:
+                       -
+                        
+       2019/03/16, Maya Posch
+       
+*/
+
+
+#include "sarge.h"
+
+#include <iostream>
+
+
+// --- SET ARGUMENT ---
+void Sarge::setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal) {
+       std::unique_ptr<Argument> arg(new Argument);
+       arg->arg_short = arg_short;
+       arg->arg_long = arg_long;
+       arg->description = desc;
+       arg->hasValue = hasVal;
+       args.push_back(std::move(arg));
+       
+       // Set up links.
+       if (!arg_short.empty()) {
+               argNames.insert(std::pair<std::string, Argument*>(arg_short, args.back().get()));
+       }
+       
+       if (!arg_long.empty()) {
+               argNames.insert(std::pair<std::string, Argument*>(arg_long, args.back().get()));
+       }
+}
+       
+
+// --- SET ARGUMENTS ---
+void Sarge::setArguments(std::vector<Argument> args) {
+       for (Argument a : args) {
+               setArgument(a.arg_short, a.arg_long, a.description, a.hasValue);
+       }
+}
+
+
+// --- PARSE ARGUMENTS ---
+bool Sarge::parseArguments(int argc, char** argv) {
+       // The first argument is the name of the executable. After it we loop through the remaining
+       // arguments, linking flags and values.
+       execName = std::string(argv[0]);
+       bool expectValue = false;
+       std::map<std::string, Argument*>::const_iterator flag_it;
+       for (int i = 1; i < argc; ++i) {
+               // Each flag will start with a '-' character. Multiple flags can be joined together in the
+               // same string if they're the short form flag type (one character per flag).
+               std::string entry(argv[i]);
+               
+               if (expectValue) {
+                       // Copy value.
+                       flag_it->second->value = entry;                 
+                       expectValue = false;
+               }
+               else if (entry.compare(0, 1, "-") == 0) {
+                       if (textArguments.size() > 0) { 
+                               std::cerr << "Flags not allowed after text arguments." << std::endl; 
+                       }
+                       
+                       // Parse flag.
+                       // First check for the long form.
+                       if (entry.compare(0, 2, "--") == 0) {
+                               // Long form of flag.
+                               entry.erase(0, 2); // Erase the double dash since we no longer need it.
+                       
+                               flag_it = argNames.find(entry);
+                               if (flag_it == argNames.end()) {
+                                       // Flag wasn't found. Abort.
+                                       std::cerr << "Long flag " << entry << " wasn't found." << std::endl;
+                                       return false;
+                               }
+                               
+                               // Mark as found.
+                               flag_it->second->parsed = true;
+                               ++flagCounter;
+                               
+                               if (flag_it->second->hasValue) {
+                                       expectValue = true; // Next argument has to be a value string.
+                               }
+                       }
+                       else {
+                               // Parse short form flag. Parse all of them sequentially. Only the last one
+                               // is allowed to have an additional value following it.
+                               entry.erase(0, 1); // Erase the dash.                           
+                               for (int i = 0; i < entry.length(); ++i) {
+                                       std::string k(&(entry[i]), 1);
+                                       flag_it = argNames.find(k);
+                                       if (flag_it == argNames.end()) {
+                                               // Flag wasn't found. Abort.
+                                               std::cerr << "Short flag " << k << " wasn't found." << std::endl;
+                                               return false;
+                                       }
+                                       
+                                       // Mark as found.
+                                       flag_it->second->parsed = true;
+                                       ++flagCounter;
+                                       
+                                       if (flag_it->second->hasValue) {
+                                               if (i != (entry.length() - 1)) {
+                                                       // Flag isn't at end, thus cannot have value.
+                                                       std::cerr << "Flag " << k << " needs to be followed by a value string."
+                                                               << std::endl;
+                                                       return false;
+                                               } else {
+                                                       expectValue = true; // Next argument has to be a value string.
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else {
+                       // Add to text argument vector.
+                       textArguments.push_back(entry);
+               }
+       }
+       
+       parsed = true;
+       
+       return true;
+}
+
+
+// --- GET FLAG ---
+// Returns whether the flag was found, along with the value if relevant.
+bool Sarge::getFlag(std::string arg_flag, std::string &arg_value) {
+       if (!parsed) { return false; }
+       
+       std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
+       if (it == argNames.end()) { return false; }
+       if (!it->second->parsed) { return false; }
+       
+       if (it->second->hasValue) { arg_value = it->second->value; }
+       
+       return true;
+}
+
+
+// --- EXISTS ---
+// Returns whether the flag was found.
+bool Sarge::exists(std::string arg_flag) {
+       if (!parsed) { return false; }
+       
+       std::map<std::string, Argument*>::const_iterator it = argNames.find(arg_flag);
+       if (it == argNames.end()) { return false; }     
+       if (!it->second->parsed) { return false; }
+       
+       return true;
+}
+
+
+// --- GET TEXT ARGUMENT ---
+// Updates the value parameter with the text argument (unbound value) if found.
+// Index starts at 0.
+// Returns true if found, else false.
+bool Sarge::getTextArgument(uint32_t index, std::string &value) {
+       if (index < textArguments.size()) { value = textArguments.at(index); return true; }
+       
+       return false;
+}
+
+
+// --- PRINT HELP ---
+// Prints out the application description, usage and available options.
+void Sarge::printHelp() {
+       std::cout << std::endl << description << std::endl;
+       std::cout << std::endl << "Usage:" << std::endl;
+       std::cout << "\t" << usage << std::endl;
+       std::cout << std::endl;
+       std::cout << "Options: " << std::endl;
+       
+       // Determine whitespace needed between arg_long and description.
+       int count = 1; 
+       std::vector<std::unique_ptr<Argument> >::const_iterator it;
+       for (it = args.cbegin(); it != args.cend(); ++it) {
+               if ((*it)->arg_long.size() > count) { 
+                       count = (*it)->arg_long.size();
+               }
+       }
+       
+       count += 3; // Number of actual spaces between the longest arg_long and description.
+
+       // Print out the options.
+       for (it = args.cbegin(); it != args.cend(); ++it) {
+               std::cout << ((*it)->arg_short.empty() ? "    " : "-" + (*it)->arg_short + ", ") 
+                                                                                                                                       << "--" << (*it)->arg_long;
+               std::cout << std::string(count - (*it)->arg_long.size(), ' ') << (*it)->description 
+                                                                                                                                       << std::endl;
+       }
+}
diff --git a/src/sarge.h b/src/sarge.h
new file mode 100644 (file)
index 0000000..ab24c72
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+       sarge.h - Header file for the Sarge command line argument parser project.
+       
+       Revision 0
+       
+       Notes:
+                       -
+                        
+       2019/03/16, Maya Posch
+       
+*/
+
+
+#include <string>
+#include <vector>
+#include <map>
+#include <memory>
+
+
+struct Argument {
+       Argument() : hasValue(false), parsed(false) { /* */ }
+       std::string arg_short;
+       std::string arg_long;
+       std::string description;
+       bool hasValue;
+       std::string value;
+       bool parsed;
+};
+
+
+
+class Sarge {
+       std::vector<std::unique_ptr<Argument> > args;
+       std::map<std::string, Argument*> argNames;
+       bool parsed = false;
+       int flagCounter = 0;
+       std::string execName;
+       std::string description;
+       std::string usage;
+       std::vector<std::string> textArguments;
+       
+public:
+       void setArgument(std::string arg_short, std::string arg_long, std::string desc, bool hasVal);
+       void setArguments(std::vector<Argument> args);
+       void setDescription(std::string desc) { this->description = desc; }
+       void setUsage(std::string use) { this->usage = use; }
+       bool parseArguments(int argc, char** argv);
+       bool getFlag(std::string arg_flag, std::string &arg_value);
+       bool exists(std::string arg_flag);
+       bool getTextArgument(uint32_t index, std::string &value);
+       void printHelp();
+       int flagCount() { return flagCounter; }
+       std::string executableName() { return execName; }
+};