;;; man page -> HTML gateway for the SU web server. -*- Scheme -*- ;;; This file is part of the Scheme Untergrund Networking package. ;;; Copyright (c) 1996-2003 by Mike Sperber. ;;; For copyright information, see the file COPYING which comes with ;;; the distribution. ;;; This uses RosettaMan ;;; (based at ftp.cs.berkeley.edu:/ucb/people/phelps/tcltk/rman.tar.Z) (define (rman-handler man-binary nroff-binary rman-binary gzcat-binary finder referencer address . maybe-man) (let ((parse-man-url (cond ((procedure? finder) finder) ((list? finder) (lambda (url) (values finder (unescape-uri (http-url-search url)) '()))) (else (let ((man-path ((infix-splitter ":") (getenv "MANPATH")))) (lambda (url) (values man-path (unescape-uri (http-url-search url)) '())))))) (reference-template (cond ((procedure? referencer) referencer) ((string? referencer) (lambda (entry section) referencer)) (else (lambda (entry section) "man?%s(%s)")))) (man (:optional maybe-man man))) (lambda (path req) (let ((request-method (request-method req))) (cond ((string=? request-method "GET") (with-fatal-error-handler (lambda (c decline) (cond ((http-error? c) (apply http-error (car (condition-stuff c)) req (cddr (condition-stuff c)))) (else (decline)))) (make-response (status-code ok) #f (time) "text/html" '() (make-writer-body (lambda (out options) (receive (man-path entry and-then) (parse-man-url (request-url req)) (emit-man-page man-binary nroff-binary rman-binary gzcat-binary entry man man-path and-then reference-template out)) (with-tag out address () (display address out))))))) (else (make-error-response (status-code method-not-allowed) req request-method))))))) (define (cat-man-page key section out) (let ((title (if section (format #f "~a(~a) manual page" key section) (format #f "~a manual page" key)))) (emit-title out title) (emit-header out 1 title) (newline out) (with-tag out body () (with-tag out pre () (copy-inport->outport (current-input-port) out))))) (define (emit-man-page man-binary nroff-binary rman-binary gzcat-binary entry man man-path and-then reference-template out) (receive (key section) (parse-man-entry entry) (let ((status (cond ((procedure? and-then) (run (| (begin (man man-binary nroff-binary gzcat-binary section key man-path)) (begin (and-then key section))) (= 1 ,out) (= 2 ,out))) (else (run (| (begin (man man-binary nroff-binary gzcat-binary section key man-path)) (,rman-binary "-fHTML" ,@and-then "-r" ,(reference-template entry section))) (= 1 ,out) (= 2 ,out)))))) (if (not (zero? status)) (error "internal error emitting man page"))))) (define parse-man-entry (let ((entry-regexp (make-regexp "(.*)\\((.)\\)"))) (lambda (s) (cond ((regexp-exec entry-regexp s) => (lambda (match) (values (match:substring match 1) (match:substring match 2)))) (else (values s #f)))))) (define (man man-binary nroff-binary gzcat-binary section key man-path) (cond ((procedure? man-path) (man-path)) ((find-man-file key section "cat" man-path) => (lambda (file) (cat-n-decode gzcat-binary file))) ((find-man-file key section "man" man-path) => (lambda (file) (nroff-n-decode nroff-binary gzcat-binary file))) (else (if (not (zero? (with-env (("MANPATH" . ,(string-join man-path ":"))) (run (,man-binary "-man" ,@(if section `(,section) '()) ,key) stdports)))) (http-error (status-code not-found) #f "man page not found"))))) (define man-default-sections '("1" "2" "3" "4" "5" "6" "7" "8" "9" "o" "l" "n" "p")) (define (find-man-file name section cat-man man-path . maybe-sections) (define (section-dir section) (lambda (dir) (file-name-as-directory (string-append (file-name-as-directory dir) cat-man section)))) (let* ((prefix (if section (string-append name "." section) (string-append name "."))) (pattern (string-append (glob-quote prefix) "*")) (sections (:optional maybe-sections man-default-sections)) (path (if section (map (section-dir section) man-path) (apply append (map (lambda (dir) (map (lambda (section) ((section-dir section) dir)) sections)) man-path))))) (let loop ((path path)) (and (not (null? path)) (let ((matches (glob (string-append (car path) pattern)))) (if (not (null? matches)) (car matches) (loop (cdr path)))))))) (define (file->man-directory file) (path-list->file-name (reverse (cdr (reverse (split-file-name (file-name-directory file))))))) (define (cat-n-decode gzcat-binary file) (let ((ext (file-name-extension file))) (cond ((string=? ".gz" ext) (run (,gzcat-binary ,file) stdports)) ((string=? ".Z" ext) (run (,gzcat-binary ,file) stdports)) (else (call-with-input-file file (lambda (port) (copy-inport->outport port (current-output-port)))))))) (define (nroff-n-decode nroff-binary gzcat-binary file) (if (not (zero? (run (| (begin (cat-n-decode gzcat-binary file)) (begin (with-cwd (file->man-directory file) (exec-epf (,nroff-binary "-man"))))) stdports))) (http-error (status-code not-found) #f "man page not found")))