     var classes = {};   // Map of classes processed or in queue to their LineInfo
 
     queue.push(rootclass);
-    classes[rootclass] = new LineInfo(rootclass, "", "(internal)", 0); // synthetic
+    classes[rootclass] = new LineInfo(rootclass, "", false, "(internal)", 0); // synthetic
 
     while (queue.length > 0) {
         var cls = queue.shift();
         for ( var l=0 ; l < lines.length ; l++ ) {
             var ln = lines[l];
-            if (classes.hasOwnProperty(ln.base) && !classes.hasOwnProperty(ln.derived)) {
-                var basecls = classes[ln.base];
-                ln.baseref = basecls;
-                basecls.subrefs.push(ln);
-                classes[ln.derived] = ln;
-                queue.push(ln.derived);
-            }
+            if (classes.hasOwnProperty(ln.base)) {
+                if (!classes.hasOwnProperty(ln.derived)) {
+                    var basecls = classes[ln.base];
+                    ln.baseref = basecls;
+                    basecls.subrefs.push(ln);
+                    classes[ln.derived] = ln;
+                    ln.defcount = 1;
+                    queue.push(ln.derived);
+                }
+                else {
+                    // Flag it as used, but it's not canonical because
+                    // it's not in the classes table.
+                    ln.defcount = 1;
+                }
+            }
+        }
+    }
+
+    // Compute lines for multiple in-tree definitions, and attributes for not-within-tree definitions
+
+    for ( var l=0 ; l < lines.length ; l++ ) {
+        var ln = lines[l];
+        if (ln.defcount > 0 && classes.hasOwnProperty(ln.derived)) {
+            var c = classes[ln.derived];
+            if (c !== ln) {
+                c.defcount++;
+                c.otherlines.push({filename: ln.filename, lineno: ln.lineno});
+            }
+        }                
+        if (ln.defcount == 0 && classes.hasOwnProperty(ln.derived)) {
+            var c = classes[ln.derived];
+            c.external = true;
+            c.exolines.push({filename: ln.filename, lineno: ln.lineno});
         }
     }
 

        var lines = readFile(fn);
        var ignore = false;
        var ignorelevel = 0;
        for ( var i=0 ; i < lines.length ; i++ ) {
            var l = lines[i];
            var result;

    [ new RegExp("^.*/shell/.*$"),
     new RegExp("^.*/extensions/ST_.*$"),
     new RegExp("^.*/extensions/JavaGlue.*$"),
     new RegExp("^.*/eval/.*$"),
     new RegExp("^.*/MMgc/GCTests.*$")];

class LineInfo
{
    function LineInfo(_derived, _base, _isExact, _filename, _lineno) {
        derived = _derived;
        base = _base;
        isExact = _isExact;
        filename = _filename;
        lineno = _lineno;
    }

    public function toString() {
        return "derived=" + derived + ", base=" + base + ", filename=" + filename + ", lineno=" + lineno + ", defcount=" + defcount;
    }

    var derived;            // name of the class being defined
    var base;               // name of its base class
    var isExact;            // true iff the class is exactly traced
    var filename;           // file where we found the definition of 'derived'
    var lineno;             // line where we found the definition of 'derived'
    var baseref = null;     // reference to the LineInfo for the base class
    var subrefs = [];       // list of known subclasses
    var destructor = false; // true if we believe this has a destructor
    var defcount = 0;       // number of definitions
    var otherlines = [];    // for in-tree lines beyond the first: locations of definition: { filename:x, lineno:n }
    var exolines = [];      // for out-of-tree lines: locations of definition: { filename:x, lineno:n }
    var external = false;   // true if there are definitions of the 'derived' name not within the tree
}

main(System.argv);
