An IP address, as mentioned before, consists of four numbers between 0 and 255 separated by dots. If we don't want to use a regular expression for this, we should use some Lisp code. Specifically, we need a function which takes a string as an argument and returns a boolean based on whether or not the string is a valid IP address:
(defun ip-address-p (string) (multiple-value-bind (matched groups) (cl-ppcre:scan-to-strings "^([0-9]{0,3})\.([0-9]{0,3})\.([0-9]{0,3})\.([0-9]{0,3})$" string) (let ((numbers (mapcar #'read-from-string (coerce groups 'list)))) (and matched (every #'(lambda (number) (<= 0 number 255)) numbers)))))
This function makes sure that a basic regular expression matches and uses it to pull apart the string. It then checks that each of the four numbers are in the proper range. Let's test it:
* (ip-address-p "12.36.212.56") T * (ip-address-p "12.00000.212.56") NIL * (ip-address-p "12.512.212.56") NIL * (ip-address-p "12.512.212.hello") NIL
It sure seems to work. Now that we have this, though, what do we do with it? We plug it into our validation code:
(toplevel-match (tag "hostlist" () (tag+ "host" ((attr "name" :matches-regexp "[a-zA-Z]+")) (tag "ip" () (pcdata :test-fn #'ip-address-p)) (tag? "description" () (pcdata)) (tag "checks" () (tag+ "check" () (pcdata))))) (parse-xml-file "hosts.xml"))
Try messing up IP addresses in the hosts.xml file and running the new code. It now detects problems!