about summary refs log tree commit diff
path: root/fun/๐Ÿ•ฐ๏ธ/bin.lisp
diff options
context:
space:
mode:
Diffstat (limited to 'fun/๐Ÿ•ฐ๏ธ/bin.lisp')
-rw-r--r--fun/๐Ÿ•ฐ๏ธ/bin.lisp91
1 files changed, 91 insertions, 0 deletions
diff --git a/fun/๐Ÿ•ฐ๏ธ/bin.lisp b/fun/๐Ÿ•ฐ๏ธ/bin.lisp
new file mode 100644
index 0000000000..1f5a2b2e05
--- /dev/null
+++ b/fun/๐Ÿ•ฐ๏ธ/bin.lisp
@@ -0,0 +1,91 @@
+(defpackage ๐Ÿ•ฐ๏ธ.bin
+  (:shadow :describe)
+  (:use :cl :opts)
+  (:import-from :uiop :quit)
+  (:import-from :local-time
+   :now :timestamp-subtimezone :+utc-zone+
+   :*default-timezone* :define-timezone)
+  (:import-from :klatre :format-dottime-offset)
+  (:import-from :๐Ÿ•ฐ๏ธ :โŒš)
+  (:export :๐Ÿš‚))
+
+(in-package :๐Ÿ•ฐ๏ธ.bin)
+(declaim (optimize (safety 3)))
+
+(opts:define-opts
+  (:name :help
+   :description "Print this help text"
+   :short #\h
+   :long "help")
+  (:name :dot-time
+   :description "Use pseudo dot-time format (implies -u)"
+   :short #\d
+   :long "dot-time")
+  (:name :utc
+   :description "Display time in UTC instead of local time"
+   :short #\u
+   :long "utc")
+  (:name :no-newline
+   :description "Don't print a trailing newline"
+   :short #\n
+   :long "no-newline"))
+
+(defun make-slash-terminated (str)
+  (if (eq (char str (1- (length str))) #\/)
+    str
+    (concatenate 'string str "/")))
+
+; TODO(sterni): upstream this into local-time
+(defun setup-default-timezone ()
+  (let* ((tz (remove #\: (uiop:getenv "TZ") :count 1))
+         (tz-dir (uiop:getenv "TZDIR"))
+         (tz-file (if (and tz tz-dir)
+                    (merge-pathnames
+                      (pathname tz)
+                      (pathname (make-slash-terminated tz-dir)))
+                    (pathname "/etc/localtime"))))
+    (handler-case
+      (define-timezone *default-timezone* tz-file :load t)
+      (t () (setf *default-timezone* +utc-zone+)))))
+
+
+(defun ๐Ÿš‚ ()
+  (let ((ts (now)))
+    (multiple-value-bind (options free-args)
+      (handler-case (opts:get-opts)
+        ; only handle subset of conditions that can happen here
+        (opts:unknown-option (c)
+          (format t "error: unknown option ~s~%" (opts:option c))
+          (quit 100)))
+
+      ; check if we have any free args we don't know what to do with
+      (when (> (length free-args) 0)
+        (write-string "error: unexpected command line argument(s): ")
+        (loop for arg in free-args
+              do (progn (write-string arg) (write-char #\space)))
+        (write-char #\newline)
+        (quit 100))
+
+      ; print help and exit
+      (when (getf options :help)
+        (opts:describe :usage-of "๐Ÿ•ฐ๏ธ")
+        (quit 0))
+
+      ; reinit *default-timezone* as it is cached from compilation
+      (setup-default-timezone)
+      ; dot-time implies UTC, naturally
+      (when (getf options :dot-time)
+        (setf (getf options :utc) t))
+      ; print clock face
+      (format t "~A" (โŒš ts (if (getf options :utc)
+                               local-time:+utc-zone+
+                               local-time:*default-timezone*)))
+      ; render dot-time offset if necessary
+      (when (getf options :dot-time)
+        (multiple-value-bind (offset-secs _dst _name)
+          (timestamp-subtimezone ts local-time:*default-timezone*)
+          (write-string
+            (format-dottime-offset (round (/ offset-secs 3600))))))
+      ; write newline if necessary
+      (when (not (getf options :no-newline))
+        (write-char #\newline)))))