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