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


$ include "seed7_05.s7i";
 
const func string: generate (in integer: length) is func
  result
    var string: password is "";
  local
    const set of char: allowed is {'!' .. '~'} - {'\\', '`'};
    const set of char: special is allowed - {'A' .. 'Z'} | {'a' .. 'z'} | {'0' .. '9'};
    var integer: index is 0;
    var char: ch is ' ';
    var boolean: ucPresent is FALSE;
    var boolean: lcPresent is FALSE;
    var boolean: digitPresent is FALSE;
    var boolean: specialPresent is FALSE;
  begin
    repeat
      password := "";
      ucPresent := FALSE;
      lcPresent := FALSE;
      digitPresent := FALSE;
      specialPresent := FALSE;
      for index range 1 to length do
        ch := rand(allowed);
        ucPresent := ucPresent or ch in {'A' .. 'Z'};
        lcPresent := lcPresent or ch in {'a' .. 'z'};
        digitPresent := digitPresent or ch in {'0' .. '9'};
        specialPresent := specialPresent or ch in special;
        password &:= ch;
      end for;
    until ucPresent and lcPresent and digitPresent and specialPresent;
  end func;
 
const proc: main is func
  local
    var integer: length is 0;
    var integer: count is 0;
  begin
    if length(argv(PROGRAM)) <> 2 or not isDigitString(argv(PROGRAM)[1]) or
       not isDigitString(argv(PROGRAM)[2]) then 
      writeln("Usage: pwgen length count");
      writeln("       pwgen -?");
      writeln("length: The length of the password (min 4)");
      writeln("count:  How many passwords should be generated");
      writeln("-?  Write this text");
    else
      length := integer(argv(PROGRAM)[1]);
      count := integer(argv(PROGRAM)[2]);
      if length < 4 then
        writeln("Passwords must be at least 4 characters long.");
      else
        for count do
          writeln(generate(length));
        end for;
      end if;
    end if;
  end func;