Create a password generation program which will generate passwords containing random ASCII characters from the following groups:

         lower-case letters:  a ──► z
         upper-case letters:  A ──► Z
                     digits:  0 ──► 9
 other printable characters:  !"#$%&'()*+,-./:;<=>?@[]^_{|}~ 
 (the above character list excludes white-space, backslash and grave) 



The generated password(s) must include   at least one   (of each of the four groups):

   lower-case letter, 
   upper-case letter,
   digit  (numeral),   and 
   one  "other"  character. 



The user must be able to specify the password length and the number of passwords to generate.

The passwords should be displayed or written to a file, one per line.

The randomness should be from a system source or library.

The program should implement a help option or button which should describe the program and options when invoked.

You may also allow the user to specify a seed value, and give the option of excluding visually similar characters.

For example:            Il1     O0     5S     2Z            where the characters are:

   capital eye, lowercase ell, the digit one
   capital oh, the digit zero 
   the digit five, capital ess
   the digit two, capital zee


import os, parseopt, random, sequtils, strformat, strutils
 
const Symbols = toSeq("!\"#$%&'()*+,-./:;<=>?@[]^_{|}~")
 
 
proc passGen(passLength = 10,
             count = 1,
             seed = 0,
             excludeSimilars = false): seq[string] =
  ## Generate a sequence of passwords.
 
  # Initialize the random number generator.
  if seed == 0: randomize()
  else: randomize(seed)
 
  # Prepare list of chars per category.
  var
    lowerLetters = toSeq('a'..'z')
    upperLetters = toSeq('A'..'Z')
    digits = toSeq('0'..'9')
 
  if excludeSimilars:
    lowerLetters.delete(lowerLetters.find('l'))
    for c in "IOSZ": upperLetters.delete(upperLetters.find(c))
    for c in "012": digits.delete(digits.find(c))
 
  let all = lowerLetters & upperLetters & digits & Symbols
 
  # Generate the passwords.
  for _ in 1..count:
    var password = newString(passLength)
    password[0] = lowerLetters.sample
    password[1] = upperLetters.sample
    password[2] = digits.sample
    password[3] = Symbols.sample
    for i in 4..<passLength:
      password[i] = all.sample
    password.shuffle()
    result.add password
 
 
proc printHelp() =
  ## Print the help message.
  echo &"Usage: {getAppFileName().lastPathPart} " &
        "[-h] [-l:length] [-c:count] [-s:seed] [-x:(true|false)]"
  echo "  -h: display this help"
  echo "  -l: length of generated passwords"
  echo "  -c: number of passwords to generate"
  echo "  -s: seed for the random number generator"
  echo "  -x: exclude similar characters"
 
 
proc getIntValue(key, val: string): int =
  ## Get a positive integer value from a string.
  try:
    result = val.parseInt()
    if result <= 0:
      raise newException(ValueError, "")
  except ValueError:
    quit &"Wrong value for option -{key}: {val}", QuitFailure
 
 
var
  passLength = 10
  count = 1
  seed = 0
  excludeSimilars = false
 
# Process options.
var parser = initOptParser()
 
for kind, key, val in parser.getopt():
  if kind != cmdShortOption:
    printHelp()
    quit(QuitFailure)
 
  case key
  of "h":
    printHelp()
    quit(QuitSuccess)
  of "l":
    passLength = getIntValue(key, val)
  of "c":
    count = getIntValue(key, val)
  of "s":
    seed = getIntValue(key, val)
  of "x":
    if val.toLowerAscii == "true":
      excludeSimilars = true
    elif val.toLowerAscii == "false":
      excludeSimilars = false
    else:
      quit &"Wrong value for option -x: {val}", QuitFailure
  else:
    quit &"Wrong option: -{key}"
 
# Display the passwords.
for pw in passGen(passLength, count, seed, excludeSimilars):
  echo pw