"Fossies" - the Fresh Open Source Software Archive

Member "ncc-2.8/nccnav/nccnav.C" (24 Jul 2008, 35640 Bytes) of package /linux/privat/old/ncc-2.8.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // sxanth@ceid.upatras.gr
    2 #include <stdio.h>
    3 #include <stdlib.h>
    4 #include <string.h>
    5 #include <ncurses.h>
    6 #include <ctype.h>
    7 #include <unistd.h>
    8 #include <sys/mman.h>
    9 #include <sys/types.h>
   10 #include <sys/stat.h>
   11 #include <signal.h>
   12 #include <fcntl.h>
   13 
   14 #include "dbstree.h"
   15 #include "inttree.h"
   16 
   17 // If you don't want an external viewer,
   18 // comment out the definitions of SOURCE_VIEWER
   19 #define SOURCE_VIEWER_INDENT "indent -st -kr | less"
   20 #define SOURCE_VIEWER "less"
   21 #define COLOR_VIEWER "vi -R"
   22 #define OUTFILE1 "nccnav.outs"
   23 
   24 static char *svr;
   25 static char CWD [1000];
   26 
   27 // "uti.mod"
   28 //
   29 // utilities
   30 //
   31 
   32 static char *strDup (char *s)
   33 {
   34     return strcpy (new char [strlen (s) + 1], s);
   35 }
   36 
   37 #define SHUT_UP_GCC(x) (void)x;
   38 
   39 static void progress ()
   40 {
   41 static  char pchars [] = "-\\|/", out [] = { '\r', '[', 0, ']' };
   42 static  int pi, ti;
   43     if (!(ti++ & 127)) {
   44         out [2] = pchars [pi = (pi + 1)%(sizeof pchars - 1)];
   45         write (fileno (stdout), out, sizeof out);
   46     }
   47 }
   48 
   49 static void aprogress ()
   50 {
   51 static  int ti;
   52     char out [] = { '\r', '[', 0, ']' };
   53     out [2] = '0' + ti++;
   54     write (fileno (stdout), out, sizeof out);
   55 }
   56 
   57 class foreach_intNode
   58 {
   59    protected:
   60 virtual void A (intNode*) = 0;
   61     void inorder (intNode*);
   62    public:
   63 };
   64 
   65 void foreach_intNode::inorder (intNode *i)
   66 {
   67     if (i->less) inorder (i->less);
   68     A (i);
   69     if (i->more) inorder (i->more);
   70 }
   71 
   72 
   73 class load_file
   74 {
   75     int fd;
   76    public:
   77     load_file (char*);
   78     int success;
   79     char *data;
   80     int len;
   81     ~load_file ();
   82 };
   83 
   84 #define ZC_OK 0
   85 #define ZC_NA 1
   86 #define ZC_AC 2
   87 #define ZC_FF 3
   88 
   89 #ifdef _POSIX_MAPPED_FILES
   90 
   91 load_file::load_file (char *f)
   92 {
   93     data = NULL;
   94     success = ZC_NA;
   95     fd = -1;
   96 
   97     struct stat statbuf;
   98     if (stat (f, &statbuf) == -1) return;
   99     len = statbuf.st_size;
  100     if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return;
  101 
  102     success = ((data = (char*) mmap (0, len, PROT_READ, MAP_PRIVATE, fd, 0))
  103          != MAP_FAILED) ? ZC_OK : ZC_FF;
  104 }
  105 
  106 load_file::~load_file ()
  107 {
  108     if (data && data != MAP_FAILED) munmap (data, len);
  109     if (fd != -1) close (fd);
  110 }
  111 #else
  112 
  113 load_file::load_file (char *f)
  114 {
  115     data = NULL;
  116     success = ZC_NA;
  117     fd = -1;
  118 
  119     struct stat statbuf;
  120     if (stat (f, &statbuf) == -1) return;
  121     len = statbuf.st_size;
  122     if (len == -1 || (fd = open (f, O_RDONLY)) == -1) return;
  123 
  124     data = (char*) malloc (len);
  125     success = read (fd, data, len) == len ? ZC_OK : ZC_FF;
  126 }
  127 
  128 load_file::~load_file ()
  129 {
  130     if (data) free (data);
  131     if (fd != -1) close (fd);
  132 }
  133 
  134 #endif
  135 // "st1.mod"
  136 //
  137 // Symbol tree
  138 //
  139 
  140 class symNode : public dbsNodeStr
  141 {
  142    public:
  143     symNode *less, *more;
  144     unsigned int i;
  145     symNode ();
  146     ~symNode ();
  147 };
  148 
  149 static dbsTree<symNode> symTree;
  150 static unsigned int nsym = 1;
  151 
  152 symNode::symNode () : dbsNodeStr ()
  153 {
  154     symTree.addself (this);
  155     i = nsym++;
  156 }
  157 
  158 symNode::~symNode ()
  159 {
  160     if (less) delete less;
  161     if (more) delete more;
  162 }
  163 
  164 unsigned int enter_symbol (char *sy)
  165 {
  166     DBS_STRQUERY = sy;
  167     symNode *s = (symNode*) symTree.dbsFind ();
  168     if (!s) {
  169         DBS_STRQUERY = strDup (DBS_STRQUERY);
  170         s = new symNode;
  171         progress ();
  172     }
  173     return s->i;
  174 }
  175 //
  176 // Hyper nodes
  177 //
  178 
  179 class hyperNode : public intNode
  180 {
  181    public:
  182     void settle ();
  183     intTree itree;
  184     unsigned int *rel;
  185     hyperNode ();
  186     void relates (unsigned int);
  187     void rsettle ();
  188 };
  189 
  190 static intTree hyperTree;
  191 
  192 hyperNode::hyperNode () : intNode (&hyperTree)
  193 { }
  194 
  195 #define SOURCE_ID   0
  196 #define STRUCT_ID   1
  197 #define CALLER_ID   2
  198 #define GLOBAL_ID   3
  199 #define CALL_ID     4
  200 #define MEMBER_ID   5
  201 #define NFIELDS     6
  202 #define RECURS_BOOST    7   /* color */
  203 
  204 #define BASE        (1000*1000)
  205 #define SOURCE_BASE (SOURCE_ID * BASE)
  206 #define CALLER_BASE (CALLER_ID * BASE)
  207 #define GLOBAL_BASE (GLOBAL_ID * BASE)
  208 #define CALL_BASE   (CALL_ID * BASE)
  209 #define STRUCT_BASE (STRUCT_ID * BASE)
  210 #define MEMBER_BASE (MEMBER_ID * BASE)
  211 #define FIELD_BASE  (NFIELDS * BASE)
  212 #define RECURS_BASE (RECURS_BOOST * BASE)
  213 
  214 static unsigned int symbol_of (unsigned int i)
  215 {
  216     return i % BASE;
  217 }
  218 
  219 static unsigned int base_of (unsigned int i)
  220 {
  221     return i / BASE;
  222 }
  223 
  224 unsigned int counts [NFIELDS], dupes;
  225 
  226 void hyperNode::relates (unsigned int y)
  227 {
  228     if (!itree.intFind (y)) new intNode (&itree);
  229     else if (base_of (y) != STRUCT_ID && base_of (Key) != STRUCT_ID)
  230         dupes++;
  231 }
  232 
  233 static void relate (int x, int y)
  234 {
  235     hyperNode *h = (hyperNode*) hyperTree.intFind (x);
  236 
  237     if (!h) {
  238         h = new hyperNode;
  239         counts [base_of (x)]++;
  240     }
  241     h->relates (y);
  242 }
  243 
  244 static int nlinks = 0;
  245 
  246 static void relative (int x, int y)
  247 {
  248     ++nlinks;
  249     relate (base_of (x) == CALLER_ID ? x - CALLER_BASE + CALL_BASE : x, y);
  250     relate (base_of (y) == CALLER_ID ? y - CALLER_BASE + CALL_BASE : y, x);
  251 }
  252 // "repl.mod"
  253 //
  254 // macro replacements
  255 //
  256 
  257 static struct repl_s {
  258     unsigned int r, f;
  259 } * replarr;
  260 
  261 static unsigned int nreplarr, areplarr;
  262 
  263 static void stor_replacement (unsigned int r, unsigned int f)
  264 {
  265     if (nreplarr == areplarr)
  266         replarr = (repl_s*) realloc (replarr, sizeof (repl_s) * (areplarr += 64));
  267     replarr [nreplarr].r = r;
  268     replarr [nreplarr++].f = f;
  269 }
  270 
  271 static void repl_line (char *l)
  272 {
  273     char *d = strchr (l, ' ');
  274 
  275     if (d) {
  276         *d = 0;
  277         stor_replacement (enter_symbol (l) + CALL_BASE, enter_symbol (d + 1) + CALL_BASE);
  278     }
  279 }
  280 
  281 void do_replacements ()
  282 {
  283     unsigned int i, j, k, nu = 0, *un = (unsigned int*) alloca (sizeof * un * nreplarr);
  284     hyperNode **hns = (hyperNode**) alloca (sizeof * hns * nreplarr);
  285 
  286     for (i = 0; i < nreplarr; i++) {
  287         for (j = 0; j < nu; j++)
  288             if (un [j] == replarr [i].r) break;
  289         if (j < nu) continue;
  290         un [nu++] = replarr [i].r;
  291         hyperNode *h = hns [j] = (hyperNode*) hyperTree.intFind (replarr [i].r);
  292         if (!h) continue;
  293         h->settle ();
  294         for (j = 0; j < (unsigned int) h->itree.cnt; j++) {
  295             hyperNode *h2 = (hyperNode*) hyperTree.intFind (h->rel [j] - CALLER_BASE + CALL_BASE);
  296             intNode *n = h2->itree.intFind (replarr [i].r);
  297             if (n) {
  298                 n->intRemove (&h2->itree);
  299                 delete n;
  300             }
  301         }
  302         h->intRemove (&hyperTree);
  303     }
  304 
  305     for (i = 0; i < nreplarr; i++) {
  306         for (j = 0; j < nu; j++)
  307             if (un [j] == replarr [i].f) break;
  308         if (j < nu) {
  309             unsigned int l, m;
  310             k = replarr [i].f;
  311             for (j = 0; un [j] != replarr [i].r; j++);
  312             for (l = 0; l < nreplarr; l++)
  313                 if (replarr [l].r == k) {
  314                     for (m = 0; m < nu; m++)
  315                         if (un [m] == replarr [l].f) break;
  316                     if (m < nu) continue;
  317                     if (hns [j]) for (m = 0; (int) m < hns [j]->itree.cnt; m++)
  318                         relative (hns [j]->rel [m], replarr [l].f);
  319                 }
  320             continue;
  321         }
  322         for (j = 0; un [j] != replarr [i].r; j++);
  323         if (hns [j]) for (k = 0; (int) k < hns [j]->itree.cnt; k++)
  324             relative (hns [j]->rel [k], replarr [i].f);
  325     }
  326 }
  327 // "ln.mod"
  328 //
  329 // Line numbers
  330 //
  331 
  332 struct file_chunk {
  333     unsigned int start, end;
  334 } NoChunk = { ~0, ~0 };
  335 
  336 class lnnode : public intNode
  337 {
  338    public:
  339     file_chunk line;
  340     lnnode (unsigned int, unsigned int);
  341 };
  342 
  343 static intTree lnTree;
  344 
  345 lnnode::lnnode (unsigned int l, unsigned int e) : intNode (&lnTree)
  346 {
  347     line.start = l;
  348     line.end = e;
  349 }
  350 
  351 static void enter_line (unsigned int f, unsigned int l, unsigned int e)
  352 {
  353     if (!lnTree.intFind (f)) new lnnode (l, e);
  354 }
  355 
  356 file_chunk line_of (unsigned int f)
  357 {
  358     lnnode *l = (lnnode*) lnTree.intFind (f);
  359     return (l) ? l->line : NoChunk;
  360 }
  361 // "agr.mod"
  362 //
  363 // Make structure -> members link
  364 //
  365 
  366 void struct_fp_link (char *s)
  367 {
  368     char *dot, ss [100];
  369 
  370     if (!(dot = strchr (s, '.'))) return;
  371     *dot = 0; strcpy (ss, s+1); *dot = '.';
  372     relative (CALL_BASE + enter_symbol (s),
  373           STRUCT_BASE + enter_symbol (ss));
  374 }
  375 
  376 void struct_link (char *s)
  377 {
  378     char *dot, ss [100];
  379 
  380     if (!(dot = strchr (s, '.'))) return;
  381     *dot = 0; strcpy (ss, s); *dot = '.';
  382     relative (MEMBER_BASE + enter_symbol (s),
  383           STRUCT_BASE + enter_symbol (ss));
  384 }
  385 //
  386 // Structure line numbers
  387 //
  388 
  389 void struct_loc (char *s)
  390 {
  391     char *p;
  392 
  393     if (!(p = strchr (s, ' ')))
  394         return;
  395     *p++ = 0;
  396     enter_line (enter_symbol (s) + STRUCT_BASE, atoi (p), atoi (strchr (p, ' ')));
  397 }
  398 //
  399 // Symbol Table
  400 //
  401 
  402 static struct st_entry {
  403     char *name;
  404     int weight;
  405 } *symbol_table;
  406 
  407 static void totable (symNode *s)
  408 {
  409 static  int order = 0;
  410     symbol_table [s->i].name = s->Name;
  411     symbol_table [s->i].weight = order++;
  412 }
  413 
  414 void make_symbol_table ()
  415 {
  416     symbol_table = new st_entry [nsym];
  417     symTree.foreach (totable);
  418     delete symTree.root;
  419 }
  420 
  421 static int weight (unsigned int i)
  422 {
  423     return symbol_table [symbol_of (i)].weight;
  424 }
  425 
  426 static char *symbol_name (unsigned int i)
  427 {
  428     return symbol_table [symbol_of (i)].name;
  429 }
  430 // "hns.mod"
  431 //
  432 // Settle hypernodes
  433 //
  434 
  435 static unsigned int *tmp_table;
  436 
  437 class enter_intnodes : public foreach_intNode
  438 {
  439     void A (intNode *i) {
  440         *tmp_table++ = i->Key;
  441     }
  442    public:
  443     enter_intnodes (intTree *I) { if (I->root) inorder (I->root); }
  444 };
  445 
  446 void hyperNode::settle ()
  447 {
  448     if (!itree.root)
  449         return;
  450     tmp_table = rel = new unsigned int [itree.cnt];
  451     enter_intnodes E (&itree);
  452     SHUT_UP_GCC (E)
  453     delete itree.root;
  454 }
  455 
  456 class hyper_settle : public foreach_intNode
  457 {
  458    protected:
  459     void A (intNode *i) {
  460         ((hyperNode*)i)->settle ();
  461     }
  462    public:
  463     hyper_settle () { inorder (hyperTree.root); }
  464 };
  465 
  466 void settle_hyperNodes ()
  467 {
  468     hyper_settle H;
  469     SHUT_UP_GCC (H)
  470 }
  471 //
  472 // Sort symbol indexes
  473 //
  474 
  475 static void swap (unsigned int v[], int i, int j)
  476 {
  477     unsigned int swp = v [i];
  478     v [i] = v [j];
  479     v [j] = swp;
  480 }
  481 
  482 void qsort (unsigned int v[], int left, int right)
  483 {
  484     int i, last, wleft;
  485 
  486     if (left >= right)
  487         return;
  488     swap (v, left, (left+right)/2);
  489     wleft = weight (v [left]);
  490     last = left;
  491     for (i = left+1; i <= right; i++)
  492         if (weight (v [i]) < wleft)
  493             swap (v, i, ++last);
  494     swap (v, last, left);
  495     qsort (v, left, last-1);
  496     qsort (v, last+1, right);
  497 }
  498 
  499 void sort_fields (unsigned int v [], int n)
  500 {
  501     int i, j, k;
  502     struct {
  503         unsigned int *v;
  504         int n;
  505     } field [NFIELDS];
  506 
  507     for (i = 0; i < NFIELDS; i++) {
  508         field [i].v = (unsigned int*) alloca (n * sizeof (int));
  509         field [i].n = 0;
  510     }
  511 
  512     for (i = 0; i < n; i++)
  513         field [base_of (v [i])].v [field [base_of (v [i])].n++] = v [i];
  514 
  515     for (j = i = 0; i < NFIELDS; i++) {
  516         if (field [i].n > 1)
  517             qsort (field [i].v, 0, field [i].n - 1);
  518         for (k = 0; k < field [i].n; k++)
  519             v [j++] = field [i].v [k];
  520     }
  521 }
  522 // "ia.mod"
  523 //
  524 // Staring tables
  525 //
  526 
  527 class hyper_gets : public foreach_intNode
  528 {
  529     void A (intNode*);
  530    public:
  531     unsigned int nfiles, nfuncs;
  532     hyper_gets ();
  533 };
  534 
  535 unsigned int *Files, *funcs, *entries, nentries, *globals, nglobals;
  536 
  537 static bool has_callers (hyperNode *h)
  538 {
  539     int i;
  540     for (i = 0; i < h->itree.cnt; i++)
  541         if (base_of (h->rel [i]) == CALLER_ID
  542         // recursive callers of self, do not count
  543         && symbol_of (h->rel [i]) != symbol_of (h->Key))
  544             return true;
  545     return false;
  546 }
  547 
  548 void hyper_gets::A (intNode *i)
  549 {
  550     hyperNode *h = (hyperNode*) i;
  551 
  552     switch (base_of (h->Key)) {
  553         case SOURCE_ID: Files [nfiles++] = h->Key; break;
  554         case GLOBAL_ID: globals [nglobals++] = h->Key; break;
  555         case CALL_ID: funcs [nfuncs++] = h->Key;
  556             if (!has_callers (h)) nentries++; break;
  557     }
  558 }
  559 
  560 hyper_gets::hyper_gets ()
  561 {
  562     nglobals = nfiles = nfuncs = nentries = 0;
  563     inorder (hyperTree.root);
  564     counts [CALL_ID] = nfuncs;
  565 }
  566 
  567 void starting_tables ()
  568 {
  569     unsigned int i, j;
  570     Files = new unsigned int [counts [SOURCE_ID]];
  571     funcs = new unsigned int [counts [CALL_ID]];
  572     globals = new unsigned int [counts [GLOBAL_ID]];
  573     hyper_gets H;
  574     SHUT_UP_GCC (H);
  575     entries = new unsigned int [nentries];
  576     for (i = j = 0; i < counts [CALL_ID]; i++)
  577         if (!has_callers ((hyperNode*) hyperTree.intFind (funcs [i])))
  578             entries [j++] = funcs [i];
  579     qsort (Files, 0, counts [SOURCE_ID] - 1);
  580     qsort (funcs, 0, counts [CALL_ID] - 1);
  581     qsort (globals, 0, counts [GLOBAL_ID] - 1);
  582     qsort (entries, 0, nentries - 1);
  583 }
  584 // "pf.mod"
  585 //
  586 // Parse File
  587 //
  588 
  589 void linenumber (char *s)
  590 {
  591     char *f;
  592     if (!(f = strchr (s, ' ')))
  593         return;
  594     *f++ = 0;
  595     enter_line (enter_symbol (s) + CALL_BASE, atoi (f), atoi (strchr (f, ' ')));
  596 }
  597 
  598 #define FMT_CALLER  'D'
  599 #define FMT_CALL    'F'
  600 #define FMT_SOURCE  'P'
  601 #define FMT_GLOBAL  'g'
  602 #define FMT_GLOBALw 'G'
  603 #define FMT_STRUCT  's'
  604 #define FMT_STRUCTw 'S'
  605 #define FMT_LINE    'L'
  606 #define FMT_SLINE   'Y'
  607 #define FMT_REPL    'R'
  608 
  609 static bool wrt;
  610 
  611 unsigned int parse_line (char *l)
  612 {
  613     int base;
  614     char tmp [1000];
  615 
  616     wrt = false;
  617 
  618     switch (l [0]) {
  619         case FMT_LINE:   base = CALL_BASE;
  620                  linenumber (l+3);  break;
  621         case FMT_CALLER: base = CALLER_BASE;    break;
  622         case FMT_CALL:   base = CALL_BASE;
  623                  if (l[3] == '*')
  624                  struct_fp_link (l+3);  break;
  625         case FMT_SOURCE: base = SOURCE_BASE;
  626                  if (!strncmp (CWD, l + 3, strlen (CWD)))
  627                  strcpy (l + 3, strcpy (tmp, l + 3 + strlen (CWD)));
  628                             break;
  629         case FMT_GLOBALw: wrt = true;
  630         case FMT_GLOBAL: base = GLOBAL_BASE;    break;
  631         case FMT_STRUCTw: wrt = true;
  632         case FMT_STRUCT: base = MEMBER_BASE;
  633                  struct_link (l+3); break;
  634         case FMT_SLINE:  base = STRUCT_BASE;
  635                  struct_loc (l + 3);    break;
  636         case FMT_REPL:   repl_line (l + 3); return ~0;
  637         default: return ~0;
  638     }
  639 
  640     return base + enter_symbol (l + 3);
  641 }
  642 
  643 #define MLINE 512
  644 void parse_file (FILE *f)
  645 {
  646 static  int dfunc = 0;
  647     int h;
  648     char line [MLINE];
  649 
  650 
  651     while (fgets (line, MLINE, f)) {
  652         line [strlen (line) - 1] = 0;
  653         if ((h = parse_line (line)) == ~0) continue;
  654         if (base_of (h) == CALLER_ID || base_of (h) == SOURCE_ID)
  655             dfunc = h;
  656         else relative (wrt ? dfunc - CALLER_BASE + CALL_BASE : dfunc, h);
  657     }
  658 }
  659 
  660 void read_file (char *p)
  661 {
  662     FILE *f = fopen (p, "r");
  663 
  664     if (!f) {
  665         printf ("No such file [%s]\n", p);
  666         exit (1);
  667     }
  668     parse_file (f);
  669     fclose (f);
  670     if (!hyperTree.root) {
  671         printf ("File [%s] empty\n", p);
  672         exit (0);
  673     }
  674     aprogress ();
  675     make_symbol_table ();
  676     aprogress ();
  677     do_replacements ();
  678     aprogress ();
  679     settle_hyperNodes ();
  680     aprogress ();
  681     starting_tables ();
  682 }
  683 //
  684 // Get Relations of Link
  685 //
  686 
  687 struct linker {
  688     unsigned int tn, *t;
  689     linker (unsigned int, bool = true);
  690 };
  691 
  692 linker::linker (unsigned int i, bool dosort)
  693 {
  694     hyperNode *h = (hyperNode*) hyperTree.intFind (i);
  695     if (dosort) sort_fields (t = h->rel, tn = h->itree.cnt);
  696     else t = h->rel, tn = h->itree.cnt;
  697 }
  698 // "recur.mod"
  699 //
  700 // Recursion detection
  701 //
  702 
  703 class recursion_detector
  704 {
  705     unsigned int caller;
  706     bool check_in (unsigned int);
  707    public:
  708     recursion_detector (unsigned int, unsigned int);
  709     unsigned int *visited;
  710     bool indeed;
  711     ~recursion_detector ();
  712 };
  713 
  714 bool recursion_detector::check_in (unsigned int c)
  715 {
  716     hyperNode *h = (hyperNode*) hyperTree.intFind (c);
  717     unsigned int cs = symbol_of (c);
  718     unsigned int *t = h->rel;
  719     unsigned int tn = h->itree.cnt;
  720     unsigned int i;
  721 
  722     for (i = 0; i < tn; i++)
  723         if (base_of (t [i]) == CALL_ID) {
  724             unsigned int s = symbol_of (t [i]);
  725             if (s == caller) {
  726                 visited [caller] = cs;
  727                 return true;
  728             } else if (!visited [s]) {
  729                 visited [s] = cs;
  730                 if (check_in (t [i])) return true;
  731             }
  732         }
  733     return false;
  734 }
  735 
  736 recursion_detector::recursion_detector (unsigned int ca, unsigned ch)
  737 {
  738     visited = (unsigned int*) calloc (nsym, sizeof (int));
  739     caller = symbol_of (ca);
  740     visited [symbol_of (ch)] = caller;
  741 
  742     indeed = check_in (ch);
  743 }
  744 
  745 recursion_detector::~recursion_detector ()
  746 {
  747     free (visited);
  748 }
  749 
  750 bool is_recursive (unsigned int caller, unsigned int child)
  751 {
  752     recursion_detector RD (caller, child);
  753     return RD.indeed;
  754 }
  755 //
  756 // make recursion path
  757 //
  758 
  759 struct recursion_path
  760 {
  761     unsigned int *path;
  762     unsigned int n;
  763     recursion_path (unsigned int, unsigned int);
  764     ~recursion_path ();
  765 };
  766 
  767 recursion_path::recursion_path (unsigned int caller, unsigned int child)
  768 {
  769     recursion_detector RD (caller, child);
  770 
  771     if (RD.indeed) {
  772         unsigned int cs = symbol_of (caller);
  773         unsigned int i, cnt;
  774 
  775         for (cnt = 2, i = RD.visited [cs]; i != cs; i = RD.visited [i])
  776             ++cnt;
  777         n = cnt;
  778         path = (unsigned int*) malloc (sizeof (int) * cnt);
  779         path [n-1] = cs + CALL_BASE;
  780         for (cnt = 1, i = RD.visited [cs]; i != cs; i = RD.visited [i])
  781             path [n-1-cnt++] = i + CALL_BASE;
  782         path [n-1-cnt++] = i + CALLER_BASE;
  783         n = cnt;
  784     } else path = 0;
  785 }
  786 
  787 recursion_path::~recursion_path ()
  788 {
  789     if (path) free (path);
  790 }
  791 // "grf.mod"
  792 //
  793 // Graphics
  794 //
  795 
  796 unsigned int scr_x, scr_y;
  797 
  798 enum APP_COLOR {
  799     BLACK, HIGHLIGHT, C_UP1, C_UP2, C_UP3, NORMAL, C_DOWN1,
  800     C_DOWN2, C_DOWN3, C_DOWN4, OTHER
  801 };
  802 
  803 enum ecol {
  804     WHITE, BLUE, RED, GREEN, YELLOW, CYAN, MAGENTA
  805 };
  806 
  807 int Gcolor (APP_COLOR a)
  808 {
  809     switch (a) {
  810     case HIGHLIGHT: return COLOR_PAIR(WHITE)|A_BOLD;
  811     case C_UP1: return COLOR_PAIR (BLUE);
  812     case C_UP2: return COLOR_PAIR (BLUE)|A_BOLD;
  813     case C_UP3: return COLOR_PAIR (RED);
  814     case NORMAL:    return COLOR_PAIR(WHITE);
  815     case C_DOWN1:   return COLOR_PAIR(YELLOW)|A_DIM;
  816     case C_DOWN2:   return COLOR_PAIR(WHITE)|A_DIM;
  817     case C_DOWN3:   return COLOR_PAIR (CYAN);
  818     case C_DOWN4:   return COLOR_PAIR(WHITE)|A_DIM;
  819     default:
  820     case OTHER: return COLOR_PAIR(MAGENTA);
  821     }
  822 }
  823 
  824 void printstr (int x, int y, char *s, APP_COLOR a)
  825 {
  826     attrset (Gcolor (a));
  827     move (y, x);
  828     /* unfortunatelly, there's a bug in ncurses and
  829        addnstr (s, -1); counts tabs as 1 character
  830        with the result that long lines go past the end
  831        of the scr_x and appear on the next line
  832      */
  833     unsigned int i;
  834     char *p;
  835     for (i = x, p = s; i < scr_x && *p; p++)
  836         if (*p == '\t') i += 8;
  837         else i++;
  838     if (!*p) printw ("%s", s);
  839     else {
  840         char tmp [256];
  841         strncpy (tmp, s, p - s);
  842         tmp [p - s] = 0;
  843         printw ("%s", tmp);
  844     }
  845 }
  846 
  847 void cls ()
  848 {
  849     clear ();
  850 }
  851 
  852 int inkey ()
  853 {
  854     return getch ();
  855 }
  856 
  857 // Init modes
  858 
  859 void init_ncurses ()
  860 {
  861     initscr ();
  862     //leaveok (stdscr, true);
  863     start_color ();
  864     init_pair (BLUE, COLOR_BLUE, COLOR_BLACK);
  865     init_pair (RED, COLOR_RED, COLOR_BLACK);
  866     init_pair (GREEN, COLOR_GREEN, COLOR_BLACK);
  867     init_pair (YELLOW, COLOR_YELLOW, COLOR_BLACK);
  868     init_pair (CYAN, COLOR_CYAN, COLOR_BLACK);
  869     init_pair (MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
  870     crmode ();
  871     keypad (stdscr, TRUE);
  872     noecho ();
  873     scr_x = COLS;
  874     scr_y = LINES-1;
  875 }
  876 
  877 void init_graphics ()
  878 {
  879     init_ncurses ();
  880 }
  881 
  882 void end_graphics ()
  883 {
  884     flash ();
  885     endwin ();
  886 }
  887 //
  888 // Text entry display
  889 //
  890 
  891 static bool has_calls (unsigned int i)
  892 {
  893     hyperNode *h = (hyperNode*) hyperTree.intFind (i);
  894     unsigned int *a = h->rel, j = h->itree.cnt;
  895 
  896     for (i = 0; i < j; i++)
  897         if (base_of (a [i]) == CALL_ID) return true;
  898     return false;
  899 }
  900 
  901 static bool has_file (unsigned int i)
  902 {
  903     hyperNode *h = (hyperNode*) hyperTree.intFind (i);
  904     unsigned int *a = h->rel, j = h->itree.cnt;
  905 
  906     for (i = 0; i < j; i++)
  907         if (base_of (a [i]) == SOURCE_ID) return true;
  908     return false;
  909 }
  910 
  911 APP_COLOR colors (unsigned int v)
  912 {
  913     switch (base_of (v)) {
  914     case SOURCE_ID: return C_UP1;
  915     case CALLER_ID: return C_UP2;
  916     case MEMBER_ID: return C_DOWN4;
  917     case GLOBAL_ID: return C_UP3;
  918     case NFIELDS:   return OTHER;
  919     case CALL_ID:   return !has_file (v) ? C_DOWN3 :
  920                    has_calls (v) ? C_DOWN1 : C_DOWN2;
  921     case RECURS_BOOST: return C_UP3;
  922     }
  923     return OTHER;
  924 }
  925 
  926 void printent (int x, int y, unsigned int e, unsigned int m, bool hi)
  927 {
  928     char trunc [180], *dot;
  929     char *s = symbol_name (e);
  930 
  931     if (strlen (s) > m)
  932         if (strchr (s, '/'))
  933             s = strcpy (trunc, strrchr (s, '/'));
  934         else
  935             s = strcpy (trunc, s + strlen (s) - m);
  936     printstr (x, y, s, hi ? HIGHLIGHT : colors (e));
  937     if ((dot = strchr (s, '.')))
  938         printstr (x + (dot - s), y, ".", NORMAL);
  939 }
  940 // "vscr.mod"
  941 //
  942 // a screenful
  943 //
  944 
  945 class nvscr {
  946    public:
  947 static  int jump_node;
  948     int ii;
  949     nvscr *next, *prev;
  950 virtual void vdraw () = 0;
  951 } *nvtop;
  952 
  953 int nvscr::jump_node;
  954 
  955 // "tdm.mod"
  956 //
  957 // Map with ncurses
  958 //
  959 
  960 struct coords {
  961     unsigned int x, y;
  962 };
  963 
  964 class TDmap : public nvscr {
  965     unsigned int epl, eps, e_spc;
  966     unsigned int *vmap, e_tot, e_cur, e_top, e_topp;
  967     coords locate (unsigned int);
  968     void add_to_list ();
  969    public:
  970     TDmap (unsigned int*, unsigned int, unsigned int, unsigned int=0);
  971     void move_arrow (int);
  972     void move_jump (unsigned int);
  973     void vdraw ()   { draw_map (); }
  974     void draw_map ();
  975     unsigned int enter ();
  976     unsigned int cursor ();
  977 virtual ~TDmap ();
  978 };
  979 
  980 void TDmap::add_to_list ()
  981 {
  982     if ((prev = nvtop)) {
  983         prev->next = this;
  984         ii = prev->ii + 1;
  985     } else ii = 0;
  986     next = 0;
  987     nvtop = this;
  988     jump_node = ii;
  989 }
  990 
  991 TDmap::~TDmap ()
  992 {
  993     if ((nvtop = prev))
  994         nvtop->next = 0;
  995 }
  996 
  997 unsigned int TDmap::enter ()
  998 {
  999     if (base_of (vmap [e_cur]) != RECURS_BOOST)
 1000         return vmap [e_cur];
 1001     return symbol_of (vmap [e_cur]) + CALL_BASE;
 1002 }
 1003 
 1004 unsigned int TDmap::cursor ()
 1005 {
 1006     return e_cur;
 1007 }
 1008 
 1009 void TDmap::move_arrow (int a)
 1010 {
 1011     unsigned int dir = 0, mov;
 1012     switch (a) {
 1013         case KEY_LEFT:  mov = 1, dir = 1; break;
 1014         case KEY_RIGHT: mov = 1; break;
 1015         case KEY_UP:    mov = epl, dir = 1; break;
 1016         case KEY_DOWN:  mov = epl; break;
 1017         case KEY_PPAGE: mov = eps, dir = 1; break;
 1018         case KEY_NPAGE: mov = eps; break;
 1019         default:    return;
 1020     }
 1021 
 1022     if (!dir) {
 1023         if (e_cur+mov < e_tot) e_cur += mov;
 1024         else if (mov == eps)
 1025             while (e_cur + epl < e_tot) e_cur += epl;
 1026         if (e_cur >= e_top + eps) e_top += eps;
 1027     } else {
 1028         e_cur = (e_cur >= mov) ? e_cur - mov : e_cur % epl;
 1029         if (e_cur < e_top) e_top = (e_top > eps) ? e_top - eps : 0;
 1030     }
 1031 }
 1032 
 1033 void TDmap::move_jump (unsigned int i)
 1034 {
 1035     e_cur = i;
 1036     e_top = (i/eps) * eps;
 1037 }
 1038 
 1039 coords TDmap::locate (unsigned int i)
 1040 {
 1041     coords r;
 1042     r.x = e_spc * (i % epl);
 1043     r.y = (i - e_top) / epl;
 1044     return r;
 1045 }
 1046 
 1047 void TDmap::draw_map ()
 1048 {
 1049     unsigned int i;
 1050     coords xy;
 1051 
 1052     if (e_topp != e_top) {
 1053         e_topp = e_top;
 1054         cls ();
 1055     }
 1056     for (i = e_top; i < e_top+eps && i < e_tot; i++) {
 1057         xy = locate (i);
 1058         printent (xy.x, xy.y, vmap [i], e_spc-1, false);
 1059     }
 1060     xy = locate (e_cur);
 1061     printent (xy.x, xy.y, vmap [e_cur], e_spc-1, true);
 1062 }
 1063 
 1064 unsigned int fieldlen (unsigned int *v, unsigned int n)
 1065 {
 1066     unsigned int i, ml;
 1067 
 1068     for (ml = i = 0; i < n; i++)
 1069         if (strlen (symbol_name (v [i])) > ml)
 1070             ml = strlen (symbol_name (v [i]));
 1071 
 1072     return (ml > scr_x/2) ? scr_x/2 : (ml < 15) ? 15 : ml+1;
 1073 }
 1074 
 1075 TDmap::TDmap (unsigned int *v, unsigned int n, unsigned int c, unsigned int ml)
 1076 {
 1077     if (!ml) ml = fieldlen (v, n);
 1078     e_spc = scr_x/(scr_x/ml);
 1079     vmap = v;
 1080     e_topp = e_tot = n;
 1081     epl = scr_x / e_spc;
 1082     eps = scr_y * epl;
 1083     move_jump (e_cur = c);
 1084     add_to_list ();
 1085 }
 1086 // "tv.mod"
 1087 //
 1088 // text Viewer
 1089 //
 1090 
 1091 #ifndef SOURCE_VIEWER
 1092 
 1093 class txtviewer
 1094 {
 1095     char **line;
 1096     unsigned int nlines, maxline, topline, tab;
 1097     void draw ();
 1098     void scrolll (int);
 1099     void main ();
 1100    public:
 1101     txtviewer (char*, char*, unsigned int);
 1102 };
 1103 
 1104 void txtviewer::draw ()
 1105 {
 1106     unsigned int i;
 1107     cls ();
 1108     for (i = topline; i < nlines && i < topline+scr_y; i++) {
 1109         if (strlen (line [i]) <= tab) continue;
 1110         printstr (0, i-topline, line [i]+tab, NORMAL);
 1111     }
 1112 }
 1113 
 1114 void txtviewer::scrolll (int key)
 1115 {
 1116     unsigned int tlp = topline, tbp = tab;
 1117     switch (key) {
 1118     case KEY_UP:
 1119         if (topline > 0) topline--;
 1120         break;
 1121     case KEY_DOWN:
 1122         if (topline + scr_y < nlines) topline++;
 1123         break;
 1124     case KEY_LEFT:
 1125         if (tab) tab--;
 1126         break;
 1127     case KEY_RIGHT:
 1128         if (maxline - tab > scr_x/2) tab++;
 1129         break;
 1130     case KEY_PPAGE:
 1131         if (topline) topline = (topline > scr_y) ? topline - scr_y : 0;
 1132         break;
 1133     case KEY_NPAGE:
 1134         if (topline + scr_y + 1 < nlines)
 1135             topline += scr_y;
 1136         break;
 1137     default:
 1138         return;
 1139     }
 1140     if (tlp != topline || tbp != tab) draw ();
 1141 }
 1142 
 1143 void txtviewer::main ()
 1144 {
 1145     int key;
 1146     while ((key = inkey ()) != '\n' && key != ' ')
 1147         scrolll (key);
 1148 }
 1149 
 1150 txtviewer::txtviewer (char *title, char *txt, unsigned int l)
 1151 {
 1152     nlines = l+1;
 1153     line = (char**) alloca (nlines * sizeof (char*));
 1154     maxline = l = 0;
 1155     line [l++] = title;
 1156     do {
 1157         line [l] = txt;
 1158         if ((txt = strchr (txt, '\n')))
 1159             *txt++ = 0;
 1160         if (strlen (line [l]) > maxline)
 1161             maxline = strlen (line [l]);
 1162     } while (++l < nlines);
 1163     topline = tab = 0;
 1164 
 1165     draw ();
 1166     main ();
 1167 }
 1168 
 1169 #else       /* SOURCE_VIEWER exists */
 1170 
 1171 void source_viewer_fatal ()
 1172 {
 1173     endwin ();
 1174     printf ("ALERT! Failed executing [%s] to display the text.\n"
 1175         " Please recompile nccnav with a different SOURCE_VIEWER value.\n", svr);
 1176     exit (1);
 1177 }
 1178 
 1179 void txtviewer (char *title, char *txt, unsigned int l, bool colored)
 1180 {
 1181     def_prog_mode();        /* Save the tty modes         */
 1182     endwin();           /* End curses mode temporarily    */
 1183     if (colored) {
 1184         unsigned int h = 0, *ip = (unsigned int*) title;
 1185         char *tmpc, cmd [100];
 1186         char tmpname [100];
 1187 
 1188         int i;
 1189         for (i = 0; i < strlen (title) / 4; i++)
 1190             h ^= *ip++;
 1191         sprintf (tmpname, "/tmp/nccnav-tmp%x.c", h);
 1192 
 1193         FILE *f = fopen (tmpc = tmpname, "w");
 1194         fputs (title, f); fputc ('\n', f);
 1195         fputs (txt, f);
 1196         fclose (f);
 1197         sprintf (cmd, COLOR_VIEWER" %s", tmpc);
 1198         system (cmd);
 1199         unlink (tmpc);
 1200     } else {
 1201         FILE *p = popen (svr, "w");
 1202         if (!p) source_viewer_fatal ();
 1203         fputs (title, p); fputc ('\n', p);
 1204         fputs (txt, p);
 1205         fflush (p);
 1206         pclose (p);
 1207     }
 1208     reset_prog_mode();      /* Return to the previous tty mode*/
 1209                     /* stored by def_prog_mode()      */
 1210     refresh();          /* Do refresh() to restore the    */
 1211                     /* Screen contents        */
 1212 }
 1213 
 1214 #endif
 1215 // "vf.mod"
 1216 //
 1217 // Extract text from file
 1218 //
 1219 
 1220 char *text_from (char *f, int l1, int l2)
 1221 {
 1222     load_file L (f);
 1223 
 1224     if (L.success != ZC_OK) return NULL;
 1225 
 1226     int i, cl;
 1227     char *r;
 1228 
 1229     for (i = 0, cl = 1; i <= L.len && cl < l1; i++)
 1230         if (L.data [i] == '\n') cl++;
 1231     if (cl < l1) return NULL;
 1232     l1 = i;
 1233     for (;i < L.len && cl <= l2; i++)
 1234         if (L.data [i] == '\n') cl++;
 1235     if (cl < l2) return NULL;
 1236     l2 = i;
 1237 
 1238     for (i = l1; i && !isspace (L.data [i]); --i);
 1239     l1 = 0;
 1240     for (; i; i--)
 1241         if (L.data [i] == '/' && L.data [i - 1] == '*')
 1242             for (i -= 2; i && (L.data [i] != '/' || L.data [i + 1] != '*'); --i);
 1243         else if (isspace (L.data [i])) continue;
 1244         else {
 1245             cl = i;
 1246             while (i && L.data [i] != '\n') --i;
 1247             for (i += !!i; isspace (L.data [i]); ++i);
 1248             if (L.data [i] != '/' || L.data [i + 1] != '/') {
 1249                 for (l1 = cl + 1; isspace (L.data [l1]); l1++);
 1250                 break;
 1251             }
 1252         }
 1253 
 1254     r = new char [1 + l2 - l1];
 1255     memcpy (r, L.data + l1, l2 - l1);
 1256     r [l2 - l1] = 0;
 1257     return r;
 1258 }
 1259 
 1260 void func_text (unsigned int f, bool color=false)
 1261 {
 1262     char *txt, *title, *fn;
 1263     file_chunk F = line_of (f);
 1264 
 1265     if (F.start == ~0U) return;
 1266     linker L (f);
 1267     fn = symbol_name (L.t [0]);
 1268     txt = text_from (fn, F.start, F.end);
 1269     if (txt) {
 1270         title = (char*) alloca (strlen (fn) + 20);
 1271         sprintf (title, "# %s %u", fn, F.start);
 1272         txtviewer (title, txt, F.end - F.start + 1, color);
 1273         delete [] txt;
 1274     }
 1275 }
 1276 // "file"
 1277 //
 1278 // view entire file
 1279 //
 1280 
 1281 void file_text (unsigned int i, bool color)
 1282 {
 1283 #ifdef SOURCE_VIEWER
 1284     def_prog_mode();        /* Save the tty modes         */
 1285     endwin();           /* End curses mode temporarily    */
 1286     if (color) {
 1287         char tmp [1024];
 1288         strcat (strcpy (tmp, COLOR_VIEWER" "), symbol_name (i));
 1289         system (tmp);
 1290     } else {
 1291         load_file L (symbol_name (i));
 1292 
 1293         if (L.success != ZC_OK) return;
 1294 
 1295         FILE *p = popen (svr, "w");
 1296         if (!p) source_viewer_fatal ();
 1297         fwrite (L.data, 1, L.len, p);
 1298         fflush (p);
 1299         pclose (p);
 1300     }
 1301     reset_prog_mode();      /* Return to the previous tty mode*/
 1302                     /* stored by def_prog_mode()      */
 1303     refresh();          /* Do refresh() to restore the    */
 1304                     /* Screen contents        */
 1305 #endif
 1306 }
 1307 // "pd.mod"
 1308 //
 1309 // pop ups
 1310 //
 1311 
 1312 void pastmode ();
 1313 void enter_othermode (unsigned int);
 1314 class FOO {};
 1315 
 1316 class popup : public nvscr {
 1317     popup *outer;
 1318     unsigned int *ent, nent;
 1319     WINDOW *w;
 1320     unsigned int top, cur, mx, my, dh, dw;
 1321     void add_to_list ();
 1322     void prefresh ();
 1323     void draw ();
 1324     void main ();
 1325    public:
 1326     popup (popup*, unsigned int, unsigned int, unsigned int);
 1327     void vdraw ();
 1328 virtual ~popup ();
 1329 };
 1330 
 1331 void popup::add_to_list ()
 1332 {
 1333     next = 0;
 1334     prev = (outer) ? outer->prev : nvtop;
 1335     prev->next = nvtop = this;
 1336     jump_node = ii = prev->ii + 1;
 1337 }
 1338 
 1339 void popup::prefresh ()
 1340 {
 1341     if (outer) outer->prefresh ();
 1342     else {
 1343         touchwin (stdscr);
 1344         wnoutrefresh (stdscr);
 1345     }
 1346     touchwin (w);
 1347     wnoutrefresh (w);
 1348 }
 1349 
 1350 #define CLEARBOX \
 1351     wclear (w);\
 1352     box (w, 0, 0);
 1353 
 1354 void popup::draw ()
 1355 {
 1356     unsigned int i;
 1357     for (i = 0; i < dh; i++) {
 1358         wattrset (w, Gcolor (i ? colors (ent [top + i]) : C_UP3));
 1359         mvwprintw (w, i, 1, "%s", symbol_name (ent [top + i]));
 1360     }
 1361     wmove (w, cur - top, 1);
 1362     wattrset (w, Gcolor (HIGHLIGHT));
 1363     wprintw (w, "%s", symbol_name (ent [cur]));
 1364     touchwin (w);
 1365     wrefresh (w);
 1366 }
 1367 
 1368 void popup::vdraw ()
 1369 {
 1370     prefresh ();
 1371     doupdate ();
 1372 }
 1373 
 1374 void popup::main ()
 1375 {
 1376     draw ();
 1377     prefresh ();
 1378     for (; ii <= jump_node; draw()) switch (inkey ()) {
 1379         case KEY_DOWN: if (cur < nent - 1)
 1380                 if (++cur == top + dh) {
 1381                     top++;
 1382                     CLEARBOX;
 1383                 }
 1384                 break;
 1385         case KEY_UP: if (cur > 0)
 1386                 if (--cur < top) {
 1387                     top--;
 1388                     CLEARBOX
 1389                 }
 1390                 break;
 1391         case KEY_PPAGE: cur = top = 0;
 1392                 CLEARBOX
 1393                 break;
 1394         case KEY_NPAGE: if (cur < nent - 1) {
 1395                 if ((cur += 6) >= nent) cur = nent - 1;
 1396                 if (cur >= top + dh) {
 1397                     top = cur - dh;
 1398                     CLEARBOX
 1399                 } }
 1400                 break;
 1401         case KEY_BACKSPACE: throw FOO ();
 1402         case KEY_LEFT:
 1403         case 'q': return;
 1404         case KEY_RIGHT: if (cur == 0) break;
 1405         case '\n': if (!cur) return;
 1406             { popup (this, ent [cur], mx + dw - 4, my + cur - top); }
 1407             vdraw ();
 1408             break;
 1409         case 'v':
 1410             func_text (ent [cur], true);
 1411             break;
 1412         case ' ':
 1413             func_text (ent [cur]);
 1414             cls ();
 1415             vdraw ();
 1416             break;
 1417         case '2': enter_othermode (ent [cur]);
 1418             cls ();
 1419             vdraw ();
 1420             break;
 1421         case '<':
 1422             pastmode ();
 1423             cls ();
 1424             vdraw ();
 1425     }
 1426 }
 1427 
 1428 popup::popup (popup *o, unsigned int f, unsigned int x, unsigned int y)
 1429 {
 1430     linker L (f);
 1431     unsigned int i, j;
 1432 
 1433     for (i = j = 0; i < L.tn; i++)
 1434         if (base_of (L.t [i]) == CALL_ID) j++;
 1435 
 1436     ent = 0;
 1437     if (!j) return;
 1438 
 1439     ent = (unsigned int*) malloc (sizeof (unsigned int) * (nent = j + 1));
 1440     ent [0] = f;
 1441     for (i = 0, j = 1; i < L.tn; i++)
 1442         if (base_of (L.t [i]) == CALL_ID)
 1443             ent [j++] = L.t [i];
 1444 
 1445     if (nent >= scr_y) {
 1446         dh = scr_y - 1;
 1447         my = 0;
 1448     } else {
 1449         dh = nent;
 1450         my = (y + dh >= scr_y) ? 0 : y;
 1451     }
 1452 
 1453     for (i = j = 0; i < nent; i++)
 1454         if ((f = strlen (symbol_name (ent [i]))) > j) j = f;
 1455     dw = j + 2;
 1456     mx = (x + dw >= scr_x) ? 0 : x;
 1457     top = cur = 0;
 1458 
 1459     outer = o;
 1460     w = newwin (dh + 1, dw, my, mx);
 1461     CLEARBOX;
 1462     add_to_list ();
 1463     main ();
 1464 }
 1465 
 1466 popup::~popup ()
 1467 {
 1468     if (ent) {
 1469         (nvtop = prev)->next = 0;
 1470         free (ent);
 1471         delwin (w);
 1472     }
 1473 }
 1474 // "mod.mod"
 1475 //
 1476 // modes
 1477 //
 1478 
 1479 void pastmode ()
 1480 {
 1481     char m [20];
 1482     nvscr *c = nvtop->prev;
 1483     for (;;) {
 1484         cls ();
 1485         sprintf (m, "%i/%i", c->ii, nvtop->ii);
 1486         printstr (0, scr_y, m, HIGHLIGHT);
 1487         c->vdraw ();
 1488         switch (inkey ()) {
 1489             case '<': if (c->prev) c = c->prev; break;
 1490             case '>': if (c->next) c = c->next; break;
 1491             case '\n': c->jump_node = c->ii; return;
 1492             default: nvtop->vdraw (); return;
 1493         }
 1494     }
 1495 }
 1496 
 1497 void othermode (unsigned int);
 1498 
 1499 void recursmode (recursion_path &RP)
 1500 {
 1501     int i;
 1502     TDmap M (RP.path, RP.n, 0, scr_x);
 1503 
 1504     M.draw_map ();
 1505     for (; M.ii <= M.jump_node; M.draw_map ()) {
 1506         printstr (0, scr_y, "Recursion unroll", C_UP2);
 1507         if ((i = inkey ()) == 'q')
 1508             break;
 1509         if (i == '\n') {
 1510             if (M.cursor () == 0) break;
 1511             othermode (M.enter ());
 1512             cls ();
 1513         } else M.move_arrow (i);
 1514     }
 1515 }
 1516 
 1517 void color_recursives (unsigned int caller, unsigned int *pa, int npa)
 1518 {
 1519     int i;
 1520     for (i = 0; i < npa; i++)
 1521         if (base_of (pa [i]) == CALL_ID && is_recursive (caller, pa [i]))
 1522             pa [i] = symbol_of (pa [i]) + RECURS_BASE;
 1523 }
 1524 
 1525 void othermode (unsigned int e)
 1526 {
 1527 static  bool rdetect = true;
 1528 
 1529     int key;
 1530     unsigned int i, j, c, *jarr;
 1531 
 1532     e = base_of (e) == CALLER_ID ? symbol_of (e) + CALL_BASE : e;
 1533     unsigned int tbase = base_of (e);
 1534     linker L (e);
 1535 
 1536     jarr = (unsigned int*) alloca ((L.tn + 1) * sizeof(int));
 1537     i = j = 0;
 1538     while (i < L.tn && base_of (L.t [i]) < CALL_ID)
 1539         jarr [j++] = L.t [i++];
 1540     jarr [c = j++] = e = symbol_of (e) + FIELD_BASE;
 1541     while (i < L.tn)
 1542         jarr [j++] = L.t [i++];
 1543 
 1544     if (rdetect && tbase == CALL_ID)
 1545         color_recursives (e, jarr + c + 1, i - c);
 1546 
 1547     TDmap M (jarr, j, c);
 1548 
 1549     M.draw_map ();
 1550     for (;M.ii <= M.jump_node; M.draw_map ()) {
 1551         if (tbase == CALL_ID && c >= 2 && base_of (jarr [1]) == SOURCE_ID) {
 1552             printstr (0, scr_y, "* FUNCTION IS       :MULTIPLE DEFINITIONS *", HIGHLIGHT);
 1553             printstr (13, scr_y,             " br0ken", C_UP2);
 1554         }
 1555         switch (key = inkey ()) {
 1556         case ' ':
 1557         case 'v':
 1558             j = base_of (M.enter ());
 1559             if (j == CALL_ID) func_text (M.enter (), key == 'v');
 1560             else if (j == CALLER_ID)
 1561                 func_text (symbol_of (M.enter ()) + CALL_BASE, key == 'v');
 1562             else if (j == NFIELDS && tbase == CALL_ID)
 1563                 func_text (symbol_of (M.enter ()) + CALL_BASE, key == 'v');
 1564             else if ((j == SOURCE_ID||(j == NFIELDS&&tbase == SOURCE_ID)))
 1565                 file_text (symbol_of (M.enter ()), key == 'v');
 1566             else if (j == STRUCT_ID || (j == NFIELDS && tbase == STRUCT_ID))
 1567                 func_text (symbol_of (M.enter ()) + STRUCT_BASE, key == 'v');
 1568             else break;
 1569             if (key != '1')
 1570                 cls ();
 1571             break;
 1572         case '\n':
 1573             if (M.enter () == e) return;
 1574             othermode (M.enter ());
 1575             cls ();
 1576             break;
 1577         case KEY_BACKSPACE:
 1578             cls ();
 1579             throw FOO();
 1580         case 'q':
 1581             cls ();
 1582             return;
 1583         case 'r':
 1584             if (base_of (M.enter ()) == CALL_ID) {
 1585                 recursion_path RP (e, M.enter ());
 1586                 if (RP.path) recursmode (RP);
 1587             }
 1588             cls ();
 1589             break;
 1590         case 'R':
 1591             if ((rdetect =! rdetect)) {
 1592                 printstr (0, scr_y, "Recursion Detector ENABLED.", OTHER);
 1593                 color_recursives (e, jarr + c + 1, i - c);
 1594             } else {
 1595                 printstr (0, scr_y, "Recursion Detector DISABLED", C_UP3);
 1596                 for (j = c; j < i+1; j++)
 1597                     if (base_of (jarr [j]) == RECURS_BOOST)
 1598                         jarr [j] -= RECURS_BASE - CALL_BASE;
 1599             }
 1600             break;
 1601         case 'm':
 1602             j = base_of (M.enter ());
 1603             if (j == CALL_ID || j == CALLER_ID || (j == NFIELDS && tbase == CALL_ID))
 1604                 try {
 1605                     j = j == CALL_ID ? M.enter () : symbol_of (M.enter ()) + CALL_BASE;
 1606                     cls ();
 1607                     popup P (0, j, 0, 0);
 1608                 } catch (FOO) { }
 1609             cls ();
 1610             break;
 1611         case '<':
 1612             pastmode ();
 1613             cls ();
 1614             break;
 1615         case 'C':
 1616             // end_graphics ();
 1617             def_prog_mode();        /* Save the tty modes         */
 1618             endwin();           /* End curses mode temporarily    */
 1619             system ("bash");
 1620             reset_prog_mode();      /* Return to the previous tty mode*/
 1621                             /* stored by def_prog_mode()      */
 1622             refresh();          /* Do refresh() to restore the    */
 1623                             /* Screen contents        */
 1624             // init_graphics ();
 1625             break;
 1626         default:
 1627             M.move_arrow (key);
 1628         }
 1629     }
 1630 }
 1631 
 1632 void enter_othermode (unsigned int e)
 1633 {
 1634     try {
 1635         othermode (e);
 1636     } catch (FOO) { }
 1637 }
 1638 
 1639 void globmode ()
 1640 {
 1641     unsigned int *t, tn;
 1642     t = globals, tn = counts [GLOBAL_ID];
 1643 
 1644     int key;
 1645     TDmap M (t, tn, 0);
 1646 
 1647     M.draw_map ();
 1648     for (;;M.draw_map ()) switch (key = inkey ()) {
 1649     case '\n':
 1650         enter_othermode (M.enter ());
 1651         cls ();
 1652         break;
 1653     case KEY_BACKSPACE:
 1654     case 'q':
 1655         return;
 1656     default:
 1657         if (key < 256 && isalpha (key)) {
 1658             unsigned int j = M.cursor ();
 1659             if (*symbol_name (t [j]) < key)
 1660                 while (j < tn
 1661                 && *symbol_name (t [j]) < key)
 1662                     j++;
 1663             else
 1664                 while (j > 0
 1665                 && *symbol_name (t [j]) > key)
 1666                     j--;
 1667             M.move_jump (j);
 1668         } else M.move_arrow (key);
 1669     }
 1670 }
 1671 
 1672 void funcmode (bool all)
 1673 {
 1674     unsigned int *t, tn;
 1675     if (all) t = funcs, tn = counts [CALL_ID];
 1676     else t = entries, tn = nentries;
 1677 
 1678     if (!tn) {
 1679         flash ();
 1680         return;
 1681     }
 1682 
 1683     int key;
 1684     TDmap M (t, tn, 0);
 1685 
 1686     M.draw_map ();
 1687     for (;;M.draw_map ()) switch (key = inkey ()) {
 1688     case '\n':
 1689         enter_othermode (M.enter ());
 1690         cls ();
 1691         break;
 1692     case KEY_BACKSPACE:
 1693     case 'q':
 1694         return;
 1695     case 'v':
 1696         func_text (M.enter (), true);
 1697         cls ();
 1698         break;
 1699     case ' ':
 1700         func_text (M.enter ());
 1701         cls ();
 1702         break;
 1703     default:
 1704         if (key < 256 && isalpha (key)) {
 1705             unsigned int j = M.cursor ();
 1706             if (*symbol_name (t [j]) < key)
 1707                 while (j < tn
 1708                 && *symbol_name (t [j]) < key)
 1709                     j++;
 1710             else
 1711                 while (j > 0
 1712                 && *symbol_name (t [j]) > key)
 1713                     j--;
 1714             M.move_jump (j);
 1715         } else M.move_arrow (key);
 1716     }
 1717 }
 1718 
 1719 void initialmode ()
 1720 {
 1721     int key;
 1722     TDmap M (Files, counts [SOURCE_ID], 0);
 1723 
 1724     M.draw_map ();
 1725     for (;;M.draw_map ()) switch (key = inkey ()) {
 1726         case '\n':
 1727             enter_othermode (M.enter ());
 1728             cls ();
 1729             break;
 1730         case KEY_BACKSPACE:
 1731         case 'q':
 1732             return;
 1733         case 'E':
 1734             funcmode (false);
 1735             cls ();
 1736             break;
 1737         case 'O':
 1738             funcmode (true);
 1739             cls ();
 1740             break;
 1741         case 'g':
 1742         case 'G':
 1743             globmode ();
 1744             cls ();
 1745             break;
 1746         default:
 1747             M.move_arrow (key);
 1748     }
 1749 }
 1750 // "main"
 1751 //
 1752 
 1753 int main (int argc, char **argv)
 1754 {
 1755     getcwd (CWD, sizeof CWD-1);
 1756     if (CWD [strlen (CWD) - 1] != '/')
 1757         strcat (CWD, "/");
 1758 #ifdef  SOURCE_VIEWER
 1759     svr = (char*) ((!strcmp (argv [0], "nccnav")) ? SOURCE_VIEWER : SOURCE_VIEWER_INDENT);
 1760 #endif
 1761     char *MapFile = (argc > 1) ? argv [1] : (char*)"Code.map";
 1762     read_file (MapFile);
 1763     signal (SIGPIPE, SIG_IGN);
 1764     init_graphics ();
 1765     initialmode ();
 1766     end_graphics ();
 1767     printf ("%i\tFilez \n%i\tfunctionz \n%i\tglobal variables \n"
 1768         "%i\tstructs\n%i\tmembers \n%i\tTotal links\n",
 1769         counts [SOURCE_ID], counts [CALL_ID], counts [GLOBAL_ID],
 1770         counts [STRUCT_ID], counts [MEMBER_ID], nlinks - dupes/2);
 1771 }