diff --git a/include/bitops.h b/include/bitops.h
index cb8f0f0ea..0b9d1030f 100644
--- a/include/bitops.h
+++ b/include/bitops.h
@@ -6,8 +6,6 @@
 #ifndef _BITOPS_H
 #define _BITOPS_H
 
-u32 is_power_of_2 (const u32 v);
-
 u32 rotl32 (const u32 a, const u32 n);
 u32 rotr32 (const u32 a, const u32 n);
 u64 rotl64 (const u64 a, const u64 n);
diff --git a/include/shared.h b/include/shared.h
index 5a57571ec..4b6cdd30c 100644
--- a/include/shared.h
+++ b/include/shared.h
@@ -10,6 +10,8 @@
 #include <string.h>
 #include <unistd.h>
 
+bool is_power_of_2 (const u32 v);
+
 u32 get_random_num (const u32 min, const u32 max);
 
 u32 mydivc32 (const u32 dividend, const u32 divisor);
diff --git a/include/types.h b/include/types.h
index 302b30626..7ab325a4b 100644
--- a/include/types.h
+++ b/include/types.h
@@ -752,87 +752,103 @@ typedef struct
 
 typedef struct
 {
-  bool  usage;
-  bool  version;
-  bool  quiet;
-  bool  benchmark;
-  bool  stdout_flag;
-  bool  show;
-  bool  left;
-  bool  username;
-  bool  remove;
-  u32   remove_timer;
-  u64   skip;
-  u64   limit;
-  bool  force;
-  bool  keyspace;
-  bool  potfile_disable;
-  char *potfile_path;
-  u32   debug_mode;
-  char *debug_file;
-  char *induction_dir;
-  char *outfile_check_dir;
-  u32   runtime;
-  u32   hash_mode;
-  u32   attack_mode;
-  char *outfile;
-  u32   outfile_format;
-  bool  outfile_autohex;
-  u32   outfile_check_timer;
-  bool  restore;
-  u32   restore_timer;
-  bool  restore_disable;
-  bool  status;
-  u32   status_timer;
-  bool  machine_readable;
-  bool  loopback;
-  u32   weak_hash_threshold;
-  char *session;
-  bool  hex_charset;
-  bool  hex_salt;
-  bool  hex_wordlist;
-  u32   rp_gen;
-  u32   rp_gen_func_min;
-  u32   rp_gen_func_max;
-  u32   rp_gen_seed;
-  char *rule_buf_l;
-  char *rule_buf_r;
-  bool  increment;
-  u32   increment_min;
-  u32   increment_max;
-  char *cpu_affinity;
-  bool  opencl_info;
-  char *opencl_devices;
-  char *opencl_platforms;
-  char *opencl_device_types;
-  u32   opencl_vector_width;
-  char *truecrypt_keyfiles;
-  char *veracrypt_keyfiles;
-  u32   veracrypt_pim;
-  u32   workload_profile;
-  u32   kernel_accel;
-  u32   kernel_loops;
-  u32   nvidia_spin_damp;
-  bool  gpu_temp_disable;
+  bool   usage;
+  bool   version;
+  bool   quiet;
+  bool   benchmark;
+  bool   stdout_flag;
+  bool   show;
+  bool   left;
+  bool   username;
+  bool   remove;
+  u32    remove_timer;
+  u64    skip;
+  u64    limit;
+  bool   force;
+  bool   keyspace;
+  bool   potfile_disable;
+  char  *potfile_path;
+  u32    debug_mode;
+  char  *debug_file;
+  char  *induction_dir;
+  char  *outfile_check_dir;
+  u32    runtime;
+  u32    hash_mode;
+  u32    attack_mode;
+  char  *outfile;
+  u32    outfile_format;
+  bool   outfile_autohex;
+  u32    outfile_check_timer;
+  bool   restore;
+  u32    restore_timer;
+  bool   restore_disable;
+  bool   status;
+  u32    status_timer;
+  bool   machine_readable;
+  bool   loopback;
+  u32    weak_hash_threshold;
+  char  *session;
+  bool   hex_charset;
+  bool   hex_salt;
+  bool   hex_wordlist;
+  u32    rp_gen;
+  u32    rp_gen_func_min;
+  u32    rp_gen_func_max;
+  u32    rp_gen_seed;
+  char  *rule_buf_l;
+  char  *rule_buf_r;
+  bool   increment;
+  u32    increment_min;
+  u32    increment_max;
+  char  *cpu_affinity;
+  bool   opencl_info;
+  char  *opencl_devices;
+  char  *opencl_platforms;
+  char  *opencl_device_types;
+  u32    opencl_vector_width;
+  char  *truecrypt_keyfiles;
+  char  *veracrypt_keyfiles;
+  u32    veracrypt_pim;
+  u32    workload_profile;
+  u32    kernel_accel;
+  u32    kernel_loops;
+  u32    nvidia_spin_damp;
+  bool   gpu_temp_disable;
   #if defined (HAVE_HWMON)
-  u32   gpu_temp_abort;
-  u32   gpu_temp_retain;
-  bool  powertune_enable;
+  u32    gpu_temp_abort;
+  u32    gpu_temp_retain;
+  bool   powertune_enable;
   #endif
-  bool  logfile_disable;
-  u32   segment_size;
-  u32   scrypt_tmto;
-  char  separator;
-  u32   bitmap_min;
-  u32   bitmap_max;
-  bool  markov_disable;
-  bool  markov_classic;
-  u32   markov_threshold;
-  char *markov_hcstat;
-  char *custom_charset_1;
-  char *custom_charset_2;
-  char *custom_charset_3;
-  char *custom_charset_4;
+  bool   logfile_disable;
+  u32    segment_size;
+  u32    scrypt_tmto;
+  char   separator;
+  u32    bitmap_min;
+  u32    bitmap_max;
+  bool   markov_disable;
+  bool   markov_classic;
+  u32    markov_threshold;
+  char  *markov_hcstat;
+  char  *custom_charset_1;
+  char  *custom_charset_2;
+  char  *custom_charset_3;
+  char  *custom_charset_4;
+  u32    rp_files_cnt;
+  char **rp_files;
+
+  bool   attack_mode_chgd;
+  bool   hash_mode_chgd;
+  bool   increment_max_chgd;
+  bool   increment_min_chgd;
+  bool   kernel_accel_chgd;
+  bool   kernel_loops_chgd;
+  bool   nvidia_spin_damp_chgd;
+  bool   opencl_vector_width_chgd;
+  bool   outfile_format_chgd;
+  bool   remove_timer_chgd;
+  bool   rp_gen_seed_chgd;
+  bool   runtime_chgd;
+  bool   workload_profile_chgd;
 
 } user_options_t;
 
