since you are coder, my $0.05 optimization.
when prompt password, it would be friendlier if user gets notified [Caps-Lock ON/OFF]
1.Easy: Echo to the the screen entmake Text dynamic entmod. (advance may consider - Textbox , GrText or Textmask )
2.Advanced: create Attributed block update value
3.Expert: If MacOS supports Actable? emulate table as dialog box, column width=row height as Grid, customize merge cell as edit cell & buttoms. hittest
4.or slide vector image?
as participant, i personal vote for Lee's because bonus [<--Backspace] compatibility & excludes includes loop for [Function] keys.
though just minor spacer bug. (to replicate, just key-in all spaces then Backspace. notice the pipe "|" walks away)
p/s: Ron's dcl is more practical but excluded due to OP's query w/o DCL
v1.1 revision
;backspace
;expr - quoted list expression for additional functionality use 'STR' as token
;example: you make a fake dialog box using attributed block, Tag name is "KEY",
;you use your-att-func which can replace or vla-put-textstring the token 'STR'
;(foo "•" 20 '(your-att-func blk (list (cons "KEY" (strcat (substr STR 10) "|")))) )
;for normal usage just put nil for expression
;(foo (chr 149) 20 nil)
(defun foo ($ n expr / l k str x)
;;hp:pword 16.04.2020
(terpri)
(princ (setq x t
str "Password: "
! str
)
)
(while (and x (setq k (grread nil)))
(if (and (= (car k) 2)
(numberp (setq x (cadr k)))
(or (= 8 x) (<= 32 x 127)) ;;
(< (length l) n)
)
(progn
(princ
(strcat "\r"
(setq str
(if (= x 8)
(if (> (strlen str) (strlen !))
(substr str 1 (1- (strlen str)))
!
)
(strcat str $)
)
)
)
)
(setq l (if (= x 8)
(if (> (length l) 0)
(cdr l)
l
)
(cons x l)
)
)
(eval expr)
)
(setq x nil)
)
)
(vl-list->string (reverse l))
)
;example: you make a fake dialog box using attributed block, Tag name is "KEY",
;you use your-att-func which can replace or vla-put-textstring the token 'STR'
(foo "•" 20 '(your-att-func blk (list (cons "KEY" (strcat (substr STR 10) "|")))) )