|
|
1.1 root 1: (setq rcs-lxref-ident
2: "$Header: lxref.l,v 1.1 83/01/26 12:16:24 jkf Exp $")
3:
4: ;------ lxref: lisp cross reference program
5: ;-- author: j foderaro
6: ; This program generates a cross reference listing of a set of one or
7: ; more lisp files. It reads the output of cross reference files
8: ; generated by the compiler. These files usually have the extension .x .
9: ; the .x files are lisp readable. There format is:
10: ; The first s-expression is (File <filename>) where <filename> is the
11: ; name of the lisp source file.
12: ; Then there is one s-expression for each function (including macros)
13: ; which is defined in the file. The car of each expression is the function
14: ; name, the cadr is the function type and the cddr is a list of those
15: ; functions called
16: ;
17: ; lxref can be run from the command level
18: ; % lxref foo.x bar.x
19: ; or in this way
20: ; % lxref
21: ; -> (lxref foo.x bar.x)
22: ;
23: ; There is one option, that is changing the ignorelevel. If a function
24: ; is called by more than ignorelevel functions then all those functions
25: ; are listed, instead a summary of the number of calls is printed. This
26: ; is useful for preventing the printing of massive lists for common
27: ; system functions such as setq.
28: ; To change the ignorelevel to 40 you would type:
29: ;
30: ; % lxref -40 foo.x bar.x
31: ;
32: ;; internal data structures used in lxref:
33: ; funcs : list of functions mentioned either as caller or as callee
34: ; on each function in funcs, the property list contains some of these
35: ; indicators:
36: ; i-seen : always contains t [this is so we can avoid (memq foo funcs)
37: ; i-type : list of the types this function was declared as. In 1-1
38: ; corresp with i-home
39: ; i-home : list of files this function was declared in. In 1-1 corresp
40: ; with i-type
41: ; i-callers: list of functions calling this function
42:
43:
44:
45:
46:
47: ; insure we have plenty of space to grow into
48: (opval 'pagelimit 9999)
49:
50:
51: (declare (special xref-readtable width ignorefuncs ignorelevel readtable
52: user-top-level poport i-seen i-type i-callers docseen
53: i-Chome i-Doc i-home funcs
54: callby-marker debug-mode
55: anno-off-marker
56: anno-on-marker))
57:
58: (setq ignorelevel 50)
59: (setq callby-marker (exploden ";.. ")
60: anno-off-marker (exploden ";.-")
61: anno-on-marker (exploden ";.+"))
62:
63: ;--- xrefinit :: called automatically upon startup
64: ;
65: (def xrefinit
66: (lambda nil
67: (let ((args (command-line-args))
68: (retval))
69: ; readtable should be the same as it was when liszt wrote
70: ; the xref file
71: (if args
72: then (signal 2 'exit) ; die on interrupt
73: (signal 15 'exit) ; die on sigterm
74: (setq user-top-level nil)
75: (let ((retval (car (errset (funcall 'lxref args)))))
76: (exit (if retval thenret else -1)))
77: else (patom "Lxref - lisp cross reference program")
78: (terpr poport)
79: (setq user-top-level nil)))))
80:
81: (setq user-top-level 'xrefinit)
82:
83: ;--- lxref :: main function
84: ;
85: (defun lxref fexpr (files)
86: (prog (p funcs i-seen i-home i-type i-callers filenm caller callee name
87: home type caller temp fname callers clength i-Chome i-Doc docseen
88: Chome Doc anno-mode debug-mode)
89:
90: (setq xref-readtable (makereadtable t))
91: (setq i-seen (gensym) i-home (gensym) i-type (gensym)
92: i-callers (gensym) i-Chome (gensym) i-Doc (gensym))
93:
94: ; check for the ignorelevel option
95: ; it must be the first option given.
96: ;
97: (If (and files (eq #/- (getcharn (car files) 1)))
98: then (If (fixp
99: (setq temp (readlist (cdr (explode (car files))))))
100: then (setq ignorelevel temp)
101: (setq files (cdr files))))
102:
103: ; process all files. if a -a is seen, go into annotate mode.
104: ; otherwise generate an xref file.
105: ;
106: (do ((ii files (cdr ii)))
107: ((null ii))
108: (if (eq '-d (car ii))
109: then (setq debug-mode t)
110: elseif anno-mode
111: then (process-annotate-file (car ii))
112: elseif (eq '-a (car ii))
113: then (setq anno-mode t)
114: else (process-xref-file (car ii))))
115: (if (not anno-mode) (generate-xref-file))
116: (return 0)))
117:
118: ;.. process-xref-file
119: (defun illegal-file (name)
120: (msg "File " name " is not a valid cross reference file" N))
121:
122: ;--- process-xref-file :: scan the information in an xref file
123: ; if the name ends in .l then change it to .x
124: ;
125: ;.. lxref
126: (defun process-xref-file (name)
127: (if debug-mode then (msg "process-xref-file: " name N))
128: (let (p fname filenm)
129: ; convert foo.l to foo.x
130: (setq fname (nreverse (exploden name)))
131: (If (and (eq #/l (car fname)) (eq #/. (cadr fname)))
132: then (setq fname (implode (nreverse (cons #/x (cdr fname)))))
133: else (setq fname name))
134:
135: ; now look for foo or foo.x
136: (If (and (null (errset (setq p (infile fname)) nil))
137: (null (errset (setq p (infile (concat fname ".x"))) nil)))
138: then (msg "Couldn't open " name N)
139: else (setq filenm (car (errset (read p))))
140: (If (dtpr filenm)
141: then (If (eq 'File (car filenm))
142: then (setq filenm (cadr filenm))
143: (process-File p filenm)
144: elseif (eq 'Chome (car filenm))
145: then (process-Chome p)
146: elseif (eq 'Doc (car filenm))
147: then (setq docseen t) (process-Doc p)
148: else (illegal-file name))
149: else (illegal-file name))
150: (close p))))
151:
152:
153: ;--- process-File :: process an xref file from liszt
154: ;
155: ;.. process-xref-file
156: (defun process-File (p filenm)
157: (let ((readtable xref-readtable))
158: (do ((jj (read p) (read p))
159: (caller)
160: (callee))
161: ((null jj) (close p))
162: (setq caller (car jj))
163: (If (not (get caller i-seen))
164: then (putprop caller t i-seen)
165: (push caller funcs)) ; add to global list
166: ; remember home of this function (and allow multiple homes)
167: (push filenm (get caller i-home))
168:
169: ; remember type of this function (and allow multiple types)
170: (push (cadr jj) (get caller i-type))
171:
172: ; for each function the caller calls
173: (do ((kk (cddr jj) (cdr kk)))
174: ((null kk))
175: (setq callee (car kk))
176: (If (not (get callee i-seen)) then (putprop callee t i-seen)
177: (push callee funcs))
178: (push (cons caller filenm) (get callee i-callers))))))
179:
180: ;.. process-xref-file
181: (defun process-Chome (p)
182: (do ((jj (read p) (read p))
183: (caller))
184: ((null jj) (close p))
185: (setq caller (car jj))
186: (If (not (get caller i-seen))
187: then (putprop caller t i-seen)
188: (push caller funcs)) ; add to global list
189: ; remember home of this function (and allow multiple homes)
190: (putprop caller (cons (cdr jj) (get caller i-Chome)) i-Chome)))
191:
192: ;--- process-Doc :: process a Doc file
193: ;
194: ; A doc file begins with an entry (Doc).
195: ; subsequent entries are (Name File) and this means that function
196: ; Name is defined in file File. This type of file is generated
197: ; by a sed and awk script passing over the franz manual. (see the
198: ; Makefile in the doc directory).
199: ;
200: ;.. process-xref-file
201: (defun process-Doc (p)
202: (do ((jj (read p) (read p))
203: (caller))
204: ((null jj) (close p))
205: (setq caller (car jj))
206: (If (not (get caller i-seen))
207: then (putprop caller t i-seen)
208: (push caller funcs)) ; add to global list
209: ; remember home of this function (and allow multiple homes)
210: (putprop caller (cons (cadr jj) (get caller i-Doc)) i-Doc)))
211:
212: ;.. generate-xref-file
213: (defun terprchk (wid)
214: (cond ((> (setq width (+ wid width)) 78.)
215: (terpr)
216: (patom " ")
217: (setq width (+ 8 wid)))))
218:
219: ; determine type of function
220: ;.. generate-xref-file
221: (defun typeit (fcn)
222: (cond ((bcdp fcn) (getdisc fcn))
223: ((dtpr fcn) (car fcn))))
224:
225:
226: ;.. lxref
227: (defun generate-xref-file ()
228: ; sort alphabetically
229: (setq funcs (sort funcs 'alphalessp))
230:
231: ; now print out the cross reference
232: (do ((ii funcs (cdr ii))
233: (name) (home) (type) (callers) (Chome) (Doc) (clength))
234: ((null ii))
235: (setq name (car ii)
236: home (get name i-home)
237: type (get name i-type)
238: callers (get name i-callers)
239: Chome (get name i-Chome)
240: Doc (get name i-Doc))
241:
242: (If (lessp (setq clength (length callers)) ignorelevel)
243: then (setq callers (sortcar callers 'alphalessp)))
244:
245: (do ((xx Chome (cdr xx)))
246: ((null xx))
247: (setq home (cons (concat "<C-code>:" (caar xx))
248: home)
249: type (cons (cadar xx) type)))
250:
251: (If (null home)
252: then (setq home (If (getd name)
253: then (setq type
254: (ncons (typeit (getd name))))
255: '(Franz-initial)
256: else '(Undefined))))
257:
258: (patom name)
259: (patom " ")
260:
261:
262: (If (null (cdr type))
263: then (patom (car type))
264: (patom " ")
265: (patom (car home))
266: else (patom "Mult def: ")
267: (mapcar '(lambda (typ hom)
268: (patom typ)
269: (patom " in ")
270: (patom hom)
271: (patom ", "))
272: type
273: home))
274:
275:
276: (If docseen
277: then (If Doc then (msg " [Doc: " (If (cdr Doc) then Doc
278: else (car Doc)) "]")
279: else (msg " [**undoc**]")))
280: (If (null callers) then (msg " *** Unreferenced ***"))
281: (terpr)
282: (patom " ")
283: (cond ((null callers))
284: ((not (lessp clength ignorelevel))
285: (patom "Called by ")
286: (print clength)
287: (patom " functions"))
288: (t (do ((jj callers (cdr jj))
289: (calle)
290: (width 8))
291: ((null jj))
292: ; only print name if in same file
293: (setq calle (caar jj))
294: (cond ((memq (cdar jj) home)
295: (terprchk (+ (flatc calle) 2))
296: (patom calle))
297: (t (terprchk (+ (flatc calle) 6 (flatc (cdar jj))))
298: (patom calle)
299: (patom " in ")
300: (patom (cdar jj))))
301: (If (cdr jj) then (patom ", ")))))
302: (terpr)
303: (terpr)
304: botloop ))
305:
306:
307: ;--- annotate code
308:
309:
310:
311: ;--- process-annotate-file :: anotate a file
312: ;
313: ;.. lxref
314: (defun process-annotate-file (filename)
315: (let (sourcep outp)
316: ; make sure file exists and write annotate file as a
317: ; file with the prefix #,
318: (if (null (errset (setq sourcep (infile filename))))
319: then (msg "will ignore that file " N)
320: else ; will write to file.A (erasing the final l)
321: (let ((filen (concat "#," filename)))
322: (setq outp (outfile filen))
323: (anno-it sourcep outp)
324: (close outp)
325: ; now mv the original filename to #dfilename
326: ; and the annotated file to the original file
327: (let ((oldcopy (concat "#." filename)))
328: (if (null (errset
329: (progn (if (probef oldcopy)
330: then (sys:unlink oldcopy))
331: (sys:link filename oldcopy)
332: (sys:unlink filename)
333: (sys:link filen filename)
334: (sys:unlink filen))))
335: then (msg "An error occured while mving files around "
336: N
337: "files possibly affected "
338: filename oldcopy filen)))))))
339:
340:
341: ;.. process-annotate-file
342: (defun anno-it (inp outp)
343: (do ((xx (read-a-line inp) (read-a-line inp))
344: (anno-it t))
345: ((null xx))
346: (if (match xx 1 callby-marker) ; flush anno lines
347: then (flush-a-line outp inp)
348: elseif (match xx 1 anno-off-marker)
349: then (setq anno-it nil) ; ';#-' turns off annotating
350: (write-a-line xx outp inp)
351: elseif (match xx 1 anno-on-marker)
352: then (setq anno-it t)
353: (write-a-line xx outp inp)
354: else (if anno-it then (anno-check xx outp))
355: (write-a-line xx outp inp))))
356:
357:
358: ;;; file reading code for annotate function
359: ; lines are read with (read-a-line port). It will read up to the
360: ; first 127 characters in the line, returning a hunk whose cxr 0 is the
361: ; max(index) + 1 of the characters in the hunk. the oversize-line flag
362: ; will be set if there are still more character to be read from this line.
363: ;
364: ; the line should be printed by calling (print-a-line buffer) or if it isn't
365: ; to be printed, (flush-a-line) should be called (which will check the
366: ; oversize-line flag and flush unread input too).
367: ;
368: (declare (special inp-buffer oversize-line))
369:
370: (setq inp-buffer (makhunk 128))
371:
372: ;.. anno-it
373: (defun read-a-line (port)
374: (setq oversize-line nil)
375: (do ((i 1 (1+ i))
376: (ch (tyi port) (tyi port)))
377: ((or (eq #\newline ch)
378: (eq #\eof ch))
379: (if (or (eq #\newline ch) (>& i 1))
380: then (rplacx 0 inp-buffer i) ; store size
381: inp-buffer ; return buffer
382: else nil)) ; return nil upon eof
383: (rplacx i inp-buffer ch)
384: (if (>& i 126)
385: then (setq oversize-line t)
386: (rplacx 0 inp-buffer (1+ i))
387: (return inp-buffer))))
388:
389: ;--- write-a-line :: write the given buffer and check for oversize-line
390: ;
391: ;.. anno-it
392: (defun write-a-line (buf oport iport)
393: (do ((max (cxr 0 buf))
394: (i 1 (1+ i)))
395: ((not (<& i max))
396: (if oversize-line
397: then (oversize-check oport iport t)
398: else (terpr oport)))
399: (tyo (cxr i buf) oport)))
400:
401: ;.. anno-it
402: (defun flush-a-line (oport iport)
403: (oversize-check oport iport nil))
404:
405: ;.. flush-a-line, write-a-line
406: (defun oversize-check (oport iport printp)
407: (if oversize-line
408: then (do ((ch (tyi iport) (tyi iport)))
409: ((or (eq ch #\eof) (eq ch #\newline))
410: (cond ((and printp (eq ch #\newline))
411: (tyo ch oport))))
412: (if printp then (tyo ch oport)))))
413:
414:
415:
416: ;.. anno-it
417: (defun anno-check (buffer outp)
418: (if (match buffer 1 '(#\lpar #/d #/e #/f))
419: then (let (funcname)
420: (if (setq funcname (find-func buffer))
421: (let ((recd (get funcname i-callers)))
422: (if recd
423: then (printrcd recd outp)))))))
424:
425: ;--- printrcd :: print a description
426: ;
427: ;.. anno-check
428: (defun printrcd (fcns port)
429: (let ((functions (sortcar fcns 'alphalessp)))
430: (print-rec functions port 0)))
431:
432: ;.. print-rec, printrcd
433: (defun print-rec (fcns p wide)
434: (if fcns
435: then (let ((size (flatc (caar fcns))))
436: (if (>& (+ size wide 2) 78)
437: then (msg (P p) N )
438: (setq wide 0))
439: (if (=& wide 0)
440: then (mapc '(lambda (x) (tyo x p)) callby-marker)
441: (setq wide (length callby-marker)))
442: (if (not (=& wide 4))
443: then (msg (P p) ", ")
444: (setq wide (+ wide 2)))
445: (msg (P p) (caar fcns))
446: (print-rec (cdr fcns) p (+ wide size 2)))
447: else (msg (P p) N)))
448:
449:
450:
451: ;--- match :: try to locate pattern in buffer
452: ; start at 'start' in buf.
453: ;.. anno-check, anno-it, match
454: (defun match (buf start pattern)
455: (if (null pattern)
456: then t
457: elseif (and (<& start (cxr 0 buf))
458: (eq (car pattern) (cxr start buf)))
459: then (match buf (1+ start) (cdr pattern))))
460:
461: ;--- find-func :: locate function name on line
462: ;
463: ;.. anno-check
464: (defun find-func (buf)
465: ; first locate first space or tab
466: (do ((i 1 (1+ i))
467: (max (cxr 0 buf))
468: (die))
469: ((or (setq die (not (<& i max)))
470: (memq (cxr i buf) '(#\space #\tab)))
471: (if die
472: then nil ; can find it, so give up
473: else ; find first non blank
474: (do ((ii i (1+ ii)))
475: ((or (setq die (not (<& ii max)))
476: (not (memq (cxr ii buf) '(#\space #\tab))))
477: (if (or die (eq (cxr ii buf) #\lpar))
478: then nil
479: else ; fid first sep or left paren
480: (do ((iii (1+ ii) (1+ iii)))
481: ((or (not (<& iii max))
482: (memq (cxr iii buf)
483: '(#\space #\tab #\lpar)))
484: (implode-fun buf ii (1- iii)))))))))))
485:
486: ;--- implode-fun :: return implode of everything between from and to in buf
487: ;
488: ;.. find-func
489: (defun implode-fun (buf from to)
490: (do ((xx (1- to) (1- xx))
491: (res (list (cxr to buf)) (cons (cxr xx buf) res)))
492: ((not (<& from xx))
493: (implode (cons (cxr from buf) res)))))
494:
495:
496:
497:
498:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.