diff --git a/include/user_options.h b/include/user_options.h
index 27c3d643f..c5c8a7cf8 100644
--- a/include/user_options.h
+++ b/include/user_options.h
@@ -252,4 +252,8 @@ void user_options_init (user_options_t *user_options);
 
 void user_options_destroy (user_options_t *user_options);
 
+int user_options_parse (user_options_t *user_options, int myargc, char **myargv);
+
+int user_options_sanity (user_options_t *user_options, int myargc, char **myargv, const u32 attack_kern);
+
 #endif // _USER_OPTIONS_H
diff --git a/src/bitops.c b/src/bitops.c
index 8c16cd816..de27e52f1 100644
--- a/src/bitops.c
+++ b/src/bitops.c
@@ -7,11 +7,6 @@
 #include "types.h"
 #include "bitops.h"
 
-u32 is_power_of_2 (const u32 v)
-{
-  return (v && !(v & (v - 1)));
-}
-
 u32 rotl32 (const u32 a, const u32 n)
 {
   return ((a << n) | (a >> (32 - n)));
diff --git a/src/hashcat.c b/src/hashcat.c
index 5bdc95412..9ff17a6be 100644
--- a/src/hashcat.c
+++ b/src/hashcat.c
@@ -676,6 +676,14 @@ int main (int argc, char **argv)
     return -1;
   }
 
+  const int rc_user_options_parse = user_options_parse (user_options, myargc, myargv);
+
+  if (rc_user_options_parse == -1) return -1;
+
+
+  // set some user option automatically
+  // depends on some other user option
+
   if (opencl_info)
   {
     quiet             = 1;
@@ -687,6 +695,112 @@ int main (int argc, char **argv)
     //stdout_flag       = 1;
   }
 
