;;; -*- Mode:LISP; Package:NETWORK; Readtable:CL; Base:10 -*- ;;; ;;; Stuff for parsing HOSTS2, network addresses. NIC later ;;; ;;; Copyright LISP Machine, Inc. 1986 ;;; See filename "Copyright.Text" for ;;; licensing and release information. (defun generate-from-hosts2-table (&optional input-file) (let ((default-input-file "SYS: CHAOS; HOSTS TEXT >") (si:*force-package* "CHAOS") (*read-base* 8) (*print-base* 8)) (cond ((and (not input-file) ;if not specified (not (probe-file default-input-file))) ;and reasonable file doesn't exist (format *query-io* "~&Default host table input file not found.") (setq input-file (global:prompt-and-read `(:pathname :defaults ,default-input-file) "Please specify file to use instead: "))) (t (if (not input-file) (setq input-file default-input-file)))) (generate-from-hosts2-table-1 input-file "SYS: SITE; HSTTBL LISP >"))) ;;; System system transformation (defun generate-from-hosts2-table-1 (input-file output-file) (let ((*package* (find-package "CHAOS")) (*read-base* 8) (*print-base* 8) (*print-radix* t) (*readtable* si:standard-readtable)) (with-open-file (output-stream output-file :direction :output :characters t) (format output-stream "~ ;;; -*- Mode: LISP;~@[ Package: ~A;~] Base: 8; Readtable:T -*- ;;; *** THIS FILE WAS AUTOMATICALLY GENERATED BY A PROGRAM, DO NOT EDIT IT *** ;;; Host table made from ~A~%" si:*force-package* (send (fs:parse-pathname input-file) :truename)) (si::write-responsibility-comment output-stream) (generate-from-hosts2-table-2 input-file output-stream) (when (global:get-site-option :non-chaos-host-table-file) (generate-from-hosts2-table-2 (global:get-site-option :non-chaos-host-table-file) output-stream))))) (defun read-hosts2-table (input-file) (let ((*package* (find-package "CHAOS")) (*read-base* 8) (*print-base* 8) (*print-radix* t) (*readtable* si:standard-readtable)) (generate-from-hosts2-table-2 input-file nil))) (defun generate-from-hosts2-table-2 (input-file output-stream) (with-open-file (input-stream input-file :direction :input :characters t) (do ((line) (eof) (i) (j) (ni) (nj) (hostl) (delim) (result)) (nil) (multiple-value-setq (line eof) (send input-stream :line-in nil)) (and eof (zerop (string-length line)) (return result)) (multiple-value-setq (i j) (parse-hosts2-table-token line 0)) (cond ((and i (string-equal line "HOST" :start1 i :end1 j)) ;; Host name (multiple-value-setq (ni nj) (parse-hosts2-table-token line (1+ j))) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ nj))) (setq hostl (ncons (substring line ni nj))) (if (char= delim #\[) (do ((l nil) (i1) (j1)) ((char= delim #\]) (incf j) (nreverse l)) (multiple-value-setq (i1 j1 delim) (parse-hosts2-table-token line (1+ j))) (if (char= delim #\Sp) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ j1))) (setq i i1 j j1 j1 i1)) (add-hosts2-table-address line i1 j1 i j hostl)) (let ((i1 i) (j1 j)) (if (char= delim #\Sp) (multiple-value-setq (i j) (parse-hosts2-table-token line (1+ j))) (setq i i1 j j1 j1 i1)) (add-hosts2-table-address line i1 j1 i j hostl))) ; (COND ((OR (GET HOSTL :CHAOS) ;If there were any chaosnet addresses ; ;; Include some popular ARPA sites for speed in SUPDUP/TELNET, etc. ; (SYS:MEMBER-EQUAL (CAR HOSTL) INCLUDED-NON-CHAOS-HOSTS)) (dotimes (k 2) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ j)))) (when i (setf (get hostl :system-type) (intern (substring line i j) ""))) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ j))) (when i (setf (get hostl :machine-type) (intern (substring line i j) ""))) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ j))) (or i (setq delim -1)) (let* ((first-name (car hostl)) (namel (ncons first-name))) (and (char= delim #\[) (do () ((char= delim #\]) (setq namel (stable-sort namel #'(lambda (x y) ;; EQ is OK here... (and (not (eq x first-name)) (< (string-length x) (string-length y))))))) (multiple-value-setq (i j delim) (parse-hosts2-table-token line (1+ j))) (unless (equal i j) ;kmc-dle's suggestion for avoiding null hostnames (push (substring line i j) namel)))) (setf (get hostl :host-names) namel)) (if output-stream (let ((*package* (or (find-package si:*force-package*) *package*))) (format output-stream "(~S ~S~{~% '~S '~S~})~2%" 'si::define-host (car hostl) (cdr hostl))) (push hostl result))))))) (defun parse-hosts2-table-token (string &optional (start 0) end) (or end (setq end (length string))) (do ((idx start (1+ idx)) (sidx) (ch)) ((>= idx end) (values sidx idx -1)) (setq ch (char string idx)) (or sidx (member ch '(#\Sp #\Tab) :test #'eq) (setq sidx idx)) (and sidx (member ch '(#\, #\Sp #\Tab #\[ #\]) :test #'eq) (return (values sidx idx ch))))) (defun add-hosts2-table-address (line net-start net-end address-start address-end hostl &aux symbol parser) (setq symbol (if (= net-start net-end) :arpa (intern (substring line net-start net-end) ""))) (when (setq parser (get symbol 'address-parser)) (setf (get hostl symbol) ;Keep addresses in original order (nconc (get hostl symbol) (ncons (funcall parser symbol line address-start address-end)))))) (defun parse-address (address network-type &optional (start 0) (end (string-length address))) "Given a string, return the parsed address for NETWORK-TYPE, a keyword" (let ((parser (get network-type 'address-parser))) (if parser (funcall parser network-type address start end) (error "Unknown network address type ~S" network-type)))) ;;; Initially supported network types. This should be sufficient (defun (:property :chaos address-parser) (ignore line start end) (parse-integer line :start start :end end :radix 8.)) (defun (:property :ru address-parser) (ignore line start end) (parse-integer line :start start :end end :radix 8.)) (defun parse-arpa-address (ignore line start end) (let ((slash (string-search-char #\/ line start end))) (dpb (parse-integer line :start start :end slash) (byte 8. 9.) (parse-integer line :start (1+ slash) :end end)))) (setf (get :arpa 'address-parser) 'parse-arpa-address) (setf (get :rcc 'address-parser) 'parse-arpa-address) (defun (:property :dial address-parser) (ignore line start end) (substring line start end)) ;A phone number is just characters. (defun parse-2part-octal-address (character line start end) (let ((sep (string-search-char character line start end))) (dpb (parse-integer line :start start :end sep :radix 8.) (byte 8 8) (parse-integer line :start (1+ sep) :end end :radix 8.)))) (defun (:property :lcs address-parser) (ignore line start end) (parse-2part-octal-address #\/ line start end)) (defun (:property :su address-parser) (ignore line start end) (parse-2part-octal-address #\# line start end)) (defun parse-internet-address-component (string from to) (let ((number (parse-integer string :start from :end to :radix 10. :junk-allowed nil))) (cond ((null number) (error "Non-number field (~A) in \"~A\"" (substring string from to) string)) ((or (> number 255.) (minusp number)) (error "Number (~D) out of range in Internet address" number)) (t number)))) (defun (:property :internet address-parser) (ignore line start end) (do ((local-to 0) (idx 3) (address 0)) ((= idx -1) address) (setq local-to (string-search-char #\. line start end)) (if (null local-to) (if (zerop idx) (setq local-to end) (error "Not enough fields for an Internet address"))) (setq address (dpb (parse-internet-address-component line start local-to) (byte 8. (* 8. idx)) address)) (decf idx) (setq start (+ local-to 1)))) ;;; Generation of standard format host table files from the current state of the machine. ;;; Someday, there will be other keyword args for namespaces, filtering, date last changed, ;;; domain suffices, etc. (defun dump-host-table-file (file format &rest keys) (let ((char (get format 'comment-character #\;)) (handler (get format 'output-handler)) (preamble-handler (get format 'preamble-handler)) (postamble-handler (get format 'postamble-handler))) (if handler (with-open-file (out file :direction :output) (si:write-responsibility-comment out char) (when preamble-handler (funcall preamble-handler out)) (apply #'dump-host-table-to-stream out handler keys) (when postamble-handler (funcall postamble-handler out))) (error "~S is not a known host table file format." format)))) (defun dump-host-table-to-stream (stream handler &rest keys) (si:do-all-hosts (h) (apply handler h stream keys))) ;;; Writes out all but the primary name, with name as file computer last. (defun write-other-host-names (host stream separator element-format) (let* ((names (send host :host-names)) (first-name (send host :name))) (loop for i from (- (length names) 1) downto 0 do (let ((name (elt names i))) (unless (string= name first-name) (format stream element-format name) (unless (zerop i) (write-string separator stream))))))) (defun unparse-address (address network-type) (funcall (or (get network-type 'si::address-unparser) 'si::default-address-unparser) address)) (defconstant default-hosts2-network-numbers '((:chaos . 7))) (global:define-site-variable *hosts2-network-numbers* :hosts2-network-numbers "An alist of network types and numbers for HOSTS2 format tables.") (defun write-hosts2-preamble (stream) (dolist (e (or *hosts2-network-numbers* default-hosts2-network-numbers)) (format stream "NET ~A, ~O~%" (car e) (cdr e))) (terpri stream)) (setf (get :hosts2 'preamble-handler) 'write-hosts2-preamble) (defun (:property :hosts2 output-handler) (host stream &rest ignore) (let ((as (send host :chaos-addresses))) (when as (format stream "HOST ~A,~C" (send host :name) #\Tab) (cond ((null (cdr as)) ; only one address (format stream "CHAOS ~O," (first as))) (t (write-char #\[ stream) (format:print-list stream "CHAOS ~O" as ",") (write-string "]," stream))) ;; Don't sweat the USER/SERVER detritus for now... (format stream "USER,~A,~A,[" (send host :system-type) (send host :machine-type)) (write-other-host-names host stream "," "~A") (write-line "]" stream)))) ;;; We really can't do the NIC format yet because we don't save information about protocols. ;;; Then there is the extended NIC format which includes Chaosnet. ;;; Stupid Unix Internet table format (but it's stupid enough for us !). ;;; Seems to allow only one Internet address per host. (setf (get :unix-internet 'comment-character) #\#) (defun (:property :unix-internet output-handler) (host stream &rest ignore) (when (send host :network-typep :internet) (format stream "~A ~C~(~A~) " (send host :unparsed-network-address :internet) #\Tab (send host :name)) (write-other-host-names host stream " " "~(~A~)") (terpri stream)))