|
|
1.1 root 1: ;$Header: vlp.l 1.4 83/05/11 23:55:23 layer Exp $
2: ;$Locker: $
3: ;
4: ; -[Wed Feb 16 12:25:15 1983 by jkf]-
5: ;
6: ; Filter for nroffing or troffing Lisp code.
7: ; This program reads files containing Lisp code and writes them out
8: ; along with the nroff (troff) commands required to make the Lisp line up.
9: ; It does this alignment by insuring that the leftmost
10: ; blank character lines up with the closest non blank character above it.
11: ;
12: ; The program operates in one of two modes: filter mode or non-filter mode.
13: ;
14: ; In filter mode, only characters between lines beginning with .Ls and .Le
15: ; are processed. The directive .Ls accepts an optional argument that is
16: ; the point size of the output. Pages have no titles and point size is set
17: ; by the macro, so most shell arguments are ignored.
18: ;
19: ; In non-filter mode, all lines in the file are processed.
20: ;
21: ; The output goes to the standard output, unless the v switch is set,
22: ; in which case it goes to vtroff.
23: ;
24: ; Usage:
25: ; vlp [-p size] [-d] [-f] [-l] [-v] [-T title1] file1 ....
26: ;
27: ; where,
28: ; -p Sets the point size to "size"
29: ; -d Turn on debugging mode
30: ; -f Run in filtered mode
31: ; -l Turns OFF the labeling of functions
32: ; -v Sends the output to vtroff
33: ; -T Sets the title of the next file to "titleN"
34: ;
35: ; NOTE: If vlp is on the right side of a pipe, the "-T" switch can not be
36: ; used.
37: ;
38: ;
39: ; Written by John Foderaro
40: ; Modified by:
41: ; Kevin Layer to have enough tags.
42: ; Jim Larus to work in filtered mode.
43: ;
44:
45: (declare (macros t))
46:
47: (allocate 'list 220)
48:
49: (declare (special user-top-level EOF lines g-psize inport
50: spaces sps chs tls trname errport outfilename
51: filtermode g-title rest funcname readstdin debug
52: spout outfile tagsleft LabelFunctions))
53:
54: (defvar space-count 500.) ; large number
55:
56: ;--- toplev :: top level lisp
57: ;
58: (defun toplev nil
59: (let ((args (command-line-args)))
60: (signal 2 'vlp-interrupt-routine)
61: (signal 15 'vlp-interrupt-routine)
62: (errset (apply 'vlp args))
63: (exit 0)))
64:
65: (eval-when (load) (setq user-top-level 'toplev))
66:
67: (setq g-psize 8
68: debug nil
69: g-filtmode nil
70: readstdin nil
71: filtermode nil
72: outfilename nil
73: LabelFunctions t)
74:
75:
76: (defun GetModTime (FileName)
77: ;
78: ; Returns the last modification time as a string.
79: ;
80: (time-string (filestat:mtime (filestat (get_pname FileName)))))
81:
82:
83: (defun vlp fexpr (args)
84: (prog (inport outfile)
85: ;check command line for switches
86: (setq args (do ((i args (cdr i)) (ll) (tem))
87: ((null i) (reverse ll))
88: (setq tem (aexplodec (car i)))
89: (cond ((eq '- (car tem)) ; if switch
90: (cond ((eq 'd (cadr tem))
91: (setq debug t))
92: ((eq 'f (cadr tem))
93: (setq filtermode t))
94: ((eq 'l (cadr tem))
95: (setq LabelFunctions nil))
96: ((eq 'p (cadr tem))
97: (setq g-psize
98: (cond ((eq '|6| (cadr i)) 6)
99: ((eq '|10| (cadr i)) 10)
100: ((eq '|12| (cadr i)) 12)
101: (t 8)))
102: (setq i (cdr i)))
103: ((eq 'v (cadr tem))
104: (setq outfilename
105: (concat "/tmp/vlp" (sys:getpid))))
106: (t (setq ll (cons (car i) ll)))))
107: (t (setq ll (cons (car i) ll)))))) ; else skip it
108: ;
109: (If debug then (msg (P errport) "args = " args N))
110: ;
111: ; insure all file names given actually exist before starting.
112: (cond ((null (do ((xx args (cdr xx)))
113: ((null xx) t)
114: (If (eq '- (getchar (car xx) 1))
115: then (setq xx (cdr xx)) ; skip switches
116: else (If (not (probef (car xx)))
117: then (msg (P errport)
118: "File does not exist: "
119: (car xx))
120: (return nil)))))
121: (return nil)))
122: ;
123: ; do before-starting-first-file actions
124: ;
125: (If outfilename then (setq outfile (outfile outfilename)))
126: (If (null args) then (setq readstdin t))
127: ;;; NOTE: the following concat'ing of strings is neccessary because
128: ;;; when vlp'ing vlp.l soelim would source the file.
129: (msg (P outfile) (concat "." "so /usr/lib/vlpmacs") N)
130: (setq g-title "")
131: ;
132: ;
133: (If readstdin
134: then (setq EOF nil)
135: (setq lines nil)
136: (If (not filtermode)
137: then (msg (P outfile) ".ps " g-psize N
138: ".vs " (+ g-psize 2) "p" N)
139: (msg (P outfile) ".Ti " '\" g-title '\" N)
140: (msg (P outfile) ".wh -1.25i He" N)
141: (msg (P outfile) ".nf" N))
142: (do ()
143: (EOF t)
144: (If filtermode then (skiptostarter))
145: (processblock))
146: else (do ((files args (cdr files)))
147: ((null files) t)
148: (If (eq '- (getchar (car files) 1))
149: then (If (eq '-p (car files))
150: then (If (null (setq g-psize
151: (cdr (assoc (cadr files)
152: '((\6 . 6)
153: (\8 . 8)
154: (\10 . 10)
155: (\12 . 12))))))
156: then (error "bad point size"
157: (cadr files)))
158: elseif (eq '-T (car files))
159: then (setq g-title (cadr files))
160: else (error "bad switch " (car files)))
161: (setq files (cdr files))
162: else (setq EOF nil)
163: (setq lines nil)
164: (cond ((null (errset
165: (setq inport (infile (car files)))))
166: (msg (P errport) "Can't open file: "
167: (car args))
168: (return nil)))
169: (If (not filtermode)
170: then (msg (P outfile)
171: ".ps " g-psize N
172: ".vs " (+ g-psize 2) "p" N)
173: (msg (P outfile)
174: ".Fi " (car files)
175: " \"" (GetModTime (car files)) '\"
176: " \"" (time-string) '\"
177: N)
178: (msg (P outfile)
179: ".Ti " '\" g-title '\" N)
180: (msg (P outfile) ".wh -1.25i He" N)
181: (msg (P outfile) ".nf" N))
182: (do ()
183: (EOF)
184: (If filtermode then (skiptostarter))
185: (processblock))
186: (close inport))))
187: (If outfilename
188: then
189: (close outfile)
190: (apply 'process
191: (ncons (concat "/bin/cat " outfilename
192: " | /usr/ucb/vtroff")))
193: (If debug
194: then (msg (P errport)
195: "Troff filename is " outfilename N)
196: else (sys:unlink outfilename)))))
197:
198:
199: ;--- skiptostarter :: skip to start directive (.Ls)
200: ;
201: (defun skiptostarter nil
202: ;
203: ; Define a simple finite state machine that reads and prints everything
204: ; up-to and including the directive .Ls.
205: ; All text outside of the .Ls directive is simply sent on to the output.
206: ;
207: (do ((State 0)
208: (c (tyi inport) (tyi inport)))
209: ((=& 4 State))
210: (cond ((and (=& State 0) (=& c #/.))
211: (setq State 1))
212: ((and (=& State 1) (=& c #/L))
213: (setq State 2))
214: ((and (=& State 2) (=& c #/s))
215: (setq State 3))
216: ((=& State 3) ; Read through arguments
217: (cond ((=& c #\lf)
218: (setq State 4)
219: (untyi c inport)))) ; In case text starts immed.
220: ((=& c #\eof)
221: (setq EOF t)
222: (return))
223: (t
224: (setq State 0)))
225: (tyo c outfile)))
226:
227: (defun processblock nil
228: ;
229: ; Read lines until we come to one that has no blank space at its begining.
230: ; Then process the previously accumulated lines.
231: ; If the last line contains the directive .Le, then find the next block
232: ; enclosed by .Ls.
233: ;
234: (do ((newline (getline) (getline)))
235: (nil)
236: (If (or EOF (and (zerop (car newline)) (cadr newline)))
237: then (flushlines)
238: (setq lines nil)
239: (If (=& (caadr newline) #/') ;then must quote ' for troff
240: then (msg (P outfile) "\\")
241: elseif (and filtermode
242: (=& (caadr newline) #/.)
243: (=& (cadadr newline) #/L)
244: (=& (caddadr newline) #/e))
245: then (msg (P outfile) ".Le" N)
246: (return nil))
247: (If EOF then (return nil)))
248: (setq lines (cons newline lines))))
249:
250:
251: ;--- getline :: read in new line and return structure:
252: ; (spaces chars)
253: ; spaces is a fixnum giving the number of spaces before the first
254: ; non-blank on the line.
255: ; chars is the list of characters appearing on the line, in order.
256: ;
257: (defun getline nil
258: (do ((col 1)
259: (spaces 0)
260: (chars nil)
261: (spacemode t)
262: (newc (tyi inport) (tyi inport) ))
263: (nil)
264: (If (=& #\eof newc)
265: then (If (null chars)
266: then (setq EOF t)
267: (return nil)
268: else (setq newc #\lf)))
269: (If (=& #\lf newc)
270: then (if (null chars)
271: then ; for totally blank lines return a large
272: ; space count so this won't be used for tags
273: (return (list space-count nil))
274: else (return (list spaces (nreverse chars)))))
275: (If (=& #\ff newc)
276: then (return (list spaces (nreverse (cons newc chars)))))
277: (If spacemode
278: then (If (=& #\sp newc)
279: then (setq spaces (1+ spaces))
280: elseif (=& #\tab newc)
281: then (setq spaces (* (/ (+ spaces 8) 8) 8))
282: else (setq spacemode nil
283: chars (list newc)))
284: else (setq chars (cons newc chars)))))
285:
286:
287: ;--- flushlines
288: ; go back on all lines and determine where to put tags and reference
289: ; tags.
290: ;
291: (defun flushlines nil
292: (let (thistag tagref rlines tagloc)
293: (inittagslist '(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
294: a b c d e f g h i j k l m n o p q r s t u v w x y z
295: 1 2 3 4 5 6 7 8 9 0))
296: (setq tagref nil tagloc nil rlines nil)
297: (do ((xx lines (cdr xx))
298: (thistagloc nil nil)
299: (pendingtags)
300: (thistagref))
301: ((null xx))
302: (setq rlines (cons (car xx) rlines)
303: spaces (caar xx)
304: thistag (alloctag))
305: ; determine which of the pending tags we can insert at this level
306: (do ((yy pendingtags (cdr yy)))
307: ((or (null yy) (>& spaces (caar yy)))
308: (setq pendingtags yy))
309: (setq thistagloc (cons (car yy) thistagloc)))
310: (setq tagloc (cons thistagloc tagloc))
311: ; free up the tags we allocated at this level.
312: (mapc '(lambda (x) (freetag (cadr x))) thistagloc)
313: ;
314: ; we only need to refer to a tags at this level if
315: ; we are not at the left edge of the paper
316: ;
317: (If (greaterp spaces 0)
318: then (setq tagref (cons thistag tagref))
319: (setq pendingtags (addtotags spaces thistag pendingtags))
320: else (setq tagref (cons nil tagref))))
321: (If debug then (msg " **tagref = " tagref N " **tagloc = " tagloc N))
322: ;
323: ; print out the lines.
324: ; the lines we want to print are in the list rlines. They are
325: ; in the correct order.
326: ;
327: (do ((yy rlines (cdr yy))
328: (commentmode nil nil)
329: (escapemode nil nil))
330: ((null yy))
331: ; first print out spaces
332: (setq sps (caar yy)
333: chs (cadar yy)
334: tls (car tagloc)
335: trname (car tagref))
336: (headercheck sps chs)
337: (If debug
338: then (msg " spaces " sps N " chs " chs N
339: " tagloc " (car tagloc) N " tagref " (car tagref) N))
340: (setq spout 0)
341: ; if there are characters on this line, print out
342: ; leading spaces
343: (if chs
344: then (do ()
345: ((eq spout sps))
346: (tyo #\sp outfile)
347: (setq spout (1+ spout)))
348:
349: (If trname then (tagrefprint trname)))
350:
351: (do ((xx chs (cdr xx)))
352: ((null xx)
353: (If (and tls (not (<& (caar tls) spout)))
354: then (do ((i spout (1+ i)))
355: ((null tls))
356: (If (=& (caar tls) i)
357: then (taglocprint (cadar tls))
358: (setq tls (cdr tls)))))
359: (If commentmode then (msg (P outfile) "\\fP"))
360: (terpr outfile))
361: (If (and tls (eq (caar tls) spout))
362: then (taglocprint (cadar tls))
363: (setq tls (cdr tls)))
364: (If (and (not escapemode)
365: (null commentmode)
366: (=& #/; (car xx)))
367: then (msg (P outfile) "\\fI")
368: (setq commentmode t)
369: elseif (=& #/\ (car xx))
370: then (tyo #/\ outfile)) ; escape backslashes
371: (If (memq (car xx) '(#/\ #//))
372: then (setq escapemode t)
373: else (setq escapemode nil))
374: (If (=& (car xx) #\ff)
375: then (msg (P outfile) N ".bp" N)
376: else (tyo (car xx) outfile))
377: (setq spout (1+ spout)))
378: (setq tagloc (cdr tagloc)
379: tagref (cdr tagref)))))
380:
381:
382: ; headercheck :: check if this is a function declaration.
383: ; currently this means that there are no more than 2 spaces before
384: ; the line begins and that the line begins with (defxxx <name>)
385: (defun headercheck (spaces chars)
386: (If (<& spaces 3)
387: then (If (setq rest (match chars '(#/( #/d #/e #/f)))
388: then (setq funcname (skippastblkn rest))
389: (If (and funcname LabelFunctions)
390: then (msg (P outfile) ".Lf " funcname N)))))
391:
392: ;--- match :: match lists
393: ; list1 - list of characters (fixnum rep)
394: ; list2 - master list of characters (fixnum rep)
395: ; list2 should be shorter than list1. If list2 is a substring of list1
396: ; then the rest of list1 will be returned. Otherwise nil is returned.
397: ;
398: (defun match (list1 list2)
399: (cond ((null list1) nil)
400: ((null list2) list1)
401: ((or (eq (car list1) (car list2))
402: (eq (car list1) (uppercase (car list2))))
403: (match (cdr list1) (cdr list2)))
404: (t nil)))
405:
406: ;--- uppercase :: convert fixnum rep to upper case
407: ; char - fixnum representation of character
408: ; convert character to upper case.
409: ;
410: (defun uppercase (ch)
411: (If (and (not (<& ch #/a)) (not (>& ch #/z)))
412: then (- ch #.(- #/a #/A))))
413:
414:
415:
416: ;--- skippastblnk :: skip to and past blank field and return following name
417: ; list - list of characters (fixnums)
418: ;
419: ; We skip past all non blanks then all blanks and finally implode the next
420: ; word after that and return it.
421: ;
422: ; bug- we can't tell the difference between a function named nil and
423: ; no valid function, but then again you can't have a function named nil.
424: ;
425: (defun skippastblkn (list)
426: (let (res)
427: ; skip to first blank
428: (do ()
429: ((null list) nil)
430: (If (=& (car list) #\sp) then (return nil))
431: (setq list (cdr list)))
432: ; skip to first non-blank
433: (do ()
434: ((null list) nil)
435: (If (not (=& (car list) #\sp)) then (return nil))
436: (setq list (cdr list)))
437: ; collect non blanks
438: (If list
439: then (do ()
440: ((null list))
441: (If (and (=& #\lpar (car list))
442: (null res))
443: then (setq list (cdr list)) ; form like "defun (foo ..)"
444: elseif (or (=& #\lpar (car list))
445: (=& #\rpar (car list))
446: (=& #\sp (car list)))
447: then (return nil)
448: else (setq res (cons (car list) res))
449: (setq list (cdr list))))
450: (If res then (implode (nreverse res))))))
451:
452: ;
453: ; add (spaces character) to list so that the spaces number are in
454: ; ascending order.
455: ;
456: (defun addtotags (spaces character oldlist)
457: (If (or (null oldlist) (>& spaces (caar oldlist)))
458: then (cons (list spaces character) oldlist)
459: else (do ((prevtag oldlist (cdr prevtag))
460: (thistag (cdr oldlist) (cdr thistag)))
461: ((or (null thistag) (>& spaces (caar thistag)))
462: (setq thistag (cons (list spaces character) thistag))
463: (rplacd prevtag thistag)))))
464:
465: (defun inittagslist (list)
466: (setq tagsleft list))
467:
468: (defun alloctag nil
469: (If (null tagsleft)
470: then (msg "Out of tags, aauuuuggh " N)
471: (exit)
472: else (prog1 (car tagsleft) (setq tagsleft (cdr tagsleft)))))
473:
474: (defun freetag (tagname)
475: (setq tagsleft (cons tagname tagsleft)))
476:
477:
478: (defun taglocprint (name)
479: (msg (P outfile) "\\k" name))
480:
481:
482: (defun tagrefprint (name)
483: (msg (P outfile) "\\h'|\\n" name "u'"))
484:
485:
486: ;---new stuff
487: ;
488: ;--- programs deal in lineblk's
489: ; A lineblk has these components:
490: ; class : normal or comment
491: ; comment is a line beginning with ;, in which case it is
492: ; not counted as far as spacing goes.
493: ; spaces : number of spaces before first useful info
494: ; dead : list of dead linblk's, that is those ready to print but
495: ; waiting for this line to be done.
496: ; tagref: tag to go to at the beginning of this line
497: ; tagloc : tags defined on this line
498: ; chars : list of characters to print
499:
500: (defun make-lineblk (class spaces chars)
501: (list class spaces nil nil nil chars))
502: (defmacro get:class (lineblk) `(car ,lineblk))
503: (defmacro set:class (lineblk val) `(setf (get:class ,lineblk) ,val))
504: (defmacro get:spaces (lineblk) `(cadr ,lineblk))
505: (defmacro set:spaces (lineblk val) `(setf (get:spaces ,lineblk) ,val))
506: (defmacro get:dead (lineblk) `(caddr ,lineblk))
507: (defmacro set:dead (lineblk val) `(setf (get:dead ,lineblk) ,val))
508: (defmacro get:tagref (lineblk) `(cadddr ,lineblk))
509: (defmacro set:tagref (lineblk val) `(setf (get:tagref ,lineblk) ,val))
510: (defmacro get:tagloc (lineblk) `(caddddr ,lineblk))
511: (defmacro set:tagloc (lineblk val) `(setf (get:tagloc ,lineblk) ,val))
512: (defmacro get:chars (lineblk) `(cadddddr ,lineblk))
513: (defmacro set:chars (lineblk val) `(setf (get:chars ,lineblk) ,val))
514:
515:
516: (defun vlp-interrupt-routine (sig)
517: (If (and (boundp 'outfilename) outfilename)
518: then (sys:unlink outfilename))
519: (exit 1))
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.