+
+  u32 attack_kern = ATTACK_KERN_NONE;
+
+  switch (attack_mode)
+  {
+    case ATTACK_MODE_STRAIGHT: attack_kern = ATTACK_KERN_STRAIGHT; break;
+    case ATTACK_MODE_COMBI:    attack_kern = ATTACK_KERN_COMBI;    break;
+    case ATTACK_MODE_BF:       attack_kern = ATTACK_KERN_BF;       break;
+    case ATTACK_MODE_HYBRID1:  attack_kern = ATTACK_KERN_COMBI;    break;
+    case ATTACK_MODE_HYBRID2:  attack_kern = ATTACK_KERN_COMBI;    break;
+  }
+
+  if (left == 1)
+  {
+    outfile_format = OUTFILE_FMT_HASH;
+  }
+
+  if (show == 1 || left == 1)
+  {
+    attack_mode = ATTACK_MODE_NONE;
+  }
+
+
+  if (benchmark == 1)
+  {
+
+  }
+  else
+  {
+    if (stdout_flag == 1) // no hash here
+    {
+      optind--;
+    }
+
+    if (keyspace == 1)
+    {
+      int num_additional_params = 1;
+
+      if (attack_kern == ATTACK_KERN_COMBI)
+      {
+        num_additional_params = 2;
+      }
+
+      int keyspace_wordlist_specified = myargc - optind - num_additional_params;
+
+      if (keyspace_wordlist_specified == 0) optind--;
+    }
+  }
+
+  if (skip != 0 && limit != 0)
+  {
+    limit += skip;
+  }
+
+  if (keyspace == 1)
+  {
+    potfile_disable = 1;
+
+    restore_disable = 1;
+
+    restore = 0;
+
+    weak_hash_threshold = 0;
+
+    quiet = 1;
+  }
+
+  if (stdout_flag == 1)
+  {
+    status_timer          = 0;
+    restore_timer         = 0;
+    restore_disable       = 1;
+    restore               = 0;
+    potfile_disable       = 1;
+    weak_hash_threshold   = 0;
+    gpu_temp_disable      = 1;
+    hash_mode             = 2000;
+    quiet                 = 1;
+    outfile_format        = OUTFILE_FMT_PLAIN;
+    kernel_accel          = 1024;
+    kernel_loops          = 1024;
+    force                 = 1;
+    outfile_check_timer   = 0;
+    session               = "stdout";
+    opencl_vector_width   = 1;
+  }
+
+  if (opencl_info == true)
+  {
+    opencl_platforms    = NULL;
+    opencl_devices      = NULL;
+    opencl_device_types = mystrdup ("1,2,3");
+  }
+
+  if (user_options->attack_mode != ATTACK_MODE_STRAIGHT)
+  {
+    weak_hash_threshold = 0;
+  }
+
+
+
+  const int rc_user_options_sanity = user_options_sanity (user_options, myargc, myargv, attack_kern);
+
+  if (rc_user_options_sanity == -1) return -1;
+
+
   /**
    * Inform user things getting started,
    * - this is giving us a visual header before preparations start, so we do not need to clear them afterwards
@@ -734,514 +848,6 @@ int main (int argc, char **argv)
     }
   }
 
-  /**
-   * sanity check
-   */
-
-  if (attack_mode > 7)
-  {
-    log_error ("ERROR: Invalid attack-mode specified");
-
-    return -1;
-  }
-
-  if (runtime_chgd && runtime == 0) // just added to remove compiler warnings for runtime_chgd
-  {
-    log_error ("ERROR: Invalid runtime specified");
-
-    return -1;
-  }
-
-  if (hash_mode_chgd && hash_mode > 14100) // just added to remove compiler warnings for hash_mode_chgd
-  {
-    log_error ("ERROR: Invalid hash-type specified");
-
-    return -1;
-  }
-
-  // renamed hash modes
-
-  if (hash_mode_chgd)
-  {
-    int n = -1;
-
-    switch (hash_mode)
-    {
-      case 123: n = 124;
-                break;
-    }
-
-    if (n >= 0)
-    {
-      log_error ("Old -m specified, use -m %d instead", n);
-
-      return -1;
-    }
-  }
-
-  if (username == 1)
-  {
-    if ((hash_mode == 2500) || (hash_mode == 5200) || ((hash_mode >= 6200) && (hash_mode <= 6299)) || ((hash_mode >= 13700) && (hash_mode <= 13799)))
-    {
-      log_error ("ERROR: Mixing support for user names and hashes of type %s is not supported", strhashtype (hash_mode));
-
-      return -1;
-    }
-  }
-
-  if (outfile_format > 16)
-  {
-    log_error ("ERROR: Invalid outfile-format specified");
-
-    return -1;
-  }
-
-  if (left == 1)
-  {
-    if (outfile_format_chgd == 1)
-    {
-      if (outfile_format > 1)
-      {
-        log_error ("ERROR: Mixing outfile-format > 1 with left parameter is not allowed");
-
-        return -1;
-      }
-    }
-    else
-    {
-      outfile_format = OUTFILE_FMT_HASH;
-    }
-  }
-
-  if (show == 1)
-  {
-    if (outfile_format_chgd == 1)
-    {
-      if ((outfile_format > 7) && (outfile_format < 16))
-      {
-        log_error ("ERROR: Mixing outfile-format > 7 with show parameter is not allowed");
-
-        return -1;
-      }
-    }
-  }
-
-  if (increment_min < INCREMENT_MIN)
-  {
-    log_error ("ERROR: Invalid increment-min specified");
-
-    return -1;
-  }
-
-  if (increment_max > INCREMENT_MAX)
-  {
-    log_error ("ERROR: Invalid increment-max specified");
-
-    return -1;
-  }
-
-  if (increment_min > increment_max)
-  {
-    log_error ("ERROR: Invalid increment-min specified");
-
-    return -1;
-  }
-
-  if ((increment == 1) && (attack_mode == ATTACK_MODE_STRAIGHT))
-  {
-    log_error ("ERROR: Increment is not allowed in attack-mode 0");
-
-    return -1;
-  }
-
-  if ((increment == 0) && (increment_min_chgd == 1))
-  {
-    log_error ("ERROR: Increment-min is only supported combined with increment switch");
-
-    return -1;
-  }
-
-  if ((increment == 0) && (increment_max_chgd == 1))
-  {
-    log_error ("ERROR: Increment-max is only supported combined with increment switch");
-
-    return -1;
-  }
-
-  if (rp_files_cnt && rp_gen)
-  {
-    log_error ("ERROR: Use of both rules-file and rules-generate is not supported");
-
-    return -1;
-  }
-
-  if (rp_files_cnt || rp_gen)
-  {
-    if (attack_mode != ATTACK_MODE_STRAIGHT)
-    {
-      log_error ("ERROR: Use of rules-file or rules-generate only allowed in attack-mode 0");
-
-      return -1;
-    }
-  }
-
-  if (rp_gen_func_min > rp_gen_func_max)
-  {
-    log_error ("ERROR: Invalid rp-gen-func-min specified");
-
-    return -1;
-  }
-
-  if (kernel_accel_chgd == 1)
-  {
-    if (force == 0)
-    {
-      log_info ("The manual use of the -n option (or --kernel-accel) is outdated");
-      log_info ("Please consider using the -w option instead");
-      log_info ("You can use --force to override this but do not post error reports if you do so");
-      log_info ("");
-
-      return -1;
-    }
-
-    if (kernel_accel < 1)
-    {
-      log_error ("ERROR: Invalid kernel-accel specified");
-
-      return -1;
-    }
-
-    if (kernel_accel > 1024)
-    {
-      log_error ("ERROR: Invalid kernel-accel specified");
-
-      return -1;
-    }
-  }
-
-  if (kernel_loops_chgd == 1)
-  {
-    if (force == 0)
-    {
-      log_info ("The manual use of the -u option (or --kernel-loops) is outdated");
-      log_info ("Please consider using the -w option instead");
-      log_info ("You can use --force to override this but do not post error reports if you do so");
-      log_info ("");
-
-      return -1;
-    }
-
-    if (kernel_loops < 1)
-    {
-      log_error ("ERROR: Invalid kernel-loops specified");
-
-      return -1;
-    }
-
-    if (kernel_loops > 1024)
-    {
-      log_error ("ERROR: Invalid kernel-loops specified");
-
-      return -1;
-    }
-  }
-
-  if ((workload_profile < 1) || (workload_profile > 4))
-  {
-    log_error ("ERROR: workload-profile %i not available", workload_profile);
-
-    return -1;
-  }
-
-  if (opencl_vector_width_chgd && (!is_power_of_2(opencl_vector_width) || opencl_vector_width > 16))
-  {
-    log_error ("ERROR: opencl-vector-width %i not allowed", opencl_vector_width);
-
-    return -1;
-  }
-
-  if (show == 1 || left == 1)
-  {
-    attack_mode = ATTACK_MODE_NONE;
-
-    if (remove == 1)
-    {
-      log_error ("ERROR: Mixing remove parameter not allowed with show parameter or left parameter");
-
-      return -1;
-    }
-
-    if (potfile_disable == 1)
-    {
-      log_error ("ERROR: Mixing potfile-disable parameter not allowed with show parameter or left parameter");
-
-      return -1;
-    }
-  }
-
-  if (show == 1)
-  {
-    if (outfile_autohex == 0)
-    {
-      log_error ("ERROR: Mixing outfile-autohex-disable parameter not allowed with show parameter");
-
-      return -1;
-    }
-  }
-
-  uint attack_kern = ATTACK_KERN_NONE;
-
-  switch (attack_mode)
-  {
-    case ATTACK_MODE_STRAIGHT: attack_kern = ATTACK_KERN_STRAIGHT; break;
-    case ATTACK_MODE_COMBI:    attack_kern = ATTACK_KERN_COMBI;    break;
-    case ATTACK_MODE_BF:       attack_kern = ATTACK_KERN_BF;       break;
-    case ATTACK_MODE_HYBRID1:  attack_kern = ATTACK_KERN_COMBI;    break;
-    case ATTACK_MODE_HYBRID2:  attack_kern = ATTACK_KERN_COMBI;    break;
-  }
-
-  if (benchmark == 1)
-  {
-    if (myargv[optind] != 0)
-    {
-      log_error ("ERROR: Invalid argument for benchmark mode specified");
-
-      return -1;
-    }
-
-    if (attack_mode_chgd == 1)
-    {
-      if (attack_mode != ATTACK_MODE_BF)
-      {
-        log_error ("ERROR: Only attack-mode 3 allowed in benchmark mode");
-
-        return -1;
-      }
-    }
-  }
-  else
-  {
-    if (stdout_flag == 1) // no hash here
-    {
-      optind--;
-    }
-
-    if (keyspace == 1)
-    {
-      int num_additional_params = 1;
-
-      if (attack_kern == ATTACK_KERN_COMBI)
-      {
-        num_additional_params = 2;
-      }
-
-      int keyspace_wordlist_specified = myargc - optind - num_additional_params;
-
-      if (keyspace_wordlist_specified == 0) optind--;
-    }
-
-    if (attack_kern == ATTACK_KERN_NONE)
-    {
-      if ((optind + 1) != myargc)
-      {
-        usage_mini_print (myargv[0]);
-
-        return -1;
-      }
-    }
-    else if (attack_kern == ATTACK_KERN_STRAIGHT)
-    {
-      if ((optind + 1) > myargc)
-      {
-        usage_mini_print (myargv[0]);
-
-        return -1;
-      }
-    }
-    else if (attack_kern == ATTACK_KERN_COMBI)
-    {
-      if ((optind + 3) != myargc)
-      {
-        usage_mini_print (myargv[0]);
-
-        return -1;
-      }
-    }
-    else if (attack_kern == ATTACK_KERN_BF)
-    {
-      if ((optind + 1) > myargc)
-      {
-        usage_mini_print (myargv[0]);
-
-        return -1;
-      }
-    }
-    else
-    {
-      usage_mini_print (myargv[0]);
-
-      return -1;
-    }
-  }
-
-  if (skip != 0 && limit != 0)
-  {
-    limit += skip;
-  }
-
-  if (keyspace == 1)
-  {
-    if (show == 1)
-    {
-      log_error ("ERROR: Combining show parameter with keyspace parameter is not allowed");
-
-      return -1;
-    }
-    else if (left == 1)
-    {
-      log_error ("ERROR: Combining left parameter with keyspace parameter is not allowed");
-
-      return -1;
-    }
-
-    potfile_disable = 1;
-
-    restore_disable = 1;
-
-    restore = 0;
-
-    weak_hash_threshold = 0;
-
-    quiet = 1;
-  }
-
-  if (stdout_flag == 1)
-  {
-    status_timer          = 0;
-    restore_timer         = 0;
-    restore_disable       = 1;
-    restore               = 0;
-    potfile_disable       = 1;
-    weak_hash_threshold   = 0;
-    gpu_temp_disable      = 1;
-    hash_mode             = 2000;
-    quiet                 = 1;
-    outfile_format        = OUTFILE_FMT_PLAIN;
-    kernel_accel          = 1024;
-    kernel_loops          = 1024;
-    force                 = 1;
-    outfile_check_timer   = 0;
-    session               = "stdout";
-    opencl_vector_width   = 1;
-  }
-
-  if (opencl_info == true)
-  {
-    opencl_platforms    = NULL;
-    opencl_devices      = NULL;
-    opencl_device_types = mystrdup ("1,2,3");
-  }
-
-  if (remove_timer_chgd == 1)
-  {
-    if (remove == 0)
-    {
-      log_error ("ERROR: Parameter remove-timer require parameter remove enabled");
-
-      return -1;
-    }
-
-    if (remove_timer < 1)
-    {
-      log_error ("ERROR: Parameter remove-timer must have a value greater than or equal to 1");
-
-      return -1;
-    }
-  }
-
-  if (loopback == 1)
-  {
-    if (attack_mode == ATTACK_MODE_STRAIGHT)
-    {
-      if ((rp_files_cnt == 0) && (rp_gen == 0))
-      {
-        log_error ("ERROR: Parameter loopback not allowed without rules-file or rules-generate");
-
-        return -1;
-      }
-    }
-    else
-    {
-      log_error ("ERROR: Parameter loopback allowed in attack-mode 0 only");
-
-      return -1;
-    }
-  }
-
-  if (debug_mode > 0)
-  {
-    if (attack_mode != ATTACK_MODE_STRAIGHT)
-    {
-      log_error ("ERROR: Parameter debug-mode option is only available with attack-mode 0");
-
-      return -1;
-    }
-
-    if ((rp_files_cnt == 0) && (rp_gen == 0))
-    {
-      log_error ("ERROR: Parameter debug-mode not allowed without rules-file or rules-generate");
-
-      return -1;
-    }
-  }
-
-  if (debug_mode > 4)
-  {
-    log_error ("ERROR: Invalid debug-mode specified");
-
-    return -1;
-  }
-
-  if (debug_file != NULL)
-  {
-    if (debug_mode < 1)
-    {
-      log_error ("ERROR: Parameter debug-file requires parameter debug-mode to be set");
-
-      return -1;
-    }
-  }
-
-  if (induction_dir != NULL)
-  {
-    if (attack_mode == ATTACK_MODE_BF)
-    {
-      log_error ("ERROR: Parameter induction-dir not allowed with brute-force attacks");
-
-      return -1;
-    }
-  }
-
-  if (attack_mode != ATTACK_MODE_STRAIGHT)
-  {
-    if ((weak_hash_threshold != WEAK_HASH_THRESHOLD) && (weak_hash_threshold != 0))
-    {
-      log_error ("ERROR: setting --weak-hash-threshold allowed only in straight-attack mode");
-
-      return -1;
-    }
-
-    weak_hash_threshold = 0;
-  }
-
-  if (nvidia_spin_damp > 100)
-  {
-    log_error ("ERROR: setting --nvidia-spin-damp must be between 0 and 100 (inclusive)");
-
-    return -1;
-  }
-
-
   /**
    * induction directory
    */
