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


BYTE FUNC GetNumParam(CHAR ARRAY text BYTE min,max)
  BYTE res
 
  DO
    PrintF("Input %S (%B-%B): ",text,min,max)
    res=InputB()
  UNTIL res>=min AND res<=max
  OD
RETURN (res)
 
BYTE FUNC GetBoolParam(CHAR ARRAY text)
  CHAR ARRAY s(255)
 
  DO
    PrintF("%S? (Y/N): ",text)
    InputS(s)
    IF s(0)=1 THEN
      IF s(1)='y OR s(1)='Y THEN
        RETURN (1)
      ELSEIF s(1)='n OR s(1)='N THEN
        RETURN (0)
      FI
    FI
  OD
RETURN (0)
 
PROC Shuffle(CHAR ARRAY s)
  BYTE i,j
  CHAR tmp
 
  i=s(0)
  WHILE i>1
  DO
    j=Rand(i)+1
    tmp=s(i) s(i)=s(j) s(j)=tmp
    i==-1
  OD
RETURN
 
BYTE FUNC Contains(CHAR ARRAY s CHAR c)
  BYTE i
 
  IF s(0)=0 THEN
    RETURN (0)
  FI
  FOR i=1 TO s(0)
  DO
    IF s(i)=c THEN
      RETURN (1)
    FI
  OD
RETURN (0)
 
CHAR FUNC RandChar(CHAR ARRAY s,unsafe)
  BYTE len
  CHAR c
 
  len=s(0)
  DO
    c=s(Rand(len)+1)
  UNTIL Contains(unsafe,c)=0
  OD
RETURN (c)
 
PROC Generate(CHAR ARRAY res BYTE len,safe)
  DEFINE PTR="CARD"
  CHAR ARRAY st,
    upper="ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    lower="abcdefghijklmnopqrstuvwxyz",
    numbers="0123456789",
    special="!""#$%&'()*+,-./:;<=>?@[]^_|",
    unsafe="Il1O05S2Z"
  PTR ARRAY sets(4)
  BYTE i
 
  sets(0)=upper   sets(1)=lower
  sets(2)=numbers sets(3)=special
 
  res(0)=len
  FOR i=1 TO len
  DO
    IF safe THEN
      res(i)=RandChar(sets(i MOD 4),unsafe)
    ELSE
      res(i)=RandChar(sets(i MOD 4),"")
    FI
  OD
  Shuffle(res)
RETURN
 
PROC PrintHelp()
  PutE()
  PrintE("Program generates random passwords")
  PrintE("containing at least one upper-case")
  PrintE("letter, one lower-case letter, one")
  PrintE("digit and one special character.")
  PrintE("It is possible to exclude visually")
  PrintE("similar characters Il1O05S2Z.")
  PutE()
RETURN
 
PROC Main()
  BYTE len,count,safe,i,again,help
  CHAR ARRAY password(255)
 
  help=GetBoolParam("Show help")
  IF help THEN
    PrintHelp()
  FI
 
  DO
    len=GetNumParam("length of password",4,30)
    safe=GetBoolParam("Exclude similar chars")
    count=GetNumParam("number of passwords",1,10)
    PutE()
    FOR i=1 TO count
    DO
      Generate(password,len,safe)
      PrintF("%B. %S%E",i,password)
    OD
    PutE()
    again=GetBoolParam("Generate again")
  UNTIL again=0
  OD
RETURN