(in-package :cl-user)

;;; You better not be using EBCDIC!
(defun ascii-lower-case-letter-p (c)
  (char<= #\a c #\z))
(defun ascii-upper-case-letter-p (c)
  (char<= #\A c #\Z))
(defun ascii-letter-p (c)
  (or (ascii-lower-case-letter-p c) (ascii-upper-case-letter-p c)))
(defun ascii-digit-p (c)
  (char<= #\0 c #\9))

(defun parse-rpm-version (string &key (start 0) (end (length string)))
  (loop :while (< start end)
    :for c = (char string start)
    :for item =
    (cond
      ((ascii-letter-p c)
       (loop :for next :from (1+ start)
         :while (and (< next end) (ascii-letter-p (char string next)))
         :finally (let ((s (subseq string start next)))
                    (setf start next)
                    (return s))))
      ((ascii-digit-p c)
       (loop :for next :from (1+ start)
         :while (and (< next end) (ascii-digit-p (char string next)))
         :finally (let ((i (parse-integer string :start start :end next)))
                    (setf start next)
                    (return i))))
      (t
       (incf start)
       nil))
    :when item :collect item))

(defun compare-rpm-version-chunks (c1 c2)
  (cond
    ((and (integerp c1) (integerp c2))
     (cond
       ((< c1 c2) '<)
       ((> c1 c2) '>)
       (t '=)))
    ((and (stringp c1) (stringp c2))
     (cond
       ((string< c1 c2) '<)
       ((string> c1 c2) '>)
       (t '=)))))

(defun compare-rpm-versions (v1 v2)
  (loop :for (c1 . r1) :on (parse-rpm-version v1)
    :for (c2 . r2) :on (parse-rpm-version v2) :do
    (case (compare-rpm-version-chunks c1 c2)
      ((<) (return '<))
      ((>) (return '>))
      ((=) nil)
      ((nil) (return nil)))
    :finally
    (cond
      (r1 (return '>))
      (r2 (return '<))
      (t (return '=)))))
