Removes use of stream-interactive-p.
This commit is contained in:
parent
0228f32cb1
commit
48dd285cd0
6 changed files with 411 additions and 1219 deletions
13
Anyfile
13
Anyfile
|
|
@ -7,10 +7,15 @@ all: loop.exe loop.lisp loop.asd scripts/build-exe.lisp \
|
|||
scripts/build-index-from-fs.lisp \
|
||||
scripts/cron-remove-inactive-users.lisp \
|
||||
scripts/migrate-add-creation-date.lisp \
|
||||
tags
|
||||
version.lisp
|
||||
|
||||
tags: loop.nw loop.lisp
|
||||
etags --language=lisp loop.lisp
|
||||
version.lisp: loop.lisp
|
||||
echo "(in-package :loop)" > version.lisp
|
||||
echo "(defparameter *git-hash* \"$$(git rev-parse --short HEAD)\")" >> version.lisp
|
||||
|
||||
TAGS: loop.lisp
|
||||
etags $(PWD)/loop.lisp
|
||||
sed -i 's|$(PWD)/loop.lisp|$(PWD)/loop.nw|g' TAGS
|
||||
|
||||
loop.exe: scripts/build-exe.lisp loop.lisp loop.asd loop.nw
|
||||
sbcl --script scripts/build-exe.lisp
|
||||
|
|
@ -29,7 +34,7 @@ format-def: loop.nw
|
|||
make-release: loop.nw
|
||||
(no tangle -Rmake-release.sh < loop.nw > make-release.tmp || \
|
||||
(rm make-release.tmp && exit 1)) && \
|
||||
mv make-release.tmp make-release
|
||||
mv make-release.tmp make-release && chmod 0755 make-release
|
||||
|
||||
release: make-release
|
||||
./make-release $$(git log --oneline -1 | awk '{print $$1}') \
|
||||
|
|
|
|||
5
loop.asd
5
loop.asd
|
|
@ -1,7 +1,8 @@
|
|||
;;; -*- Mode: LISP; Syntax: COMMON-LISP; -*-
|
||||
(asdf:defsystem :LOOP
|
||||
:version "a89e088"
|
||||
:version "0.0.1"
|
||||
:description "An NNTP server for a circle of friends."
|
||||
:depends-on (:lisp-unit :str :uiop :cl-ppcre :local-time :clsql-sqlite3 :clingon
|
||||
:filesystem-utils :ironclad/digest/sha256)
|
||||
:components ((:file "loop")))
|
||||
:components ((:file "loop")
|
||||
(:file "version")))
|
||||
|
|
|
|||
463
loop.lisp
463
loop.lisp
|
|
@ -248,8 +248,8 @@
|
|||
|
||||
(defun prepend-response-with (message r)
|
||||
(make-response
|
||||
:code (response-code r)
|
||||
:data (data message (crlf) (response-data r))
|
||||
:code (response-code r)
|
||||
:data (list message (crlf) (response-data r))
|
||||
:multi-line (response-multi-line r)
|
||||
:request (response-request r)))
|
||||
|
||||
|
|
@ -264,21 +264,21 @@
|
|||
(t (error (format nil "append-crlf-if-needed: unsupported type: ~a" (type-of seq))))))
|
||||
|
||||
(defun send-response! (r)
|
||||
(let ((bs (data (integer->string (response-code r)) " "
|
||||
(append-crlf-if-needed (response-data r)))))
|
||||
(my-write bs *standard-output*)
|
||||
(stderr ">>> ~a" (bytes->string (ucs-2->ascii bs))))
|
||||
(when (response-multi-line r)
|
||||
(let ((bs (data "." (crlf))))
|
||||
(my-write bs *standard-output*)
|
||||
(stderr ">>> ~a" (bytes->string (ucs-2->ascii bs)))))
|
||||
;; Write the response code and a space
|
||||
(send-chunk! (integer->string (response-code r)) *standard-output*)
|
||||
(send-chunk! " " *standard-output*)
|
||||
;; Write the actual payload (status text + article body)
|
||||
(send-chunk! (response-data r) *standard-output*)
|
||||
;; Ensure a CRLF at the end of the payload
|
||||
(send-chunk! (crlf) *standard-output*)
|
||||
(when (response-multi-line r)
|
||||
(send-chunk! "." *standard-output*)
|
||||
(send-chunk! (crlf) *standard-output*))
|
||||
(force-output)
|
||||
r)
|
||||
|
||||
(defun my-write (ls-of-bytes s)
|
||||
(if (interactive-stream-p s)
|
||||
(write-sequence (mapcar #'code-char ls-of-bytes) s)
|
||||
(write-sequence ls-of-bytes s)))
|
||||
(write-sequence ls-of-bytes s))
|
||||
|
||||
(defun parse-request (r)
|
||||
(let* ((collapsed-s (str:collapse-whitespaces (request-said r)))
|
||||
|
|
@ -463,8 +463,19 @@
|
|||
(index-from-fs!))
|
||||
|
||||
(defun parse-article (v)
|
||||
(let ((parts (split-vector (vector 13 10 13 10) v nil :limit 2)))
|
||||
(make-article :headers (map 'string #'code-char (car parts)) :body (cadr parts))))
|
||||
(let ((pos (search #(13 10 13 10) v)))
|
||||
(if pos
|
||||
(make-article
|
||||
:headers (map 'string #'code-char (subseq v 0 pos))
|
||||
;; Create a zero-copy window into the original byte array.
|
||||
:body (make-array (- (length v) (+ pos 4))
|
||||
:element-type (array-element-type v)
|
||||
:displaced-to v
|
||||
:displaced-index-offset (+ pos 4)))
|
||||
;; What should we do if no body is found? Is that legal NNTP?
|
||||
(make-article
|
||||
:headers (map 'string #'code-char v)
|
||||
:body (make-array 0 :element-type (array-element-type v))))))
|
||||
|
||||
(defun hs-space-collapsed (hs)
|
||||
(cl-ppcre:regex-replace-all (format nil "~a[ ~a]+" (crlf-string) #\tab) hs " "))
|
||||
|
|
@ -490,9 +501,6 @@
|
|||
|
||||
(defun string-integer? (s) (ignore-errors (parse-integer s)))
|
||||
|
||||
(defun get-header-from-article (h a)
|
||||
(get-header h (parse-headers (article-headers (parse-article a)))))
|
||||
|
||||
(defun get-header (key hs)
|
||||
(let ((pair (assoc key hs :test #'string=)))
|
||||
(if pair (cdr pair) "")))
|
||||
|
|
@ -501,18 +509,50 @@
|
|||
(let* ((a-string (fetch-article g i))
|
||||
(a-parsed (parse-article a-string))
|
||||
(headers (parse-headers (article-headers a-parsed))))
|
||||
(enrich-headers headers a-string)))
|
||||
(enrich-headers headers a-string a-parsed)))
|
||||
|
||||
(defun enrich-headers (hs a)
|
||||
(defun enrich-headers (hs a-raw a-parsed)
|
||||
(append hs
|
||||
`(("line-count" . ,(format nil "~a" (nlines (article-body (parse-article a)))))
|
||||
("byte-count" . ,(format nil "~a" (length a))))))
|
||||
`(("line-count" . ,(format nil "~a" (nlines (article-body a-parsed))))
|
||||
("byte-count" . ,(format nil "~a" (length a-raw))))))
|
||||
|
||||
(defun nlines (v) (length (split-vector (crlf) v nil)))
|
||||
(defun nlines (v)
|
||||
(loop for b across v
|
||||
count (= b 10)))
|
||||
|
||||
(defun read-headers-raw (path)
|
||||
"Reads from PATH chunk by chunk until CRLF CRLF is found.
|
||||
Returns the byte vector of the headers. Closes the file immediately."
|
||||
(with-open-file (in path :element-type '(unsigned-byte 8) :if-does-not-exist nil)
|
||||
(when in
|
||||
(let ((acc (make-array 8192 :element-type '(unsigned-byte 8)
|
||||
:adjustable t :fill-pointer 0))
|
||||
(buffer (make-array 8192 :element-type '(unsigned-byte 8)))
|
||||
(delim #(13 10 13 10)))
|
||||
(loop
|
||||
(let ((n (read-sequence buffer in)))
|
||||
(when (zerop n)
|
||||
(return acc)) ; EOF reached without body delimiter
|
||||
|
||||
;; Append new bytes to our accumulator
|
||||
(let ((start-search (length acc)))
|
||||
(dotimes (i n)
|
||||
(vector-push-extend (aref buffer i) acc))
|
||||
|
||||
;; Search for the delimiter. We subtract 3 from our start
|
||||
;; index to catch delimiters that span across the chunk boundary.
|
||||
(let ((pos (search delim acc :start2 (max 0 (- start-search 3)))))
|
||||
(when pos
|
||||
;; Return exactly up to the end of the delimiter
|
||||
(return (subseq acc 0 (+ pos 4))))))))))))
|
||||
|
||||
(defstruct file-stream-payload
|
||||
(path "" :type string)
|
||||
(start-pos 0 :type integer))
|
||||
|
||||
(defun fetch-article (g i)
|
||||
(in-groups
|
||||
(read-file-raw (format nil "~a/~a" g i))))
|
||||
(read-headers-raw (format nil "~a/~a" g i))))
|
||||
|
||||
(defun read-file-raw (path)
|
||||
(with-open-file
|
||||
|
|
@ -525,11 +565,6 @@
|
|||
(read-sequence a in)
|
||||
a))))
|
||||
|
||||
(defun fetch-body (g i)
|
||||
(article-body (parse-article (fetch-article g i))))
|
||||
|
||||
(defun encode-body (a) a)
|
||||
|
||||
(defun extract-mid (a)
|
||||
(lookup "message-id" (parse-headers (article-headers (parse-article a)))))
|
||||
|
||||
|
|
@ -608,37 +643,43 @@
|
|||
(typical-cmd-head-body-article r #'article-response))
|
||||
|
||||
(defun article-response (r g i)
|
||||
(typical-cmd-response 220 r g i #'(lambda (a) (encode-body a))))
|
||||
(typical-cmd-response 220 r g i :article))
|
||||
|
||||
(defun head-response (r g i)
|
||||
(typical-cmd-response 221 r g i #'(lambda (a) (article-headers (parse-article a)))))
|
||||
(typical-cmd-response 221 r g i :head))
|
||||
|
||||
(defun body-response (r g i)
|
||||
(typical-cmd-response 222 r g i #'(lambda (a) (encode-body (article-body (parse-article a))))))
|
||||
(typical-cmd-response 222 r g i :body))
|
||||
|
||||
(defun typical-cmd-response (code r g i get-data)
|
||||
(handler-case
|
||||
(let ((a (fetch-article g i)))
|
||||
(cond ((null a)
|
||||
(make-response
|
||||
:code 400 :request r
|
||||
:data (format nil "article ~a/~a does not exist" g i)))
|
||||
(t
|
||||
(prepend-response-with
|
||||
(format nil "~a ~a" i (extract-mid a))
|
||||
(make-response :multi-line 'yes :code code
|
||||
:request r :data (funcall get-data a))))))
|
||||
(defun typical-cmd-response (code r g i requested-part)
|
||||
(handler-case
|
||||
(let* ((path (format nil "groups/~a/~a" g i))
|
||||
(headers-raw (read-headers-raw path)))
|
||||
(if (null headers-raw)
|
||||
(make-response :code 400 :request r :data (format nil "article ~a/~a does not exist" g i))
|
||||
(let* ((a (parse-article headers-raw))
|
||||
;; FIX: Pass the raw bytes to extract-mid, not the parsed struct
|
||||
(mid (extract-mid headers-raw))
|
||||
(header-length (length headers-raw))
|
||||
(payload
|
||||
(ecase requested-part
|
||||
(:head (article-headers a))
|
||||
(:body (make-file-stream-payload :path path :start-pos header-length))
|
||||
(:article (list (article-headers a)
|
||||
(crlf)
|
||||
(make-file-stream-payload :path path :start-pos header-length))))))
|
||||
|
||||
(prepend-response-with (format nil "~a ~a" i mid)
|
||||
(make-response :multi-line 'yes :code code :request r :data payload)))))
|
||||
|
||||
(sb-posix:syscall-error (c)
|
||||
(let ((msg (format nil "article ~a/~a: ~a" g i c)))
|
||||
(stderr msg)
|
||||
(make-response
|
||||
:code 400 :request r :data msg)))
|
||||
(make-response :code 400 :request r :data msg)))
|
||||
(error (c)
|
||||
(let ((msg (format nil "unexpected condition when reading article ~a/~a: ~a" g i c)))
|
||||
(stderr msg)
|
||||
(make-response
|
||||
:code 400 :request r
|
||||
:data msg)))))
|
||||
(make-response :code 400 :request r :data msg)))))
|
||||
|
||||
(defun cmd-next (r)
|
||||
(with-auth
|
||||
|
|
@ -926,13 +967,20 @@
|
|||
(coerce mid 'string)))
|
||||
|
||||
(defun unparse-article (parsed)
|
||||
(data
|
||||
(let ((ls))
|
||||
(dolist (h (parse-headers (article-headers parsed)))
|
||||
(setq ls (cons (data (str:capitalize (car h)) ": " (cdr h) (crlf)) ls)))
|
||||
(nreverse ls))
|
||||
(crlf)
|
||||
(article-body parsed)))
|
||||
;; 1. Build the new headers as a standard string
|
||||
(let* ((headers-str (with-output-to-string (s)
|
||||
(dolist (h (parse-headers (article-headers parsed)))
|
||||
(format s "~a: ~a~c~c" (str:capitalize (car h)) (cdr h) #\return #\linefeed))
|
||||
;; Add the final \r\n separating headers from the body
|
||||
(format s "~c~c" #\return #\linefeed)))
|
||||
;; 2. Convert the string to a small byte array (~1 KB)
|
||||
(headers-bytes (make-array (length headers-str)
|
||||
:element-type '(unsigned-byte 8)
|
||||
:initial-contents (map 'list #'char-code headers-str)))
|
||||
;; 3. Get the displaced, zero-copy body array
|
||||
(body-bytes (article-body parsed)))
|
||||
;; 4. Let SBCL compile this into a fast C-level memcpy
|
||||
(concatenate '(vector (unsigned-byte 8)) headers-bytes body-bytes)))
|
||||
|
||||
(defun ensure-header (h fn bs)
|
||||
(let* ((headers (parse-headers (article-headers (parse-article bs)))))
|
||||
|
|
@ -978,13 +1026,13 @@
|
|||
(make-response :code code :request r :data reply))))))))
|
||||
|
||||
(defun post (bs)
|
||||
(let ((ngs (newsgroups-header->list
|
||||
(get-header "newsgroups" (parse-headers
|
||||
(article-headers
|
||||
(parse-article bs)))))))
|
||||
(let* ((parsed (parse-article bs))
|
||||
(ngs (newsgroups-header->list
|
||||
(get-header "newsgroups" (parse-headers (article-headers parsed)))))
|
||||
(a (ensure-date (ensure-mid bs)))
|
||||
(mid (extract-mid a)))
|
||||
(dolist (ng ngs)
|
||||
(let ((a (ensure-date (ensure-mid bs))))
|
||||
(save-article-insist ng (get-next-article-id ng) a (extract-mid a))))
|
||||
(save-article-insist ng (get-next-article-id ng) a mid))
|
||||
(values 240 (data "Thank you! Your article has been saved."))))
|
||||
|
||||
(defun newsgroups-header->list (s)
|
||||
|
|
@ -1029,78 +1077,159 @@
|
|||
(declare (ignore low len))
|
||||
(1+ high)))
|
||||
|
||||
(defun nntp-read-article (&optional acc)
|
||||
;; Returns List-of Byte.
|
||||
(let* ((ls (nntp-read-line)))
|
||||
(stderr "I read: [~A]" ls)
|
||||
(cond ;; a single period "." on a line marks the end of the
|
||||
;; article. That is, 46 == (byte #\.)
|
||||
((and (= 1 (length ls))
|
||||
(= 46 (aref ls 0)))
|
||||
(flatten (add-crlf-between acc)))
|
||||
(t
|
||||
(nntp-read-article
|
||||
(append acc (list (coerce ls 'list))))))))
|
||||
(defun nntp-read-article ()
|
||||
;; Returns a vector of bytes: the article, CRLF between lines, up to
|
||||
;; (excluding) the "." terminator. VECTOR-PUSH-EXTEND grows the
|
||||
;; buffer in amortized O(1) per push, so total cost is O(size of the
|
||||
;; article) rather than O([size of the article]^2).
|
||||
(let ((buf (make-array 8192
|
||||
:element-type '(unsigned-byte 8)
|
||||
:adjustable t
|
||||
:fill-pointer 0)))
|
||||
(loop
|
||||
(let ((ls (nntp-read-line)))
|
||||
(stderr "nntp-read-line: [~A]" ls)
|
||||
(when (eq ls :eof) (error 'end-of-file :stream *standard-input*))
|
||||
(when (end-of-article? ls) (return buf))
|
||||
(map nil (lambda (b) (vector-push-extend b buf)) ls)
|
||||
(vector-push-extend 13 buf)
|
||||
(vector-push-extend 10 buf)))))
|
||||
|
||||
(defun end-of-article? (ls)
|
||||
;; A line is the NNTP terminator only if it's exactly the single byte
|
||||
;; 46 (#\.). LENGTH/ELT instead of EQUAL-against-a-list, since
|
||||
;; NNTP-READ-LINE hands back a vector.
|
||||
(and ls (not (eq ls :eof)) (= (length ls) 1) (= (elt ls 0) 46)))
|
||||
|
||||
(defun nntp-read-line-buffered (stream buffer)
|
||||
(if (interactive-stream-p stream)
|
||||
(get-interactive-line stream)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels
|
||||
((find-lf (start end)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels ((find-lf (start end)
|
||||
(position 10 data :start start :end end))
|
||||
(extract-line (lf-pos)
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
(end-line (if (and (> lf-pos start)
|
||||
(= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos) ; strip the preceding cr
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
;; If the previous character is CR (13), strip it
|
||||
(end-line (if (and (> lf-pos start) (= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos)
|
||||
lf-pos))
|
||||
(line (subseq data start end-line)))
|
||||
(line (subseq data start end-line)))
|
||||
(setf (nntp-stream-buffer-head buffer) (1+ lf-pos))
|
||||
line))
|
||||
(refill-and-extract-line ()
|
||||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(remaining (- tail head)))
|
||||
;; 1. Prevent buffer saturation/overflow
|
||||
(when (= remaining capacity)
|
||||
(stderr "line too long in the network buffer: [~A]"
|
||||
(subseq data head tail))
|
||||
(stderr "line too long in the network buffer: [~A]" (subseq data head tail))
|
||||
(error "no space left on the network buffer for the current line"))
|
||||
;; There is space for more lines, but there's a
|
||||
;; partial line waiting to be completed, so we'll
|
||||
;; shift it to the begining of the buffer, so we can
|
||||
;; have more space (as %unix-read might produce more
|
||||
;; data below).
|
||||
|
||||
;; 2. Shift remaining bytes to the start (fast memmove)
|
||||
(when (> remaining 0)
|
||||
(replace data data :start1 0 :start2 head :end2 tail))
|
||||
(setf (nntp-stream-buffer-head buffer) 0)
|
||||
(setf (nntp-stream-buffer-tail buffer) remaining)
|
||||
;; Do the actual reading: we'll block until bytes
|
||||
;; arrive and return whatever the kernel has for us.
|
||||
(let* ((n (%unix-read stream data remaining (- capacity remaining)))
|
||||
|
||||
;; 3. Fetch data via the unified dispatch mechanism
|
||||
(let* ((n (fetch-more-bytes stream data remaining (- capacity remaining)))
|
||||
(new-tail (+ remaining n)))
|
||||
(setf (nntp-stream-buffer-tail buffer) new-tail)
|
||||
(let ((lf-pos (find-lf remaining new-tail)))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))) ; still no lf; keep reading
|
||||
;; the main program
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer)
|
||||
(nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos) ; line already in buffer; no i/o at all
|
||||
(refill-and-extract-line)))))))
|
||||
(refill-and-extract-line)))))))
|
||||
|
||||
;; --- Main flow ---
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer) (nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))
|
||||
|
||||
(defun nntp-read-line-buffered-old (stream buffer)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels
|
||||
((find-lf (start end)
|
||||
;; remember that LF = byte 10
|
||||
(position 10 data :start start :end end))
|
||||
(extract-line (lf-pos)
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
(end-line (if (and (> lf-pos start)
|
||||
(= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos) ; strip the preceding cr
|
||||
lf-pos))
|
||||
(line (subseq data start end-line)))
|
||||
(setf (nntp-stream-buffer-head buffer) (1+ lf-pos))
|
||||
line))
|
||||
(refill-and-extract-line ()
|
||||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(remaining (- tail head)))
|
||||
(when (= remaining capacity)
|
||||
(stderr "line too long in the network buffer: [~A]"
|
||||
(subseq data head tail))
|
||||
(error "no space left on the network buffer for the current line"))
|
||||
;; There is space for more lines, but there's a
|
||||
;; partial line waiting to be completed, so we'll
|
||||
;; shift it to the begining of the buffer, so we can
|
||||
;; have more space (as %unix-read might produce more
|
||||
;; data below).
|
||||
(when (> remaining 0)
|
||||
(replace data data :start1 0 :start2 head :end2 tail))
|
||||
(setf (nntp-stream-buffer-head buffer) 0)
|
||||
(setf (nntp-stream-buffer-tail buffer) remaining)
|
||||
;; Do the actual reading: we'll block until bytes
|
||||
;; arrive and return whatever the kernel has for us.
|
||||
(let* ((n (%unix-read stream data remaining (- capacity remaining)))
|
||||
(new-tail (+ remaining n)))
|
||||
(setf (nntp-stream-buffer-tail buffer) new-tail)
|
||||
(let ((lf-pos (find-lf remaining new-tail)))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))) ; Still no lf; keep reading.
|
||||
;; The main program
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer)
|
||||
(nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos) ; line already in buffer; no i/o at all
|
||||
(refill-and-extract-line))))))
|
||||
|
||||
(defun fetch-more-bytes (stream buf start max-count)
|
||||
;; Attempt the fast, native C-level POSIX read. If `fd-stream-of`
|
||||
;; fails to find an underlying file descriptor (e.g., SLIME REPL
|
||||
;; streams), it signals a TYPE-ERROR. We catch it and fallback.
|
||||
|
||||
;; This is kinda silly. Let's remove %portable-read because we
|
||||
;; really don't care about the SMILE REPL, though we use it.
|
||||
(handler-case (%unix-read stream buf start max-count)
|
||||
(type-error ()
|
||||
(%portable-read stream buf start max-count))))
|
||||
|
||||
(defun %portable-read (stream buf start max-count)
|
||||
;; Blocks for the VERY FIRST byte to respect the wait-for-data contract,
|
||||
;; preventing busy-waiting loops.
|
||||
(let ((first-byte (read-byte stream nil :eof)))
|
||||
(when (eq first-byte :eof)
|
||||
(error 'end-of-file :stream stream))
|
||||
(setf (aref buf start) first-byte)
|
||||
|
||||
;; Slurp any IMMEDIATELY available bytes to minimize looping overhead,
|
||||
;; up to max-count. 'listen' returns T if a byte is ready on the wire.
|
||||
(let ((count 1))
|
||||
(loop while (and (< count max-count) (listen stream))
|
||||
do (let ((b (read-byte stream nil :eof)))
|
||||
(when (eq b :eof) (loop-finish))
|
||||
(setf (aref buf (+ start count)) b)
|
||||
(incf count)))
|
||||
count)))
|
||||
|
||||
(defun %unix-read (stream buf start max-count)
|
||||
;;; ──────────────────────────────────────
|
||||
;;; One POSIX read(2) on STREAM's underlying fd.
|
||||
;;;
|
||||
;;; Blocks until at least one byte arrives, then returns immediately with
|
||||
;;; however many bytes the kernel has buffered (up to MAX-COUNT).
|
||||
;;; Retries transparently on EINTR (signals can interrupt system calls).
|
||||
;;; Signals END-OF-FILE when the peer closes the connection.
|
||||
;;; ──────────────────────────────────────
|
||||
(let ((fd (sb-sys:fd-stream-fd (fd-stream-of stream))))
|
||||
(sb-sys:with-pinned-objects (buf)
|
||||
(loop
|
||||
|
|
@ -1109,10 +1238,10 @@
|
|||
(sb-sys:sap+ (sb-sys:vector-sap buf) start)
|
||||
max-count)
|
||||
(cond
|
||||
((and (null n) (= err sb-unix:eintr))) ; signal interrupted syscall – retry
|
||||
((and (null n) (= err sb-unix:eintr)))
|
||||
((or (null n) (zerop n))
|
||||
(error 'end-of-file :stream stream)) ; error or clean EOF (client closed)
|
||||
(t (return n)))))))) ; got at least one byte
|
||||
(error 'end-of-file :stream stream))
|
||||
(t (return n))))))))
|
||||
|
||||
(defun fd-stream-of (stream)
|
||||
"Peel away SYNONYM-STREAM and TWO-WAY-STREAM wrappers until we reach
|
||||
|
|
@ -1122,60 +1251,6 @@
|
|||
(synonym-stream (fd-stream-of (symbol-value (synonym-stream-symbol stream))))
|
||||
(two-way-stream (fd-stream-of (two-way-stream-input-stream stream)))))
|
||||
|
||||
(defun nntp-read-line-buffered-old (stream buffer)
|
||||
(if (interactive-stream-p stream)
|
||||
(get-interactive-line stream)
|
||||
(let ((data (nntp-stream-buffer-data buffer)))
|
||||
(labels ((find-lf (start end)
|
||||
(position 10 data :start start :end end))
|
||||
(extract-line (lf-pos)
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
;; if the previous byte is CR, strip it out
|
||||
(end-line (if (and (> lf-pos start) (= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos)
|
||||
lf-pos))
|
||||
(line (subseq data start end-line)))
|
||||
;; move the pointer past the LF and return the line
|
||||
(setf (nntp-stream-buffer-head buffer) (1+ lf-pos))
|
||||
line))
|
||||
(refill-and-extract-line ()
|
||||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(remaining (- tail head)))
|
||||
;; first of all, if there's no more space, then
|
||||
;; simply give up the entire operation. It's up
|
||||
;; to the caller to decide what to do.
|
||||
(when (= remaining (length data))
|
||||
(stderr "line too long in the network buffer: [~A]"
|
||||
(subseq data head remaining))
|
||||
(error "no space left on the network buffer for the current line"))
|
||||
;; 1. shift remaining bytes to the beginning of the array
|
||||
(when (> remaining 0)
|
||||
(replace data data :start1 0 :start2 head :end2 tail))
|
||||
(setf (nntp-stream-buffer-head buffer) 0)
|
||||
(setf (nntp-stream-buffer-tail buffer) remaining)
|
||||
;; 2. invoke read-sequence to fill the rest of the buffer
|
||||
(let* ((new-tail-pos (read-sequence data stream :start remaining)))
|
||||
(if (= new-tail-pos remaining)
|
||||
;; no new bytes arrived, so I presume eof has arrived
|
||||
(if (> remaining 0)
|
||||
(let ((line (subseq data 0 remaining)))
|
||||
(setf (nntp-stream-buffer-tail buffer) 0)
|
||||
line)
|
||||
(error 'end-of-file :stream stream))
|
||||
(progn ;; new bytes arrived
|
||||
(setf (nntp-stream-buffer-tail buffer) new-tail-pos)
|
||||
|
||||
(let ((lf-pos (find-lf remaining new-tail-pos)))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))))) ; very long line
|
||||
;; --- main program ---
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer) (nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos) ; quickly extract a line
|
||||
(refill-and-extract-line))))))) ; get more data from the network
|
||||
|
||||
(defun get-interactive-line (stream)
|
||||
(let ((line-bytes
|
||||
(make-array 0
|
||||
|
|
@ -1191,13 +1266,6 @@
|
|||
nil
|
||||
line-bytes)))))
|
||||
|
||||
(defun nntp-read-line-slow (&optional (s *standard-input*) acc) ;; Returns List-of Byte.
|
||||
(let ((x (read-byte s)))
|
||||
(cond ((or (null x) (= x 10))
|
||||
(let ((bs (and acc (nreverse (if (= (car acc) 13) (cdr acc) acc)))))
|
||||
bs))
|
||||
(t (nntp-read-line-slow s (cons x acc))))))
|
||||
|
||||
(defun list->bytes (ls)
|
||||
(mapcar #'data->bytes ls))
|
||||
|
||||
|
|
@ -1212,6 +1280,37 @@
|
|||
((vectorp d) (vector->bytes d))
|
||||
(t (error (format nil "type ~a is not supported" (type-of d))))))
|
||||
|
||||
(defun send-chunk! (chunk s)
|
||||
"Writes chunks sequentially to the stream."
|
||||
(cond ((stringp chunk)
|
||||
(my-write (string->bytes chunk) s))
|
||||
((vectorp chunk)
|
||||
(my-write chunk s))
|
||||
((listp chunk)
|
||||
(dolist (c chunk)
|
||||
(send-chunk! c s)))
|
||||
((integerp chunk)
|
||||
(write-byte chunk s))
|
||||
((file-stream-payload-p chunk)
|
||||
(stream-file-to-socket chunk s))))
|
||||
|
||||
(defun stream-file-to-socket (payload out-stream)
|
||||
"Opens the file, seeks to the start of the body, and streams to the socket
|
||||
while applying on-the-fly NNTP dot-stuffing."
|
||||
(with-open-file (in (file-stream-payload-path payload)
|
||||
:element-type '(unsigned-byte 8))
|
||||
(file-position in (file-stream-payload-start-pos payload))
|
||||
(let ((buf (make-array 8192 :element-type '(unsigned-byte 8)))
|
||||
(at-line-start t)) ; The body implicitly starts at the beginning of a line
|
||||
(loop for n = (read-sequence buf in)
|
||||
while (plusp n)
|
||||
do (loop for i from 0 below n
|
||||
for byte = (aref buf i)
|
||||
do (when (and at-line-start (= byte 46)) ; 46 is '.'
|
||||
(write-byte 46 out-stream))
|
||||
(write-byte byte out-stream)
|
||||
(setf at-line-start (= byte 10))))))) ; 10 is LF (\n)
|
||||
|
||||
(defun add-crlf-between (ls-of-ls)
|
||||
;; Add \r\n to each ``line''. Returns List-of Byte.
|
||||
(mapcar #'(lambda (ls) (append ls (list 13 10))) ls-of-ls))
|
||||
|
|
@ -1603,7 +1702,7 @@
|
|||
(clingon:make-command
|
||||
:name "loop"
|
||||
:description "An NNTP server for a circle of friends."
|
||||
:version "a89e088"
|
||||
:version "0.0.1"
|
||||
:license "GPL v3"
|
||||
:options (cli/options)
|
||||
:handler #'cli/main-with-handlers))
|
||||
|
|
@ -1618,7 +1717,7 @@
|
|||
(return)))))
|
||||
(end-of-file (c)
|
||||
(declare (ignore c))
|
||||
(stderr "EOF~%"))
|
||||
(stderr "a client disconnected with EOF~%"))
|
||||
(error (e)
|
||||
(stderr "fatal: ~A" e))))
|
||||
|
||||
|
|
@ -1639,7 +1738,10 @@
|
|||
(send-response!
|
||||
(make-response
|
||||
:code 200
|
||||
:data "Welcome! I am LOOP a89e088. Say ``help'' for a menu.")))
|
||||
:data (welcome-message))))
|
||||
|
||||
(defun welcome-message ()
|
||||
(cl-ppcre:regex-replace-all "HASH" "Welcome! I am LOOP 0.0.1 hash HASH. Say ``help'' for a menu." *git-hash*))
|
||||
(setq lisp-unit:*print-failures* t)
|
||||
(define-test dispatching
|
||||
(assert-true (equalp (empty-response) (dispatch (make-request)))))
|
||||
|
|
@ -1663,18 +1765,3 @@ Newsgroups: local.test, bad.newsgroup
|
|||
|
||||
A bad test from the biggest mouth of the south.
|
||||
"))
|
||||
|
||||
(define-test post-wrong-newsgroup
|
||||
(multiple-value-bind (code msg) (post (string->bytes a-bad-post))
|
||||
(declare (ignore msg))
|
||||
(assert-true (equal code 400))))
|
||||
|
||||
(define-test post-okay
|
||||
(read-accounts!)
|
||||
(connect-index! "test.db")
|
||||
(create-index!)
|
||||
(setq *client* (make-client :username "ROOT" :auth? 'yes))
|
||||
(multiple-value-bind (code msg) (post (string->bytes a-post))
|
||||
(declare (ignore msg))
|
||||
(assert-true (equal code 240)))
|
||||
(clsql:disconnect))
|
||||
|
|
|
|||
160
loop.nw
160
loop.nw
|
|
@ -405,7 +405,7 @@ there remains a \verb|\n| or \verb|\r\n| in the [[*standard-input*]],
|
|||
which we need to wipe out, or [[nntp-read-line]] will consume an empty
|
||||
line, which is what causes that 400 response.
|
||||
|
||||
<<Loop's REPL.lisp>>=
|
||||
<<Loop REPL.lisp>>=
|
||||
(defun repl (r)
|
||||
(in-package :loop)
|
||||
(loop
|
||||
|
|
@ -594,9 +594,7 @@ else should we handle this?
|
|||
|
||||
<<Procedures for requests and responses.lisp>>=
|
||||
(defun my-write (ls-of-bytes s)
|
||||
(if (interactive-stream-p s)
|
||||
(write-sequence (mapcar #'code-char ls-of-bytes) s)
|
||||
(write-sequence ls-of-bytes s)))
|
||||
(write-sequence ls-of-bytes s))
|
||||
@ %def my-write
|
||||
|
||||
\section{Main loop}
|
||||
|
|
@ -646,7 +644,10 @@ itself---so we can cascade actions based on a user's request.
|
|||
(send-response!
|
||||
(make-response
|
||||
:code 200
|
||||
:data "<<Welcome message>>")))
|
||||
:data (welcome-message))))
|
||||
|
||||
(defun welcome-message ()
|
||||
(cl-ppcre:regex-replace-all "HASH" "<<Welcome message>>" *git-hash*))
|
||||
@ %def main
|
||||
|
||||
\noindent It's always useful to know which version exactly we're
|
||||
|
|
@ -660,7 +661,7 @@ dealing with:
|
|||
So we put a release tag on \lp's banner.
|
||||
|
||||
<<Welcome message>>=
|
||||
Welcome! I am <<Name>> <<Version>>. Say ``help'' for a menu.
|
||||
Welcome! I am <<Name>> <<Version>> hash HASH. Say ``help'' for a menu.
|
||||
@
|
||||
|
||||
\noindent We take the opportunity and describe \lp's package,
|
||||
|
|
@ -675,7 +676,7 @@ An NNTP server for a circle of friends.
|
|||
@
|
||||
|
||||
<<Version>>=
|
||||
a89e088
|
||||
0.0.1
|
||||
@
|
||||
|
||||
\section{Parsing of requests}
|
||||
|
|
@ -1902,6 +1903,15 @@ is cross-posting. First, notice that we're---so far---duplicating
|
|||
articles on the file system. (We will undo that once we reimplement
|
||||
our index. More to follow.)
|
||||
|
||||
{\bf Note to self}. As professor G.~has written, we'll need to
|
||||
redesign [[post]] to take two arguments, the article in [[tmp-path]] (its
|
||||
first argument) and then [[headers-raw]] (its second argument). We
|
||||
then parse the headers with [[parse-headers]] so that we can grab the
|
||||
newsgroups to which the article is to be posted so that we handle the
|
||||
job to [[save-article-insist]]. Of course, we'll need to get a
|
||||
message-id as well, but let's worry about that later. (Yeah, we need
|
||||
to optimize [[get-next-article-id]].)
|
||||
|
||||
<<Command post.lisp>>=
|
||||
(defun post (bs)
|
||||
(let* ((parsed (parse-article bs))
|
||||
|
|
@ -2149,18 +2159,15 @@ productivity and education.
|
|||
|
||||
<<Command post.lisp>>=
|
||||
(defun nntp-read-line-buffered (stream buffer)
|
||||
(if (interactive-stream-p stream)
|
||||
(get-interactive-line stream)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels
|
||||
((find-lf (start end)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels ((find-lf (start end)
|
||||
(position 10 data :start start :end end))
|
||||
(extract-line (lf-pos)
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
(end-line (if (and (> lf-pos start)
|
||||
(= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos) ; strip the preceding cr
|
||||
;; If the previous character is CR (13), strip it
|
||||
(end-line (if (and (> lf-pos start) (= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos)
|
||||
lf-pos))
|
||||
(line (subseq data start end-line)))
|
||||
(setf (nntp-stream-buffer-head buffer) (1+ lf-pos))
|
||||
|
|
@ -2169,34 +2176,109 @@ productivity and education.
|
|||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(remaining (- tail head)))
|
||||
;; 1. Prevent buffer saturation/overflow
|
||||
(when (= remaining capacity)
|
||||
(stderr "line too long in the network buffer: [~A]"
|
||||
(subseq data head tail))
|
||||
(stderr "line too long in the network buffer: [~A]" (subseq data head tail))
|
||||
(error "no space left on the network buffer for the current line"))
|
||||
;; There is space for more lines, but there's a
|
||||
;; partial line waiting to be completed, so we'll
|
||||
;; shift it to the begining of the buffer, so we can
|
||||
;; have more space (as %unix-read might produce more
|
||||
;; data below).
|
||||
|
||||
;; 2. Shift remaining bytes to the start (fast memmove)
|
||||
(when (> remaining 0)
|
||||
(replace data data :start1 0 :start2 head :end2 tail))
|
||||
(setf (nntp-stream-buffer-head buffer) 0)
|
||||
(setf (nntp-stream-buffer-tail buffer) remaining)
|
||||
;; Do the actual reading: we'll block until bytes
|
||||
;; arrive and return whatever the kernel has for us.
|
||||
(let* ((n (%unix-read stream data remaining (- capacity remaining)))
|
||||
|
||||
;; 3. Fetch data via the unified dispatch mechanism
|
||||
(let* ((n (fetch-more-bytes stream data remaining (- capacity remaining)))
|
||||
(new-tail (+ remaining n)))
|
||||
(setf (nntp-stream-buffer-tail buffer) new-tail)
|
||||
(let ((lf-pos (find-lf remaining new-tail)))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))) ; Still no lf; keep reading.
|
||||
;; The main program
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer)
|
||||
(nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos) ; line already in buffer; no i/o at all
|
||||
(refill-and-extract-line)))))))
|
||||
(refill-and-extract-line)))))))
|
||||
|
||||
;; --- Main flow ---
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer) (nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))
|
||||
|
||||
(defun nntp-read-line-buffered-old (stream buffer)
|
||||
(let* ((data (nntp-stream-buffer-data buffer))
|
||||
(capacity (length data)))
|
||||
(labels
|
||||
((find-lf (start end)
|
||||
;; remember that LF = byte 10
|
||||
(position 10 data :start start :end end))
|
||||
(extract-line (lf-pos)
|
||||
(let* ((start (nntp-stream-buffer-head buffer))
|
||||
(end-line (if (and (> lf-pos start)
|
||||
(= (aref data (1- lf-pos)) 13))
|
||||
(1- lf-pos) ; strip the preceding cr
|
||||
lf-pos))
|
||||
(line (subseq data start end-line)))
|
||||
(setf (nntp-stream-buffer-head buffer) (1+ lf-pos))
|
||||
line))
|
||||
(refill-and-extract-line ()
|
||||
(let* ((head (nntp-stream-buffer-head buffer))
|
||||
(tail (nntp-stream-buffer-tail buffer))
|
||||
(remaining (- tail head)))
|
||||
(when (= remaining capacity)
|
||||
(stderr "line too long in the network buffer: [~A]"
|
||||
(subseq data head tail))
|
||||
(error "no space left on the network buffer for the current line"))
|
||||
;; There is space for more lines, but there's a
|
||||
;; partial line waiting to be completed, so we'll
|
||||
;; shift it to the begining of the buffer, so we can
|
||||
;; have more space (as %unix-read might produce more
|
||||
;; data below).
|
||||
(when (> remaining 0)
|
||||
(replace data data :start1 0 :start2 head :end2 tail))
|
||||
(setf (nntp-stream-buffer-head buffer) 0)
|
||||
(setf (nntp-stream-buffer-tail buffer) remaining)
|
||||
;; Do the actual reading: we'll block until bytes
|
||||
;; arrive and return whatever the kernel has for us.
|
||||
(let* ((n (%unix-read stream data remaining (- capacity remaining)))
|
||||
(new-tail (+ remaining n)))
|
||||
(setf (nntp-stream-buffer-tail buffer) new-tail)
|
||||
(let ((lf-pos (find-lf remaining new-tail)))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos)
|
||||
(refill-and-extract-line))))))) ; Still no lf; keep reading.
|
||||
;; The main program
|
||||
(let ((lf-pos (find-lf (nntp-stream-buffer-head buffer)
|
||||
(nntp-stream-buffer-tail buffer))))
|
||||
(if lf-pos
|
||||
(extract-line lf-pos) ; line already in buffer; no i/o at all
|
||||
(refill-and-extract-line))))))
|
||||
|
||||
(defun fetch-more-bytes (stream buf start max-count)
|
||||
;; Attempt the fast, native C-level POSIX read. If `fd-stream-of`
|
||||
;; fails to find an underlying file descriptor (e.g., SLIME REPL
|
||||
;; streams), it signals a TYPE-ERROR. We catch it and fallback.
|
||||
|
||||
;; This is kinda silly. Let's remove %portable-read because we
|
||||
;; really don't care about the SMILE REPL, though we use it.
|
||||
(handler-case (%unix-read stream buf start max-count)
|
||||
(type-error ()
|
||||
(%portable-read stream buf start max-count))))
|
||||
|
||||
(defun %portable-read (stream buf start max-count)
|
||||
;; Blocks for the VERY FIRST byte to respect the wait-for-data contract,
|
||||
;; preventing busy-waiting loops.
|
||||
(let ((first-byte (read-byte stream nil :eof)))
|
||||
(when (eq first-byte :eof)
|
||||
(error 'end-of-file :stream stream))
|
||||
(setf (aref buf start) first-byte)
|
||||
|
||||
;; Slurp any IMMEDIATELY available bytes to minimize looping overhead,
|
||||
;; up to max-count. 'listen' returns T if a byte is ready on the wire.
|
||||
(let ((count 1))
|
||||
(loop while (and (< count max-count) (listen stream))
|
||||
do (let ((b (read-byte stream nil :eof)))
|
||||
(when (eq b :eof) (loop-finish))
|
||||
(setf (aref buf (+ start count)) b)
|
||||
(incf count)))
|
||||
count)))
|
||||
|
||||
(defun %unix-read (stream buf start max-count)
|
||||
;;; One POSIX read(2) on STREAM's underlying fd.
|
||||
|
|
@ -2230,6 +2312,11 @@ productivity and education.
|
|||
Phew! That was a lot! Take heart, though: you've just gone through the
|
||||
most complicated task of \Lp. All the rest is a total breeze.
|
||||
|
||||
{\bf Note to self}. Let's not allow a large line. If a line is too
|
||||
large, we should just truncate it. Also, let's try to avoid this
|
||||
branching out. We can probably find a solution that's general enough
|
||||
for both terminal and socket.
|
||||
|
||||
<<Command post.lisp>>=
|
||||
(defun get-interactive-line (stream)
|
||||
(let ((line-bytes
|
||||
|
|
@ -3183,7 +3270,7 @@ something to think about.
|
|||
<<Table of commands.lisp>>
|
||||
<<Macros.lisp>>
|
||||
<<Little procedures.lisp>>
|
||||
<<Loop's REPL.lisp>>
|
||||
<<Loop REPL.lisp>>
|
||||
<<Procedures for requests and responses.lisp>>
|
||||
<<Essential operations relative to the index.lisp>>
|
||||
<<How to create and connect to the index.lisp>>
|
||||
|
|
@ -3245,7 +3332,8 @@ The \lp\ system definition:
|
|||
:version "<<Version>>"
|
||||
:description "<<Description>>"
|
||||
:depends-on (<<List of packages to be loaded.lisp>>)
|
||||
:components ((:file "loop")))
|
||||
:components ((:file "loop")
|
||||
(:file "version")))
|
||||
@
|
||||
|
||||
\section{Other source code}
|
||||
|
|
@ -3267,7 +3355,7 @@ finds our definitions of interest and inserts a new blank line before
|
|||
the definition. Such action makes function definitions separated by
|
||||
two blank lines in some cases. We then remove the excess with the
|
||||
second program. Notice we need the {\tt -E} option because we're
|
||||
using the {\tt |} metacharacter.
|
||||
using the {\tt \textbar} metacharacter.
|
||||
|
||||
The second program find a blank line as its first step. Then we say
|
||||
{\tt N} to expand the pattern space to include the next line. Then we
|
||||
|
|
|
|||
0
make-release
Normal file → Executable file
0
make-release
Normal file → Executable file
989
noweb.sty
989
noweb.sty
|
|
@ -1,989 +0,0 @@
|
|||
% noweb.sty -- LaTeX support for noweb
|
||||
% DON'T read or edit this file! Use ...noweb-source/tex/support.nw instead.
|
||||
{\obeyspaces\AtBeginDocument{\global\let =\ }} % from texbook, p 381
|
||||
\def\nwopt@nomargintag{\let\nwmargintag=\@gobble}
|
||||
\def\nwopt@margintag{%
|
||||
\def\nwmargintag##1{\leavevmode\llap{##1\kern\nwmarginglue\kern\codemargin}}}
|
||||
\def\nwopt@margintag{%
|
||||
\def\nwmargintag##1{\leavevmode\kern-\codemargin\nwthemargintag{##1}\kern\codemargin}}
|
||||
\def\nwthemargintag#1{\llap{#1\kern\nwmarginglue}}
|
||||
\nwopt@margintag
|
||||
\newdimen\nwmarginglue
|
||||
\nwmarginglue=0.3in
|
||||
\def\nwtagstyle{\footnotesize\Rm}
|
||||
% make \hsize in code sufficient for 88 columns
|
||||
\setbox0=\hbox{\tt m}
|
||||
\newdimen\codehsize
|
||||
\codehsize=91\wd0 % 88 columns wasn't enough; I don't know why
|
||||
\newdimen\codemargin
|
||||
\codemargin=0pt
|
||||
\newdimen\nwdefspace
|
||||
\nwdefspace=\codehsize
|
||||
% need to use \textwidth in {\LaTeX} to handle styles with
|
||||
% non-standard margins (David Bruce). Don't know why we sometimes
|
||||
% wanted \hsize. 27 August 1997.
|
||||
%% \advance\nwdefspace by -\hsize\relax
|
||||
\ifx\textwidth\undefined
|
||||
\advance\nwdefspace by -\hsize\relax
|
||||
\else
|
||||
\advance\nwdefspace by -\textwidth\relax
|
||||
\fi
|
||||
\chardef\other=12
|
||||
\def\setupcode{%
|
||||
\chardef\\=`\\
|
||||
\chardef\{=`\{
|
||||
\chardef\}=`\}
|
||||
\catcode`\$=\other
|
||||
\catcode`\&=\other
|
||||
\catcode`\#=\other
|
||||
\catcode`\%=\other
|
||||
\catcode`\~=\other
|
||||
\catcode`\_=\other
|
||||
\catcode`\^=\other
|
||||
\catcode`\"=\other % fixes problem with german.sty
|
||||
\obeyspaces\Tt
|
||||
}
|
||||
%\let\nwlbrace=\{
|
||||
%\let\nwrbrace=\}
|
||||
\def\nwendquote{\relax\ifhmode\spacefactor=1000 \fi}
|
||||
{\catcode`\^^M=\active % make CR an active character
|
||||
\gdef\newlines{\catcode`\^^M=\active % make CR an active character
|
||||
\def^^M{\par\startline}}%
|
||||
\gdef\eatline#1^^M{\relax}%
|
||||
}
|
||||
%%% DON'T \gdef^^M{\par\startline}}% in case ^^M appears in a \write
|
||||
\def\startline{\noindent\hskip\parindent\ignorespaces}
|
||||
\def\nwnewline{\ifvmode\else\hfil\break\leavevmode\hbox{}\fi}
|
||||
\def\setupmodname{%
|
||||
\catcode`\$=3
|
||||
\catcode`\&=4
|
||||
\catcode`\#=6
|
||||
\catcode`\%=14
|
||||
\catcode`\~=13
|
||||
\catcode`\_=8
|
||||
\catcode`\^=7
|
||||
\catcode`\ =10
|
||||
\catcode`\^^M=5
|
||||
\let\nwlbrace\lbrace
|
||||
\let\nwrbrace\rbrace
|
||||
\let\{\nwlbrace
|
||||
\let\}\nwrbrace
|
||||
% bad news --- don't know what catcode to give "
|
||||
\Rm}
|
||||
\def\LA{\begingroup\maybehbox\bgroup\setupmodname\It$\langle$}
|
||||
\def\RA{\/$\rangle$\egroup\endgroup}
|
||||
\def\code{\leavevmode\begingroup\setupcode\newlines}
|
||||
\def\edoc{\endgroup}
|
||||
\let\maybehbox\relax
|
||||
\newbox\equivbox
|
||||
\setbox\equivbox=\hbox{$\equiv$}
|
||||
\newbox\plusequivbox
|
||||
\setbox\plusequivbox=\hbox{$\mathord{+}\mathord{\equiv}$}
|
||||
% \moddef can't have an argument because there might be \code...\edoc
|
||||
\def\moddef{\leavevmode\kern-\codemargin\LA}
|
||||
\def\endmoddef{\RA\ifmmode\equiv\else\unhcopy\equivbox\fi
|
||||
\nobreak\hfill\nobreak}
|
||||
\def\plusendmoddef{\RA\ifmmode\mathord{+}\mathord{\equiv}\else\unhcopy\plusequivbox\fi
|
||||
\nobreak\hfill\nobreak}
|
||||
\def\chunklist{%
|
||||
\errhelp{I changed \chunklist to \nowebchunks.
|
||||
I'll try to avoid such incompatible changes in the future.}%
|
||||
\errmessage{Use \string\nowebchunks\space instead of \string\chunklist}}
|
||||
\def\nowebchunks{\message{<Warning: You need noweave -x to use \string\nowebchunks>}}
|
||||
\def\nowebindex{\message{<Warning: You need noweave -index to use \string\nowebindex>}}
|
||||
% here is support for the new-style (capitalized) font-changing commands
|
||||
% thanks to Dave Love
|
||||
\ifx\documentstyle\undefined
|
||||
\let\Rm=\rm \let\It=\it \let\Tt=\tt % plain
|
||||
\else\ifx\selectfont\undefined
|
||||
\let\Rm=\rm \let\It=\it \let\Tt=\tt % LaTeX OFSS
|
||||
\else % LaTeX NFSS
|
||||
\def\Rm{\reset@font\rm}
|
||||
\def\It{\reset@font\it}
|
||||
\def\Tt{\reset@font\tt}
|
||||
\def\Bf{\reset@font\bf}
|
||||
\fi\fi
|
||||
\ifx\reset@font\undefined \let\reset@font=\relax \fi
|
||||
\def\noweboptions#1{%
|
||||
\def\@nwoptionlist{#1}%
|
||||
\@for\@nwoption:=\@nwoptionlist\do{%
|
||||
\@ifundefined{nwopt@\@nwoption}{%
|
||||
\@latexerr{There is no such noweb option as '\@nwoption'}\@eha}{%
|
||||
\csname nwopt@\@nwoption\endcsname}}}
|
||||
\codemargin=10pt
|
||||
\advance\codehsize by \codemargin % make room for indentation of code
|
||||
\advance\nwdefspace by \codemargin % and fix adjustment for def/use
|
||||
\def\setcodemargin#1{%
|
||||
\advance\codehsize by -\codemargin % make room for indentation of code
|
||||
\advance\nwdefspace by -\codemargin % and fix adjustment for def/use
|
||||
\codemargin=#1
|
||||
\advance\codehsize by \codemargin % make room for indentation of code
|
||||
\advance\nwdefspace by \codemargin % and fix adjustment for
|
||||
% def/use
|
||||
}
|
||||
\def\nwopt@shift{%
|
||||
\dimen@=-0.8in
|
||||
\if@twoside % Values for two-sided printing:
|
||||
\advance\evensidemargin by \dimen@
|
||||
\else % Values for one-sided printing:
|
||||
\advance\evensidemargin by \dimen@
|
||||
\advance\oddsidemargin by \dimen@
|
||||
\fi
|
||||
% \advance \marginparwidth -\dimen@
|
||||
}
|
||||
\let\nwopt@noshift\@empty
|
||||
\def\nwbegincode#1{%
|
||||
\begingroup
|
||||
\topsep \nwcodetopsep
|
||||
\@beginparpenalty \@highpenalty
|
||||
\@endparpenalty -\@highpenalty
|
||||
\@begincode }
|
||||
\def\nwendcode{\endtrivlist \endgroup \filbreak} % keeps code on 1 page
|
||||
|
||||
\newenvironment{webcode}{%
|
||||
\@begincode
|
||||
}{%
|
||||
\endtrivlist}
|
||||
\def\@begincode{%
|
||||
\trivlist \item[]%
|
||||
\leftskip\@totalleftmargin \advance\leftskip\codemargin
|
||||
\rightskip\hsize \advance\rightskip -\codehsize
|
||||
\parskip\z@ \parindent\z@ \parfillskip\@flushglue
|
||||
\linewidth\codehsize
|
||||
\@@par
|
||||
\def\par{\leavevmode\null \@@par \penalty\nwcodepenalty}%
|
||||
\obeylines
|
||||
\@noligs \ifx\verbatim@nolig@list\undefined\else
|
||||
\let\do=\nw@makeother \verbatim@nolig@list \do@noligs\`
|
||||
\fi
|
||||
\setupcode \frenchspacing \@vobeyspaces
|
||||
\nowebsize \setupcode
|
||||
\let\maybehbox\mbox }
|
||||
\newskip\nwcodetopsep \nwcodetopsep = 3pt plus 1.2pt minus 1pt
|
||||
\let\nowebsize=\normalsize
|
||||
\def\nwopt@tinycode{\let\nowebsize=\tiny}
|
||||
\def\nwopt@footnotesizecode{\let\nowebsize=\footnotesize}
|
||||
\def\nwopt@scriptsizecode{\let\nowebsize=\scriptsize}
|
||||
\def\nwopt@smallcode{\let\nowebsize=\small}
|
||||
\def\nwopt@normalsizecode{\let\nowebsize=\normalsize}
|
||||
\def\nwopt@largecode{\let\nowebsize=\large}
|
||||
\def\nwopt@Largecode{\let\nowebsize=\Large}
|
||||
\def\nwopt@LARGEcode{\let\nowebsize=\LARGE}
|
||||
\def\nwopt@hugecode{\let\nowebsize=\huge}
|
||||
\def\nwopt@Hugecode{\let\nowebsize=\Huge}
|
||||
\newcount\nwcodepenalty \nwcodepenalty=\@highpenalty
|
||||
\def\nw@makeother#1{\catcode`#1=12 }
|
||||
\def\nwbegindocs#1{\ifvmode\noindent\fi}
|
||||
\let\nwenddocs=\relax
|
||||
\let\nwdocspar=\filbreak
|
||||
\def\@nwsemifilbreak#1{\vskip0pt plus#1\penalty-200\vskip0pt plus -#1}
|
||||
\newdimen\nwbreakcodespace
|
||||
\nwbreakcodespace=0.2in % by default, leave no more than this on a page
|
||||
\def\nwopt@breakcode{%
|
||||
\def\nwdocspar{\@nwsemifilbreak{0.2in}}%
|
||||
\def\nwendcode{\endtrivlist\endgroup} % ditches filbreak
|
||||
}
|
||||
\raggedbottom
|
||||
\def\code{\leavevmode\begingroup\setupcode\@vobeyspaces\obeylines}
|
||||
\let\edoc=\endgroup
|
||||
\newdimen\@original@textwidth
|
||||
\def\ps@noweb{%
|
||||
\@original@textwidth=\textwidth
|
||||
\let\@mkboth\@gobbletwo
|
||||
\def\@oddfoot{}\def\@evenfoot{}% No feet.
|
||||
\if@twoside % If two-sided printing.
|
||||
\def\@evenhead{\hbox to \@original@textwidth{%
|
||||
\Rm \thepage\qquad{\Tt\leftmark}\hfil\today}}% Left heading.
|
||||
\def\@oddhead{\hbox to \@original@textwidth{%
|
||||
\Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading.
|
||||
\else % If one-sided printing.
|
||||
\def\@oddhead{\hbox to \@original@textwidth{%
|
||||
\Rm \today\hfil{\Tt\leftmark}\qquad\thepage}}% Right heading.
|
||||
\let\@evenhead\@oddhead
|
||||
\fi
|
||||
\let\chaptermark\@gobble
|
||||
\let\sectionmark\@gobble
|
||||
\let\subsectionmark\@gobble
|
||||
\let\subsubsectionmark\@gobble
|
||||
\let\paragraphmark\@gobble
|
||||
\let\subparagraphmark\@gobble
|
||||
\def\nwfilename{\begingroup\let\do\@makeother\dospecials
|
||||
\catcode`\{=1 \catcode`\}=2 \nw@filename}
|
||||
\def\nw@filename##1{\endgroup\markboth{##1}{##1}\let\nw@filename=\nw@laterfilename}%
|
||||
}
|
||||
\def\nw@laterfilename#1{\endgroup\clearpage \markboth{#1}{#1}}
|
||||
\let\nwfilename=\@gobble
|
||||
\def\nwcodecomment#1{\@@par\penalty\nwcodepenalty
|
||||
\if@firstnwcodecomment
|
||||
\vskip\nwcodecommentsep\penalty\nwcodepenalty\@firstnwcodecommentfalse
|
||||
\fi%
|
||||
\hspace{-\codemargin}{%
|
||||
\rightskip=0pt plus1in
|
||||
\interlinepenalty\nwcodepenalty
|
||||
\let\\\relax\footnotesize\Rm #1\@@par\penalty\nwcodepenalty}}
|
||||
\def\@nwalsodefined#1{\nwcodecomment{\@nwlangdepdef\ \nwpageprep\ \@pagesl{#1}.}}
|
||||
\def\@nwused#1{\nwcodecomment{\@nwlangdepcud\ \nwpageprep\ \@pagesl{#1}.}}
|
||||
\def\@nwnotused#1{\nwcodecomment{\@nwlangdeprtc.}}
|
||||
\def\nwoutput#1{\nwcodecomment{\@nwlangdepcwf\ {\Tt \@stripstar#1*\stripped}.}}
|
||||
\def\@stripstar#1*#2\stripped{#1}
|
||||
\newcommand{\nwprevdefptr}[1]{%
|
||||
\mbox{$\mathord{\triangleleft}\,\mathord{\mbox{\subpageref{#1}}}$}}
|
||||
\newcommand{\nwnextdefptr}[1]{%
|
||||
\mbox{$\mathord{\mbox{\subpageref{#1}}}\,\mathord{\triangleright}$}}
|
||||
|
||||
\newcommand{\@nwprevnextdefs}[2]{%
|
||||
{\nwtagstyle
|
||||
\ifx\relax#1\else ~~\nwprevdefptr{#1}\fi
|
||||
\ifx\relax#2\else ~~\nwnextdefptr{#2}\fi}}
|
||||
\newcommand{\@nwusesondefline}[1]{{\nwtagstyle~~(\@pagenumsl{#1})}}
|
||||
\newcommand{\@nwstartdeflinemarkup}{\nobreak\hskip 1.5em plus 1fill\nobreak}
|
||||
\newcommand{\@nwenddeflinemarkup}{\nobreak\hskip \nwdefspace minus\nwdefspace\nobreak}
|
||||
\def\nwopt@longxref{%
|
||||
\let\nwalsodefined\@nwalsodefined
|
||||
\let\nwused\@nwused
|
||||
\let\nwnotused\@nwnotused
|
||||
\let\nwprevnextdefs\@gobbletwo
|
||||
\let\nwusesondefline\@gobble
|
||||
\let\nwstartdeflinemarkup\relax
|
||||
\let\nwenddeflinemarkup\relax
|
||||
}
|
||||
\def\nwopt@shortxref{%
|
||||
\let\nwalsodefined\@gobble
|
||||
\let\nwused\@gobble
|
||||
\let\nwnotused\@gobble
|
||||
\let\nwprevnextdefs\@nwprevnextdefs
|
||||
\let\nwusesondefline\@nwusesondefline
|
||||
\let\nwstartdeflinemarkup\@nwstartdeflinemarkup
|
||||
\let\nwenddeflinemarkup\@nwenddeflinemarkup
|
||||
}
|
||||
\def\nwopt@noxref{%
|
||||
\let\nwalsodefined\@gobble
|
||||
\let\nwused\@gobble
|
||||
\let\nwnotused\@gobble
|
||||
\let\nwprevnextdefs\@gobbletwo
|
||||
\let\nwusesondefline\@gobble
|
||||
\let\nwstartdeflinemarkup\relax
|
||||
\let\nwenddeflinemarkup\relax
|
||||
}
|
||||
\nwopt@shortxref % to hell with backward compatibility!
|
||||
\newskip\nwcodecommentsep \nwcodecommentsep=3pt plus 1pt minus 1pt
|
||||
\newif\if@firstnwcodecomment\@firstnwcodecommenttrue
|
||||
\newcount\@nwlopage\newcount\@nwhipage % range lo..hi-1
|
||||
\newcount\@nwlosub % subpage of lo
|
||||
\newcount\@nwhisub % subpage of hi
|
||||
\def\@nwfirstpage#1#2#3{% subpage page xref-tag
|
||||
\@nwlopage=#2 \@nwlosub=#1
|
||||
\def\@nwloxreftag{#3}%
|
||||
\advance\@nwpagecount by \@ne
|
||||
\@nwhipage=\@nwlopage\advance\@nwhipage by \@ne }
|
||||
\def\@nwnextpage#1#2#3{% subpage page xref-tag
|
||||
\ifnum\@nwhipage=#2
|
||||
\advance\@nwhipage by \@ne
|
||||
\advance\@nwpagecount by \@ne
|
||||
\@nwhisub=#1
|
||||
\def\@nwhixreftag{#3}\else
|
||||
\ifnum#2<\@nwlopage \advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\@nwfirstpage{#1}{#2}{#3}\else
|
||||
\ifnum#2>\@nwhipage \advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\@nwfirstpage{#1}{#2}{#3}\else
|
||||
\@nwlosub=0 \@nwhisub=0
|
||||
\fi\fi\fi
|
||||
}
|
||||
\newcount\@nwpagetemp
|
||||
\newcount\@nwpagecount
|
||||
\def\@nwfirstpagel#1{% label
|
||||
\@ifundefined{r@#1}{\@warning{Reference `#1' on page \thepage \space undefined}%
|
||||
\nwix@cons\nw@pages{\\{\bf ??}}}{%
|
||||
\edef\@tempa{\noexpand\@nwfirstpage\subpagepair{#1}{#1}}\@tempa}}
|
||||
\def\@nwnextpagel#1{% label
|
||||
\@ifundefined{r@#1}{\@warning{Reference `#1' on page \thepage \space undefined}%
|
||||
\nwix@cons\nw@pages{\\{\bf ??}}}{%
|
||||
\edef\@tempa{\noexpand\@nwnextpage\subpagepair{#1}{#1}}\@tempa}}
|
||||
\def\@pagesl#1{% list of labels
|
||||
\gdef\nw@pages{}\@nwpagecount=0
|
||||
\def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1%
|
||||
\advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\def\\##1{\@nwhyperpagenum##1}%
|
||||
\ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}}
|
||||
\def\@nwhyperpagenum#1#2{\nwhyperreference{#2}{#1}}
|
||||
|
||||
\def\@pagenumsl#1{% list of labels -- doesn't include word `pages', commas, or `and'
|
||||
\gdef\nw@pages{}\@nwpagecount=0
|
||||
\def\\##1{\@nwfirstpagel{##1}\let\\=\@nwnextpagel}#1%
|
||||
\advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa%
|
||||
\def\\##1{\@nwhyperpagenum##1\let\\=\@nwpagenumslrest}\nw@pages}
|
||||
\def\@nwpagenumslrest#1{~\@nwhyperpagenum#1}
|
||||
\def\subpages#1{% list of {{subpage}{page}}
|
||||
\gdef\nw@pages{}\@nwpagecount=0
|
||||
\def\\##1{\edef\@tempa{\noexpand\@nwfirstpage##1{}}\@tempa
|
||||
\def\\####1{\edef\@tempa{\noexpand\@nwnextpage####1}\@tempa}}#1%
|
||||
\advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa\def\\##1{\@firstoftwo##1}%
|
||||
\ifnum\@nwpagecount=1 \nwpageword \else \nwpagesword\fi~\commafy{\nw@pages}}
|
||||
\def\@nwaddrange{\advance\@nwhipage by \m@ne
|
||||
\ifnum\@nwhipage=\@nwlopage
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}}%
|
||||
\else
|
||||
\count@=\@nwhipage \advance\count@ by \m@ne
|
||||
\ifnum\count@=\@nwlopage % consecutive pages
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwlosub}{\number\@nwlopage}}%
|
||||
{\@nwloxreftag}}%
|
||||
\noexpand\noexpand\noexpand\\%
|
||||
{{\nwthepagenum{\number\@nwhisub}{\number\@nwhipage}}
|
||||
{\@nwhixreftag}}}%
|
||||
\else \ifnum\@nwlopage<110 \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100 \multiply\count@ by 100
|
||||
\ifnum\count@=\@nwlopage \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}\else
|
||||
\count@=\@nwlopage \divide\count@ by 100
|
||||
\@nwpagetemp=\@nwhipage \divide\@nwpagetemp by 100
|
||||
\ifnum\count@=\@nwpagetemp % lo--least 2 digits of hi
|
||||
\multiply\@nwpagetemp by 100
|
||||
\advance \@nwhipage by -\@nwpagetemp
|
||||
\edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\else \edef\@tempa{\noexpand\noexpand\noexpand\\{{\number\@nwlopage--\number\@nwhipage}{}}}%
|
||||
\fi
|
||||
\fi
|
||||
\fi%
|
||||
\fi
|
||||
\fi%
|
||||
\edef\@tempa{\noexpand\nwix@cons\noexpand\nw@pages{\@tempa}}\@tempa}
|
||||
\def\nwpageword{\@nwlangdepchk} % chunk, was page
|
||||
\def\nwpagesword{\@nwlangdepchks} % chunk, was page
|
||||
\def\nwpageprep{\@nwlangdepin} % in, was on
|
||||
\newcommand\nw@genericref[2]{% what to do, name of ref
|
||||
\expandafter\nw@g@nericref\csname r@#2\endcsname#1{#2}}
|
||||
\newcommand\nw@g@nericref[3]{% control sequence, what to do, name
|
||||
\ifx#1\relax
|
||||
\ref{#3}% trigger the standard `undefined ref' mechanisms
|
||||
\else
|
||||
\expandafter#2#1.\\%
|
||||
\fi}
|
||||
\def\nw@selectone#1#2#3\\{#1}
|
||||
\def\nw@selecttwo#1#2#3\\{#2}
|
||||
\def\nw@selectonetwo#1#2#3\\{{#1}{#2}}
|
||||
\newcommand{\subpageref}[1]{%
|
||||
\nwhyperreference{#1}{\nw@genericref\@subpageref{#1}}}
|
||||
\def\@subpageref#1#2#3\\{%
|
||||
\@ifundefined{2on#2}{#2}{\nwthepagenum{#1}{#2}}}
|
||||
\newcommand{\subpagepair}[1]{% % produces {subpage}{page}
|
||||
\@ifundefined{r@#1}%
|
||||
{{0}{0}}%
|
||||
{\nw@genericref\@subpagepair{#1}}}
|
||||
\def\@subpagepair#1#2#3\\{%
|
||||
\@ifundefined{2on#2}{{0}{#2}}{{#1}{#2}}}
|
||||
\newcommand{\sublabel}[1]{%
|
||||
\leavevmode % needed to make \@bsphack work
|
||||
\@bsphack
|
||||
\nwblindhyperanchor{#1}%
|
||||
\if@filesw {\let\thepage\relax
|
||||
\def\protect{\noexpand\noexpand\noexpand}%
|
||||
\edef\@tempa{\write\@auxout{\string
|
||||
\newsublabel{#1}{{}{\thepage}}}}%
|
||||
\expandafter}\@tempa
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
\newcommand{\nosublabel}[1]{%
|
||||
\@bsphack\if@filesw {\let\thepage\relax
|
||||
\def\protect{\noexpand\noexpand\noexpand}%
|
||||
\edef\@tempa{\write\@auxout{\string
|
||||
\newlabel{#1}{{0}{\thepage}}}}%
|
||||
\expandafter}\@tempa
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
\newcommand\newsublabel{%
|
||||
\nw@settrailers
|
||||
\global\let\newsublabel\@newsublabel
|
||||
\@newsublabel}
|
||||
\newcommand{\@newsublabel}[2]{%
|
||||
\edef\this@page{\@cdr#2\@nil}%
|
||||
\ifx\this@page\last@page\else
|
||||
\sub@page=\z@
|
||||
\fi
|
||||
\edef\last@page{\this@page}
|
||||
\advance\sub@page by \@ne
|
||||
\ifnum\sub@page=\tw@
|
||||
\global\@namedef{2on\this@page}{}%
|
||||
\fi
|
||||
\pendingsublabel{#1}%
|
||||
\edef\@tempa##1{\noexpand\newlabel{##1}%
|
||||
{{\number\sub@page}{\this@page}\nw@labeltrailers}}%
|
||||
\pending@sublabels
|
||||
\def\pending@sublabels{}}
|
||||
\newcommand\nw@settrailers{% -- won't work on first run
|
||||
\@ifpackageloaded{nameref}%
|
||||
{\gdef\nw@labeltrailers{{}{}{}}}%
|
||||
{\gdef\nw@labeltrailers{}}}
|
||||
\renewcommand\nw@settrailers{%
|
||||
\@ifundefined{@secondoffive}%
|
||||
{\gdef\nw@labeltrailers{}}%
|
||||
{\gdef\nw@labeltrailers{{}{}{}}}}
|
||||
\newcommand{\nextchunklabel}[1]{%
|
||||
\nwblindhyperanchor{#1}% % looks slightly bogus --- nr
|
||||
\@bsphack\if@filesw {\let\thepage\relax
|
||||
\edef\@tempa{\write\@auxout{\string\pendingsublabel{#1}}}%
|
||||
\expandafter}\@tempa
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
\newcommand\pendingsublabel[1]{%
|
||||
\def\@tempa{\noexpand\@tempa}%
|
||||
\edef\pending@sublabels{\noexpand\@tempa{#1}\pending@sublabels}}
|
||||
\def\pending@sublabels{}
|
||||
\def\last@page{\relax}
|
||||
\newcount\sub@page
|
||||
\def\@alphasubpagenum#1#2{#2\ifnum#1=0 \else\@alph{#1}\fi}
|
||||
\def\@nosubpagenum#1#2{#2}
|
||||
\def\@numsubpagenum#1#2{#2\ifnum#1=0 \else.\@arabic{#1}\fi}
|
||||
\def\nwopt@nosubpage{\let\nwthepagenum=\@nosubpagenum\nwopt@nomargintag}
|
||||
\def\nwopt@numsubpage{\let\nwthepagenum=\@numsubpagenum}
|
||||
\def\nwopt@alphasubpage{\let\nwthepagenum=\@alphasubpagenum}
|
||||
\nwopt@alphasubpage
|
||||
\newcount\@nwalph@n
|
||||
\let\@nwalph@d\@tempcnta
|
||||
\let\@nwalph@bound\@tempcntb
|
||||
\def\@nwlongalph#1{{%
|
||||
\@nwalph@n=#1\advance\@nwalph@n by-1
|
||||
\@nwalph@bound=26
|
||||
\loop\ifnum\@nwalph@n<\@nwalph@bound\else
|
||||
\advance\@nwalph@n by -\@nwalph@bound
|
||||
\multiply\@nwalph@bound by 26
|
||||
\repeat
|
||||
\loop\ifnum\@nwalph@bound>1
|
||||
\divide\@nwalph@bound by 26
|
||||
\@nwalph@d=\@nwalph@n\divide\@nwalph@d by \@nwalph@bound
|
||||
% d := d * bound ; n -:= d; d := d / bound --- saves a temporary
|
||||
\multiply\@nwalph@d by \@nwalph@bound
|
||||
\advance\@nwalph@n by -\@nwalph@d
|
||||
\divide\@nwalph@d by \@nwalph@bound
|
||||
\advance\@nwalph@d by 1 \@alph{\@nwalph@d}%
|
||||
\repeat
|
||||
}}
|
||||
\newcount\nw@chunkcount
|
||||
\nw@chunkcount=\@ne
|
||||
\newcommand{\weblabel}[1]{%
|
||||
\@bsphack
|
||||
\nwblindhyperanchor{#1}%
|
||||
\if@filesw {\let\thepage\relax
|
||||
\def\protect{\noexpand\noexpand\noexpand}%
|
||||
\edef\@tempa{\write\@auxout{\string
|
||||
\newsublabel{#1}{{}{\number\nw@chunkcount}}}}%
|
||||
\expandafter}\@tempa
|
||||
\global\advance\nw@chunkcount by \@ne
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
\def\nwopt@webnumbering{%
|
||||
\let\sublabel=\weblabel
|
||||
\def\nwpageword{chunk}\def\nwpagesword{chunks}%
|
||||
\def\nwpageprep{in}}
|
||||
% \nwindexdefn{printable name}{identifying label}{label of chunk}
|
||||
% \nwindexuse{printable name}{identifying label}{label of chunk}
|
||||
|
||||
\def\nwindexdefn#1#2#3{\@auxix{\protect\nwixd}{#2}{#3}}
|
||||
\def\nwindexuse#1#2#3{\@auxix{\protect\nwixu}{#2}{#3}}
|
||||
|
||||
\def\@auxix#1#2#3{% {marker}{id label}{subpage label}
|
||||
\@bsphack\if@filesw {\let\nwixd\relax\let\nwixu\relax
|
||||
\def\protect{\noexpand\noexpand\noexpand}%
|
||||
\edef\@tempa{\write\@auxout{\string\nwixadd{#1}{#2}{#3}}}%
|
||||
\expandafter}\@tempa
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
% \nwixadd{marker}{idlabel}{subpage label}
|
||||
\def\nwixadd#1#2#3{%
|
||||
\@ifundefined{nwixl@#2}%
|
||||
{\global\@namedef{nwixl@#2}{#1{#3}}}%
|
||||
{\expandafter\nwix@cons\csname nwixl@#2\endcsname{#1{#3}}}}
|
||||
\def\@nwsubscriptident#1#2{\mbox{$\mbox{#1}_{\mathrm{\subpageref{#2}}}$}}
|
||||
\def\@nwnosubscriptident#1#2{#1}
|
||||
\def\@nwhyperident#1#2{\leavevmode\nwhyperreference{#2}{#1}}
|
||||
\def\nwopt@subscriptidents{%
|
||||
\let\nwlinkedidentq\@nwsubscriptident
|
||||
\let\nwlinkedidentc\@nwsubscriptident
|
||||
}
|
||||
\def\nwopt@nosubscriptidents{%
|
||||
\let\nwlinkedidentq\@nwnosubscriptident
|
||||
\let\nwlinkedidentc\@nwnosubscriptident
|
||||
}
|
||||
\def\nwopt@hyperidents{%
|
||||
\let\nwlinkedidentq\@nwhyperident
|
||||
\let\nwlinkedidentc\@nwhyperident
|
||||
}
|
||||
\def\nwopt@nohyperidents{%
|
||||
\let\nwlinkedidentq\@nwnosubscriptident
|
||||
\let\nwlinkedidentc\@nwnosubscriptident
|
||||
}
|
||||
\def\nwopt@subscriptquotedidents{%
|
||||
\let\nwlinkedidentq\@nwsubscriptident
|
||||
}
|
||||
\def\nwopt@nosubscriptquotedidents{%
|
||||
\let\nwlinkedidentq\@nwnosubscriptident
|
||||
}
|
||||
\def\nwopt@hyperquotedidents{%
|
||||
\let\nwlinkedidentq\@nwhyperident
|
||||
}
|
||||
\def\nwopt@nohyperquotedidents{%
|
||||
\let\nwlinkedidentq\@nwnosubscriptident
|
||||
}
|
||||
\nwopt@hyperidents
|
||||
\newcount\@commacount
|
||||
\def\commafy#1{%
|
||||
{\nwix@listcount{#1}\@commacount=\nwix@counter
|
||||
\let\@comma@each=\\%
|
||||
\ifcase\@commacount\let\\=\@comma@each\or\let\\=\@comma@each\or
|
||||
\def\\{\def\\{ \@nwlangdepand\ \@comma@each}\@comma@each}\else
|
||||
\def\\{\def\\{, %
|
||||
\advance\@commacount by \m@ne
|
||||
\ifnum\@commacount=1 \@nwlangdepand~\fi\@comma@each}\@comma@each}\fi
|
||||
#1}}
|
||||
\def\nwix@cons#1#2{% {list}{\marker{element}}
|
||||
{\toks0=\expandafter{#1}\def\@tempa{#2}\toks2=\expandafter{\@tempa}%
|
||||
\xdef#1{\the\toks0 \the\toks2 }}}
|
||||
\def\nwix@uses#1{% {label}
|
||||
\def\nwixu{\\}\let\nwixd\@gobble\@nameuse{nwixl@#1}}
|
||||
\def\nwix@defs#1{% {label}
|
||||
\def\nwixd{\\}\let\nwixu\@gobble\@nameuse{nwixl@#1}}
|
||||
\newcount\nwix@counter
|
||||
\def\nwix@listcount#1{% {list with \\}
|
||||
{\count@=0
|
||||
\def\\##1{\advance\count@ by \@ne }%
|
||||
#1\global\nwix@counter=\count@ }}
|
||||
\def\nwix@usecount#1{\nwix@listcount{\nwix@uses{#1}}}
|
||||
\def\nwix@defcount#1{\nwix@listcount{\nwix@defs{#1}}}
|
||||
\def\nwix@id@defs#1{% index pair
|
||||
{{\Tt \@car#1\@nil}%
|
||||
\def\\##1{\nwix@defs@space\subpageref{##1}}\nwix@defs{\@cdr#1\@nil}}}
|
||||
% useful above to change ~ into something that can break
|
||||
% this option is undocumented because I think breakdefs is always right
|
||||
\def\nwopt@breakdefs{\def\nwix@defs@space{\penalty200\ }}
|
||||
\def\nwopt@nobreakdefs{\def\nwix@defs@space{~}} % old code
|
||||
\nwopt@breakdefs
|
||||
\def\nwidentuses#1{% list of index pairs
|
||||
\nwcodecomment{\@nwlangdepuss\ \let\\=\nwix@id@defs\commafy{#1}.}}
|
||||
\def\nwix@totaluses#1{% list of index pairs
|
||||
{\count@=0
|
||||
\def\\##1{\nwix@usecount{\@cdr##1\@nil}\advance\count@ by\nwix@counter}%
|
||||
#1\global\nwix@counter\count@ }}
|
||||
\def\nwix@id@uses#1#2{% {ident}{label}
|
||||
\nwix@usecount{#2}\ifnum\nwix@counter>0
|
||||
{\advance\leftskip by \codemargin
|
||||
\nwcodecomment{{\Tt #1}, \@nwlangdepusd\ \nwpageprep\ \@pagesl{\nwix@uses{#2}}.}}%
|
||||
\else
|
||||
\ifnw@hideunuseddefs\else
|
||||
{\advance\leftskip by \codemargin \nwcodecomment{{\Tt #1}, \@nwlangdepnvu.}}%
|
||||
\fi
|
||||
\fi}
|
||||
\def\nwidentdefs#1{% list of index pairs
|
||||
\ifnw@hideunuseddefs\nwix@totaluses{#1}\else\nwix@listcount{#1}\fi
|
||||
\ifnum\nwix@counter>0
|
||||
\nwcodecomment{\@nwlangdepdfs:}%
|
||||
{\def\\##1{\nwix@id@uses ##1}#1}%
|
||||
\fi}
|
||||
\newif\ifnw@hideunuseddefs\nw@hideunuseddefsfalse
|
||||
\def\nwopt@hideunuseddefs{\nw@hideunuseddefstrue}
|
||||
\def\nwopt@noidentxref{%
|
||||
\let\nwidentdefs\@gobble
|
||||
\let\nwidentuses\@gobble}
|
||||
\def\nw@underlinedefs{% {list with \nwixd, \nwixu}
|
||||
\let\\=\relax\def\nw@comma{, }
|
||||
\def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}%
|
||||
\def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}}
|
||||
|
||||
\def\nw@indexline#1#2{%
|
||||
{\indent {\Tt #1}: \nw@underlinedefs\@nameuse{nwixl@#2}\par}}
|
||||
|
||||
\newenvironment{thenowebindex}{\parindent=-10pt \parskip=\z@
|
||||
\advance\leftskip by 10pt
|
||||
\advance\rightskip by 0pt plus1in\par\@afterindenttrue
|
||||
\def\\##1{\nw@indexline##1}}{}
|
||||
\def\nowebindex{%
|
||||
\@ifundefined{nwixs@i}%
|
||||
{\@warning{The \string\nowebindex\space is empty}}%
|
||||
{\begin{thenowebindex}\@nameuse{nwixs@i}\end{thenowebindex}}}
|
||||
\def\nowebindex@external{%
|
||||
{\let\nwixadds@c=\@gobble
|
||||
\def\nwixadds@i##1{\nw@indexline##1}%
|
||||
\def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}%
|
||||
\begin{thenowebindex}\@input{\jobname.nwi}\end{thenowebindex}}}
|
||||
\def\nwixlogsorted#1#2{% list data
|
||||
\@bsphack\if@filesw
|
||||
\toks0={#2}\immediate\write\@auxout{\string\nwixadds{#1}{\the\toks0}}
|
||||
\if@nobreak \ifvmode\nobreak\fi\fi\fi\@esphack}
|
||||
\def\nwixadds#1#2{%
|
||||
\@ifundefined{nwixs@#1}%
|
||||
{\global\@namedef{nwixs@#1}{\\{#2}}}%
|
||||
{\expandafter\nwix@cons\csname nwixs@#1\endcsname{\\{#2}}}}
|
||||
\let\nwixaddsx=\@gobbletwo
|
||||
\def\nwopt@externalindex{%
|
||||
\ifx\nwixadds\@gobbletwo % already called
|
||||
\else
|
||||
\let\nwixaddsx=\nwixadds \let\nwixadds=\@gobbletwo
|
||||
\let\nowebindex=\nowebindex@external
|
||||
\let\nowebchunks=\nowebchunks@external
|
||||
\fi}
|
||||
\def\nowebchunks{%
|
||||
\@ifundefined{nwixs@c}%
|
||||
{\@warning{The are no \string\nowebchunks}}%
|
||||
{\begin{thenowebchunks}\@nameuse{nwixs@c}\end{thenowebchunks}}}
|
||||
\def\nowebchunks@external{%
|
||||
{\let\nwixadds@i=\@gobble
|
||||
\def\nwixadds@c##1{\nw@onechunk##1}%
|
||||
\def\nwixaddsx##1##2{\@nameuse{nwixadds@##1}{##2}}%
|
||||
\begin{thenowebchunks}\@input{\jobname.nwi}\end{thenowebchunks}}}
|
||||
\@namedef{r@nw@notdef}{{0}{(\@nwlangdepnvd)}}
|
||||
\def\nw@chunkunderlinedefs{% {list of labels with \nwixd, \nwixu}
|
||||
\let\\=\relax\def\nw@comma{, }
|
||||
\def\nwixd##1{\\\underline{\subpageref{##1}}\let\\\nw@comma}%
|
||||
\def\nwixu##1{\\\subpageref{##1}\let\\\nw@comma}}
|
||||
\def\nw@onechunk#1#2#3{% {name}{label of first definition}{list with \nwixd, \nwixu}
|
||||
\@ifundefined{r@#2}{}{%
|
||||
\indent\LA #1~{\nwtagstyle\subpageref{#2}}\RA
|
||||
\if@nwlongchunks{~\nw@chunkunderlinedefs#3}\fi\par}}
|
||||
\newenvironment{thenowebchunks}{\vskip3pt
|
||||
\parskip=\z@\parindent=-10pt \advance\leftskip by 10pt
|
||||
\advance\rightskip by 0pt plus10pt \@afterindenttrue
|
||||
\def\\##1{\nw@onechunk##1}}{}
|
||||
\newif\if@nwlongchunks
|
||||
\@nwlongchunksfalse
|
||||
\let\nwopt@longchunks\@nwlongchunkstrue
|
||||
\newcommand\@nw@hyper@ref{\hyperreference} % naras
|
||||
\newcommand\@nw@hyper@anc{\blindhyperanchor} % naras
|
||||
\newcommand\@nw@hyperref@ref[2]{\hyperlink{noweb.#1}{#2}} % nr
|
||||
\newcommand\@nw@hyperref@anc[1]{\hypertarget{noweb.#1}{\relax}} % nr
|
||||
%%\renewcommand\@nw@hyperref@ref[2]{{#2}} % nr
|
||||
%%\renewcommand\@nw@hyperref@anc[1]{} % nr
|
||||
\newcommand\nwhyperreference{%
|
||||
\@ifundefined{hyperlink}
|
||||
{\@ifundefined{hyperreference}
|
||||
{\global\let\nwhyperreference\@gobble}
|
||||
{\global\let\nwhyperreference\@nw@hyper@ref}}
|
||||
{\global\let\nwhyperreference\@nw@hyperref@ref}%
|
||||
\nwhyperreference
|
||||
}
|
||||
|
||||
\newcommand\nwblindhyperanchor{%
|
||||
\@ifundefined{hyperlink}
|
||||
{\@ifundefined{hyperreference}
|
||||
{\global\let\nwblindhyperanchor\@gobble}
|
||||
{\global\let\nwblindhyperanchor\@nw@hyper@anc}}
|
||||
{\global\let\nwblindhyperanchor\@nw@hyperref@anc}%
|
||||
\nwblindhyperanchor
|
||||
}
|
||||
\newcommand\nwanchorto{%
|
||||
\begingroup\let\do\@makeother\dospecials
|
||||
\catcode`\{=1 \catcode`\}=2 \nw@anchorto}
|
||||
\newcommand\nw@anchorto[1]{\endgroup\def\nw@next{#1}\nw@anchortofin}
|
||||
\newcommand\nw@anchortofin[1]{#1\footnote{See URL \texttt{\nw@next}.}}
|
||||
\let\nwanchorname\@gobble
|
||||
\newif\ifhtml
|
||||
\htmlfalse
|
||||
\let\nwixident=\relax
|
||||
\def\nwbackslash{\char92}
|
||||
\def\nwlbrace{\char123}
|
||||
\def\nwrbrace{\char125}
|
||||
\def\nwopt@english{%
|
||||
\def\@nwlangdepdef{This definition is continued}%
|
||||
\def\@nwlangdepcud{This code is used}%
|
||||
\def\@nwlangdeprtc{Root chunk (not used in this document)}%
|
||||
\def\@nwlangdepcwf{This code is written to file}%
|
||||
\def\@nwlangdepchk{chunk}%
|
||||
\def\@nwlangdepchks{chunks}%
|
||||
\def\@nwlangdepin{in}%
|
||||
\def\@nwlangdepand{and}%
|
||||
\def\@nwlangdepuss{Uses}%
|
||||
\def\@nwlangdepusd{used}%
|
||||
\def\@nwlangdepnvu{never used}%
|
||||
\def\@nwlangdepdfs{Defines}%
|
||||
\def\@nwlangdepnvd{never defined}%
|
||||
}
|
||||
\let\nwopt@american\nwopt@english
|
||||
\def\nwopt@portuges{%
|
||||
\def\@nwlangdepdef{Defini\c{c}\~ao continuada em}%
|
||||
% This definition is continued
|
||||
\def\@nwlangdepcud{C\'odigo usado em}%
|
||||
% This code is used
|
||||
\def\@nwlangdeprtc{Fragmento de topo (sem uso no documento)}%
|
||||
% Root chunk (not used in this document)
|
||||
\def\@nwlangdepcwf{Este c\'odigo foi escrito no ficheiro}%
|
||||
% This code is written to file
|
||||
\def\@nwlangdepchk{fragmento}%
|
||||
% chunk
|
||||
\def\@nwlangdepchks{fragmentos}%
|
||||
% chunks
|
||||
\def\@nwlangdepin{no(s)}%
|
||||
% in
|
||||
\def\@nwlangdepand{e}%
|
||||
% and
|
||||
\def\@nwlangdepuss{Usa}%
|
||||
% Uses
|
||||
\def\@nwlangdepusd{usado}%
|
||||
% used
|
||||
\def\@nwlangdepnvu{nunca usado}%
|
||||
% never used
|
||||
\def\@nwlangdepdfs{Define}%
|
||||
% Defines
|
||||
\def\@nwlangdepnvd{nunca definido}%
|
||||
% never defined
|
||||
}
|
||||
\def\nwopt@brazil{%
|
||||
\def\@nwlangdepdef{Defini\c{c}\~{a}o continua}%
|
||||
% This definition is continued
|
||||
\def\@nwlangdepcud{C\'odigo usado}%
|
||||
% This code is used
|
||||
\def\@nwlangdeprtc{Trecho raiz, isto é, este trecho come\c{c}a aqui}%
|
||||
% Root chunk (not used in this document)
|
||||
\def\@nwlangdepcwf{C\'odigo escrito no arquivo}%
|
||||
% This code is written to file
|
||||
\def\@nwlangdepchk{trecho}%
|
||||
% chunk
|
||||
\def\@nwlangdepchks{trechos}%
|
||||
% chunks
|
||||
\def\@nwlangdepin{@}%
|
||||
% in
|
||||
\def\@nwlangdepand{e}%
|
||||
% and
|
||||
\def\@nwlangdepuss{Usa}%
|
||||
% Uses
|
||||
\def\@nwlangdepusd{usado}%
|
||||
% used
|
||||
\def\@nwlangdepnvu{nunca usado}%
|
||||
% never used
|
||||
\def\@nwlangdepdfs{Define}%
|
||||
% Defines
|
||||
\def\@nwlangdepnvd{nunca definido}%
|
||||
% never defined
|
||||
}
|
||||
\def\nwopt@frenchb{%
|
||||
\def\@nwlangdepdef{Suite de la d\'efinition}%
|
||||
% This definition is continued
|
||||
\def\@nwlangdepcud{Ce code est employ\'e}%
|
||||
% This code is used
|
||||
\def\@nwlangdeprtc{Morceau racine (pas employ\'e dans ce document)}%
|
||||
% Root chunk (not used in this document)
|
||||
\def\@nwlangdepcwf{Ce code est \'ecrit dans le fichier}%
|
||||
% This code is written to file
|
||||
\def\@nwlangdepchk{le morceau}%
|
||||
% chunk
|
||||
\def\@nwlangdepchks{les morceaux}%
|
||||
% chunks
|
||||
\def\@nwlangdepin{dans}%
|
||||
% in
|
||||
\def\@nwlangdepand{et}%
|
||||
% and
|
||||
\def\@nwlangdepuss{Utilise}%
|
||||
% Uses
|
||||
\def\@nwlangdepusd{utilis\'{e}}%
|
||||
% used
|
||||
\def\@nwlangdepnvu{jamais employ\'{e}}%
|
||||
% never used
|
||||
\def\@nwlangdepdfs{D\'{e}finit}%
|
||||
% Defines
|
||||
% Cannot use the accent here: \def\@nwlangdepnvd{jamais d\'{e}fini}%
|
||||
\def\@nwlangdepnvd{jamais defini}%
|
||||
% never defined
|
||||
}
|
||||
\let\nwopt@french\nwopt@frenchb
|
||||
\def\nwopt@german{%
|
||||
\def\@nwlangdepdef{Diese Definition wird fortgesetzt}%
|
||||
% This definition is continued
|
||||
\def\@nwlangdepcud{Dieser Code wird benutzt}%
|
||||
% This code is used
|
||||
\def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}%
|
||||
% Root chunk (not used in this document)
|
||||
%\def\@nwlangdepcwf{Dieser Code schreibt man zum File}%
|
||||
\def\@nwlangdepcwf{Dieser Code geht in Datei}%
|
||||
% This code is written to file
|
||||
\def\@nwlangdepchk{dem Teil}%
|
||||
% chunk
|
||||
%\def\@nwlangdepchks{Teils}%
|
||||
\def\@nwlangdepchks{den Teilen}%
|
||||
% chunks
|
||||
\def\@nwlangdepin{in}%
|
||||
% in
|
||||
\def\@nwlangdepand{und}%
|
||||
% and
|
||||
\def\@nwlangdepuss{Benutzt}%
|
||||
% Uses
|
||||
\def\@nwlangdepusd{benutzt}%
|
||||
% used
|
||||
\def\@nwlangdepnvu{nicht benutzt}%
|
||||
% never used
|
||||
\def\@nwlangdepdfs{Definiert}%
|
||||
% Defines
|
||||
\def\@nwlangdepnvd{nicht definiert}%
|
||||
% never defined
|
||||
}
|
||||
\def\nwopt@german{%
|
||||
\def\@nwlangdepdef{Diese Definition wird fortgesetzt}%
|
||||
% This definition is continued
|
||||
\def\@nwlangdepcud{Dieser Code wird benutzt}%
|
||||
% This code is used
|
||||
\def\@nwlangdeprtc{Hauptteil (nicht in diesem Dokument benutzt)}%
|
||||
% Root chunk (not used in this document)
|
||||
\def\@nwlangdepcwf{Dieser Code schreibt man zum File}%
|
||||
% This code is written to file
|
||||
\def\@nwlangdepchk{Teil}%
|
||||
% chunk
|
||||
\def\@nwlangdepchks{Teils}%
|
||||
% chunks
|
||||
\def\@nwlangdepin{im}%
|
||||
% in
|
||||
\def\@nwlangdepand{und}%
|
||||
% and
|
||||
\def\@nwlangdepuss{Benutzt}%
|
||||
% Uses
|
||||
\def\@nwlangdepusd{benutzt}%
|
||||
% used
|
||||
\def\@nwlangdepnvu{nicht benutzt}%
|
||||
% never used
|
||||
\def\@nwlangdepdfs{Definiert}%
|
||||
% Defines
|
||||
\def\@nwlangdepnvd{nicht definiert}%
|
||||
% never defined
|
||||
}
|
||||
\let\nwopt@ngerman\nwopt@german
|
||||
\ifx\languagename\undefined % default is English
|
||||
\noweboptions{english}
|
||||
\else
|
||||
\@ifundefined{nwopt@\languagename}
|
||||
{\noweboptions{english}}
|
||||
{\expandafter\noweboptions\expandafter{\languagename}}
|
||||
\fi
|
||||
\let\obeyedspace\@xobeysp
|
||||
Loading…
Reference in a new issue