(in-package :germinal-med) (defun list-entries (feed-file &optional (root *germinal-root*) (server-name *germinal-server-name*)) (let* ((feed-path-directory (make-pathname :directory (append (pathname-directory (fad:pathname-as-directory root)) (cdr (pathname-directory feed-file))))) (feed-file-absolute (merge-pathnames feed-path-directory (file-namestring feed-file))) (feed-file-directory-relative (enough-namestring feed-path-directory (fad:pathname-as-directory root))) (entries)) (with-open-file (in feed-file-absolute :direction :input) (loop for line = (read-line in nil 'eof) until (eq line 'eof) do (when (and (> (length line) 3) (string= "=> " line :start2 0 :end2 3)) (multiple-value-bind (link/date offset) (split-sequence:split-sequence #\Space line :start 3 :count 2) ;; check for valid date, otherwise discard (when (local-time:parse-timestring (string-trim " []<>()\" " (second link/date)) :fail-on-error nil) (setf entries (cons (list (concatenate 'string "gemini://" server-name "/" (namestring (merge-pathnames feed-file-directory-relative (first link/date)))) (local-time:format-timestring nil (local-time:parse-timestring (second link/date))) (string-trim "-: " (subseq line offset))) entries))))) finally (return (sort entries #'string> :key #'second)))))) (defconstant +atom-header+ " ") (defconstant +atom-footer+ "") (defun make-entry (entry) "Create xml for single entry. entry is '(url date title)." (let ((xml (make-string-output-stream))) (format xml "~% ") (format xml "~% ~A" (third entry)) (format xml "~% " (first entry)) (format xml "~% ~A" (first entry)) (format xml "~% ~A" (second entry)) (format xml "~% ~%") (get-output-stream-string xml))) (defun make-atom (feed-file &key (root *germinal-root*) (server-name *germinal-server-name*) (atom-url "/feed") (atom-id (concatenate 'string server-name atom-url)) ; make this unique and constant (author "Snarky Glogman") (title "Untitled Feed")) "Create XML string in atom format from feed file in Gemini feed syntax, i.e. Gemini URL lines with date." (let* ((entries (list-entries feed-file root server-name)) (last-updated (second (first entries))) (xml (make-string-output-stream))) (format xml "~A~%" +atom-header+) (format xml "~% ~A~%" title) (format xml "~& " atom-url) (format xml "~& ~A~%" last-updated) (format xml "~& ~A~%" author) (format xml "~& ~A~%" atom-id) (loop for entry in entries do (format xml "~A" (make-entry entry))) (format xml "~%~A~%" +atom-footer+) (format nil "~A~%" (get-output-stream-string xml)))) (defun atom-view-de (request &optional name &rest junk) (declare (ignore junk)) (make-response 20 "application/atom+xml" (format nil "~A" (make-atom "b/00-inhaltsverzeichnis.gmi" :atom-url (germinal::path-info request) :atom-id (germinal::url request) :author "Wolfgang Mederle" :title "Deutschsprachige Artikel auf mederle.de")))) (defun atom-view-en (request &optional name &rest junk) (declare (ignore junk)) (make-response 20 "application/atom+xml" (format nil "~A" (make-atom "b/00-table-of-contents.gmi" :atom-url (germinal::path-info request) :atom-id (germinal::url request) :author "Wolfgang Mederle" :title "English language articles on mederle.de"))))