zone.lisp: Add seconds-to-timespec conversion and use it when dumping SOA. master
authorMark Wooding <mdw@distorted.org.uk>
Sun, 5 May 2024 01:50:03 +0000 (02:50 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Sun, 5 May 2024 01:50:03 +0000 (02:50 +0100)
zone.lisp

index 3456b45..0979120 100644 (file)
--- a/zone.lisp
+++ b/zone.lisp
        (push r a)
        (setf val q)))))
 
        (push r a)
        (setf val q)))))
 
-(let ((unit-scale (make-hash-table)))
+(let ((unit-scale (make-hash-table))
+      (scales nil))
 
   (dolist (item `(((:second :seconds :sec :secs :s) ,1)
                  ((:minute :minutes :min :mins :m) ,60)
                  ((:hour :hours :hr :hrs :h) ,(* 60 60))
                  ((:day :days :dy :dys :d) ,(* 24 60 60))
                  ((:week :weeks :wk :wks :w) ,(* 7 24 60 60))))
 
   (dolist (item `(((:second :seconds :sec :secs :s) ,1)
                  ((:minute :minutes :min :mins :m) ,60)
                  ((:hour :hours :hr :hrs :h) ,(* 60 60))
                  ((:day :days :dy :dys :d) ,(* 24 60 60))
                  ((:week :weeks :wk :wks :w) ,(* 7 24 60 60))))
-    (destructuring-bind (units scale) item
-      (dolist (unit units) (setf (gethash unit unit-scale) scale))))
+    (destructuring-bind
+       ((&whole units singular plural &rest hunoz) scale) item
+      (declare (ignore hunoz))
+      (dolist (unit units) (setf (gethash unit unit-scale) scale))
+      (push (cons scale (cons singular plural)) scales)))
+  (setf scales (sort scales #'> :key #'car))
 
   (export 'timespec-seconds)
   (defun timespec-seconds (ts)
 
   (export 'timespec-seconds)
   (defun timespec-seconds (ts)
                                               unit)))))
                          (convert (+ acc (to-integer (* count scale)))
                                   tail)))))))
                                               unit)))))
                          (convert (+ acc (to-integer (* count scale)))
                                   tail)))))))
-      (convert 0 ts))))
+      (convert 0 ts)))
+
+  (export 'seconds-timespec)
+  (defun seconds-timespec (secs)
+    "Convert a count of seconds to a time specification."
+    (let ((sign (if (minusp secs) -1 +1)) (secs (abs secs)))
+    (collecting ()
+      (loop (cond ((zerop secs)
+                  (unless (collected) (collect-append '(0 :seconds)))
+                  (return))
+                 ((< secs 60)
+                  (collect (* secs sign))
+                  (collect (if (= secs 1) :second :seconds))
+                  (return))
+                 (t
+                  (let ((match (find secs scales :test #'>= :key #'car)))
+                    (multiple-value-bind (quot rem) (floor secs (car match))
+                      (collect (* quot sign))
+                      (collect (if (= quot 1) (cadr match) (cddr match)))
+                      (setf secs rem))))))))))
 
 (defun hash-table-keys (ht)
   "Return a list of the keys in hashtable HT."
 
 (defun hash-table-keys (ht)
   "Return a list of the keys in hashtable HT."
@@ -1676,20 +1700,20 @@ $TTL ~2@*~D~2%"
                  copy)))
       (format *zone-output-stream* "~
 ~A~30TIN SOA~40T~A (
                  copy)))
       (format *zone-output-stream* "~
 ~A~30TIN SOA~40T~A (
-~55@A~60T ;administrator
-~45T~10D~60T ;serial
-~45T~10D~60T ;refresh
-~45T~10D~60T ;retry
-~45T~10D~60T ;expire
-~45T~10D )~60T ;min-ttl~2%"
+~55@A~58T; administrator
+~45T~10D~58T; serial
+~45T~10D~58T; refresh: ~{~D ~(~A~)~^ ~}
+~45T~10D~58T; retry: ~{~D ~(~A~)~^ ~}
+~45T~10D~58T; expire: ~{~D ~(~A~)~^ ~}
+~45T~10D )~58T; min-ttl: ~{~D ~(~A~)~^ ~}~2%"
              (bind-output-hostname (zone-name zone))
              (bind-hostname (soa-source soa))
              admin
              (soa-serial soa)
              (bind-output-hostname (zone-name zone))
              (bind-hostname (soa-source soa))
              admin
              (soa-serial soa)
-             (soa-refresh soa)
-             (soa-retry soa)
-             (soa-expire soa)
-             (soa-min-ttl soa))))
+             (soa-refresh soa) (seconds-timespec (soa-refresh soa))
+             (soa-retry soa) (seconds-timespec (soa-retry soa))
+             (soa-expire soa) (seconds-timespec (soa-expire soa))
+             (soa-min-ttl soa) (seconds-timespec (soa-min-ttl soa)))))
 
 (export 'bind-format-record)
 (defun bind-format-record (zr format &rest args)
 
 (export 'bind-format-record)
 (defun bind-format-record (zr format &rest args)