diff --git a/src/shared.c b/src/shared.c
index 0b11f52cc..ee7aba2ab 100644
--- a/src/shared.c
+++ b/src/shared.c
@@ -7,6 +7,11 @@
 #include "types.h"
 #include "shared.h"
 
+bool is_power_of_2 (const u32 v)
+{
+  return (v && !(v & (v - 1)));
+}
+
 u32 get_random_num (const u32 min, const u32 max)
 {
   if (min == max) return (min);
diff --git a/src/user_options.c b/src/user_options.c
index e509e4e6d..400de484a 100644
--- a/src/user_options.c
+++ b/src/user_options.c
@@ -6,7 +6,10 @@
 #include "common.h"
 #include "types.h"
 #include "memory.h"
+#include "logging.h"
 #include "interface.h"
+#include "shared.h"
+#include "usage.h"
 #include "user_options.h"
 
 void user_options_init (user_options_t *user_options)
@@ -90,9 +93,547 @@ void user_options_init (user_options_t *user_options)
   user_options->version                   = VERSION;
   user_options->weak_hash_threshold       = WEAK_HASH_THRESHOLD;
   user_options->workload_profile          = WORKLOAD_PROFILE;
+  user_options->rp_files_cnt              = 0;
+  user_options->rp_files                  = NULL;
 }
 
 void user_options_destroy (user_options_t *user_options)
 {
+  myfree (user_options->rp_files);
+
   myfree (user_options);
 }
