The Poor Man's (or Woman's) Intrusion Detection System
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

190 lines
7.0 KiB

  1. //////////////////////////////////////////////////////////////////////
  2. // C++ Base Application Object
  3. // Written by Jonathan A. Foster <jon@jfpossibilities.com
  4. // Started August 31st, 2020
  5. //
  6. // This is a base application object for traditional C/C++
  7. // applications with a "int main(int argc, char//argv)" function.
  8. //////////////////////////////////////////////////////////////////////
  9. #ifndef __IDS_CLI_H__
  10. #define __IDS_CLI_H__
  11. #include <string>
  12. #include <ostream>
  13. #include <stdexcept>
  14. //////////////////////////////////////////////////////////////////////
  15. // Busy indicator aka. "Live Bug"
  16. //
  17. // This makes it stupid simple to step through a string that contains
  18. // a single character per "frame". You can either call next() to get
  19. // the next char in a sequence or use << with an ostream.
  20. //////////////////////////////////////////////////////////////////////
  21. struct LiveBug {
  22. std::string seq;
  23. int p;
  24. LiveBug(): seq("|/-\\"), p(0) {}
  25. inline char next() { if(p>=seq.size()) p=0; return seq[p++]; }
  26. };
  27. inline std::ostream &operator<<(std::ostream &o, LiveBug &bug) {
  28. return o << bug.next();
  29. }
  30. //////////////////////////////////////////////////////////////////////
  31. // Base Unix Application
  32. //
  33. // Its apparent to me tha all applications should be objects. Therefor
  34. // running an app should consist of setting up an object from a class,
  35. // running something on it. And then tearing it back down. With that
  36. // in mind the intent of this class is to provide the boiler plate
  37. // needed for this model and the bridge from the typical C/C++ main().
  38. //
  39. // How to use:
  40. // 1. Create a sublcass of this class with your app's functionality.
  41. // 2. Override do_switch(), do_switch_arg() and do_arg() to process
  42. // arguments passed from the OS. See main() for the built-in
  43. // processing.
  44. // 3. Override main to provide functionality not triggered by args,
  45. // including setup, teardown or even to replace the argument
  46. // processing. Don't call the inherited method if you don't need
  47. // the argument handling.
  48. // 4. override crash() if you want to do something more than dump
  49. // the exception string to cerr.
  50. // 5. Tell the compiler about your class with the MAIN(class)
  51. // macro.
  52. //
  53. // I'm calling this a "Unix" application because its what developed
  54. // the current main() interface concept. This is also emulated /
  55. // provided on other platforms, but it started in Unix.
  56. //////////////////////////////////////////////////////////////////////
  57. // globally accessible pointer to allow polymorphism
  58. struct cBaseApp; extern cBaseApp *baseapp;
  59. struct cBaseApp {
  60. int command_argc; // main(argc ...)
  61. char **command_args; // main(... argv)
  62. int ExitCode; // App's code returned to OS.
  63. /// STARTUP ///
  64. //cBaseApp();
  65. cBaseApp():
  66. command_argc(0),
  67. command_args(0),
  68. ExitCode(0)
  69. { if(!baseapp) baseapp = this; } // Only the first gets registered
  70. /// Initialize ///
  71. //
  72. // This is called, prior to this->main(), with the argc & args from the C
  73. // main() entry point. Override this to perform any initialization here that
  74. // can't be done before the CLI args are known.
  75. //
  76. virtual cBaseApp &init(int argc, char **argv);
  77. /// argument handlers ///
  78. //
  79. // Override these routines to implement argument processing. These are char//
  80. // instead of string since this is how they come to us from the OS.
  81. //
  82. // how many args needed for val
  83. virtual unsigned do_switch(const char *arg);
  84. // proccess a val for switch
  85. virtual void do_switch_arg(const char *sw, const std::string &val) { }
  86. // process a non-switch arg.
  87. virtual void do_arg(const char *arg) { }
  88. // TODO: switch lookup methods ("has switch?", "what's switch val?", ...)
  89. /// new main() - loop through args ///
  90. //
  91. // This implementation runs through the arguments passed in using the usual
  92. // GNU semantics. It calls do_switch() and do_arg(), depending on the
  93. // presence of a leading "-", passing the current argument (without leading
  94. // dash(es)). if do_switch() returns > 0 then that many arguments are passed
  95. // to do_switch_val() with the corresponding switch that triggered the
  96. // calls. Typically do_switch() would only return 0 or 1.
  97. //
  98. // Switches are defined as arguments starting with one or two dashes. A
  99. // single "--" switch will terminate switch processing, sending all
  100. // remaining args to do_arg(). Switches with names longer than one char must
  101. // be preceded with two dashes ("--" long switches).
  102. //
  103. // A switch starting with a single dash is a short switch, consisting of a
  104. // single character. More than one character can follow a dash in which case
  105. // they will be treated as a series of single char switches
  106. // (Ex: -abc = -a -b -c). Only the last one is allowed to have an argument
  107. // (do_switch()>0).
  108. //
  109. // This should be overridden to provide any functionality not directly
  110. // triggered by switches or args and its functionality can be replaced
  111. // completely if you don't need arguments.
  112. //
  113. virtual int main();
  114. /// Provide help text for CLI arg parse errors
  115. //
  116. // This is intended to show a command line help message on the terminal
  117. // about what the proper CLI syntax is. The return is the desired exit
  118. // code. The default is 1. This implementation will provide the app meta
  119. // data, if present. This simplified method is used so an exception is
  120. // not required to call it.
  121. virtual int help();
  122. /// Catch exceptions ///
  123. //
  124. // This is called by the boiler plate main() (see bottom) when an excpetion
  125. // falls out. This is meant to handle std::exception descendents. You can
  126. // catch other odd bals in main() above and deal with then accordingly. But
  127. // good style dictates all exceptions should descend from std::exception.
  128. // The return value is the application's exit code.
  129. //
  130. virtual int crash(const std::exception &e);
  131. /// virtualize destructor ///
  132. virtual ~cBaseApp() { }
  133. };
  134. //////////////////////////////////////////////////////////////////////
  135. // An exception class to signal CLI errors
  136. //////////////////////////////////////////////////////////////////////
  137. struct CLIerror: public std::runtime_error {
  138. CLIerror(const std::string &s): runtime_error(s) {}
  139. };
  140. //////////////////////////////////////////////////////////////////////
  141. // Macro to bridge the C/C++ main() to the app class' main()
  142. //
  143. // This sets up a global static variable named "app" that contains
  144. // your application object. It then copies the CLI args into it and
  145. // calls main. An std::exception handler is setup to pass exceptions
  146. // to the "app" object for further handling prior to exiting the app.
  147. // The app's exit code is returned from app.main() or app.crash().
  148. //////////////////////////////////////////////////////////////////////
  149. #define MAIN(__APPCLASS__) \
  150. __APPCLASS__ app; \
  151. int main(int argc, char **args) { \
  152. try { \
  153. return app.init(argc, args).main();\
  154. } catch(const std::exception &e) { \
  155. return app.crash(e); \
  156. } \
  157. }
  158. #endif