mirror of
https://github.com/PDP-10/its.git
synced 2026-03-25 01:47:08 +00:00
1352 lines
42 KiB
Common Lisp
1352 lines
42 KiB
Common Lisp
;;; -*- Mode: Lisp -*-
|
||
|
||
;;;; New INQUIR - User interface to the LSR1 database of ITS users.
|
||
;;; - CStacy, November 1983.
|
||
;;;
|
||
;;; Replaces awful MIDAS program of the same name.
|
||
;;; Includes lots of KMP packages and influence.
|
||
;;;
|
||
;;; (c) 1983, Massachusetts Institute of Technology. All rights reserved.
|
||
;;; Permission to copy all or part of this material is granted, provided
|
||
;;; that the copies are not made or distributed for resale, the MIT
|
||
;;; copyright notice and reference to the source file and the software
|
||
;;; distribution version appear, and that notice is given that copying
|
||
;;; is by permission of Massachusetts Institute of Technology.
|
||
|
||
;Things to do:
|
||
; o WHOIS command (FIRST-NAME-FIRST,FIRST-LINE-OF)
|
||
; o Add LGOTIM pkg
|
||
; o Finish implementing updates propogation feature
|
||
; o Inquire reap
|
||
; o Stats
|
||
|
||
|
||
;;;; Basic Support and Setup.
|
||
|
||
(herald inquir) ;INQUIR Version.
|
||
;There may also be INQFIX files.
|
||
;(putprop 'inquir "Experimental" 'release-status)
|
||
|
||
(setq base 10. ibase 10. *nopoint t) ;Reasonable numbers.
|
||
|
||
(sstatus feature noldmsg) ;Quietly.
|
||
|
||
;;; Macros.
|
||
(eval-when (eval compile)
|
||
(if (not (get 'umlmac 'version))
|
||
(load '((lisp) umlmac fasl))))
|
||
|
||
;;; Lisp Machine compatability.
|
||
(eval-when (eval compile)
|
||
(load '((liblsp) lispm fasl)))
|
||
|
||
;;; KMP's reader and fancy completing command reader.
|
||
(load '((inquir) reader))
|
||
(load '((liblsp) comrd))
|
||
|
||
;;; KMP & CSTACY's Fake Strings.
|
||
(if (not (status feature fake-s))
|
||
(load '((inquir) fake-s fasl)))
|
||
|
||
;;; TTY hackery.
|
||
(eval-when (eval compile) (load '((liblsp) tty)))
|
||
|
||
;;; LSR1 database access.
|
||
(if (not (status feature lsrrtn))
|
||
(load '((inquir) lsrrtn fasl)))
|
||
|
||
;;; HOSTS3 database access.
|
||
(if (not (status feature netrtn))
|
||
(load '((inquir) netrtn fasl)))
|
||
|
||
;;; LOGOUT TIMES database access.
|
||
;(if (not (status feature netrtn))
|
||
; (load '((spacy) lgotim fasl)))
|
||
|
||
;;; Declarations to keep COMPLR happy.
|
||
|
||
(declare (*lexpr
|
||
substring string-search string-append
|
||
terminate-command abort-command bug
|
||
abort-command-handler flush-command-handler
|
||
pretty-more-handler flush-inquir
|
||
change-item verify-item
|
||
update-one-machine)
|
||
(*expr
|
||
string-upcase string-downcase
|
||
reader *completing-read *completing-read1
|
||
char-upcase string string-equal
|
||
lsritm lsrunm lsrmap lsrnxt lsrdta))
|
||
; host-equal lookup-host
|
||
; map-lgotim last-logout))
|
||
|
||
|
||
;;;; Low-level Lisp extensions.
|
||
|
||
;;; Macro to define accessor macros for preoperties.
|
||
|
||
(defmacro define-property-cell (name)
|
||
`(defmacro ,name (x) `(get ,x ',',name)))
|
||
|
||
;;; Lisp Machine Lisp has this general-purpose
|
||
;;; MEMbership testing function.
|
||
;;; (MEM #'EQ FOO BAR) <==> (MEMQ FOO BAR)
|
||
|
||
(defun mem (pred item list)
|
||
(do l list (cdr l) (null l)
|
||
(and (funcall pred item (car l))
|
||
(return l))))
|
||
|
||
;;; A limited kind of READ-FROM-STRING.
|
||
|
||
(defun list-from-string (s)
|
||
(readlist (explodec (string-append "(" s ")" ))))
|
||
|
||
|
||
|
||
;;;; The Inquir items.
|
||
|
||
;;; These are the item numbers defined in and used for LSRRTNs.
|
||
|
||
(declare (special I$UNAM I$NAME I$NICK I$LOCL I$MITA I$MITT I$HOMA
|
||
I$HOMT I$SUPR I$PROJ I$DIR I$AUTH I$GRP I$REL
|
||
I$BRTH I$REM I$NETA I$ALTR I$MACH))
|
||
|
||
|
||
;;; The names of each of the items is a command the user can type.
|
||
;;; Asking for HELP on it will tell him the brief command description.
|
||
;;;
|
||
|
||
;;; We store the description of the item on plist of its symbolic name.
|
||
;;; The aggregate "current INQUIR entry" we are hacking is
|
||
;;; *INQUIR-ITEMS*, a list of the item symbols.
|
||
|
||
(defvar *suname* nil
|
||
"The UNAME of the current INQUIR entry we are modifying.")
|
||
|
||
(defvar *inquir-items* nil
|
||
"All the item symbols.")
|
||
|
||
(define-property-cell inquir-value)
|
||
(define-property-cell i$number)
|
||
(define-property-cell inqupd-name)
|
||
(define-property-cell doc)
|
||
(define-property-cell windy-doc)
|
||
(define-property-cell max-length)
|
||
|
||
(defmacro def-item (item max-length i$number inqupd-name doc windy-doc)
|
||
`(progn 'compile
|
||
(setf (inquir-value ',item) nil)
|
||
(setf (i$number ',item) ,i$number)
|
||
(setf (doc ',item) ',doc)
|
||
(setf (windy-doc ',item) ',windy-doc)
|
||
(setf (inqupd-name ',item) ,inqupd-name)
|
||
(setf (max-length ',item) ,max-length)
|
||
(when (not (member ',item *inquir-items*))
|
||
(push ',item *inquir-items*))))
|
||
|
||
|
||
|
||
(def-item USER-NAME 6. I$UNAM "UNAME" "User name"
|
||
"~&What you intend to usually log in as.~
|
||
~%If you don't have one picked out, use your initials.")
|
||
|
||
(def-item AUTHORIZATION 16. I$AUTH "AUTHO" "Authorization"
|
||
"~&Whether you are a good guy (wear a white hat).")
|
||
|
||
(def-item NAME 40. I$NAME "NAME" "Name"
|
||
"~&Full name. Last name first, as in /"Luser, John Q./"~
|
||
~%If you have a suffix, such as /"Jr./", put it at the end, after a second~
|
||
~%comma: /"Luser, John Q., Jr./". Please capitalize your name as you wish~
|
||
~%the machine to.")
|
||
|
||
(def-item NICK-NAME 30. I$NICK "NICK" "Nickname"
|
||
"~&Nickname. What you like to be called.")
|
||
|
||
(def-item MIT-ADDRESS 80. I$MITA "MITAD" "MIT address"
|
||
"~&MIT building # and room #, as in /"10-251/".~
|
||
~%(Note: 545 Technology Sq. is building # NE43.)~
|
||
~%If you do not frequent MIT, use your address at work.")
|
||
|
||
(def-item MIT-PHONE 30. I$MITT "MITTE" "MIT phone #"
|
||
"~&Your MIT phone number(s), as in /"3-1415/".")
|
||
|
||
(def-item HOME-ADDRESS 80. I$HOMA "HOMAD" "Home address"
|
||
"~&Your home address.")
|
||
|
||
(def-item HOME-PHONE 30. I$HOMT "HOMTE" "Home phone #"
|
||
"~&Your home phone number.")
|
||
|
||
(def-item NETWORK-ADDRESS 40. I$NETA "NETAD" "Network address"
|
||
"~&This is where you specify your /"home address/" on the network;~
|
||
~&where you receive your computer mail.~
|
||
~&~%If you want to receive your computer mail on this system, just
|
||
~&type a Return. Otherwise, enter a network host name, or~
|
||
~&username@hostname. Your computer mail will be forwarded there.~
|
||
~&~%Do not put a U.S. mail address here, since the network mailing~
|
||
~&programs will not understand what you mean.
|
||
~&~%Note : o Do not give just as username with no hostname.~
|
||
~& o Do not specify a TAC as the hostname, since TACs are~
|
||
~& unable able to receive mail for you.~
|
||
~&~%Just type a Return if you don't know what to do with this field.")
|
||
|
||
(def-item SUPERVISOR 30. I$SUPR "SUPER" "Supervisor"
|
||
"~&Your supervisor//advisor's name(s).")
|
||
|
||
(def-item PROJECT 40. I$PROJ "PROJE" "Project"
|
||
"~&The name of your project.~
|
||
~%This will fill in /"I am hacking _____ for <Supervisor>/".")
|
||
|
||
(def-item FILE-DIRECTORIES 40. I$DIR "FILDI" "File directory name(s)"
|
||
"~&The name of your home directory.~
|
||
~%Type the names of the directories you want as your HSNAME on the~
|
||
~%various ITS machines. You should not mention machines on which~
|
||
~%you have a directory of your own.~
|
||
~&~%A directory with no machine name applies to all machines that~
|
||
~%such a directory exists on.~
|
||
~&~%Example: /"SIPB@MC,KBS/" specifies SIPB on the MC machine, and KBS~
|
||
~%on all other machines with a KBS directory.~
|
||
~&~%Note: Do not specify USERSn or GUESTn directories here.~
|
||
~& These are chosen for you by the system, and are~
|
||
~& subject to change, and ARE NOT CONSTANT FROM ONE~
|
||
~& MACHINE TO ANOTHER.")
|
||
|
||
|
||
(def-item GROUP-AFFILIATION 1. I$GRP "GROUP" "Group"
|
||
"~&Group affiliation. Enter one letter only.~
|
||
~%Choose the one that fits best, from the following list:~
|
||
~%A - Artificial Intelligence Lab person.~
|
||
~%B - Educational Computing Group person.~
|
||
~%C - Theory Group person.~
|
||
~%L - Laboratory for Computer Science person.~
|
||
~%P - Plasma Fusion Center person.~
|
||
~%S - MIT guest - student//staff//faculty not in one of the other groups.~
|
||
~%T - Guest (tourist).~
|
||
~%V - NIL Group.+
|
||
~%Z - Clinical Decision Making person.~
|
||
~%+ - Official maintainter//Liaison on some MIT computer system.~
|
||
~%$ - Official maintainter//Liaison on some ARPANET computer system.~
|
||
~%@ - This is an alias for someone known under another name.~
|
||
~%O - Other. This designates a program, not a person.")
|
||
|
||
(def-item RELATION-TO-GROUP 1. I$REL "RELAT" "Relation"
|
||
"~&Your Relation to your group. Enter one letter only.~
|
||
~%Choose the one that fits, best from the following list:~
|
||
~%A - Administrative F - Faculty~
|
||
~%G - Graduate student P - Publications//Editing~
|
||
~%R - Research associate S - DSR (sponsored research) staff~
|
||
~%U - Undergraduate student ~
|
||
~%X - Ex-user (former MIT staff//faculty)~
|
||
~% N - None~%")
|
||
|
||
(def-item BIRTHDAY 20. I$BRTH "BIRTH" "Birthday"
|
||
"~&Birthday. The format should be as in /"January 1/".~
|
||
~%(ie, no year or European-style formats).")
|
||
|
||
(def-item REMARKS 630. I$REM "REMAR" "Remarks"
|
||
"~&Remarks. Put anything in here you want to add.~
|
||
~%It will be printed whenever someone looks you up.")
|
||
|
||
;(def-item MACHINES-KNOWN-ON 80. I$MACH "MACHI" "Machines known on"
|
||
; "~&This is not used at the present time.~
|
||
; ~&Your INQUIR entry is automatically known on all ITS machines.")
|
||
;
|
||
;(def-item LOCAL-FIELDS 30. I$LOCL "LOCAL" "Local fields"
|
||
; "~&This is not used at the present time. It goes with Machines Known On")
|
||
|
||
(def-item ALTERATION-DATE 0. I$ALTR "ALTER" "Last alteration"
|
||
"~&This says who last altered your INQUIR entry, and when they did so.~
|
||
~&(This is filled in automatically for you.")
|
||
|
||
|
||
|
||
;;;; Groups and Relations
|
||
;;; (We don't use this yet; need it for INQREP stuff later.)
|
||
|
||
;;; Associate the group letters with their documentation.
|
||
|
||
(defvar *inquir-groups-alist*
|
||
(list
|
||
(cons #/A "Artificial Intelligence Lab person")
|
||
(cons #/B "Educational Computing Group person")
|
||
(cons #/C "Theory Group person")
|
||
(cons #/L "Laboratory for Computer Science person")
|
||
(cons #/P "Plasma Fusion Center person")
|
||
(cons #/S "MIT guest - student//staff//faculty not in one of the other groups")
|
||
(cons #/T "Guest (tourist)")
|
||
(cons #/V "NIL Group")
|
||
(cons #/X "Ex-user (former MIT staff//faculty")
|
||
(cons #/Z "Clinical Decision Making person")
|
||
(cons #/+ "Official Maintainter//Liaison on some MIT computer system")
|
||
(cons #/$ "Official Maintainter//Liaison on some ARPANET computer system")
|
||
(cons #/@ "This is an alias for someone known under another name")
|
||
(cons #/O "Other. This designates a program, not a person")))
|
||
|
||
;;; Associates the relation letters with their documentation.
|
||
|
||
(defvar *inquir-relats-alist*
|
||
(list
|
||
(cons #/A "Administrative")
|
||
(cons #/F "Faculty")
|
||
(cons #/G "Graduate student")
|
||
(cons #/P "Publications//Editing")
|
||
(cons #/R "Research associate")
|
||
(cons #/S "DSR (sponsored research) staff")
|
||
(cons #/U "Undergraduate student ")
|
||
(cons #/N "None")))
|
||
|
||
|
||
|
||
;;;; I/O Support functions.
|
||
|
||
;;; Feep the bell.
|
||
|
||
(defun beep ()
|
||
(tyo #^G)) ;Ding.
|
||
|
||
;;; Function to strip away those nasty chars.
|
||
|
||
(defun trim-bad-chars (char-list)
|
||
(delete '#.(ascii #\Null) char-list))
|
||
|
||
;;; "A" or "An"? from KMP's ANIMAL technology.
|
||
|
||
(defun a-or-an (symbol)
|
||
(let ((ch (char-upcase (getcharn symbol 1)))
|
||
(len (flatc symbol)))
|
||
(cond ((= len 1)
|
||
(cond ((member ch '(#/A #/E #/F #/H #/I #/L #/M
|
||
#/N #/O #/R #/S #/X))
|
||
'an)
|
||
(t 'a)))
|
||
(t
|
||
(cond ((member (char-upcase ch) '(#/A #/E #/I #/O #/U))
|
||
'an)
|
||
(t 'a))))))
|
||
|
||
|
||
|
||
;;; When we pass reading functions around, there callers
|
||
;;; sometimes need to know what the best break character
|
||
;;; is, for documentation purposes.
|
||
|
||
(defvar *terminator* #\return "The terminator we're looking for")
|
||
|
||
(defmacro with-terminator (terminator &body forms)
|
||
`(let ((*terminator* ,terminator))
|
||
,@forms))
|
||
|
||
|
||
;;; REASONABLE-DISPLAY-TERMINAL? returns T if the TTY is something
|
||
;;; I could stand with e.g. automatic LISTME turned on.
|
||
|
||
(defun reasonable-display-terminal? (&optional (tty (status ttyofa)))
|
||
(let ((tty-flags (cdr (status filemode tty)))
|
||
(tty-speed (status ospeed tty)))
|
||
(if (and (memq 'rubout tty-flags)
|
||
(memq 'cursorpos tty-flags)
|
||
(or (zerop tty-speed)
|
||
(>= tty-speed 1200.))
|
||
(> (car (status ttysize tty))
|
||
(plus 2. (length *inquir-items*))))
|
||
t
|
||
NIL)))
|
||
|
||
|
||
;;; DISPLAY-ITEM prints an item as for a LISTME display.
|
||
|
||
(defvar *display-start-col* 25.)
|
||
|
||
(defun display-item (stream item &optional (start-col *display-start-col*)
|
||
(max-width #.(cdr (status ttysize))))
|
||
(format stream "~&~A:~VT" (doc item) start-col) ;Print item name.
|
||
(let* ((val (inquir-value item))
|
||
(chars (if val (exploden val)))) ;Get value.
|
||
(do ((c chars (cdr c))
|
||
(new-line-flag nil)
|
||
(pos-avail max-width))
|
||
((null c))
|
||
(let ((c (car c)))
|
||
(cond (new-line-flag
|
||
(cond ((= c #\LF)
|
||
(tyo c stream)
|
||
(format stream "~VT" start-col)
|
||
(setq pos-avail max-width))
|
||
(t
|
||
(format stream "~VT" start-col)
|
||
(setq pos-avail max-width)
|
||
(tyo c stream))))
|
||
(t
|
||
(tyo c stream)
|
||
(setq pos-avail (1- pos-avail))))
|
||
(setq new-line-flag (= c #\CR))
|
||
(when (zerop pos-avail)
|
||
(terpri stream)
|
||
(format stream "~VT" start-col)
|
||
(setq pos-avail max-width)))))) ;Restart count.
|
||
|
||
|
||
;;; Print the name and value of an item as INQUPD likes to see it.
|
||
|
||
(defun print-inqupd-item (stream item)
|
||
(format stream "~&~A:~C" (inqupd-name item) #\tab) ;Item name.
|
||
(let* ((val (inquir-value item))
|
||
(chars (if val (exploden val)))
|
||
(new-line-flag nil))
|
||
(dolist (c chars)
|
||
(cond (new-line-flag
|
||
(cond ((= c #\LF)
|
||
(tyo c stream)
|
||
(tyo #\tab stream))
|
||
(t
|
||
(tyo #\tab stream)
|
||
(tyo c stream))))
|
||
(t
|
||
(tyo c stream)))
|
||
(setq new-line-flag (= c #\CR)))))
|
||
|
||
|
||
|
||
;;;; Prettier MORE processing.
|
||
|
||
;;; Stolen from TNP;FORT.
|
||
;;; CATCH-**MORE**
|
||
;;; Catch a throw to flush output after a **MORE**.
|
||
|
||
|
||
(defvar *can-flush-more* nil)
|
||
(defvar *in-more-break* nil)
|
||
|
||
(defmacro catch-**more** (&body body)
|
||
`(*catch '*can-flush-more*
|
||
(let ((*can-flush-more* t))
|
||
,@body)))
|
||
|
||
|
||
;;; DONT-CATCH-**MORE**
|
||
(defmacro dont-catch-**more** (&body body)
|
||
`(let ((*can-flush-more* nil))
|
||
,@body))
|
||
|
||
(defun **more** (stream)
|
||
(nointerrupt nil)
|
||
(do-with-tty-off
|
||
(let ((*in-more-break* t))
|
||
(cond (*can-flush-more* (maybe-**more** stream (status ttycons stream)))
|
||
(t (surely-**more** stream (status ttycons stream)))))))
|
||
|
||
(defun maybe-**more** (outstream instream)
|
||
(let ((where (cursorpos outstream)))
|
||
(cursorpos 'l outstream)
|
||
(format outstream "--More?--")
|
||
(do ((c (tyi instream) (tyi instream))
|
||
(flag t))
|
||
(nil)
|
||
(cond ((= c #\rubout)
|
||
(cursorpos (car where) (cdr where) outstream)
|
||
(cursorpos 'l outstream)
|
||
(format outstream "--Output flushed--~%")
|
||
(*throw '*can-flush-more* t))
|
||
((= c #\space)
|
||
(cursorpos (car where) (cdr where) outstream)
|
||
(cursorpos 'l outstream)
|
||
(format outstream "--Continuing--~%")
|
||
(return t))
|
||
(flag
|
||
(setq flag nil)
|
||
(format outstream
|
||
"(Type SPACE to continue or RUBOUT to flush output)"))))))
|
||
|
||
(defun surely-**more** (outstream instream)
|
||
(let ((where (cursorpos outstream)))
|
||
(cursorpos 'l outstream)
|
||
(format outstream "--Pause--")
|
||
(do ((c (tyi instream) (tyi instream))
|
||
(flag t))
|
||
(nil)
|
||
(cond ((= c #\space)
|
||
(cursorpos (car where) (cdr where) outstream)
|
||
(cursorpos 'l outstream)
|
||
(format outstream "--Continuing--~%")
|
||
(return t))
|
||
(flag
|
||
(setq flag nil)
|
||
(format outstream
|
||
"(Type SPACE to continue)"))))))
|
||
|
||
|
||
|
||
;;;; Hacking items
|
||
|
||
;;; These variables control the mode of interaction:
|
||
|
||
(defvar *brief-prompt* "What next? ")
|
||
(defvar *windy-prompt* "What next? (Type ? for assistance): ")
|
||
|
||
(defvar *prompt* *brief-prompt*
|
||
"The top-level command prompt.")
|
||
|
||
|
||
(defvar *windy-mode* nil
|
||
"T for maximum verbosity")
|
||
|
||
(defvar *display-mode* nil
|
||
"T to do automatic LISTME after each change.")
|
||
|
||
(defvar *verify-mode* nil
|
||
"T to require verification on input")
|
||
|
||
|
||
(defvar *modified* nil
|
||
"T if the current entry has been changed.")
|
||
|
||
(defvar *inhibit-listme?* nil
|
||
"T to inhibit LISTME in CHANGE-ITEM.")
|
||
|
||
;;; CHANGE-ITEM is called by commands which change items.
|
||
;;; It provides a uniform kind of prompt, and hacks verification.
|
||
;;; You pass it a function to read the item and the argumentsts for same.
|
||
|
||
(defun change-item (item-name read-fn &rest read-args)
|
||
(lexpr-funcall #'change-item1 item-name read-fn read-fn read-args)
|
||
(if (and *display-mode*
|
||
(not *inhibit-listme?*))
|
||
(listme)))
|
||
|
||
|
||
(defun change-item1 (item-name read-fn verify-fn &rest read-args)
|
||
(let ((val (inquir-value item-name)))
|
||
(if val ;If something already filled in
|
||
(format t "~&~A~%~A" (doc item-name) val)) ; print it out.
|
||
(terpri t)
|
||
(format t (if *windy-mode* (windy-doc item-name)
|
||
(doc item-name)))
|
||
(if *terminator*
|
||
(format t "~&End your input with a ~@:C.~%" *terminator*)
|
||
(format t "~&"))
|
||
(setf (inquir-value item-name) (lexpr-funcall read-fn read-args))
|
||
(setq *modified* t)
|
||
(if *verify-mode* ; Now make sure he really meant it.
|
||
(do ((newnewval
|
||
(lexpr-funcall #'verify-item item-name verify-fn read-args)
|
||
(lexpr-funcall #'verify-item item-name verify-fn read-args)))
|
||
((not newnewval)) ))))
|
||
|
||
|
||
;;; Returns NIL when the user is satisfied with the changes.
|
||
|
||
(defun verify-item (item-name read-fn &rest read-args &aux newval)
|
||
(format t "~& Is this right for ~A?" (doc item-name))
|
||
(format t "~& Type what it should look like, or ~@:C if this looks good.~
|
||
~&~A~%"
|
||
(or *terminator* #\Return)
|
||
(inquir-value item-name))
|
||
(setq newval (lexpr-funcall read-fn read-args)) ;Get a replacement value.
|
||
(cond ((not (string-equal newval "")) ;Store it.
|
||
(setf (inquir-value item-name) newval)
|
||
newval))) ;Return it, or NIL if there was no replacement.
|
||
|
||
|
||
;;; TTY item reader, calls READER.
|
||
;;; Returns a string.
|
||
|
||
(defun inputter (&optional (prompt "->")
|
||
(max #.(* 24. 80.))
|
||
multi?
|
||
upcase?)
|
||
(*catch 'too-long
|
||
(let ((chars (nreverse
|
||
(mapcar #'(lambda (x)
|
||
(setq x (getcharn x 1))
|
||
(if upcase? (setq x (char-upcase x)))
|
||
x)
|
||
(reader prompt (if multi? '(#^C) '(#\CR #\LF #^C)))))))
|
||
(if (and multi?
|
||
(> (length chars) 1)
|
||
(= (car chars) #\CR))
|
||
(pop chars)) ;Flush trailing CR.
|
||
(when (> (length chars) max)
|
||
(format t "~&That's too long for this item.")
|
||
(dolist (c chars) ;Let him try again.
|
||
(untyi c tyi))
|
||
(*throw 'too-long t))
|
||
(string (implode (nreverse (trim-bad-chars chars)))))))
|
||
|
||
|
||
;;; For reading in items:
|
||
;;; PROMPT-TYI for single chars.
|
||
;;; PROMPT-READLINE for single lines.
|
||
;;; PROMPT-INPUT for multiple lines.
|
||
;;; INPUT-SIXBIT for six uppercase chars.
|
||
|
||
(defun prompt-readline (&optional (prompt "->") max)
|
||
(inputter prompt max nil nil))
|
||
|
||
(defun prompt-tyi (&optional (prompt "->") (upcase? t) &aux c)
|
||
(format t "~A" prompt)
|
||
(setq c (tyi))
|
||
(if (or (equal c #\CR) (equal c #\LF))
|
||
""
|
||
(if upcase? (ascii (char-upcase c))
|
||
(ascii c))))
|
||
|
||
|
||
(defun input-sixbit (&optional (prompt "->"))
|
||
(inputter prompt 6. nil t))
|
||
|
||
(defun prompt-input (&optional (prompt "->") (max))
|
||
(inputter prompt max t nil))
|
||
|
||
|
||
;;; INPUT-NETADDR is for typing in network addresses.
|
||
|
||
(defun input-netaddr (&rest read-args)
|
||
(let ((netaddr (lexpr-funcall #'inputter read-args)))
|
||
(if (string-equal netaddr "")
|
||
(string (status site))
|
||
netaddr)))
|
||
|
||
|
||
|
||
;;; Fancy completing command reader, calls *COMPLETING-READ1.
|
||
|
||
(defun get-input-line (prompt options &optional (over-rubout-allowed? t))
|
||
(cond ((zerop (charpos tyo)) (format t "~&~A" prompt)))
|
||
(prog1 (do () (nil)
|
||
(let ((command (*completing-read1 prompt
|
||
options
|
||
'()
|
||
tyi
|
||
tyo
|
||
t
|
||
t
|
||
'(#\ALT)
|
||
'(#\RETURN #\SPACE)
|
||
t
|
||
over-rubout-allowed?)))
|
||
(cond ((memq command '(over-rubout line-rubout))
|
||
(abort-command))
|
||
((atom command) ;line-rubout, word-rubout, etc.
|
||
(format t "~&~A" prompt))
|
||
(t (return (cadr command))))))
|
||
(cond ((and (plusp (listen tyi)) (= (tyipeek nil tyi) #\LF))
|
||
(tyo (tyi tyi) tyo)))))
|
||
|
||
|
||
|
||
;;;; LSR1 and LOGOUT TIMES database hacking.
|
||
;;;
|
||
;;; LSRRTN primitives:
|
||
;;;
|
||
;;; (LSRMAP <number of pages to use>) ==> file object.
|
||
;;; (LSRDTA <file-object>) ==> core address of first entry or -1
|
||
;;; (LSRNXT <file-obj> <core address>) ==> core address of next entry or -1
|
||
;;; (LSRUNM <file obj returned by LSRMAP> <uname>) ==> core address or -1
|
||
;;; (LSRITM <item number> <magic number>) ==> item symbol frob
|
||
;;;
|
||
;;; NETRTN primitives:
|
||
;;;
|
||
;;; (MAP-HOSTS3) ==> gets the network database in core.
|
||
;;; (OWN-HOST network-number) ==> host.
|
||
;;; (HOST-EQUAL host1 host2) ==> T iff they refer to same host.
|
||
;;; (LOOKUP-HOST host-name) ==> host-number.
|
||
;;; (HOST-SIXBIT-NAME host) ==> sixbit host name.
|
||
|
||
|
||
(defvar *lsr1* nil) ;LSR1 file object.
|
||
;(defvar *logout-times* nil) ;LGOTIM file object.
|
||
|
||
;;; Map databases.
|
||
|
||
(defun map-inquir ()
|
||
(setq *lsr1* (lsrmap 20.)))
|
||
|
||
;(defun map-logout-times ()
|
||
; (setq *logout-times* (map-lgotim)))
|
||
|
||
|
||
;;; GET-ENTRY reads the data for the current INQUIR entry from the
|
||
;;; LSR1 database. The LSR1 database must already be mapped in.
|
||
;;; If the named user does not have an entry, GET-ENTRY returns NIL.
|
||
|
||
(defun get-entry (uname)
|
||
(without-interrupts ;No interruptions, please.
|
||
;; Remember who the current entry belongs to.
|
||
;; Try looking up in the LSR1 database.
|
||
(let ((entry (lsrunm *lsr1* uname))
|
||
(val))
|
||
(cond
|
||
((and entry (not (= entry -1)))
|
||
(dolist (item *inquir-items*) ;Fill in current entry from LSR1.
|
||
(setq val (lsritm (i$number item) entry))
|
||
(setf (inquir-value item) (string val)))
|
||
t)
|
||
(t ;If unknown, just set Uname.
|
||
(setf (inquir-value 'user-name) (string (string-upcase uname)))
|
||
nil)))))
|
||
|
||
|
||
(defvar *default-local-items-string*
|
||
"FILE-DIRECTORIES GROUP-AFFILIATION RELATION-TO-GROUP")
|
||
|
||
|
||
;;; LAST-ALTERATION returns the current timestamp string for an ALTER item.
|
||
|
||
(defun last-alteration ()
|
||
(lexpr-funcall #'format nil "~A ~2,'0D~2,'0D~2,'0D-~2,'0D~2,'0D~2,'0D"
|
||
(status uname)
|
||
(append (status date) (status daytime))))
|
||
|
||
|
||
;;; UPDATE-INQUIR updates the LSR1 file from the current Inquir entry.
|
||
;;; (Actually, it mails off a request for INQUPD to do so.)
|
||
;;; This is the mail routine usually called by DONE.
|
||
|
||
(defvar *inqupd-request-file* "DSK:.MAIL.;MAIL >")
|
||
|
||
;;; We have to use this until we get HOSTS3 going in Lisp.
|
||
;;; Would you believe...until we get domains going in Lisp!
|
||
|
||
(defun old-style-update-inquir ()
|
||
(setf (inquir-value 'machines-known-on)
|
||
(string-append *suname* "@AI @MC @ML @MD @DB"))
|
||
(if (or (null (inquir-value 'authorization))
|
||
(string-equal (inquir-value 'authorization) ""))
|
||
(setf (inquir-value 'authorization) "*"))
|
||
(setf (inquir-value 'local-fields) "FILDI GROUP RELAT")
|
||
(setf (inquir-value 'alteration-date) (last-alteration))
|
||
(with-open-file (s *inqupd-request-file* '(out))
|
||
(format s "~&FROM-PROGRAM:~A~
|
||
~&AUTHOR:~A~
|
||
~&RCPT:(UPDATE-ITS-INQUIR)"
|
||
(status jname)
|
||
(status uname))
|
||
(format s "~&TEXT;-1~
|
||
~&BEGIN:~%~
|
||
~&SUNAME:~C~A"
|
||
#\tab *suname*)
|
||
(dolist (item *inquir-items*)
|
||
(print-inqupd-item s item))
|
||
(format s "~&END:~%")))
|
||
|
||
|
||
;;;; UPDATE-INQUIR is called by the DONE command to update all the
|
||
;;;; databases mentioned.
|
||
;
|
||
;(defun update-inquir ()
|
||
; ;; Set up the ALTER item.
|
||
; (setf (inquir-value 'alteration-date) (last-alteration))
|
||
; (let ((macs (list-from-string (inquir-value 'machines-known-on)))
|
||
; (updated-hosts nil))
|
||
; (dolist (mac macs)
|
||
; (let* ((machine (substring mac (1+ (string-search "@" mac))))
|
||
; (suname (substring mac 0 (string-search "@" mac)))
|
||
; (host (lookup-host machine)))
|
||
;
|
||
; (when (not (mem #'host-equal host updated-hosts))
|
||
; (update-one-machine suname machine)
|
||
;; (push host updated-hosts))))))
|
||
;;;;; *** Need to make sure local host is updated with all the items!
|
||
;
|
||
;
|
||
;
|
||
;
|
||
;;;; UPDATE-ONE-MACHINE sends off an update for a single database.
|
||
;;;; It can optionally ignore the LOCAL-FIELDS item.
|
||
;
|
||
;(defun update-one-machine (suname machi &optional (use-local-fields? t))
|
||
; (with-open-file (s *inqupd-request-file* '(out))
|
||
; (format s "~&FROM-PROGRAM:~A~%~
|
||
; ~&AUTHOR:~A~%~
|
||
; ~&RCPT:(UPDATE-INQUIR ~A)~%~
|
||
; ~&TEXT;-1~
|
||
; ~&BEGIN:~%~
|
||
; ~&SUNAME:~C~A"
|
||
; (status jname)
|
||
; (status uname)
|
||
; machi
|
||
; #\tab suname)
|
||
; (let ((locals (list-from-string (inquir-value 'local-items))))
|
||
; (dolist (item *inquir-items*)
|
||
; (if (and use-local-fields?
|
||
; (not (member item locals)))
|
||
; (print-inqupd-item s item)))
|
||
; (format s "~&END:~%"))))
|
||
|
||
|
||
|
||
|
||
;;;; Commands.
|
||
|
||
;;; Amazing hairy macro defining macro frobozz.
|
||
|
||
(defmacro def-command-type (name)
|
||
(let ((doc (symbolconc name '-documentation))
|
||
(var (symbolconc '* name 's*))
|
||
(def (symbolconc 'define- name))
|
||
(fn (symbolconc name '-fn)))
|
||
`(progn 'compile
|
||
(defmacro ,fn (x) `(get ,x ',',name))
|
||
(defmacro ,doc (x) `(get ,x ',',doc))
|
||
(defvar ,var '())
|
||
(defmacro ,def (name doc &body body)
|
||
`(progn 'compile
|
||
(push ',name ,',var)
|
||
(defun (,name ,',name) () ,@body)
|
||
(setf (,',doc ',name) ',doc)
|
||
',name))
|
||
(,def HELP "Gives help on a given command type."
|
||
(format t "~&~A"
|
||
(,doc (get-input-line
|
||
',(format nil "Help with ~A: " name)
|
||
,var)))
|
||
(terminate-command))
|
||
',name)))
|
||
|
||
|
||
(defun not-implemented ()
|
||
(format t "~&I don't know how to do that to ~A ~A, yet!"
|
||
(string-downcase (a-or-an *suname*))
|
||
*suname*))
|
||
|
||
|
||
|
||
;;;; Primary commands.
|
||
|
||
(def-command-type command)
|
||
|
||
(define-command QUIT "Exits the program without updating any entries."
|
||
(cond (*modified*
|
||
(if (yes-or-no-p "~&Cancel all these changes? ")
|
||
(flush-inquir t)
|
||
(format t "~&Use the DONE command when you are happy with ~
|
||
your changes.")))
|
||
(t
|
||
(flush-inquir t))))
|
||
|
||
|
||
(define-command DONE "Exits the program and starts the update."
|
||
(cond (*modified*
|
||
(old-style-update-inquir)
|
||
(format t "~&Your changes have been sent for processing and~
|
||
~&should be completed in a little while."))
|
||
(t
|
||
(format t "~&(No changes made.)")))
|
||
(format t "~&Thank you.~%")
|
||
(flush-inquir t))
|
||
|
||
|
||
;;; Commands which affect the mode of interaction.
|
||
|
||
(define-command WINDY "Make INQUIR be verbose."
|
||
(setq *windy-mode* t)
|
||
(setq *prompt* *windy-prompt*)
|
||
(format t "~&Maximum verbosity."))
|
||
|
||
|
||
(define-command BRIEF "Make INQUIR be less verbose."
|
||
(setq *windy-mode* nil)
|
||
(setq *prompt* *brief-prompt*))
|
||
|
||
(define-command VERIFY "Make INQUIR verify all your changes."
|
||
(setq *verify-mode* t))
|
||
|
||
|
||
(define-command NOVERIFY "Make INQUIR stop asking you to verify all your changes."
|
||
(setq *verify-mode* nil))
|
||
|
||
|
||
(define-command LISTME "Show what your entry looks like."
|
||
(listme))
|
||
|
||
;;; Routine to list our entry.
|
||
;;; Called automatically from CHANGE-ITEM when in display mode.
|
||
|
||
(defun listme ()
|
||
(cursorpos 'C)
|
||
(dolist (item *inquir-items*)
|
||
(display-item t item))
|
||
(terpri)
|
||
(terpri))
|
||
|
||
(define-command DESCRIBE
|
||
"Tell about a particular INQUIR item."
|
||
(let ((item (get-input-line "Describe item: " *inquir-items*)))
|
||
(format t (string-append "~&~%" (doc item) ".~&"
|
||
(or (windy-doc item) "")
|
||
"~2&Current value is:~&~A~2%")
|
||
(inquir-value item))))
|
||
|
||
(define-command REVERT
|
||
"Cancel all changes; read your old info back in from the database."
|
||
(if (yes-or-no-p t "~%Cancel all these changes? ")
|
||
(cond ((get-entry *suname*)
|
||
(format t "~&OK.")
|
||
(setq *modified* nil)
|
||
(listme))
|
||
(t
|
||
(abort-command "You have no previous info on file.")))
|
||
(abort-command)))
|
||
|
||
|
||
;(define-command NOTME "Update someone else's entry."
|
||
; (not-implemented))
|
||
|
||
;(define-command OTHER "Prefix to other, less commonly used commands."
|
||
; (not-implemented))
|
||
|
||
(define-command ALL "Change all of your items."
|
||
(change-all-items nil))
|
||
|
||
;;; CHANGE-ALL-ITEMS is for changing all the fields in an entry.
|
||
;;; FIRST-TIME? is whether we should ask only for the items we
|
||
;;; expect novices running INQUIR for the first time to fill in.
|
||
|
||
(defun change-all-items (first-time?)
|
||
(let ((*inhibit-listme?* t))
|
||
(dolist (cmd '(NAME NICK-NAME MIT-ADDRESS MIT-PHONE
|
||
HOME-ADDRESS HOME-PHONE NETWORK-ADDRESS
|
||
SUPERVISOR PROJECT GROUP RELATION BIRTHDAY REMARKS))
|
||
(funcall (command-fn cmd)))
|
||
(if (not first-time?)
|
||
(dolist (cmd '(AUTHORIZATION FILE-DIRECTORIES))
|
||
(funcall (command-fn cmd))))))
|
||
|
||
;(define-command WHOIS "Display your entry like WHOIS would show it."
|
||
; (let ((uname (inquir-value 'uname)))
|
||
; (format t "~2&~A ~C~C ~A Last logout ~A"
|
||
; uname
|
||
; (inquir-value 'group-affiliation)
|
||
; (inquir-value 'relation-to-group)
|
||
; (first-name-first (inquir-value 'name))
|
||
; (implode (last-logout *logout-times* uname)))
|
||
; (format t " (~A) [~A] hacking ~A for ~A~
|
||
; ~% Birthday ~A; ~A; ~A~
|
||
; ~% ~A; ~A~%"
|
||
; (inquir-item 'nick-name)
|
||
; (inquir-item 'network-address)
|
||
; (inquir-item 'project)
|
||
; (inquir-item 'supervisor)
|
||
; (inquir-item 'birthday)
|
||
; (first-line-of (inquir-item 'mit-address))
|
||
; (inquir-item 'mit-phone)
|
||
; (first-line-of (inquir-item 'home-address))
|
||
; (inquir-item 'home-phone))))
|
||
|
||
|
||
;;;; Commands which change items
|
||
|
||
;;; I had considered making a form which would automatically
|
||
;;; write these commands, but I don't think it will save
|
||
;;; enough typing to bother with. Something like:
|
||
;;;
|
||
;;; (defmacro def-item-command (item cmd-doc input-fn)
|
||
;;; `(progn 'compile
|
||
;;; (setf (initial-asked? ',item) ,initial-asked?)
|
||
;;; (define-command ,item ,cmd-doc
|
||
;;; (change-item ',item ,input-fn "->" (max-length ',item)))))
|
||
;;;
|
||
|
||
(define-command USER-NAME "Changes your username."
|
||
(change-item 'user-name #'inputter "->" 6. nil t))
|
||
|
||
(define-command NAME "Changes your full personal name."
|
||
(change-item 'name #'inputter "->" (max-length 'name)))
|
||
|
||
(define-command NICK-NAME "Changes your nickname."
|
||
(change-item 'nick-name #'inputter "->" (max-length 'nick-name)))
|
||
|
||
(define-command GROUP "Changes your group affiliation."
|
||
(with-terminator nil
|
||
(change-item 'group-affiliation #'prompt-tyi "->" t)))
|
||
|
||
(define-command RELATION "Changes your group relation."
|
||
(with-terminator nil
|
||
(change-item 'relation-to-group #'prompt-tyi "->" t)))
|
||
|
||
(define-command AUTHORIZATION "Say whether you are a good-guy."
|
||
(change-item 'authorization #'inputter "->"
|
||
(max-length 'authorization)))
|
||
|
||
(define-command MIT-ADDRESS "Change your MIT (work) address."
|
||
(with-terminator #^C
|
||
(change-item 'mit-address #'inputter
|
||
"->
|
||
"
|
||
(max-length 'mit-address)
|
||
t
|
||
nil)))
|
||
|
||
(define-command HOME-ADDRESS "Change your home address."
|
||
(with-terminator #^C
|
||
(change-item 'home-address #'inputter
|
||
"->
|
||
"
|
||
(max-length 'home-address)
|
||
t
|
||
nil)))
|
||
|
||
(define-command MIT-PHONE "Change your MIT (office) phone."
|
||
(change-item 'mit-phone #'inputter "->" (max-length 'mit-phone)))
|
||
|
||
(define-command HOME-PHONE "Change your home phone."
|
||
(change-item 'home-phone #'inputter "->" (max-length 'home-phone)))
|
||
|
||
(define-command SUPERVISOR "Change your supervisor."
|
||
(change-item 'supervisor #'inputter "->" (max-length 'supervisor)))
|
||
|
||
(define-command PROJECT "Change your project."
|
||
(change-item 'project #'inputter "->" (max-length 'project)))
|
||
|
||
(define-command FILE-DIRECTORIES "Change your home file directories."
|
||
(change-item 'file-directories #'inputter
|
||
"->"
|
||
(max-length 'file-directories)))
|
||
|
||
(define-command BIRTHDAY "Change your birthday."
|
||
(change-item 'birthday #'inputter "->" (max-length 'birthday)))
|
||
|
||
(define-command REMARKS "Change your remarks."
|
||
(with-terminator #^C
|
||
(change-item 'remarks #'inputter "->"
|
||
(max-length 'remarks)
|
||
t
|
||
nil)))
|
||
|
||
(define-command NETWORK-ADDRESS "Change your electronic mail address."
|
||
(change-item1 'network-address
|
||
#'input-netaddr
|
||
#'inputter "->" (max-length 'network-address) nil nil)
|
||
(if (and *display-mode*
|
||
(not *inhibit-listme?*))
|
||
(listme)))
|
||
|
||
|
||
;(define-command MACHINES "Change which database your entry is updated on."
|
||
; (not-implemented))
|
||
|
||
;(define-command LOCAL-FIELDS "Change which fields are not propogated to foreign INQUIR databases."
|
||
; (not-implemented))
|
||
|
||
|
||
|
||
;;;; Other commands.
|
||
|
||
(def-command-type extended-command)
|
||
|
||
(define-command EXTENDED
|
||
"Preface to less frequently used commands."
|
||
(funcall (extended-command-fn
|
||
(get-input-line "What else? " *extended-commands*))))
|
||
|
||
(define-command DISPLAY-MODE
|
||
"Manually force display terminal mode on."
|
||
(setq *display-mode* t))
|
||
|
||
(define-command NODISPLAY-MODE
|
||
"Manually force display terminal mode off."
|
||
(setq *display-mode* nil))
|
||
|
||
|
||
(define-extended-command DELETE
|
||
"Delete the entry you are editing from the database.
|
||
INQUIR will forget all about this person."
|
||
(terpri t)
|
||
(cond ((yes-or-no-p t "Do you really want to flush ~:[your~*~;~A's~] INQUIR entry? "
|
||
(not (eq (status xuname) *suname*))
|
||
*suname*)
|
||
(setf (inquir-value 'user-name) "")
|
||
(setq *modified* t)
|
||
(format t "~&OK.")
|
||
(if *display-mode* (listme)))
|
||
(t
|
||
(abort-command))))
|
||
|
||
;(define-extended-command LIST-USERS
|
||
; "List users."
|
||
; ;; Massive non-destruction!
|
||
; (not-implemented))
|
||
|
||
|
||
;;; BREAK loop is for hackers only.
|
||
;;; Maybe should be binding ^X and ^G to normal frobs, and ^B to Abort Command.
|
||
|
||
(define-extended-command BREAK "Enter a break loop for debugging!"
|
||
(setq *can-flush-more* nil)
|
||
(break "Type P to continue.")
|
||
(setq *can-flush-more* t))
|
||
|
||
|
||
(define-extended-command IGNORE
|
||
"Ignore you and get back to regular commands."
|
||
(terminate-command))
|
||
|
||
|
||
|
||
;;;; Reaping the INQUIR database.
|
||
|
||
;;; These should really be on a seperate INQREP comtab.
|
||
|
||
(define-extended-command MASSIVE
|
||
"Do massive updates to the database."
|
||
;; Massive destruction! user could supply predicate somehow.
|
||
(not-implemented))
|
||
|
||
(define-extended-command NEXT-LUSER
|
||
"Update the next luser's entry (used after a MASSIVE command.)"
|
||
(not-implemented))
|
||
|
||
|
||
|
||
;;;; Spiels.
|
||
|
||
;;; If we are started as inqchk, we give a little explanatory spiel
|
||
;;; before getting down to business.
|
||
|
||
(defun inqchk-spiel ()
|
||
(format t
|
||
"~2%The program /"INQUIR/" is being run for you so that you can tell us~
|
||
~%who you are. When you are done running inquir you will be left in~
|
||
~%DDT, the command interpreter for this computer system."))
|
||
|
||
;;; Here is the long spiel explaining what inquir is, and giving general help.
|
||
|
||
(defun explain-inquir-spiel ()
|
||
(format t
|
||
"~&~%The purpose of this program is to maintain a list of users for this~
|
||
~%computer. It will lead you by the hand; just provide the information~
|
||
~%requested. If you know what you are doing and don't like being lead,~
|
||
~%use the BRIEF command.~
|
||
~2%Each user on ITS has an individual user-name, or /"UNAME/"; a word of~
|
||
~%six characters or less, which you use to log in. INQUIR keeps its~
|
||
~%records filed under people's UNAMEs.~
|
||
~2%Your dossier consists of several items; each is one piece of ~
|
||
~%information, such as your name, or your home address.
|
||
~%If you are completely unknown, INQUIR will ask you for all of the~
|
||
~%items in order. If you are already known, you can type the name~
|
||
~%of any items you wish to alter.~
|
||
~2%You can leave any of the items blank, if you feel the questions are~
|
||
~%too nosey. Most people fill in most of the items so that people in~
|
||
~%the user community can find out about them.~
|
||
~2%If you are on a display terminal, your entire dossier will be shown~
|
||
~%frequently. On printing terminals, you must type the LISTME command~
|
||
~%to see it.~
|
||
~2%If you make a mistake typing, you can use the Rubout key to erase a~
|
||
~%character. Typing ^U (holding the Control key down and striking /"U/")~
|
||
~%will erase the line you are typing. To redisplay the line you are~
|
||
~%typing, type ^R. To clear the screen and redisplay the line, type ^L.~
|
||
~2%When INQUIR is ready for a command, it will prompt you.~
|
||
~%You can type a question mark (?) to see what the possibilities are.~
|
||
~%If you type a question mark in the middle of a command name,
|
||
~%INQUIR will show you what the possible completions are.
|
||
~%Typing a Space or an Escape will complete the command name for you."))
|
||
|
||
|
||
;;;; Startup.
|
||
|
||
;;; TOPLEVEL is magically called when we start up.
|
||
|
||
(defun toplevel ()
|
||
(initialize) ;Map in database, etc.
|
||
(dont-catch-**more**
|
||
(start-inquir)) ;Prologue.
|
||
(do ((first-time-thru t nil))
|
||
(nil) ;This is the command infinite-loop.
|
||
(*catch 'command-abort
|
||
(catch-**more**
|
||
(bind-ttyint ((#^G #'abort-command-handler)
|
||
(#^S #'flush-command-handler)
|
||
(#^B NIL) (#^C NIL) (#^D NIL) (#^R NIL)
|
||
(#^T NIL) (#^V NIL) (#^W NIL) (#^X NIL))
|
||
(when (and first-time-thru
|
||
*display-mode*)
|
||
(listme))
|
||
(format t "~&")
|
||
(when (not
|
||
(errset
|
||
(funcall
|
||
(command-fn (get-input-line *prompt*
|
||
*commands* nil)))
|
||
t))
|
||
(bug nil)))))))
|
||
|
||
(defun initialize ()
|
||
(without-interrupts
|
||
(setq *inquir-items* (nreverse *inquir-items*))
|
||
(setq *commands* (nreverse *commands*))
|
||
(map-inquir) ;Map in the LSR1 database.
|
||
#| (map-logout-times) |# ;Map in the LOGOUT TIMES database.
|
||
(setq *display-mode* (reasonable-display-terminal?))
|
||
(endpagefn t #'**more**))) ;Turn on **MORE** handler.
|
||
|
||
|
||
;;; START-INQUIR makes sure the luser knows who is being hacked,
|
||
;;; figures out how verbose to be, and makes new users fill out
|
||
;;; everything before returning to the main program.
|
||
|
||
(defun start-inquir ()
|
||
(unless
|
||
(errset
|
||
(*catch 'command-abort
|
||
(catch-**more**
|
||
(bind-ttyint ((#^G #'flush-inquir-handler)
|
||
(#^S #'flush-inquir-handler)
|
||
(#^B NIL) (#^C NIL) (#^D NIL) (#^R NIL)
|
||
(#^T NIL) (#^V NIL) (#^W NIL) (#^X NIL))
|
||
(let ((inqchk? (eq (status jname) 'INQCHK))
|
||
(jcl (jcl-as-string)))
|
||
(setq *suname* (or jcl
|
||
(string-upcase (status xuname))))
|
||
(cond
|
||
(inqchk? ;When started by DDT init file,
|
||
(when (get-entry *suname*) ; if luser already exists
|
||
(flush-inquir t)) ; then punt right now.
|
||
(unless (yes-or-no-p t "~%You have logged in as ~A, which is a new UNAME.~
|
||
~&Is this what you you intended? "
|
||
*suname*)
|
||
(format t "~&Please type ^Z and try again.")
|
||
(valret "0U"))
|
||
(inqchk-spiel)
|
||
(funcall (command-fn 'WINDY))
|
||
(funcall (command-fn 'VERIFY))
|
||
(enter-new-user))
|
||
(t ;See who the luser is hacking.
|
||
(format t "~&~@[~A ~]INQUIR.~D"
|
||
(get 'inquir 'release-status)
|
||
(get 'inquir 'version))
|
||
(when (null jcl)
|
||
(when (null (get-entry *suname*))
|
||
(format t "~%~A is not known." *suname*))
|
||
(unless (y-or-n-p t "~&Is ~A your user name? " *suname*)
|
||
(format t "~&Well, that's who you are logged in as.~
|
||
~&If you want to modify someone else,~
|
||
~&invoke INQUIR with their UNAME as JCL.")
|
||
(flush-inquir t)))
|
||
(cond ((null (get-entry *suname*))
|
||
(unless jcl
|
||
(explain-inquir-spiel)
|
||
(funcall (command-fn 'WINDY))
|
||
(funcall (command-fn 'VERIFY)))
|
||
(enter-new-user))
|
||
(t
|
||
;; Luser exists, so just set the verbosity level.
|
||
(funcall (command-fn 'NOVERIFY))
|
||
(funcall (command-fn (if jcl 'BRIEF 'WINDY)))))))))))
|
||
t)
|
||
(bug t)))
|
||
|
||
(defun enter-new-user ()
|
||
(*catch 'command-abort
|
||
(bind-ttyint ((#^G #'abort-command-handler)
|
||
(#^S #'flush-command-handler)
|
||
(#^B NIL) (#^C NIL) (#^D NIL) (#^R NIL)
|
||
(#^T NIL) (#^V NIL) (#^W NIL) (#^X NIL))
|
||
(terpri t)
|
||
(change-all-items t)))) ;Then put luser to work.
|
||
|
||
(defun jcl-as-string (&aux jcl)
|
||
"JCL-AS-STRING finds out if there is JCL, and returns it or NIL.
|
||
Flushes spaces and JCL-terminators."
|
||
(setq jcl (status jcl))
|
||
(when jcl
|
||
(let ((jcl (delete '#.(ascii #\return)
|
||
(delete '#.(ascii #\space)
|
||
(delete '#.(ascii #^C)
|
||
(delete '#.(ascii #^_) jcl))))))
|
||
(if jcl ;Prevent "".
|
||
(string-upcase (substring (implode jcl) 0 6))))))
|
||
|
||
|
||
|
||
;;; TERMINATE-COMMAND, gets us back to toplevel.
|
||
;;;
|
||
;;; ABORT-COMMAND prints an error message for the user and aborts
|
||
;;; the current command.
|
||
;;;
|
||
;;; FLUSH-INQUIR is the way out of the program, rather than just QUIT.
|
||
;;; We offer the user the chance to flush INQUIR (or optionally don't bother.)
|
||
|
||
|
||
(defun terminate-command (&rest ignore)
|
||
(setq *can-flush-more* t)
|
||
(*throw 'command-abort t))
|
||
|
||
(defun abort-command (&optional msg)
|
||
(format t "~&~:[Command aborted.~;~A~]" msg msg)
|
||
(terminate-command))
|
||
|
||
(defun flush-inquir-handler (stream nil)
|
||
(clear-input stream)
|
||
(flush-inquir))
|
||
|
||
(defun flush-inquir (&optional without-asking)
|
||
(if (or without-asking
|
||
(yes-or-no-p t "~&~%Do you really want to flush this program now? "))
|
||
(quit)))
|
||
|
||
;;; Bug reporting
|
||
|
||
(defun bug (&optional awful? &rest ignore)
|
||
(format t "~&~2%A bug in INQUIR has been encountered.~
|
||
~%This should never happen.~
|
||
~%Please send a message explaining the circumstances leading to this ~
|
||
~%to Bug-INQUIR@MIT-MC. Thank you. ~%")
|
||
(valret (if awful?
|
||
":PDUMP CRASH;INQUIR >
|
||
:BUG INQUIR An INQUIR crash file was just dumped.
|
||
:KILL
|
||
"
|
||
":PDUMP CRASH;INQUIR >
|
||
:BUG INQUIR An INQUIR crash file was just dumped.
|
||
:CONTINUE
|
||
"))
|
||
(terpri t))
|
||
|
||
|
||
;;; Interrupt handlers.
|
||
;;; ABORT-COMMAND-HANDLER and FLUSH-COMMAND-HANDLER abort commands.
|
||
|
||
(defun abort-command-handler (&rest ignore)
|
||
(clear-input t)
|
||
(abort-command))
|
||
|
||
(defun flush-command-handler (&rest ignore)
|
||
(clear-input t)
|
||
(format t "~&Flushed.~%")
|
||
(terminate-command))
|
||
|
||
(defun flush-fallthru-handler (&rest ignore)
|
||
(clear-input t)
|
||
(format t "~&Flushed.~%"))
|
||
|
||
|
||
;;;; Utility Functions
|
||
|
||
;;; Searching the INQUIR database.
|
||
|
||
(defun find (item-no target-string fn &optional (stream t) (display item-no))
|
||
(do ((entry (lsrdta *lsr1*) (lsrnxt *lsr1* entry))
|
||
(hits 0.)
|
||
(count 0. (1+ count)))
|
||
((= -1 entry) (format stream "~&~%~D//~D entries." hits count))
|
||
(when (string-search target-string (string (lsritm item-no entry)))
|
||
(funcall fn entry display stream)
|
||
(setq hits (1+ hits)))))
|
||
|
||
|
||
|
||
(defun print-entry (entry item-no &optional (stream t))
|
||
(format stream "~%~A~10T~A~&~A~%"
|
||
(lsritm i$unam entry)
|
||
(lsritm i$name entry)
|
||
(lsritm item-no entry)))
|
||
|
||
|
||
|
||
;;; Local Modes:
|
||
;;; Lisp with-open-file Indent:1
|
||
;;; Lisp with-terminator Indent:1
|
||
;;; Lisp bind-ttyint Indent:1
|
||
;;; Compile Command::qc inquir;inquir >(t):inquir;inqdmp
|
||
;;; End:
|