+
+int user_options_parse (user_options_t *user_options, int myargc, char **myargv)
+{
+  int c = -1;
+
+  optind = 1;
+  optopt = 0;
+
+  int option_index = 0;
+
+  while (((c = getopt_long (myargc, myargv, short_options, long_options, &option_index)) != -1) && optopt == 0)
+  {
+    switch (c)
+    {
+      case IDX_HELP:                      user_options->usage                     = true;           break;
+      case IDX_VERSION:                   user_options->version                   = true;           break;
+      case IDX_RESTORE:                   user_options->restore                   = true;           break;
+      case IDX_QUIET:                     user_options->quiet                     = true;           break;
+      case IDX_SHOW:                      user_options->show                      = true;           break;
+      case IDX_LEFT:                      user_options->left                      = true;           break;
+      case IDX_USERNAME:                  user_options->username                  = true;           break;
+      case IDX_REMOVE:                    user_options->remove                    = true;           break;
+      case IDX_REMOVE_TIMER:              user_options->remove_timer              = atoi (optarg);
+                                          user_options->remove_timer_chgd         = true;           break;
+      case IDX_POTFILE_DISABLE:           user_options->potfile_disable           = true;           break;
+      case IDX_POTFILE_PATH:              user_options->potfile_path              = optarg;         break;
+      case IDX_DEBUG_MODE:                user_options->debug_mode                = atoi (optarg);  break;
+      case IDX_DEBUG_FILE:                user_options->debug_file                = optarg;         break;
+      case IDX_INDUCTION_DIR:             user_options->induction_dir             = optarg;         break;
+      case IDX_OUTFILE_CHECK_DIR:         user_options->outfile_check_dir         = optarg;         break;
+      case IDX_FORCE:                     user_options->force                     = true;           break;
+      case IDX_SKIP:                      user_options->skip                      = atoll (optarg); break;
+      case IDX_LIMIT:                     user_options->limit                     = atoll (optarg); break;
+      case IDX_KEYSPACE:                  user_options->keyspace                  = true;           break;
+      case IDX_BENCHMARK:                 user_options->benchmark                 = true;           break;
+      case IDX_STDOUT_FLAG:               user_options->stdout_flag               = true;           break;
+      case IDX_RESTORE_DISABLE:           user_options->restore_disable           = true;           break;
+      case IDX_STATUS:                    user_options->status                    = true;           break;
+      case IDX_STATUS_TIMER:              user_options->status_timer              = atoi (optarg);  break;
+      case IDX_MACHINE_READABLE:          user_options->machine_readable          = true;           break;
+      case IDX_LOOPBACK:                  user_options->loopback                  = true;           break;
+      case IDX_WEAK_HASH_THRESHOLD:       user_options->weak_hash_threshold       = atoi (optarg);  break;
+      case IDX_SESSION:                   user_options->session                   = optarg;         break;
+      case IDX_HASH_MODE:                 user_options->hash_mode                 = atoi (optarg);
+                                          user_options->hash_mode_chgd            = true;           break;
+      case IDX_RUNTIME:                   user_options->runtime                   = atoi (optarg);
+                                          user_options->runtime_chgd              = true;           break;
+      case IDX_ATTACK_MODE:               user_options->attack_mode               = atoi (optarg);
+                                          user_options->attack_mode_chgd          = true;           break;
+      case IDX_RP_FILE:                   user_options->rp_files[user_options->rp_files_cnt++]
+                                                                                  = optarg;         break;
+      case IDX_RP_GEN:                    user_options->rp_gen                    = atoi (optarg);  break;
+      case IDX_RP_GEN_FUNC_MIN:           user_options->rp_gen_func_min           = atoi (optarg);  break;
+      case IDX_RP_GEN_FUNC_MAX:           user_options->rp_gen_func_max           = atoi (optarg);  break;
+      case IDX_RP_GEN_SEED:               user_options->rp_gen_seed               = atoi (optarg);
+                                          user_options->rp_gen_seed_chgd          = true;           break;
+      case IDX_RULE_BUF_L:                user_options->rule_buf_l                = optarg;         break;
+      case IDX_RULE_BUF_R:                user_options->rule_buf_r                = optarg;         break;
+      case IDX_MARKOV_DISABLE:            user_options->markov_disable            = true;           break;
+      case IDX_MARKOV_CLASSIC:            user_options->markov_classic            = true;           break;
+      case IDX_MARKOV_THRESHOLD:          user_options->markov_threshold          = atoi (optarg);  break;
+      case IDX_MARKOV_HCSTAT:             user_options->markov_hcstat             = optarg;         break;
+      case IDX_OUTFILE:                   user_options->outfile                   = optarg;         break;
+      case IDX_OUTFILE_FORMAT:            user_options->outfile_format            = atoi (optarg);
+                                          user_options->outfile_format_chgd       = true;           break;
+      case IDX_OUTFILE_AUTOHEX_DISABLE:   user_options->outfile_autohex           = 0;              break;
+      case IDX_OUTFILE_CHECK_TIMER:       user_options->outfile_check_timer       = atoi (optarg);  break;
+      case IDX_HEX_CHARSET:               user_options->hex_charset               = true;           break;
+      case IDX_HEX_SALT:                  user_options->hex_salt                  = true;           break;
+      case IDX_HEX_WORDLIST:              user_options->hex_wordlist              = true;           break;
+      case IDX_CPU_AFFINITY:              user_options->cpu_affinity              = optarg;         break;
+      case IDX_OPENCL_INFO:               user_options->opencl_info               = true;           break;
+      case IDX_OPENCL_DEVICES:            user_options->opencl_devices            = optarg;         break;
+      case IDX_OPENCL_PLATFORMS:          user_options->opencl_platforms          = optarg;         break;
+      case IDX_OPENCL_DEVICE_TYPES:       user_options->opencl_device_types       = optarg;         break;
+      case IDX_OPENCL_VECTOR_WIDTH:       user_options->opencl_vector_width       = atoi (optarg);
+                                          user_options->opencl_vector_width_chgd  = true;           break;
+      case IDX_WORKLOAD_PROFILE:          user_options->workload_profile          = atoi (optarg);
+                                          user_options->workload_profile_chgd     = true;           break;
+      case IDX_KERNEL_ACCEL:              user_options->kernel_accel              = atoi (optarg);
+                                          user_options->kernel_accel_chgd         = true;           break;
+      case IDX_KERNEL_LOOPS:              user_options->kernel_loops              = atoi (optarg);
+                                          user_options->kernel_loops_chgd         = true;           break;
+      case IDX_NVIDIA_SPIN_DAMP:          user_options->nvidia_spin_damp          = atoi (optarg);
+                                          user_options->nvidia_spin_damp_chgd     = true;           break;
+      case IDX_GPU_TEMP_DISABLE:          user_options->gpu_temp_disable          = true;           break;
+      #if defined (HAVE_HWMON)
+      case IDX_GPU_TEMP_ABORT:            user_options->gpu_temp_abort            = atoi (optarg);  break;
+      case IDX_GPU_TEMP_RETAIN:           user_options->gpu_temp_retain           = atoi (optarg);  break;
+      case IDX_POWERTUNE_ENABLE:          user_options->powertune_enable          = true;           break;
+      #endif // HAVE_HWMON
+      case IDX_LOGFILE_DISABLE:           user_options->logfile_disable           = true;           break;
+      case IDX_TRUECRYPT_KEYFILES:        user_options->truecrypt_keyfiles        = optarg;         break;
+      case IDX_VERACRYPT_KEYFILES:        user_options->veracrypt_keyfiles        = optarg;         break;
+      case IDX_VERACRYPT_PIM:             user_options->veracrypt_pim             = atoi (optarg);  break;
+      case IDX_SEGMENT_SIZE:              user_options->segment_size              = atoi (optarg);  break;
+      case IDX_SCRYPT_TMTO:               user_options->scrypt_tmto               = atoi (optarg);  break;
+      case IDX_SEPARATOR:                 user_options->separator                 = optarg[0];      break;
+      case IDX_BITMAP_MIN:                user_options->bitmap_min                = atoi (optarg);  break;
+      case IDX_BITMAP_MAX:                user_options->bitmap_max                = atoi (optarg);  break;
+      case IDX_INCREMENT:                 user_options->increment                 = true;           break;
+      case IDX_INCREMENT_MIN:             user_options->increment_min             = atoi (optarg);
+                                          user_options->increment_min_chgd        = true;           break;
+      case IDX_INCREMENT_MAX:             user_options->increment_max             = atoi (optarg);
+                                          user_options->increment_max_chgd        = true;           break;
+      case IDX_CUSTOM_CHARSET_1:          user_options->custom_charset_1          = optarg;         break;
+      case IDX_CUSTOM_CHARSET_2:          user_options->custom_charset_2          = optarg;         break;
+      case IDX_CUSTOM_CHARSET_3:          user_options->custom_charset_3          = optarg;         break;
+      case IDX_CUSTOM_CHARSET_4:          user_options->custom_charset_4          = optarg;         break;
+
+      default:
+      {
+        log_error ("ERROR: Invalid argument specified");
+
+        return -1;
+      }
+    }
+  }
+
+  if (optopt != 0)
+  {
+    log_error ("ERROR: Invalid argument specified");
+
+    return -1;
+  }
+
+  return 0;
+}
+
+int user_options_sanity (user_options_t *user_options, int myargc, char **myargv, const u32 attack_kern)
+{
+  if (user_options->attack_mode > 7)
+  {
+    log_error ("ERROR: Invalid attack-mode specified");
+
+    return -1;
+  }
+
+  if (user_options->runtime_chgd == true && user_options->runtime == 0)
+  {
+    log_error ("ERROR: Invalid runtime specified");
+
+    return -1;
+  }
+
+  if (user_options->hash_mode > 14100)
+  {
+    log_error ("ERROR: Invalid hash-type specified");
+
+    return -1;
+  }
+
+  if (user_options->username == 1)
+  {
+    if  ((user_options->hash_mode == 2500)
+     ||  (user_options->hash_mode == 5200)
+     || ((user_options->hash_mode >= 6200)  && (user_options->hash_mode <= 6299))
+     || ((user_options->hash_mode >= 13700) && (user_options->hash_mode <= 13799)))
+    {
+      log_error ("ERROR: Mixing support for user names and hashes of type %s is not supported", strhashtype (user_options->hash_mode));
+
+      return -1;
+    }
+  }
+
+  if (user_options->outfile_format > 16)
+  {
+    log_error ("ERROR: Invalid outfile-format specified");
+
+    return -1;
+  }
+
+  if (user_options->left == 1)
+  {
+    if (user_options->outfile_format_chgd == true)
+    {
+      log_error ("ERROR: Mixing outfile-format > 1 with left parameter is not allowed");
+
+      return -1;
+    }
+  }
+
+  if (user_options->show == 1)
+  {
+    if (user_options->outfile_format_chgd == true)
+    {
+      log_error ("ERROR: Mixing outfile-format > 7 with show parameter is not allowed");
+
+      return -1;
+    }
+  }
+
+  if (user_options->increment_min < INCREMENT_MIN)
+  {
+    log_error ("ERROR: Invalid increment-min specified");
+
+    return -1;
+  }
+
+  if (user_options->increment_max > INCREMENT_MAX)
+  {
+    log_error ("ERROR: Invalid increment-max specified");
+
+    return -1;
+  }
+
+  if (user_options->increment_min > user_options->increment_max)
+  {
+    log_error ("ERROR: Invalid increment-min specified");
+
+    return -1;
+  }
+
+  if ((user_options->increment == 1) && (user_options->attack_mode == ATTACK_MODE_STRAIGHT))
+  {
+    log_error ("ERROR: Increment is not allowed in attack-mode 0");
+
+    return -1;
+  }
+
+  if ((user_options->increment == 0) && (user_options->increment_min_chgd == true))
+  {
+    log_error ("ERROR: Increment-min is only supported combined with increment switch");
+
+    return -1;
+  }
+
+  if ((user_options->increment == 0) && (user_options->increment_max_chgd == true))
+  {
+    log_error ("ERROR: Increment-max is only supported combined with increment switch");
+
+    return -1;
+  }
+
+  if (user_options->rp_files_cnt && user_options->rp_gen)
+  {
+    log_error ("ERROR: Use of both rules-file and rules-generate is not supported");
+
+    return -1;
+  }
+
+  if (user_options->rp_files_cnt || user_options->rp_gen)
+  {
+    if (user_options->attack_mode != ATTACK_MODE_STRAIGHT)
+    {
+      log_error ("ERROR: Use of rules-file or rules-generate only allowed in attack-mode 0");
+
+      return -1;
+    }
+  }
+
+  if (user_options->rp_gen_func_min > user_options->rp_gen_func_max)
+  {
+    log_error ("ERROR: Invalid rp-gen-func-min specified");
+
+    return -1;
+  }
+
+  if (user_options->kernel_accel_chgd == true)
+  {
+    if (user_options->force == 0)
+    {
+      log_info ("The manual use of the -n option (or --kernel-accel) is outdated");
+      log_info ("Please consider using the -w option instead");
+      log_info ("You can use --force to override this but do not post error reports if you do so");
+      log_info ("");
+
+      return -1;
+    }
+
+    if (user_options->kernel_accel < 1)
+    {
+      log_error ("ERROR: Invalid kernel-accel specified");
+
+      return -1;
+    }
+
+    if (user_options->kernel_accel > 1024)
+    {
+      log_error ("ERROR: Invalid kernel-accel specified");
+
+      return -1;
+    }
+  }
+
+  if (user_options->kernel_loops_chgd == true)
+  {
+    if (user_options->force == 0)
+    {
+      log_info ("The manual use of the -u option (or --kernel-loops) is outdated");
+      log_info ("Please consider using the -w option instead");
+      log_info ("You can use --force to override this but do not post error reports if you do so");
+      log_info ("");
+
+      return -1;
+    }
+
+    if (user_options->kernel_loops < 1)
+    {
+      log_error ("ERROR: Invalid kernel-loops specified");
+
+      return -1;
+    }
+
+    if (user_options->kernel_loops > 1024)
+    {
+      log_error ("ERROR: Invalid kernel-loops specified");
+
+      return -1;
+    }
+  }
+
+  if ((user_options->workload_profile < 1) || (user_options->workload_profile > 4))
+  {
+    log_error ("ERROR: workload-profile %i not available", user_options->workload_profile);
+
+    return -1;
+  }
+
+  if (user_options->opencl_vector_width_chgd == true && (!is_power_of_2 (user_options->opencl_vector_width) || user_options->opencl_vector_width > 16))
+  {
+    log_error ("ERROR: opencl-vector-width %i not allowed", user_options->opencl_vector_width);
+
+    return -1;
+  }
+
+  if (user_options->show == 1 || user_options->left == 1)
+  {
+    if (user_options->remove == 1)
+    {
+      log_error ("ERROR: Mixing remove parameter not allowed with show parameter or left parameter");
+
+      return -1;
+    }
+
+    if (user_options->potfile_disable == 1)
+    {
+      log_error ("ERROR: Mixing potfile-disable parameter not allowed with show parameter or left parameter");
+
+      return -1;
+    }
+  }
+
+  if (user_options->show == 1)
+  {
+    if (user_options->outfile_autohex == 0)
+    {
+      log_error ("ERROR: Mixing outfile-autohex-disable parameter not allowed with show parameter");
+
+      return -1;
+    }
+  }
+
+  if (user_options->keyspace == 1)
+  {
+    if (user_options->show == 1)
+    {
+      log_error ("ERROR: Combining show parameter with keyspace parameter is not allowed");
+
+      return -1;
+    }
+    else if (user_options->left == 1)
+    {
+      log_error ("ERROR: Combining left parameter with keyspace parameter is not allowed");
+
+      return -1;
+    }
+  }
+
+  if (user_options->remove_timer_chgd == true)
+  {
+    if (user_options->remove == 0)
+    {
+      log_error ("ERROR: Parameter remove-timer require parameter remove enabled");
+
+      return -1;
+    }
+
+    if (user_options->remove_timer < 1)
+    {
+      log_error ("ERROR: Parameter remove-timer must have a value greater than or equal to 1");
+
+      return -1;
+    }
+  }
+
+  if (user_options->loopback == 1)
+  {
+    if (user_options->attack_mode == ATTACK_MODE_STRAIGHT)
+    {
+      if ((user_options->rp_files_cnt == 0) && (user_options->rp_gen == 0))
+      {
+        log_error ("ERROR: Parameter loopback not allowed without rules-file or rules-generate");
+
+        return -1;
+      }
+    }
+    else
+    {
+      log_error ("ERROR: Parameter loopback allowed in attack-mode 0 only");
+
+      return -1;
+    }
+  }
+
+
+  if (user_options->debug_mode > 0)
+  {
+    if (user_options->attack_mode != ATTACK_MODE_STRAIGHT)
+    {
+      log_error ("ERROR: Parameter debug-mode option is only available with attack-mode 0");
+
+      return -1;
+    }
+
+    if ((user_options->rp_files_cnt == 0) && (user_options->rp_gen == 0))
+    {
+      log_error ("ERROR: Parameter debug-mode not allowed without rules-file or rules-generate");
+
+      return -1;
+    }
+  }
+
+  if (user_options->debug_mode > 4)
+  {
+    log_error ("ERROR: Invalid debug-mode specified");
+
+    return -1;
+  }
+
+  if (user_options->debug_file != NULL)
+  {
+    if (user_options->debug_mode < 1)
+    {
+      log_error ("ERROR: Parameter debug-file requires parameter debug-mode to be set");
+
+      return -1;
+    }
+  }
+
+  if (user_options->induction_dir != NULL)
+  {
+    if (user_options->attack_mode == ATTACK_MODE_BF)
+    {
+      log_error ("ERROR: Parameter induction-dir not allowed with brute-force attacks");
+
+      return -1;
+    }
+  }
+
+  if (user_options->attack_mode != ATTACK_MODE_STRAIGHT)
+  {
+    if ((user_options->weak_hash_threshold != WEAK_HASH_THRESHOLD) && (user_options->weak_hash_threshold != 0))
+    {
+      log_error ("ERROR: setting --weak-hash-threshold allowed only in straight-attack mode");
+
+      return -1;
+    }
+  }
+
+  if (user_options->nvidia_spin_damp > 100)
+  {
+    log_error ("ERROR: setting --nvidia-spin-damp must be between 0 and 100 (inclusive)");
+
+    return -1;
+  }
+
+  if (user_options->benchmark == 1)
+  {
+    if (myargv[optind] != 0)
+    {
+      log_error ("ERROR: Invalid argument for benchmark mode specified");
+
+      return -1;
+    }
+
+    if (user_options->attack_mode_chgd == true)
+    {
+      if (user_options->attack_mode != ATTACK_MODE_BF)
+      {
+        log_error ("ERROR: Only attack-mode 3 allowed in benchmark mode");
+
+        return -1;
+      }
+    }
+  }
+  else
+  {
+    if (attack_kern == ATTACK_KERN_NONE)
+    {
+      if ((optind + 1) != myargc)
+      {
+        usage_mini_print (myargv[0]);
+
+        return -1;
+      }
+    }
+    else if (attack_kern == ATTACK_KERN_STRAIGHT)
+    {
+      if ((optind + 1) > myargc)
+      {
+        usage_mini_print (myargv[0]);
+
+        return -1;
+      }
+    }
+    else if (attack_kern == ATTACK_KERN_COMBI)
+    {
+      if ((optind + 3) != myargc)
+      {
+        usage_mini_print (myargv[0]);
+
+        return -1;
+      }
+    }
+    else if (attack_kern == ATTACK_KERN_BF)
+    {
+      if ((optind + 1) > myargc)
+      {
+        usage_mini_print (myargv[0]);
+
+        return -1;
+      }
+    }
+    else
+    {
+      usage_mini_print (myargv[0]);
+
+      return -1;
+    }
+  }
+
+  return 0;
+}